1// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include <functional>
6
7#include "platform/assert.h"
8
9#include "vm/class_finalizer.h"
10#include "vm/compiler/backend/il_test_helper.h"
11#include "vm/symbols.h"
12#include "vm/type_testing_stubs.h"
13#include "vm/type_testing_stubs_test.h"
14#include "vm/unit_test.h"
15
16#if defined(TARGET_ARCH_ARM64) || defined(TARGET_ARCH_ARM) || \
17 defined(TARGET_ARCH_X64)
18
19namespace dart {
20
21static void FinalizeAndCanonicalize(const Class& klass, AbstractType* type) {
22 *type = ClassFinalizer::FinalizeType(klass, *type);
23 ASSERT(type->IsCanonical());
24}
25
26static void CanonicalizeTAV(TypeArguments* tav) {
27 *tav = tav->Canonicalize();
28}
29
30static void RunTTSTest(
31 const Object& instance,
32 const AbstractType& dst_type,
33 const TypeArguments& instantiator_tav,
34 const TypeArguments& function_tav,
35 std::function<void(const Object& result, const SubtypeTestCache& stc)> lazy,
36 std::function<void(const Object& result,
37 const SubtypeTestCache& stc,
38 const Bool& abi_regs_modified,
39 const Bool& rest_regs_modified)> nonlazy) {
40 ASSERT(instantiator_tav.IsNull() || instantiator_tav.IsCanonical());
41 ASSERT(function_tav.IsNull() || function_tav.IsCanonical());
42 auto thread = Thread::Current();
43
44 // Build a stub which will do calling conversion to call TTS stubs.
45 const auto& klass =
46 Class::Handle(thread->isolate()->class_table()->At(kInstanceCid));
47 const auto& symbol = String::Handle(
48 Symbols::New(thread, OS::SCreate(thread->zone(), "TTSTest")));
49 const auto& function = Function::Handle(
50 Function::New(symbol, FunctionLayout::kRegularFunction, false, false,
51 false, false, false, klass, TokenPosition::kNoSource));
52 compiler::ObjectPoolBuilder pool_builder;
53 const auto& invoke_tts = Code::Handle(
54 StubCode::Generate("InvokeTTS", &pool_builder, &GenerateInvokeTTSStub));
55 const auto& pool =
56 ObjectPool::Handle(ObjectPool::NewFromBuilder(pool_builder));
57 invoke_tts.set_object_pool(pool.raw());
58 invoke_tts.set_owner(function);
59 invoke_tts.set_exception_handlers(
60 ExceptionHandlers::Handle(ExceptionHandlers::New(0)));
61
62 EXPECT(pool.Length() == 2);
63 const intptr_t kSubtypeTestCacheIndex = 0;
64
65 const auto& arguments_descriptor =
66 Array::Handle(ArgumentsDescriptor::NewBoxed(0, 6));
67 const auto& arguments = Array::Handle(Array::New(6));
68 const auto& abi_regs_modified_box = Array::Handle(Array::New(1));
69 const auto& rest_regs_modified_box = Array::Handle(Array::New(1));
70 arguments.SetAt(0, abi_regs_modified_box);
71 arguments.SetAt(1, rest_regs_modified_box);
72 arguments.SetAt(2, instance);
73 arguments.SetAt(3, instantiator_tav);
74 arguments.SetAt(4, function_tav);
75 arguments.SetAt(5, dst_type);
76
77 // Ensure we have a) uninitialized TTS b) no/empty SubtypeTestCache.
78 dst_type.SetTypeTestingStub(StubCode::LazySpecializeTypeTest());
79 EXPECT(dst_type.type_test_stub() == StubCode::LazySpecializeTypeTest().raw());
80 EXPECT(pool.ObjectAt(kSubtypeTestCacheIndex) == Object::null());
81
82 auto& result = Object::Handle();
83 auto& result2 = Object::Handle();
84 auto& abi_regs_modified = Bool::Handle();
85 auto& rest_regs_modified = Bool::Handle();
86 auto& tts = Code::Handle();
87 auto& tts2 = Code::Handle();
88 auto& stc = SubtypeTestCache::Handle();
89 auto& stc2 = SubtypeTestCache::Handle();
90
91 // First invocation will a) specialize the TTS b) may create SubtypeTestCache
92 result = DartEntry::InvokeCode(invoke_tts, arguments_descriptor, arguments,
93 thread);
94 stc ^= pool.ObjectAt(kSubtypeTestCacheIndex);
95 tts = dst_type.type_test_stub();
96 if (!result.IsError()) {
97 EXPECT(tts.raw() != StubCode::LazySpecializeTypeTest().raw());
98 }
99 lazy(result, stc);
100
101 // Second invocation will a) keep TTS b) keep optional SubtypeTestCache
102 result2 = DartEntry::InvokeCode(invoke_tts, arguments_descriptor, arguments,
103 thread);
104 stc2 ^= pool.ObjectAt(kSubtypeTestCacheIndex);
105 tts2 = dst_type.type_test_stub();
106 abi_regs_modified ^= abi_regs_modified_box.At(0);
107 rest_regs_modified ^= rest_regs_modified_box.At(0);
108 EXPECT(result2.IsError() || !abi_regs_modified.IsNull());
109 EXPECT(tts2.raw() == tts.raw());
110 EXPECT(stc2.raw() == stc.raw());
111 nonlazy(result2, stc2, abi_regs_modified, rest_regs_modified);
112
113 // Third invocation will a) explicitly install TTS beforehand b) keep optional
114 // SubtypeTestCache
115 // (This is to simulate AOT where we don't use lazy specialization but
116 // precompile the TTS)
117 auto& dst_type_to_specialize = AbstractType::Handle(dst_type.raw());
118 if (dst_type_to_specialize.IsTypeParameter()) {
119 dst_type_to_specialize = TypeParameter::Cast(dst_type).GetFromTypeArguments(
120 instantiator_tav, function_tav);
121 }
122 TypeTestingStubGenerator::SpecializeStubFor(thread, dst_type_to_specialize);
123 tts = dst_type.type_test_stub();
124
125 result2 = DartEntry::InvokeCode(invoke_tts, arguments_descriptor, arguments,
126 thread);
127 stc2 ^= pool.ObjectAt(kSubtypeTestCacheIndex);
128 tts2 = dst_type.type_test_stub();
129 abi_regs_modified ^= abi_regs_modified_box.At(0);
130 rest_regs_modified ^= rest_regs_modified_box.At(0);
131 EXPECT(result2.IsError() || !abi_regs_modified.IsNull());
132 EXPECT(tts2.raw() == tts.raw());
133 EXPECT(stc2.raw() == stc.raw());
134 nonlazy(result2, stc2, abi_regs_modified, rest_regs_modified);
135}
136
137static void ExpectLazilyHandledViaTTS(const Object& result,
138 const SubtypeTestCache& stc) {
139 // Ensure the type test succeeded.
140 EXPECT(result.IsNull());
141 // Ensure we didn't fall back to the subtype test cache.
142 EXPECT(stc.IsNull());
143}
144
145static void ExpectHandledViaTTS(const Object& result,
146 const SubtypeTestCache& stc,
147 const Bool& abi_regs_modified,
148 const Bool& rest_regs_modified) {
149 ExpectLazilyHandledViaTTS(result, stc);
150 // Ensure the TTS abi registers were preserved.
151 EXPECT(!abi_regs_modified.value());
152 // Ensure the non-TTS abi registers were preserved.
153 EXPECT(!rest_regs_modified.value());
154}
155
156static void ExpectLazilyHandledViaSTC(const Object& result,
157 const SubtypeTestCache& stc) {
158 // Ensure the type test succeeded.
159 EXPECT(result.IsNull());
160 // Ensure we did fall back to the subtype test cache.
161 EXPECT(!stc.IsNull());
162 // Ensure the test is marked as succeeding in the STC.
163 EXPECT(stc.NumberOfChecks() == 1);
164 SubtypeTestCacheTable entries(Array::Handle(stc.cache()));
165 EXPECT(entries[0].Get<SubtypeTestCache::kTestResult>() ==
166 Object::bool_true().raw());
167}
168
169static void ExpectHandledViaSTC(const Object& result,
170 const SubtypeTestCache& stc,
171 const Bool& abi_regs_modified,
172 const Bool& rest_regs_modified) {
173 ExpectLazilyHandledViaSTC(result, stc);
174 // Ensure the TTS/STC abi registers were preserved.
175 EXPECT(!abi_regs_modified.value());
176}
177
178static void ExpectLazilyFailedViaTTS(const Object& result,
179 const SubtypeTestCache& stc) {
180 // Ensure we have not updated STC (which we shouldn't do in case the type test
181 // fails, i.e. an exception is thrown).
182 EXPECT(stc.IsNull());
183 // Ensure we get a proper exception for the type test.
184 EXPECT(result.IsUnhandledException());
185 const auto& error =
186 Instance::Handle(UnhandledException::Cast(result).exception());
187 EXPECT(strstr(error.ToCString(), "_TypeError"));
188}
189
190static void ExpectFailedViaTTS(const Object& result,
191 const SubtypeTestCache& stc,
192 const Bool& abi_regs_modified,
193 const Bool& rest_regs_modified) {
194 ExpectLazilyFailedViaTTS(result, stc);
195}
196
197static void ExpectLazilyFailedViaSTC(const Object& result,
198 const SubtypeTestCache& stc) {
199 // Ensure we have not updated STC (which we shouldn't do in case the type test
200 // fails, i.e. an exception is thrown).
201 EXPECT(stc.IsNull());
202 // Ensure we get a proper exception for the type test.
203 EXPECT(result.IsUnhandledException());
204 const auto& error =
205 Instance::Handle(UnhandledException::Cast(result).exception());
206 EXPECT(strstr(error.ToCString(), "_TypeError"));
207}
208
209static void ExpectFailedViaSTC(const Object& result,
210 const SubtypeTestCache& stc,
211 const Bool& abi_regs_modified,
212 const Bool& rest_regs_modified) {
213 ExpectLazilyFailedViaSTC(result, stc);
214}
215
216const char* kSubtypeRangeCheckScript =
217 R"(
218 class I<T, U> {}
219 class I2 {}
220
221 class Base<T> {}
222
223 class A extends Base<int> {}
224 class A1 extends A implements I2 {}
225 class A2<T> extends A implements I<int, T> {}
226
227 class B extends Base<String> {}
228 class B1 extends B implements I2 {}
229 class B2<T> extends B implements I<T, String> {}
230
231 genericFun<A, B>() {}
232
233 createI() => I<int, String>();
234 createI2() => I2();
235 createBaseInt() => Base<int>();
236 createA() => A();
237 createA1() => A1();
238 createA2() => A2<int>();
239 createB() => B();
240 createB1() => B1();
241 createB2() => B2<int>();
242 createBaseIStringDouble() => Base<I<String, double>>();
243 createBaseA2Int() => Base<A2<int>>();
244 createBaseA2A1() => Base<A2<A1>>();
245 createBaseB2Int() => Base<B2<int>>();
246)";
247
248ISOLATE_UNIT_TEST_CASE(TTS_SubtypeRangeCheck) {
249 const auto& root_library =
250 Library::Handle(LoadTestScript(kSubtypeRangeCheckScript));
251 const auto& class_a = Class::Handle(GetClass(root_library, "A"));
252 const auto& class_base = Class::Handle(GetClass(root_library, "Base"));
253 const auto& class_i = Class::Handle(GetClass(root_library, "I"));
254 const auto& class_i2 = Class::Handle(GetClass(root_library, "I2"));
255 const auto& class_null = Class::Handle(Class::null());
256
257 const auto& obj_i = Object::Handle(Invoke(root_library, "createI"));
258 const auto& obj_i2 = Object::Handle(Invoke(root_library, "createI2"));
259 const auto& obj_baseint =
260 Object::Handle(Invoke(root_library, "createBaseInt"));
261 const auto& obj_a = Object::Handle(Invoke(root_library, "createA"));
262 const auto& obj_a1 = Object::Handle(Invoke(root_library, "createA1"));
263 const auto& obj_a2 = Object::Handle(Invoke(root_library, "createA2"));
264 const auto& obj_b = Object::Handle(Invoke(root_library, "createB"));
265 const auto& obj_b1 = Object::Handle(Invoke(root_library, "createB1"));
266 const auto& obj_b2 = Object::Handle(Invoke(root_library, "createB2"));
267
268 const auto& type_dynamic = Type::Handle(Type::DynamicType());
269 auto& type_object = Type::Handle(Type::ObjectType());
270 type_object = type_object.ToNullability(Nullability::kNullable, Heap::kNew);
271
272 const auto& tav_null = TypeArguments::Handle(TypeArguments::null());
273
274 auto& tav_object = TypeArguments::Handle(TypeArguments::New(1));
275 tav_object.SetTypeAt(0, type_object);
276 CanonicalizeTAV(&tav_object);
277
278 auto& tav_object_dynamic = TypeArguments::Handle(TypeArguments::New(2));
279 tav_object_dynamic.SetTypeAt(0, type_object);
280 tav_object_dynamic.SetTypeAt(1, type_dynamic);
281 CanonicalizeTAV(&tav_object_dynamic);
282
283 auto& tav_dynamic_t = TypeArguments::Handle(TypeArguments::New(2));
284 tav_dynamic_t.SetTypeAt(0, type_dynamic);
285 tav_dynamic_t.SetTypeAt(
286 1, TypeParameter::Handle(GetClassTypeParameter(class_base, "T")));
287 CanonicalizeTAV(&tav_dynamic_t);
288
289 // We will generate specialized TTS for instantiated interface types
290 // where there are no type arguments or the type arguments are top
291 // types.
292 //
293 // obj as A // Subclass ranges
294 // obj as Base<Object> // Subclass ranges with top-type tav
295 // obj as I2 // Subtype ranges
296 // obj as I<Object, dynamic> // Subtype ranges with top-type tav
297 //
298
299 // <...> as A
300 const auto& type_a = AbstractType::Handle(class_a.RareType());
301 RunTTSTest(obj_i, type_a, tav_null, tav_null, ExpectLazilyFailedViaTTS,
302 ExpectFailedViaTTS);
303 RunTTSTest(obj_i2, type_a, tav_null, tav_null, ExpectLazilyFailedViaTTS,
304 ExpectFailedViaTTS);
305 RunTTSTest(obj_baseint, type_a, tav_null, tav_null, ExpectLazilyFailedViaTTS,
306 ExpectFailedViaTTS);
307 RunTTSTest(obj_a, type_a, tav_null, tav_null, ExpectLazilyHandledViaTTS,
308 ExpectHandledViaTTS);
309 RunTTSTest(obj_a1, type_a, tav_null, tav_null, ExpectLazilyHandledViaTTS,
310 ExpectHandledViaTTS);
311 RunTTSTest(obj_a2, type_a, tav_null, tav_null, ExpectLazilyHandledViaTTS,
312 ExpectHandledViaTTS);
313 RunTTSTest(obj_b, type_a, tav_null, tav_null, ExpectLazilyFailedViaTTS,
314 ExpectFailedViaTTS);
315 RunTTSTest(obj_b1, type_a, tav_null, tav_null, ExpectLazilyFailedViaTTS,
316 ExpectFailedViaTTS);
317 RunTTSTest(obj_b2, type_a, tav_null, tav_null, ExpectLazilyFailedViaTTS,
318 ExpectFailedViaTTS);
319
320 // <...> as Base<Object>
321 auto& type_base = AbstractType::Handle(
322 Type::New(class_base, tav_object, TokenPosition::kNoSource));
323 FinalizeAndCanonicalize(class_null, &type_base);
324 RunTTSTest(obj_i, type_base, tav_null, tav_null, ExpectLazilyFailedViaTTS,
325 ExpectFailedViaTTS);
326 RunTTSTest(obj_i2, type_base, tav_null, tav_null, ExpectLazilyFailedViaTTS,
327 ExpectFailedViaTTS);
328 RunTTSTest(obj_baseint, type_base, tav_null, tav_null,
329 ExpectLazilyHandledViaTTS, ExpectHandledViaTTS);
330 RunTTSTest(obj_a, type_base, tav_null, tav_null, ExpectLazilyHandledViaTTS,
331 ExpectHandledViaTTS);
332 RunTTSTest(obj_a1, type_base, tav_null, tav_null, ExpectLazilyHandledViaTTS,
333 ExpectHandledViaTTS);
334 RunTTSTest(obj_a2, type_base, tav_null, tav_null, ExpectLazilyHandledViaTTS,
335 ExpectHandledViaTTS);
336 RunTTSTest(obj_b, type_base, tav_null, tav_null, ExpectLazilyHandledViaTTS,
337 ExpectHandledViaTTS);
338 RunTTSTest(obj_b1, type_base, tav_null, tav_null, ExpectLazilyHandledViaTTS,
339 ExpectHandledViaTTS);
340 RunTTSTest(obj_b2, type_base, tav_null, tav_null, ExpectLazilyHandledViaTTS,
341 ExpectHandledViaTTS);
342
343 // <...> as I2
344 const auto& type_i2 = AbstractType::Handle(class_i2.RareType());
345 RunTTSTest(obj_i, type_i2, tav_null, tav_null, ExpectLazilyFailedViaTTS,
346 ExpectFailedViaTTS);
347 RunTTSTest(obj_i2, type_i2, tav_null, tav_null, ExpectLazilyHandledViaTTS,
348 ExpectHandledViaTTS);
349 RunTTSTest(obj_baseint, type_i2, tav_null, tav_null, ExpectLazilyFailedViaTTS,
350 ExpectFailedViaTTS);
351 RunTTSTest(obj_a, type_i2, tav_null, tav_null, ExpectLazilyFailedViaTTS,
352 ExpectFailedViaTTS);
353 RunTTSTest(obj_a1, type_i2, tav_null, tav_null, ExpectLazilyHandledViaTTS,
354 ExpectHandledViaTTS);
355 RunTTSTest(obj_a2, type_i2, tav_null, tav_null, ExpectLazilyFailedViaTTS,
356 ExpectFailedViaTTS);
357 RunTTSTest(obj_b, type_i2, tav_null, tav_null, ExpectLazilyFailedViaTTS,
358 ExpectFailedViaTTS);
359 RunTTSTest(obj_b1, type_i2, tav_null, tav_null, ExpectLazilyHandledViaTTS,
360 ExpectHandledViaTTS);
361 RunTTSTest(obj_b2, type_i2, tav_null, tav_null, ExpectLazilyFailedViaTTS,
362 ExpectFailedViaTTS);
363
364 // <...> as I<Object, dynamic>
365 auto& type_i_object_dynamic = AbstractType::Handle(
366 Type::New(class_i, tav_object_dynamic, TokenPosition::kNoSource));
367 FinalizeAndCanonicalize(class_null, &type_i_object_dynamic);
368 RunTTSTest(obj_i, type_i_object_dynamic, tav_null, tav_null,
369 ExpectLazilyHandledViaTTS, ExpectHandledViaTTS);
370 RunTTSTest(obj_i2, type_i_object_dynamic, tav_null, tav_null,
371 ExpectLazilyFailedViaTTS, ExpectFailedViaTTS);
372 RunTTSTest(obj_baseint, type_i_object_dynamic, tav_null, tav_null,
373 ExpectLazilyFailedViaTTS, ExpectFailedViaTTS);
374 RunTTSTest(obj_a, type_i_object_dynamic, tav_null, tav_null,
375 ExpectLazilyFailedViaTTS, ExpectFailedViaTTS);
376 RunTTSTest(obj_a1, type_i_object_dynamic, tav_null, tav_null,
377 ExpectLazilyFailedViaTTS, ExpectFailedViaTTS);
378 RunTTSTest(obj_a2, type_i_object_dynamic, tav_null, tav_null,
379 ExpectLazilyHandledViaTTS, ExpectHandledViaTTS);
380 RunTTSTest(obj_b, type_i_object_dynamic, tav_null, tav_null,
381 ExpectLazilyFailedViaTTS, ExpectFailedViaTTS);
382 RunTTSTest(obj_b1, type_i_object_dynamic, tav_null, tav_null,
383 ExpectLazilyFailedViaTTS, ExpectFailedViaTTS);
384 RunTTSTest(obj_b2, type_i_object_dynamic, tav_null, tav_null,
385 ExpectLazilyHandledViaTTS, ExpectHandledViaTTS);
386
387 // We do not generate TTS for uninstantiated types if we would need to use
388 // subtype range checks for the class of the interface type.
389 //
390 // obj as I<dynamic, T>
391 //
392 auto& type_dynamic_t = AbstractType::Handle(
393 Type::New(class_i, tav_dynamic_t, TokenPosition::kNoSource));
394 FinalizeAndCanonicalize(class_base, &type_dynamic_t);
395 RunTTSTest(obj_i, type_dynamic_t, tav_object, tav_null,
396 ExpectLazilyHandledViaSTC, ExpectHandledViaSTC);
397 RunTTSTest(obj_i2, type_dynamic_t, tav_object, tav_null,
398 ExpectLazilyFailedViaSTC, ExpectFailedViaSTC);
399 RunTTSTest(obj_baseint, type_dynamic_t, tav_object, tav_null,
400 ExpectLazilyFailedViaSTC, ExpectFailedViaSTC);
401 RunTTSTest(obj_a, type_dynamic_t, tav_object, tav_null,
402 ExpectLazilyFailedViaSTC, ExpectFailedViaSTC);
403 RunTTSTest(obj_a1, type_dynamic_t, tav_object, tav_null,
404 ExpectLazilyFailedViaSTC, ExpectFailedViaSTC);
405 RunTTSTest(obj_a2, type_dynamic_t, tav_object, tav_null,
406 ExpectLazilyHandledViaSTC, ExpectHandledViaSTC);
407 RunTTSTest(obj_b, type_dynamic_t, tav_object, tav_null,
408 ExpectLazilyFailedViaSTC, ExpectFailedViaSTC);
409 RunTTSTest(obj_b1, type_dynamic_t, tav_object, tav_null,
410 ExpectLazilyFailedViaSTC, ExpectFailedViaSTC);
411 RunTTSTest(obj_b2, type_dynamic_t, tav_object, tav_null,
412 ExpectLazilyHandledViaSTC, ExpectHandledViaSTC);
413
414 // obj as Object (with null safety)
415 Isolate* isolate = Isolate::Current();
416 if (isolate->null_safety()) {
417 auto& type_non_nullable_object =
418 Type::Handle(isolate->object_store()->non_nullable_object_type());
419 RunTTSTest(obj_a, type_non_nullable_object, tav_null, tav_null,
420 ExpectLazilyHandledViaTTS, ExpectHandledViaTTS);
421 RunTTSTest(Object::null_object(), type_non_nullable_object, tav_null,
422 tav_null, ExpectLazilyFailedViaTTS, ExpectFailedViaTTS);
423 }
424}
425
426ISOLATE_UNIT_TEST_CASE(TTS_GenericSubtypeRangeCheck) {
427 const auto& root_library =
428 Library::Handle(LoadTestScript(kSubtypeRangeCheckScript));
429 const auto& class_a1 = Class::Handle(GetClass(root_library, "A1"));
430 const auto& class_a2 = Class::Handle(GetClass(root_library, "A2"));
431 const auto& class_base = Class::Handle(GetClass(root_library, "Base"));
432 const auto& class_i = Class::Handle(GetClass(root_library, "I"));
433 const auto& fun_generic =
434 Function::Handle(GetFunction(root_library, "genericFun"));
435 const auto& class_null = Class::Handle(Class::null());
436
437 const auto& obj_i = Object::Handle(Invoke(root_library, "createI"));
438 const auto& obj_i2 = Object::Handle(Invoke(root_library, "createI2"));
439 const auto& obj_baseint =
440 Object::Handle(Invoke(root_library, "createBaseInt"));
441 const auto& obj_a = Object::Handle(Invoke(root_library, "createA"));
442 const auto& obj_a1 = Object::Handle(Invoke(root_library, "createA1"));
443 const auto& obj_a2 = Object::Handle(Invoke(root_library, "createA2"));
444 const auto& obj_b = Object::Handle(Invoke(root_library, "createB"));
445 const auto& obj_b1 = Object::Handle(Invoke(root_library, "createB1"));
446 const auto& obj_b2 = Object::Handle(Invoke(root_library, "createB2"));
447 const auto& obj_basea2int =
448 Object::Handle(Invoke(root_library, "createBaseA2Int"));
449 const auto& obj_basea2a1 =
450 Object::Handle(Invoke(root_library, "createBaseA2A1"));
451 const auto& obj_baseb2int =
452 Object::Handle(Invoke(root_library, "createBaseB2Int"));
453 const auto& obj_baseistringdouble =
454 Object::Handle(Invoke(root_library, "createBaseIStringDouble"));
455
456 const auto& type_dynamic = Type::Handle(Type::DynamicType());
457 auto& type_int = Type::Handle(Type::IntType());
458 if (!TestCase::IsNNBD()) {
459 type_int = type_int.ToNullability(Nullability::kLegacy, Heap::kNew);
460 }
461 auto& type_string = Type::Handle(Type::StringType());
462 if (!TestCase::IsNNBD()) {
463 type_string = type_string.ToNullability(Nullability::kLegacy, Heap::kNew);
464 }
465 auto& type_object = Type::Handle(Type::ObjectType());
466 type_object = type_object.ToNullability(
467 TestCase::IsNNBD() ? Nullability::kNullable : Nullability::kLegacy,
468 Heap::kNew);
469 auto& type_a1 = Type::Handle(class_a1.DeclarationType());
470 if (!TestCase::IsNNBD()) {
471 type_a1 = type_a1.ToNullability(Nullability::kLegacy, Heap::kNew);
472 }
473 FinalizeAndCanonicalize(class_null, &type_a1);
474
475 const auto& tav_null = TypeArguments::Handle(TypeArguments::null());
476
477 auto& tav_object_dynamic = TypeArguments::Handle(TypeArguments::New(2));
478 tav_object_dynamic.SetTypeAt(0, type_object);
479 tav_object_dynamic.SetTypeAt(1, type_dynamic);
480 CanonicalizeTAV(&tav_object_dynamic);
481
482 auto& tav_dynamic_int = TypeArguments::Handle(TypeArguments::New(2));
483 tav_dynamic_int.SetTypeAt(0, type_dynamic);
484 tav_dynamic_int.SetTypeAt(1, type_int);
485 CanonicalizeTAV(&tav_dynamic_int);
486
487 auto& tav_dynamic_string = TypeArguments::Handle(TypeArguments::New(2));
488 tav_dynamic_string.SetTypeAt(0, type_dynamic);
489 tav_dynamic_string.SetTypeAt(1, type_string);
490 CanonicalizeTAV(&tav_dynamic_string);
491
492 auto& tav_int = TypeArguments::Handle(TypeArguments::New(1));
493 tav_int.SetTypeAt(0, type_int);
494 CanonicalizeTAV(&tav_int);
495
496 auto& type_i_object_dynamic = AbstractType::Handle(
497 Type::New(class_i, tav_object_dynamic, TokenPosition::kNoSource));
498 FinalizeAndCanonicalize(class_null, &type_i_object_dynamic);
499 const auto& tav_iod = TypeArguments::Handle(TypeArguments::New(1));
500 tav_iod.SetTypeAt(0, type_i_object_dynamic);
501
502 // We will generate specialized TTS for instantiated interface types
503 // where there are no type arguments or the type arguments are top
504 // types.
505 //
506 // obj as Base<I<Object, dynamic>> // Subclass ranges for Base, subtype
507 // // ranges tav arguments.
508 // obj as Base<T> // Subclass ranges for Base, type
509 // // equality for instantiator type arg T
510 // obj as Base<B> // Subclass ranges for Base, type
511 // // equality for function type arg B.
512 //
513
514 // <...> as Base<I<Object, dynamic>>
515 auto& type_base_i_object_dynamic = AbstractType::Handle(
516 Type::New(class_base, tav_iod, TokenPosition::kNoSource));
517 FinalizeAndCanonicalize(class_null, &type_base_i_object_dynamic);
518 RunTTSTest(obj_baseb2int, type_base_i_object_dynamic, tav_null, tav_null,
519 ExpectLazilyHandledViaTTS, ExpectHandledViaTTS);
520 RunTTSTest(obj_baseistringdouble, type_base_i_object_dynamic, tav_null,
521 tav_null, ExpectLazilyHandledViaTTS, ExpectHandledViaTTS);
522 RunTTSTest(obj_a, type_base_i_object_dynamic, tav_null, tav_null,
523 ExpectLazilyFailedViaTTS, ExpectFailedViaTTS);
524 RunTTSTest(obj_a1, type_base_i_object_dynamic, tav_null, tav_null,
525 ExpectLazilyFailedViaTTS, ExpectFailedViaTTS);
526 RunTTSTest(obj_a2, type_base_i_object_dynamic, tav_null, tav_null,
527 ExpectLazilyFailedViaTTS, ExpectFailedViaTTS);
528 RunTTSTest(obj_b, type_base_i_object_dynamic, tav_null, tav_null,
529 ExpectLazilyFailedViaTTS, ExpectFailedViaTTS);
530 RunTTSTest(obj_b1, type_base_i_object_dynamic, tav_null, tav_null,
531 ExpectLazilyFailedViaTTS, ExpectFailedViaTTS);
532 RunTTSTest(obj_b2, type_base_i_object_dynamic, tav_null, tav_null,
533 ExpectLazilyFailedViaTTS, ExpectFailedViaTTS);
534
535 // <...> as Base<T> with T instantiantiator type parameter (T == int)
536 const auto& tav_baset = TypeArguments::Handle(TypeArguments::New(1));
537 tav_baset.SetTypeAt(
538 0, TypeParameter::Handle(GetClassTypeParameter(class_base, "T")));
539 auto& type_base_t = AbstractType::Handle(
540 Type::New(class_base, tav_baset, TokenPosition::kNoSource));
541 FinalizeAndCanonicalize(class_base, &type_base_t);
542 RunTTSTest(obj_baseint, type_base_t, tav_int, tav_null,
543 ExpectLazilyHandledViaTTS, ExpectHandledViaTTS);
544 RunTTSTest(obj_baseistringdouble, type_base_t, tav_int, tav_null,
545 ExpectLazilyFailedViaTTS, ExpectFailedViaTTS);
546
547 // <...> as Base<B> with B function type parameter
548 const auto& tav_baseb = TypeArguments::Handle(TypeArguments::New(1));
549 tav_baseb.SetTypeAt(
550 0, TypeParameter::Handle(GetFunctionTypeParameter(fun_generic, "B")));
551 auto& type_base_b = AbstractType::Handle(
552 Type::New(class_base, tav_baseb, TokenPosition::kNoSource));
553 FinalizeAndCanonicalize(class_null, &type_base_b);
554 // With B == int
555 RunTTSTest(obj_baseint, type_base_b, tav_null, tav_dynamic_int,
556 ExpectLazilyHandledViaTTS, ExpectHandledViaTTS);
557 RunTTSTest(obj_baseistringdouble, type_base_b, tav_null, tav_dynamic_int,
558 ExpectLazilyFailedViaTTS, ExpectFailedViaTTS);
559 // With B == dynamic (null vector)
560 RunTTSTest(obj_baseint, type_base_b, tav_null, tav_null,
561 ExpectLazilyHandledViaTTS, ExpectHandledViaTTS);
562 RunTTSTest(obj_i2, type_base_b, tav_null, tav_null, ExpectLazilyFailedViaTTS,
563 ExpectFailedViaTTS);
564
565 // We do not generate TTS for uninstantiated types if we would need to use
566 // subtype range checks for the class of the interface type.
567 //
568 // obj as I<dynamic, String> // I is generic & implemented.
569 // obj as Base<A2<T>> // A2<T> is not instantiated.
570 // obj as Base<A2<A1>> // A2<A1> is not a rare type.
571 //
572
573 // <...> as I<dynamic, String>
574 RELEASE_ASSERT(class_i.is_implemented());
575 auto& type_i_dynamic_string = Type::Handle(
576 Type::New(class_i, tav_dynamic_string, TokenPosition::kNoSource));
577 type_i_dynamic_string = type_i_dynamic_string.ToNullability(
578 Nullability::kNonNullable, Heap::kNew);
579 FinalizeAndCanonicalize(class_null, &type_i_dynamic_string);
580 RunTTSTest(obj_i, type_i_dynamic_string, tav_null, tav_null,
581 ExpectLazilyHandledViaSTC, ExpectHandledViaSTC);
582 RunTTSTest(obj_baseint, type_i_dynamic_string, tav_null, tav_null,
583 ExpectLazilyFailedViaSTC, ExpectFailedViaSTC);
584
585 // <...> as Base<A2<T>>
586 const auto& tav_t = TypeArguments::Handle(TypeArguments::New(1));
587 tav_t.SetTypeAt(
588 0, TypeParameter::Handle(GetClassTypeParameter(class_base, "T")));
589 auto& type_a2_t =
590 Type::Handle(Type::New(class_a2, tav_t, TokenPosition::kNoSource));
591 type_a2_t = type_a2_t.ToNullability(Nullability::kLegacy, Heap::kNew);
592 FinalizeAndCanonicalize(class_null, &type_a2_t);
593 const auto& tav_a2_t = TypeArguments::Handle(TypeArguments::New(1));
594 tav_a2_t.SetTypeAt(0, type_a2_t);
595 auto& type_base_a2_t =
596 Type::Handle(Type::New(class_base, tav_a2_t, TokenPosition::kNoSource));
597 type_base_a2_t =
598 type_base_a2_t.ToNullability(Nullability::kNonNullable, Heap::kNew);
599 FinalizeAndCanonicalize(class_null, &type_base_a2_t);
600 RunTTSTest(obj_basea2int, type_base_a2_t, tav_null, tav_null,
601 ExpectLazilyHandledViaSTC, ExpectHandledViaSTC);
602 RunTTSTest(obj_baseint, type_base_a2_t, tav_null, tav_null,
603 ExpectLazilyFailedViaSTC, ExpectFailedViaSTC);
604
605 // <...> as Base<A2<A1>>
606 const auto& tav_a1 = TypeArguments::Handle(TypeArguments::New(1));
607 tav_a1.SetTypeAt(0, type_a1);
608 auto& type_a2_a1 =
609 Type::Handle(Type::New(class_a2, tav_a1, TokenPosition::kNoSource));
610 type_a2_a1 = type_a2_a1.ToNullability(Nullability::kLegacy, Heap::kNew);
611 FinalizeAndCanonicalize(class_null, &type_a2_a1);
612 const auto& tav_a2_a1 = TypeArguments::Handle(TypeArguments::New(1));
613 tav_a2_a1.SetTypeAt(0, type_a2_a1);
614 auto& type_base_a2_a1 =
615 Type::Handle(Type::New(class_base, tav_a2_a1, TokenPosition::kNoSource));
616 type_base_a2_a1 =
617 type_base_a2_a1.ToNullability(Nullability::kNonNullable, Heap::kNew);
618 FinalizeAndCanonicalize(class_null, &type_base_a2_a1);
619 RunTTSTest(obj_basea2a1, type_base_a2_a1, tav_null, tav_null,
620 ExpectLazilyHandledViaSTC, ExpectHandledViaSTC);
621 RunTTSTest(obj_basea2int, type_base_a2_a1, tav_null, tav_null,
622 ExpectLazilyFailedViaSTC, ExpectFailedViaSTC);
623}
624
625ISOLATE_UNIT_TEST_CASE(TTS_Regress40964) {
626 const char* kScript =
627 R"(
628 class A<T> {
629 test(x) => x as B<T>;
630 }
631 class B<T> {}
632 class C<T> {}
633
634 createACint() => A<C<int>>();
635 createBCint() => B<C<int>>();
636 createBCnum() => B<C<num>>();
637 )";
638
639 const auto& root_library = Library::Handle(LoadTestScript(kScript));
640 const auto& class_b = Class::Handle(GetClass(root_library, "B"));
641 const auto& class_null = Class::Handle(Class::null());
642
643 const auto& acint = Object::Handle(Invoke(root_library, "createACint"));
644 const auto& bcint = Object::Handle(Invoke(root_library, "createBCint"));
645 const auto& bcnum = Object::Handle(Invoke(root_library, "createBCnum"));
646
647 // dst_type = B<T>
648 const auto& dst_tav = TypeArguments::Handle(TypeArguments::New(1));
649 dst_tav.SetTypeAt(0,
650 TypeParameter::Handle(GetClassTypeParameter(class_b, "T")));
651 auto& dst_type =
652 Type::Handle(Type::New(class_b, dst_tav, TokenPosition::kNoSource));
653 FinalizeAndCanonicalize(class_null, &dst_type);
654 const auto& cint_tav =
655 TypeArguments::Handle(Instance::Cast(acint).GetTypeArguments());
656 const auto& function_tav = TypeArguments::Handle();
657
658 // a as B<T> -- a==B<C<int>, T==<C<int>>
659 RunTTSTest(bcint, dst_type, cint_tav, function_tav, ExpectLazilyHandledViaTTS,
660 ExpectHandledViaTTS);
661
662 // a as B<T> -- a==B<C<num>, T==<C<int>>
663 RunTTSTest(bcnum, dst_type, cint_tav, function_tav, ExpectLazilyFailedViaTTS,
664 ExpectFailedViaTTS);
665}
666
667} // namespace dart
668
669#endif // defined(TARGET_ARCH_ARM64) || defined(TARGET_ARCH_ARM) || \
670 // defined(TARGET_ARCH_X64)
671