Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
type_testing_stubs_test.cc
Go to the documentation of this file.
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
13#include "vm/flags.h"
14#include "vm/lockers.h"
15#include "vm/stack_frame.h"
16#include "vm/symbols.h"
18#include "vm/unit_test.h"
19#include "vm/zone_text_buffer.h"
20
21#if !defined(TARGET_ARCH_IA32)
22
23namespace dart {
24
25DECLARE_FLAG(int, max_subtype_cache_entries);
26// Note that flags that this affects may only mutable in some modes, e.g.,
27// tracing type checks can only be done in DEBUG mode.
29 trace_type_testing_stub_tests,
30 false,
31 "Trace type testing stub tests");
33 print_type_testing_stub_test_headers,
34 true,
35 "Print headers for executed type testing stub tests");
36
38 public:
40 : old_trace_type_checks_(FLAG_trace_type_checks),
41 old_disassemble_stubs_(FLAG_disassemble_stubs) {
42 if (FLAG_trace_type_testing_stub_tests) {
43#if defined(DEBUG)
44 FLAG_trace_type_checks = true;
45#endif
46#if defined(FORCE_INCLUDE_DISASSEMBLER) || !defined(PRODUCT)
47 FLAG_disassemble_stubs = true;
48#endif
49 }
50 }
52 if (FLAG_trace_type_testing_stub_tests) {
53#if defined(DEBUG)
54 FLAG_trace_type_checks = old_trace_type_checks_;
55#endif
56#if defined(FORCE_INCLUDE_DISASSEMBLER) || !defined(PRODUCT)
57 FLAG_disassemble_stubs = old_disassemble_stubs_;
58#endif
59 }
60 }
61
62 private:
63 const bool old_trace_type_checks_;
64 const bool old_disassemble_stubs_;
65};
66
67#define __ assembler->
68
70 auto calculate_breadcrumb = [](const Register& reg) {
71 return 0x10 + 2 * (static_cast<intptr_t>(reg));
72 };
73
74 __ EnterDartFrame(0);
75
76 for (intptr_t i = 0; i < kNumberOfCpuRegisters; ++i) {
77 if (((1 << i) & kDartAvailableCpuRegs) == 0) continue;
78 if (((1 << i) & TypeTestABI::kAbiRegisters) != 0) continue;
79 if (((1 << i) & TTSInternalRegs::kInternalRegisters) != 0) continue;
80 const Register reg = static_cast<Register>(i);
81 __ LoadImmediate(reg, calculate_breadcrumb(reg));
82 }
83
84 // Load the arguments into the right TTS calling convention registers.
85 const intptr_t instance_offset =
86 (kCallerSpSlotFromFp + 3) * compiler::target::kWordSize;
87 const intptr_t inst_type_args_offset =
88 (kCallerSpSlotFromFp + 2) * compiler::target::kWordSize;
89 const intptr_t fun_type_args_offset =
90 (kCallerSpSlotFromFp + 1) * compiler::target::kWordSize;
91 const intptr_t dst_type_offset =
92 (kCallerSpSlotFromFp + 0) * compiler::target::kWordSize;
93
94 __ LoadMemoryValue(TypeTestABI::kInstanceReg, FPREG, instance_offset);
96 inst_type_args_offset);
98 fun_type_args_offset);
99 __ LoadMemoryValue(TypeTestABI::kDstTypeReg, FPREG, dst_type_offset);
100
101 const intptr_t subtype_test_cache_index = __ object_pool_builder().AddObject(
102 Object::null_object(), compiler::ObjectPoolBuilderEntry::kPatchable);
103 const intptr_t dst_name_index = __ object_pool_builder().AddObject(
104 Symbols::OptimizedOut(), compiler::ObjectPoolBuilderEntry::kPatchable);
105 ASSERT_EQUAL(subtype_test_cache_index + 1, dst_name_index);
106 ASSERT(__ constant_pool_allowed());
107
109 assembler, TypeTestABI::kDstTypeReg, subtype_test_cache_index);
110
111 // We have the guarantee that TTS preserves all input registers, if the TTS
112 // handles the type test successfully.
113 //
114 // Let the test know which TTS abi registers were not preserved.
115 ASSERT(((1 << static_cast<intptr_t>(TypeTestABI::kInstanceReg)) &
117 // First we check the instance register, freeing it up in case there are no
118 // other safe registers to use since we need two registers: one to accumulate
119 // the register mask, another to load the array address when saving the mask.
120 __ LoadFromOffset(TypeTestABI::kScratchReg, FPREG, instance_offset);
121 compiler::Label instance_matches, done_with_instance;
123 __ BranchIf(EQUAL, &instance_matches, compiler::Assembler::kNearJump);
124 __ LoadImmediate(TypeTestABI::kScratchReg,
125 1 << static_cast<intptr_t>(TypeTestABI::kInstanceReg));
126 __ Jump(&done_with_instance, compiler::Assembler::kNearJump);
127 __ Bind(&instance_matches);
128 __ LoadImmediate(TypeTestABI::kScratchReg, 0);
129 __ Bind(&done_with_instance);
130 for (intptr_t i = 0; i < kNumberOfCpuRegisters; ++i) {
131 if (((1 << i) & TypeTestABI::kPreservedAbiRegisters) == 0) continue;
132 const Register reg = static_cast<Register>(i);
134 switch (reg) {
136 // Skip the already handled instance register.
137 continue;
139 __ LoadFromOffset(TypeTestABI::kInstanceReg, FPREG, dst_type_offset);
140 break;
142 __ LoadFromOffset(TypeTestABI::kInstanceReg, FPREG,
143 fun_type_args_offset);
144 break;
146 __ LoadFromOffset(TypeTestABI::kInstanceReg, FPREG,
147 inst_type_args_offset);
148 break;
149 default:
150 FATAL("Unexpected register %s", RegisterNames::RegisterName(reg));
151 break;
152 }
153 __ CompareRegisters(reg, TypeTestABI::kInstanceReg);
155 __ AddImmediate(TypeTestABI::kScratchReg, 1 << i);
156 __ Bind(&done);
157 }
159 __ LoadFromOffset(TypeTestABI::kInstanceReg, FPREG,
160 (kCallerSpSlotFromFp + 5) * compiler::target::kWordSize);
162 compiler::target::Array::element_offset(0));
163
164 // Let the test know which non-TTS abi registers were not preserved.
165 __ LoadImmediate(TypeTestABI::kScratchReg, 0);
166 for (intptr_t i = 0; i < kNumberOfCpuRegisters; ++i) {
167 if (((1 << i) & kDartAvailableCpuRegs) == 0) continue;
168 if (((1 << i) & TypeTestABI::kAbiRegisters) != 0) continue;
169 const Register reg = static_cast<Register>(i);
171 __ CompareImmediate(reg, calculate_breadcrumb(reg));
173 __ AddImmediate(TypeTestABI::kScratchReg, 1 << i);
174 __ Bind(&done);
175 }
177 __ LoadFromOffset(TypeTestABI::kInstanceReg, FPREG,
178 (kCallerSpSlotFromFp + 4) * compiler::target::kWordSize);
180 compiler::target::Array::element_offset(0));
181
182 // Set the return from the stub to be null.
183 __ LoadObject(CallingConventions::kReturnReg, Object::null_object());
184 __ LeaveDartFrame();
185 __ Ret();
186}
187
188#undef __
189
192 ASSERT(type->IsCanonical());
193}
194
196 *tav = tav->Canonicalize(Thread::Current());
197}
198
199#if defined(TESTING)
200// Defined before DRT_TypeCheck in runtime_entry.cc.
201extern bool TESTING_runtime_entered_on_TTS_invocation;
202extern bool TESTING_found_hash_STC_entry;
203#endif
204
206 // The TTS invocation should enter the runtime and trigger a TypeError.
208 // The TTS invocation should return a result from the TTS stub without
209 // entering the runtime and without an associated STC entry.
211 // The TTS invocation should return a result from checking the STC without
212 // entering the runtime. The STC prior to invocation should be non-null and
213 // include a matching entry for the test case.
215 // The TTS invocation should enter the runtime and add a new entry to the
216 // STC, creating a new STC if necessary.
218 // The TTS invocation should enter the runtime and return a successful check
219 // but without adding an entry to the STC. This should only happen when the
220 // STC has hit the limit described by FLAG_max_subtype_cache_entries prior
221 // to the invocation.
223 // The TTS invocation should enter the runtime and the TTS is specialized
224 // after this test case. This test case should not create a new STC or modify
225 // an existing STC.
226 //
227 // Used only within TTSTestState::InvokeLazilySpecializedStub. This is
228 // needed because there is a single special case for updating the STC that
229 // happens only when specializing the lazy specialization stub, and so we
230 // need to distinguish specialization and respecialization.
232 // The TTS invocation should enter the runtime and the TTS is respecialized
233 // after this test case. This test case should not create a new STC or modify
234 // an existing STC.
235 //
236 // Used only when calling TTSTestState::InvokeExistingStub.
238 // Used for static assert below only.
240};
241
242static const char* kTestResultStrings[] = {
243 "fails in runtime",
244 "passes in TTS",
245 "passes in STC stub",
246 "passes in runtime, adding new STC entry",
247 "passes in runtime, no changes to max size STC",
248 "passes in runtime, initial TTS stub specialization",
249 "passes in runtime, TTS stub respecialized",
250};
251
252// Just to make sure the above are kept in sync.
253static_assert(sizeof(kTestResultStrings) >=
255 "kTestResultStrings has too few entries");
256static_assert(sizeof(kTestResultStrings) <=
258 "kTestResultStrings has extra entries");
259
265
266 TTSTestCase(const Object& obj,
267 const TypeArguments& i_tav,
268 const TypeArguments& f_tav,
269 const TTSTestResult result = kTTS)
270 : instance(obj),
271 instantiator_tav(i_tav),
272 function_tav(f_tav),
274
275 bool ShouldEnterRuntime() const {
276 switch (expected_result) {
277 case kTTS:
279 return false;
280 case kFail:
281 case kNewSTCEntry:
282 case kRuntimeCheck:
283 case kSpecialize:
284 case kRespecialize:
285 return true;
286 default:
287 UNREACHABLE();
288 }
289 }
290
291 bool HasSameSTCEntry(const TTSTestCase& other) const {
292 if (instantiator_tav.ptr() != other.instantiator_tav.ptr()) {
293 return false;
294 }
295 if (function_tav.ptr() != other.function_tav.ptr()) {
296 return false;
297 }
298 if (instance.IsClosure() && other.instance.IsClosure()) {
299 const auto& closure = Closure::Cast(instance);
300 const auto& other_closure = Closure::Cast(other.instance);
301 const auto& sig = FunctionType::Handle(
302 Function::Handle(closure.function()).signature());
303 const auto& other_sig = FunctionType::Handle(
304 Function::Handle(other_closure.function()).signature());
305 return sig.ptr() == other_sig.ptr() &&
306 closure.instantiator_type_arguments() ==
307 other_closure.instantiator_type_arguments() &&
308 closure.function_type_arguments() ==
309 other_closure.function_type_arguments() &&
310 closure.delayed_type_arguments() ==
311 other_closure.delayed_type_arguments();
312 }
313 const intptr_t cid = instance.GetClassId();
314 const intptr_t other_cid = other.instance.GetClassId();
315 if (cid != other_cid) {
316 return false;
317 }
318 const auto& cls = Class::Handle(instance.clazz());
319 if (cls.NumTypeArguments() == 0) {
320 return true;
321 }
322 return Instance::Cast(instance).GetTypeArguments() ==
323 Instance::Cast(other.instance).GetTypeArguments();
324 }
325
326 bool HasSTCEntry(const SubtypeTestCache& cache,
327 const AbstractType& dst_type,
328 Bool* out_result = nullptr,
329 intptr_t* out_index = nullptr) const {
330 if (cache.IsNull()) return false;
331 if (instance.IsClosure()) {
332 const auto& closure = Closure::Cast(instance);
333 const auto& sig = FunctionType::Handle(
334 Function::Handle(closure.function()).signature());
335 const auto& closure_instantiator_type_arguments =
336 TypeArguments::Handle(closure.instantiator_type_arguments());
337 const auto& closure_function_type_arguments =
338 TypeArguments::Handle(closure.function_type_arguments());
339 const auto& closure_delayed_type_arguments =
340 TypeArguments::Handle(closure.delayed_type_arguments());
341 return cache.HasCheck(
342 sig, dst_type, closure_instantiator_type_arguments, instantiator_tav,
343 function_tav, closure_function_type_arguments,
344 closure_delayed_type_arguments, out_index, out_result);
345 }
346 const auto& id_smi = Smi::Handle(Smi::New(instance.GetClassId()));
347 const auto& cls = Class::Handle(instance.clazz());
348 auto& instance_type_arguments = TypeArguments::Handle();
349 if (cls.NumTypeArguments() > 0) {
350 instance_type_arguments = Instance::Cast(instance).GetTypeArguments();
351 }
352 return cache.HasCheck(id_smi, dst_type, instance_type_arguments,
354 Object::null_type_arguments(),
355 Object::null_type_arguments(), out_index, out_result);
356 }
357
358 private:
359 DISALLOW_ALLOCATION();
360};
361
362// Takes an existing test case and creates a failing test case from it.
363static TTSTestCase Failure(const TTSTestCase& original) {
364 return TTSTestCase(original.instance, original.instantiator_tav,
365 original.function_tav, kFail);
366}
367
368// Takes an existing test case and creates a passing test case that finds an
369// existing STC entry without entering the runtime.
370static TTSTestCase STCCheck(const TTSTestCase& original) {
371 return TTSTestCase(original.instance, original.instantiator_tav,
373}
374
375// Takes an existing test case and creates a passing test case that adds a
376// new STC entry.
377static TTSTestCase FalseNegative(const TTSTestCase& original) {
378 return TTSTestCase(original.instance, original.instantiator_tav,
379 original.function_tav, kNewSTCEntry);
380}
381
382// Takes an existing test case and creates a test case that should go to the
383// runtime and pass but not modify the STC, as the STC has grown too large.
384static TTSTestCase RuntimeCheck(const TTSTestCase& original) {
385 return TTSTestCase(original.instance, original.instantiator_tav,
386 original.function_tav, kRuntimeCheck);
387}
388
389// Takes an existing test case and creates a passing test case that should go t
390// the runtime and (re)specialize the STC.
391static TTSTestCase Respecialization(const TTSTestCase& original) {
392 return TTSTestCase(original.instance, original.instantiator_tav,
393 original.function_tav, kRespecialize);
394}
395
396class TTSTestState : public ValueObject {
397 public:
399 : thread_(thread),
400 type_(AbstractType::Handle(zone(), type.ptr())),
401 modified_abi_regs_box_(Array::Handle(zone(), Array::New(1))),
402 modified_rest_regs_box_(Array::Handle(zone(), Array::New(1))),
403 tts_invoker_(
404 Code::Handle(zone(), CreateInvocationStub(thread_, zone()))),
405 pool_(ObjectPool::Handle(zone(), tts_invoker_.object_pool())),
406 arguments_descriptor_(
407 Array::Handle(ArgumentsDescriptor::NewBoxed(0, 6))),
408 previous_tts_stub_(Code::Handle(zone())),
409 previous_stc_(SubtypeTestCache::Handle(zone())),
410 last_arguments_(Array::Handle(zone())),
411 last_tested_type_(AbstractType::Handle(zone())),
412 new_tts_stub_(Code::Handle(zone())),
413 last_stc_(SubtypeTestCache::Handle(zone())),
414 last_result_(Object::Handle(zone())) {
415 if (FLAG_print_type_testing_stub_test_headers) {
416 THR_Print("Creating test state for type %s\n", type.ToCString());
417 }
418 }
419
420 Zone* zone() const { return thread_->zone(); }
421 const SubtypeTestCache& last_stc() const { return last_stc_; }
422 // For cases where the STC may have been reset/removed, like reloading.
423 const SubtypeTestCachePtr current_stc() const {
424 return SubtypeTestCache::RawCast(pool_.ObjectAt(kSubtypeTestCacheIndex));
425 }
426
427 AbstractTypePtr TypeToTest(const TTSTestCase& test_case) const {
428 if (type_.IsTypeParameter()) {
429 return TypeParameter::Cast(type_).GetFromTypeArguments(
430 test_case.instantiator_tav, test_case.function_tav);
431 }
432 return type_.ptr();
433 }
434
435 void ClearCache() {
436 pool_.SetObjectAt(kSubtypeTestCacheIndex, Object::null_object());
437 }
438
440 // This shouldn't be used except within InvokeLazilySpecializedStub.
441 EXPECT_NE(kSpecialize, test_case.expected_result);
442 // A new stub is being installed, so we won't respecialize.
443 EXPECT_NE(kRespecialize, test_case.expected_result);
444 last_tested_type_ = TypeToTest(test_case);
445 {
446 // To make sure we output the disassembled stub if desired.
449 thread_, last_tested_type_);
450 }
451 last_tested_type_.SetTypeTestingStub(previous_tts_stub_);
452 PrintInvocationHeader("eagerly specialized", test_case);
453 InvokeStubHelper(test_case);
454 // Treat it as a failure if the stub respecializes, since we're attempting
455 // to simulate AOT mode.
456 EXPECT(previous_tts_stub_.ptr() == new_tts_stub_.ptr());
457 }
458
460 bool should_specialize = true) {
461 // This is governed by the should_specialize parameter.
462 EXPECT_NE(kSpecialize, test_case.expected_result);
463 // A new stub is being installed, so we won't respecialize.
464 EXPECT_NE(kRespecialize, test_case.expected_result);
465 last_tested_type_ = TypeToTest(test_case);
466 const auto& specializing_stub =
468 last_tested_type_, /*lazy_specialize=*/true));
469 last_tested_type_.SetTypeTestingStub(specializing_stub);
470 const TTSTestCase initial_case{
471 test_case.instance, test_case.instantiator_tav, test_case.function_tav,
472 should_specialize && test_case.expected_result != kFail
474 : test_case.expected_result};
475 PrintInvocationHeader("lazy specialized", initial_case);
476 InvokeStubHelper(initial_case);
477 if (initial_case.expected_result == kSpecialize) {
478 EXPECT(previous_tts_stub_.ptr() != new_tts_stub_.ptr());
479 } else {
480 EXPECT(previous_tts_stub_.ptr() == new_tts_stub_.ptr());
481 }
482 }
483
484 void InvokeExistingStub(const TTSTestCase& test_case) {
485 // This shouldn't be used except within InvokeLazilySpecializedStub.
486 EXPECT_NE(kSpecialize, test_case.expected_result);
487 last_tested_type_ = TypeToTest(test_case);
488 PrintInvocationHeader("existing", test_case);
489 InvokeStubHelper(test_case);
490 // Only respecialization should result in a new stub.
491 EXPECT_EQ(test_case.expected_result == kRespecialize,
492 previous_tts_stub_.ptr() != new_tts_stub_.ptr());
493 }
494
495 private:
496 static constexpr intptr_t kSubtypeTestCacheIndex = 0;
497
498 SmiPtr modified_abi_regs() const {
499 if (modified_abi_regs_box_.At(0)->IsHeapObject()) return Smi::null();
500 return Smi::RawCast(modified_abi_regs_box_.At(0));
501 }
502 SmiPtr modified_rest_regs() const {
503 if (modified_rest_regs_box_.At(0)->IsHeapObject()) return Smi::null();
504 return Smi::RawCast(modified_rest_regs_box_.At(0));
505 }
506
507 void PrintInvocationHeader(const char* stub_type,
508 const TTSTestCase& test_case) {
509 if (!FLAG_print_type_testing_stub_test_headers) return;
510 LogBlock lb;
511 const auto& tts = Code::Handle(zone(), last_tested_type_.type_test_stub());
512 auto* const stub_name = StubCode::NameOfStub(tts.EntryPoint());
513 THR_Print("Testing %s %s stub for type %s\n",
514 stub_name == nullptr ? "optimized" : stub_name, stub_type,
515 last_tested_type_.ToCString());
516 if (last_tested_type_.ptr() != type_.ptr()) {
517 THR_Print(" Original type: %s\n", type_.ToCString());
518 }
519 THR_Print(" Instance: %s\n", test_case.instance.ToCString());
520 THR_Print(" Instantiator TAV: %s\n",
521 test_case.instantiator_tav.ToCString());
522 THR_Print(" Function TAV: %s\n", test_case.function_tav.ToCString());
523 THR_Print(" Expected result: %s\n",
524 kTestResultStrings[test_case.expected_result]);
525 }
526
527 static CodePtr CreateInvocationStub(Thread* thread, Zone* zone) {
528 const auto& klass = Class::Handle(
529 zone, thread->isolate_group()->class_table()->At(kInstanceCid));
530 const auto& symbol = String::Handle(
531 zone, Symbols::New(thread, OS::SCreate(zone, "TTSTest")));
532 const auto& signature = FunctionType::Handle(zone, FunctionType::New());
533 const auto& function = Function::Handle(
535 signature, symbol, UntaggedFunction::kRegularFunction, false,
536 false, false, false, false, klass, TokenPosition::kNoSource));
537
538 TraceStubInvocationScope scope;
539 compiler::ObjectPoolBuilder pool_builder;
540 SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
541 compiler::Assembler assembler(&pool_builder);
542 GenerateInvokeTTSStub(&assembler);
543 const Code& invoke_tts = Code::Handle(Code::FinalizeCodeAndNotify(
544 "InvokeTTS", nullptr, &assembler, Code::PoolAttachment::kNotAttachPool,
545 /*optimized=*/false));
546
547 const auto& pool =
549 invoke_tts.set_object_pool(pool.ptr());
550 invoke_tts.set_owner(function);
551 invoke_tts.set_exception_handlers(
553 EXPECT_EQ(2, pool.Length());
554
555 if (FLAG_support_disassembler && FLAG_disassemble_stubs) {
556 Disassembler::DisassembleStub(symbol.ToCString(), invoke_tts);
557 }
558
559 return invoke_tts.ptr();
560 }
561
562 void InvokeStubHelper(const TTSTestCase& test_case) {
563 ASSERT(test_case.instantiator_tav.IsNull() ||
564 test_case.instantiator_tav.IsCanonical());
565 ASSERT(test_case.function_tav.IsNull() ||
566 test_case.function_tav.IsCanonical());
567
568 modified_abi_regs_box_.SetAt(0, Object::null_object());
569 modified_rest_regs_box_.SetAt(0, Object::null_object());
570
571 last_arguments_ = Array::New(6);
572 last_arguments_.SetAt(0, modified_abi_regs_box_);
573 last_arguments_.SetAt(1, modified_rest_regs_box_);
574 last_arguments_.SetAt(2, test_case.instance);
575 last_arguments_.SetAt(3, test_case.instantiator_tav);
576 last_arguments_.SetAt(4, test_case.function_tav);
577 last_arguments_.SetAt(5, type_);
578
579 previous_tts_stub_ = last_tested_type_.type_test_stub();
580 previous_stc_ = current_stc();
581 {
582 SafepointMutexLocker ml(
584 previous_stc_ = previous_stc_.Copy(thread_);
585 }
586#if defined(TESTING)
587 // Clear the runtime entered flag prior to invocation.
588 TESTING_runtime_entered_on_TTS_invocation = false;
589#endif
590 {
591 TraceStubInvocationScope scope;
592 last_result_ = DartEntry::InvokeCode(tts_invoker_, arguments_descriptor_,
593 last_arguments_, thread_);
594 }
595#if defined(TESTING)
596 EXPECT_EQ(test_case.ShouldEnterRuntime(),
597 TESTING_runtime_entered_on_TTS_invocation);
598#endif
599 new_tts_stub_ = last_tested_type_.type_test_stub();
600 last_stc_ = current_stc();
601 if (test_case.expected_result == kFail) {
602 EXPECT(!last_result_.IsNull());
603 EXPECT(last_result_.IsError());
604 EXPECT(last_result_.IsUnhandledException());
605 if (last_result_.IsUnhandledException()) {
606 const auto& error = Instance::Handle(
607 UnhandledException::Cast(last_result_).exception());
608 EXPECT(strstr(error.ToCString(), "_TypeError"));
609 }
610 } else {
611 EXPECT(last_result_.IsNull());
612 if (!last_result_.IsNull()) {
613 EXPECT(last_result_.IsError());
614 EXPECT(last_result_.IsUnhandledException());
615 if (last_result_.IsUnhandledException()) {
616 const auto& exception = UnhandledException::Cast(last_result_);
617 FAIL("%s", exception.ToErrorCString());
618 }
619 } else {
620 EXPECT(new_tts_stub_.ptr() != StubCode::LazySpecializeTypeTest().ptr());
621 ReportModifiedRegisters(modified_abi_regs());
622 // If we shouldn't go to the runtime, report any unexpected changes in
623 // non-ABI registers.
624 if (!test_case.ShouldEnterRuntime()) {
625 ReportModifiedRegisters(modified_rest_regs());
626 }
627 }
628 }
629 ReportUnexpectedSTCChanges(test_case);
630 }
631
632 static void ReportModifiedRegisters(SmiPtr encoded_reg_mask) {
633 if (encoded_reg_mask == Smi::null()) {
634 FAIL("No modified register information");
635 return;
636 }
637 const intptr_t reg_mask = Smi::Value(encoded_reg_mask);
638 for (intptr_t i = 0; i < kNumberOfCpuRegisters; i++) {
639 if (((1 << i) & reg_mask) != 0) {
640 const Register reg = static_cast<Register>(i);
641 FAIL("%s was modified", RegisterNames::RegisterName(reg));
642 }
643 }
644 }
645
646 void ReportMissingOrChangedEntries(const SubtypeTestCache& old_cache,
647 const SubtypeTestCache& new_cache) {
648 auto& cid_or_sig = Object::Handle(zone());
649 auto& type = AbstractType::Handle(zone());
650 auto& instance_type_args = TypeArguments::Handle(zone());
651 auto& instantiator_type_args = TypeArguments::Handle(zone());
652 auto& function_type_args = TypeArguments::Handle(zone());
653 auto& instance_parent_type_args = TypeArguments::Handle(zone());
654 auto& instance_delayed_type_args = TypeArguments::Handle(zone());
655 auto& old_result = Bool::Handle(zone());
656 auto& new_result = Bool::Handle(zone());
657 SafepointMutexLocker ml(
659 intptr_t i = 0;
660 while (old_cache.GetNextCheck(&i, &cid_or_sig, &type, &instance_type_args,
661 &instantiator_type_args, &function_type_args,
662 &instance_parent_type_args,
663 &instance_delayed_type_args, &old_result)) {
664 if (!new_cache.HasCheck(
665 cid_or_sig, type, instance_type_args, instantiator_type_args,
666 function_type_args, instance_parent_type_args,
667 instance_delayed_type_args, /*index=*/nullptr, &new_result)) {
668 FAIL("New STC is missing check in old STC");
669 }
670 if (old_result.value() != new_result.value()) {
671 FAIL("New STC has different result from old STC");
672 }
673 }
674 }
675
676 // Returns whether the given test case is expected to have updated the STC.
677 // Should only be called after TTS invocation, as it uses last_tested_type_.
678 bool ShouldUpdateCache(const TTSTestCase& test_case) const {
679 if (test_case.expected_result == kSpecialize) {
680 // Check to see if we got a default stub from specializing. Should match
681 // the definition of would_update_cache_if_not_lazy in DRT_TypeCheck.
682 const bool would_update_cache_if_not_lazy =
683 !test_case.instance.IsNull() &&
684 (last_tested_type_.type_test_stub() ==
685 StubCode::DefaultNullableTypeTest().ptr() ||
686 last_tested_type_.type_test_stub() ==
687 StubCode::DefaultTypeTest().ptr());
688 return would_update_cache_if_not_lazy && previous_stc_.IsNull();
689 } else {
690 return test_case.expected_result == kNewSTCEntry;
691 }
692 }
693
694 void ReportUnexpectedSTCChanges(const TTSTestCase& test_case) {
695 const bool hit_check_cap =
696 !previous_stc_.IsNull() &&
697 previous_stc_.NumberOfChecks() >= FLAG_max_subtype_cache_entries;
698 EXPECT(test_case.expected_result != kRuntimeCheck || hit_check_cap);
699 const bool should_update_cache = ShouldUpdateCache(test_case);
700 if (should_update_cache) {
701 // We should not have already had an entry.
702 EXPECT(!test_case.HasSTCEntry(previous_stc_, type_));
703 // We should have changed the STC to include the new entry.
704 EXPECT(!last_stc_.IsNull());
705 if (!last_stc_.IsNull()) {
706 EXPECT(previous_stc_.IsNull() ||
707 previous_stc_.cache() != last_stc_.cache());
708 // We only should have added one check.
709 EXPECT_EQ(
710 previous_stc_.IsNull() ? 1 : previous_stc_.NumberOfChecks() + 1,
711 last_stc_.NumberOfChecks());
712 if (!previous_stc_.IsNull()) {
713 // Make sure all the checks in the previous STC are still there.
714 ReportMissingOrChangedEntries(previous_stc_, last_stc_);
715 }
716 }
717 } else {
718 // Whatever STC existed before, if any, should be unchanged.
719 if (previous_stc_.IsNull()) {
720 EXPECT(last_stc_.IsNull());
721 } else {
722 EXPECT(!last_stc_.IsNull());
723 const auto& previous_array =
724 Array::Handle(zone(), previous_stc_.cache());
725 const auto& last_array = Array::Handle(zone(), last_stc_.cache());
726 EXPECT(last_array.Equals(previous_array));
727 }
728 }
729
730 const bool has_stc_entry = test_case.HasSTCEntry(last_stc_, type_);
731 // We also expect an entry if we expect an STC hit in the STC stub or in
732 // the runtime.
733 const bool expects_stc_entry =
734 should_update_cache || test_case.expected_result == kExistingSTCEntry;
735 if ((!expects_stc_entry && has_stc_entry) ||
736 (expects_stc_entry && !has_stc_entry)) {
737 ZoneTextBuffer buffer(zone());
738 buffer.Printf("%s STC entry for:\n instance:%s\n destination type: %s",
739 expects_stc_entry ? "Expected" : "Did not expect",
740 test_case.instance.ToCString(), type_.ToCString());
741 if (last_tested_type_.ptr() != type_.ptr()) {
742 buffer.Printf("\n tested type: %s", last_tested_type_.ToCString());
743 }
744 buffer.AddString("\ngot:");
745 if (last_stc_.IsNull()) {
746 buffer.AddString(" null");
747 } else {
748 buffer.AddString("\n");
749 SafepointMutexLocker ml(
751 last_stc_.WriteToBuffer(zone(), &buffer, " ");
752 }
753 THR_Print("%s\n", buffer.buffer());
754 FAIL("unexpected STC modification");
755 }
756 }
757
758 Thread* const thread_;
759 const AbstractType& type_;
760 const Array& modified_abi_regs_box_;
761 const Array& modified_rest_regs_box_;
762 const Code& tts_invoker_;
763 const ObjectPool& pool_;
764 const Array& arguments_descriptor_;
765 Code& previous_tts_stub_;
766 SubtypeTestCache& previous_stc_;
767 Array& last_arguments_;
768 AbstractType& last_tested_type_;
769 Code& new_tts_stub_;
770 SubtypeTestCache& last_stc_;
771 Object& last_result_;
772};
773
774// Tests three situations in turn with the test case and with an
775// appropriate null object test:
776// 1) Install the lazy specialization stub for JIT and test.
777// 2) Test again without installing a stub, so using the stub resulting from 1.
778// 3) Install an eagerly specialized stub, similar to AOT mode but keeping any
779// STC created by the earlier steps, and test.
780static void RunTTSTest(const AbstractType& dst_type,
781 const TTSTestCase& test_case,
782 bool should_specialize = true) {
783 // We're testing null here, there's no need to call this with null.
784 EXPECT(!test_case.instance.IsNull());
785 // should_specialize is what governs whether specialization is expected here.
786 EXPECT_NE(kSpecialize, test_case.expected_result);
787 EXPECT_NE(kRespecialize, test_case.expected_result);
788 // We're creating a new STC so it _can't_ use an existing entry.
789 EXPECT_NE(kExistingSTCEntry, test_case.expected_result);
790
791 bool null_should_fail = !Instance::NullIsAssignableTo(
792 dst_type, test_case.instantiator_tav, test_case.function_tav);
793 const TTSTestCase null_test{Instance::Handle(), test_case.instantiator_tav,
794 test_case.function_tav,
795 null_should_fail ? kFail : kTTS};
796
797 // First test the lazy case.
798 {
800
801 // We only get specialization when testing null if the type being tested
802 // isn't nullable and it is either a Type or a RecordType.
803 const auto& type_to_test =
804 AbstractType::Handle(state.TypeToTest(test_case));
805 const bool null_should_specialize =
806 null_should_fail &&
807 (type_to_test.IsType() || type_to_test.IsRecordType());
808
809 // First check the null case. This should _never_ create an STC.
810 state.InvokeLazilySpecializedStub(null_test, null_should_specialize);
811 state.InvokeExistingStub(null_test);
812 EXPECT(state.last_stc().IsNull());
813
814 // Now run the actual test case, starting with a fresh stub.
815 state.InvokeLazilySpecializedStub(test_case, should_specialize);
816 if (test_case.expected_result == kNewSTCEntry) {
817 if (state.last_stc().IsNull()) {
818 // The stub specialized to a non-default stub, so we need to run the
819 // test again to see it added to the cache.
820 state.InvokeExistingStub(test_case);
821 } else {
822 // The stub doesn't specialize or it specialized to a default stub,
823 // in which case the entry did get added to the STC already.
824 }
825 state.InvokeExistingStub(STCCheck(test_case));
826 } else {
827 state.InvokeExistingStub(test_case);
828 }
829 }
830
831 // Now test the eager case with a fresh stub/null STC.
832 {
834
835 // First check the null case. This should _never_ create an STC.
836 state.InvokeEagerlySpecializedStub(null_test);
837 state.InvokeExistingStub(null_test);
838 EXPECT(state.last_stc().IsNull());
839
840 state.InvokeEagerlySpecializedStub(test_case);
841 if (test_case.expected_result == kNewSTCEntry) {
842 // When eagerly specializing, the first invocation always adds to the STC.
843 state.InvokeExistingStub(STCCheck(test_case));
844 } else {
845 // Other cases should be idempotent.
846 state.InvokeExistingStub(test_case);
847 }
848 }
849}
850
852 R"(
853 class I<T, U> {}
854 class I2 {}
855
856 class Base<T> {}
857
858 class A extends Base<int> {}
859 class A1 extends A implements I2 {}
860 class A2<T> extends A implements I<int, T> {}
861
862 class B extends Base<String> {}
863 class B1 extends B implements I2 {}
864 class B2<T> extends B implements I<T, String> {}
865
866 genericFun<A, B>() {}
867
868 createI() => I<int, String>();
869 createI2() => I2();
870 createBaseInt() => Base<int>();
871 createBaseNull() => Base<Null>();
872 createBaseNever() => Base<Never>();
873 createA() => A();
874 createA1() => A1();
875 createA2() => A2<int>();
876 createB() => B();
877 createB1() => B1();
878 createB2() => B2<int>();
879 createBaseIStringDouble() => Base<I<String, double>>();
880 createBaseA2Int() => Base<A2<int>>();
881 createBaseA2A1() => Base<A2<A1>>();
882 createBaseB2Int() => Base<B2<int>>();
883)";
884
885ISOLATE_UNIT_TEST_CASE(TTS_SubtypeRangeCheck) {
886 const auto& root_library =
888 const auto& class_a = Class::Handle(GetClass(root_library, "A"));
889 const auto& class_base = Class::Handle(GetClass(root_library, "Base"));
890 const auto& class_i = Class::Handle(GetClass(root_library, "I"));
891 const auto& class_i2 = Class::Handle(GetClass(root_library, "I2"));
892
893 const auto& obj_i = Object::Handle(Invoke(root_library, "createI"));
894 const auto& obj_i2 = Object::Handle(Invoke(root_library, "createI2"));
895 const auto& obj_base_int =
896 Object::Handle(Invoke(root_library, "createBaseInt"));
897 const auto& obj_base_null =
898 Object::Handle(Invoke(root_library, "createBaseNull"));
899 const auto& obj_base_never =
900 Object::Handle(Invoke(root_library, "createBaseNever"));
901 const auto& obj_a = Object::Handle(Invoke(root_library, "createA"));
902 const auto& obj_a1 = Object::Handle(Invoke(root_library, "createA1"));
903 const auto& obj_a2 = Object::Handle(Invoke(root_library, "createA2"));
904 const auto& obj_b = Object::Handle(Invoke(root_library, "createB"));
905 const auto& obj_b1 = Object::Handle(Invoke(root_library, "createB1"));
906 const auto& obj_b2 = Object::Handle(Invoke(root_library, "createB2"));
907
908 const auto& type_dynamic = Type::Handle(Type::DynamicType());
909 auto& type_object = Type::Handle(Type::ObjectType());
910 type_object = type_object.ToNullability(Nullability::kNullable, Heap::kNew);
911
912 const auto& tav_null = TypeArguments::Handle(TypeArguments::null());
913
914 auto& tav_object = TypeArguments::Handle(TypeArguments::New(1));
915 tav_object.SetTypeAt(0, type_object);
916 CanonicalizeTAV(&tav_object);
917
918 auto& tav_object_dynamic = TypeArguments::Handle(TypeArguments::New(2));
919 tav_object_dynamic.SetTypeAt(0, type_object);
920 tav_object_dynamic.SetTypeAt(1, type_dynamic);
921 CanonicalizeTAV(&tav_object_dynamic);
922
923 auto& tav_dynamic_t = TypeArguments::Handle(TypeArguments::New(2));
924 tav_dynamic_t.SetTypeAt(0, type_dynamic);
925 tav_dynamic_t.SetTypeAt(
927 CanonicalizeTAV(&tav_dynamic_t);
928
929 // We will generate specialized TTS for instantiated interface types
930 // where there are no type arguments or the type arguments are top
931 // types.
932 //
933 // obj as A // Subclass ranges
934 // obj as Base<Object?> // Subclass ranges with top-type tav
935 // obj as I2 // Subtype ranges
936 // obj as I<Object?, dynamic> // Subtype ranges with top-type tav
937 //
938
939 // <...> as A
940 const auto& type_a = AbstractType::Handle(class_a.RareType());
941 RunTTSTest(type_a, Failure({obj_i, tav_null, tav_null}));
942 RunTTSTest(type_a, Failure({obj_i2, tav_null, tav_null}));
943 RunTTSTest(type_a, Failure({obj_base_int, tav_null, tav_null}));
944 RunTTSTest(type_a, {obj_a, tav_null, tav_null});
945 RunTTSTest(type_a, {obj_a1, tav_null, tav_null});
946 RunTTSTest(type_a, {obj_a2, tav_null, tav_null});
947 RunTTSTest(type_a, Failure({obj_b, tav_null, tav_null}));
948 RunTTSTest(type_a, Failure({obj_b1, tav_null, tav_null}));
949 RunTTSTest(type_a, Failure({obj_b2, tav_null, tav_null}));
950
951 // <...> as Base<Object?>
952 auto& type_base = AbstractType::Handle(Type::New(class_base, tav_object));
953 FinalizeAndCanonicalize(&type_base);
954 RunTTSTest(type_base, Failure({obj_i, tav_null, tav_null}));
955 RunTTSTest(type_base, Failure({obj_i2, tav_null, tav_null}));
956 RunTTSTest(type_base, {obj_base_int, tav_null, tav_null});
957 RunTTSTest(type_base, {obj_base_null, tav_null, tav_null});
958 RunTTSTest(type_base, {obj_a, tav_null, tav_null});
959 RunTTSTest(type_base, {obj_a1, tav_null, tav_null});
960 RunTTSTest(type_base, {obj_a2, tav_null, tav_null});
961 RunTTSTest(type_base, {obj_b, tav_null, tav_null});
962 RunTTSTest(type_base, {obj_b1, tav_null, tav_null});
963 RunTTSTest(type_base, {obj_b2, tav_null, tav_null});
964
965 // Base<Null|Never> as Base<int?>
966 // This is a regression test verifying that we don't fall through into
967 // runtime for Null and Never.
968 auto& type_nullable_int = Type::Handle(Type::IntType());
969 type_nullable_int = type_nullable_int.ToNullability(
971 Heap::kNew);
972 auto& tav_nullable_int = TypeArguments::Handle(TypeArguments::New(1));
973 tav_nullable_int.SetTypeAt(0, type_nullable_int);
974 CanonicalizeTAV(&tav_nullable_int);
975 auto& type_base_nullable_int =
976 AbstractType::Handle(Type::New(class_base, tav_nullable_int));
977 FinalizeAndCanonicalize(&type_base_nullable_int);
978 RunTTSTest(type_base_nullable_int, {obj_base_null, tav_null, tav_null});
979 RunTTSTest(type_base_nullable_int, {obj_base_never, tav_null, tav_null});
980
981 if (TestCase::IsNNBD()) {
982 // Base<Null|Never> as Base<int>
983 auto& type_int = Type::Handle(Type::IntType());
984 type_int = type_int.ToNullability(Nullability::kNonNullable, Heap::kNew);
985 auto& tav_int = TypeArguments::Handle(TypeArguments::New(1));
986 tav_int.SetTypeAt(0, type_int);
987 CanonicalizeTAV(&tav_int);
988 auto& type_base_int = Type::Handle(Type::New(class_base, tav_int));
989 type_base_int =
990 type_base_int.ToNullability(Nullability::kNonNullable, Heap::kNew);
991 FinalizeAndCanonicalize(&type_base_int);
992 RunTTSTest(type_base_int, Failure({obj_base_null, tav_null, tav_null}));
993 RunTTSTest(type_base_int, {obj_base_never, tav_null, tav_null});
994 }
995
996 // <...> as I2
997 const auto& type_i2 = AbstractType::Handle(class_i2.RareType());
998 RunTTSTest(type_i2, Failure({obj_i, tav_null, tav_null}));
999 RunTTSTest(type_i2, {obj_i2, tav_null, tav_null});
1000 RunTTSTest(type_i2, Failure({obj_base_int, tav_null, tav_null}));
1001 RunTTSTest(type_i2, Failure({obj_a, tav_null, tav_null}));
1002 RunTTSTest(type_i2, {obj_a1, tav_null, tav_null});
1003 RunTTSTest(type_i2, Failure({obj_a2, tav_null, tav_null}));
1004 RunTTSTest(type_i2, Failure({obj_b, tav_null, tav_null}));
1005 RunTTSTest(type_i2, {obj_b1, tav_null, tav_null});
1006 RunTTSTest(type_i2, Failure({obj_b2, tav_null, tav_null}));
1007
1008 // <...> as I<Object, dynamic>
1009 auto& type_i_object_dynamic =
1010 AbstractType::Handle(Type::New(class_i, tav_object_dynamic));
1011 FinalizeAndCanonicalize(&type_i_object_dynamic);
1012 RunTTSTest(type_i_object_dynamic, {obj_i, tav_null, tav_null});
1013 RunTTSTest(type_i_object_dynamic, Failure({obj_i2, tav_null, tav_null}));
1014 RunTTSTest(type_i_object_dynamic,
1015 Failure({obj_base_int, tav_null, tav_null}));
1016 RunTTSTest(type_i_object_dynamic, Failure({obj_a, tav_null, tav_null}));
1017 RunTTSTest(type_i_object_dynamic, Failure({obj_a1, tav_null, tav_null}));
1018 RunTTSTest(type_i_object_dynamic, {obj_a2, tav_null, tav_null});
1019 RunTTSTest(type_i_object_dynamic, Failure({obj_b, tav_null, tav_null}));
1020 RunTTSTest(type_i_object_dynamic, Failure({obj_b1, tav_null, tav_null}));
1021 RunTTSTest(type_i_object_dynamic, {obj_b2, tav_null, tav_null});
1022
1023 // We do generate TTSes for uninstantiated types when we need to use
1024 // subtype range checks for the class of the interface type, but the TTS
1025 // may be partial (returns a false negative in some cases that means going
1026 // to the STC/runtime).
1027 //
1028 // obj as I<dynamic, T>
1029 //
1030 auto& type_dynamic_t =
1031 AbstractType::Handle(Type::New(class_i, tav_dynamic_t));
1032 FinalizeAndCanonicalize(&type_dynamic_t);
1033 RunTTSTest(type_dynamic_t, {obj_i, tav_object, tav_null});
1034 RunTTSTest(type_dynamic_t, Failure({obj_i2, tav_object, tav_null}));
1035 RunTTSTest(type_dynamic_t, Failure({obj_base_int, tav_object, tav_null}));
1036 RunTTSTest(type_dynamic_t, Failure({obj_a, tav_object, tav_null}));
1037 RunTTSTest(type_dynamic_t, Failure({obj_a1, tav_object, tav_null}));
1038 RunTTSTest(type_dynamic_t, {obj_a2, tav_object, tav_null});
1039 RunTTSTest(type_dynamic_t, Failure({obj_b, tav_object, tav_null}));
1040 RunTTSTest(type_dynamic_t, Failure({obj_b1, tav_object, tav_null}));
1041 RunTTSTest(type_dynamic_t, FalseNegative({obj_b2, tav_object, tav_null}));
1042
1043 // obj as Object (with null safety)
1044 auto isolate_group = IsolateGroup::Current();
1045 auto& type_non_nullable_object =
1046 Type::Handle(isolate_group->object_store()->non_nullable_object_type());
1047 RunTTSTest(type_non_nullable_object, {obj_a, tav_null, tav_null});
1048}
1049
1050ISOLATE_UNIT_TEST_CASE(TTS_GenericSubtypeRangeCheck) {
1051 const auto& root_library =
1053 const auto& class_a1 = Class::Handle(GetClass(root_library, "A1"));
1054 const auto& class_a2 = Class::Handle(GetClass(root_library, "A2"));
1055 const auto& class_base = Class::Handle(GetClass(root_library, "Base"));
1056 const auto& class_i = Class::Handle(GetClass(root_library, "I"));
1057 const auto& fun_generic =
1058 Function::Handle(GetFunction(root_library, "genericFun"));
1059
1060 const auto& obj_i = Object::Handle(Invoke(root_library, "createI"));
1061 const auto& obj_i2 = Object::Handle(Invoke(root_library, "createI2"));
1062 const auto& obj_base_int =
1063 Object::Handle(Invoke(root_library, "createBaseInt"));
1064 const auto& obj_a = Object::Handle(Invoke(root_library, "createA"));
1065 const auto& obj_a1 = Object::Handle(Invoke(root_library, "createA1"));
1066 const auto& obj_a2 = Object::Handle(Invoke(root_library, "createA2"));
1067 const auto& obj_b = Object::Handle(Invoke(root_library, "createB"));
1068 const auto& obj_b1 = Object::Handle(Invoke(root_library, "createB1"));
1069 const auto& obj_b2 = Object::Handle(Invoke(root_library, "createB2"));
1070 const auto& obj_basea2int =
1071 Object::Handle(Invoke(root_library, "createBaseA2Int"));
1072 const auto& obj_basea2a1 =
1073 Object::Handle(Invoke(root_library, "createBaseA2A1"));
1074 const auto& obj_baseb2int =
1075 Object::Handle(Invoke(root_library, "createBaseB2Int"));
1076 const auto& obj_baseistringdouble =
1077 Object::Handle(Invoke(root_library, "createBaseIStringDouble"));
1078
1079 const auto& type_dynamic = Type::Handle(Type::DynamicType());
1080 auto& type_int = Type::Handle(Type::IntType());
1081 if (!TestCase::IsNNBD()) {
1082 type_int = type_int.ToNullability(Nullability::kLegacy, Heap::kNew);
1083 }
1084 auto& type_string = Type::Handle(Type::StringType());
1085 if (!TestCase::IsNNBD()) {
1086 type_string = type_string.ToNullability(Nullability::kLegacy, Heap::kNew);
1087 }
1088 auto& type_object = Type::Handle(Type::ObjectType());
1089 type_object = type_object.ToNullability(
1091 Heap::kNew);
1092 auto& type_a1 = Type::Handle(class_a1.DeclarationType());
1093 if (!TestCase::IsNNBD()) {
1094 type_a1 = type_a1.ToNullability(Nullability::kLegacy, Heap::kNew);
1095 }
1096 FinalizeAndCanonicalize(&type_a1);
1097
1098 const auto& tav_null = TypeArguments::Handle(TypeArguments::null());
1099
1100 auto& tav_object_dynamic = TypeArguments::Handle(TypeArguments::New(2));
1101 tav_object_dynamic.SetTypeAt(0, type_object);
1102 tav_object_dynamic.SetTypeAt(1, type_dynamic);
1103 CanonicalizeTAV(&tav_object_dynamic);
1104
1105 auto& tav_dynamic_int = TypeArguments::Handle(TypeArguments::New(2));
1106 tav_dynamic_int.SetTypeAt(0, type_dynamic);
1107 tav_dynamic_int.SetTypeAt(1, type_int);
1108 CanonicalizeTAV(&tav_dynamic_int);
1109
1110 auto& tav_dynamic_string = TypeArguments::Handle(TypeArguments::New(2));
1111 tav_dynamic_string.SetTypeAt(0, type_dynamic);
1112 tav_dynamic_string.SetTypeAt(1, type_string);
1113 CanonicalizeTAV(&tav_dynamic_string);
1114
1115 auto& tav_int = TypeArguments::Handle(TypeArguments::New(1));
1116 tav_int.SetTypeAt(0, type_int);
1117 CanonicalizeTAV(&tav_int);
1118
1119 auto& type_i_object_dynamic =
1120 AbstractType::Handle(Type::New(class_i, tav_object_dynamic));
1121 FinalizeAndCanonicalize(&type_i_object_dynamic);
1122 const auto& tav_iod = TypeArguments::Handle(TypeArguments::New(1));
1123 tav_iod.SetTypeAt(0, type_i_object_dynamic);
1124
1125 // We will generate specialized TTS for instantiated interface types
1126 // where there are no type arguments or the type arguments are top
1127 // types.
1128 //
1129 // obj as Base<I<Object, dynamic>> // Subclass ranges for Base, subtype
1130 // // ranges tav arguments.
1131 // obj as Base<T> // Subclass ranges for Base, type
1132 // // equality for instantiator type arg T
1133 // obj as Base<B> // Subclass ranges for Base, type
1134 // // equality for function type arg B.
1135 //
1136
1137 // <...> as Base<I<Object, dynamic>>
1138 auto& type_base_i_object_dynamic =
1139 AbstractType::Handle(Type::New(class_base, tav_iod));
1140 FinalizeAndCanonicalize(&type_base_i_object_dynamic);
1141 RunTTSTest(type_base_i_object_dynamic, {obj_baseb2int, tav_null, tav_null});
1142 RunTTSTest(type_base_i_object_dynamic,
1143 {obj_baseistringdouble, tav_null, tav_null});
1144 RunTTSTest(type_base_i_object_dynamic, Failure({obj_a, tav_null, tav_null}));
1145 RunTTSTest(type_base_i_object_dynamic, Failure({obj_a1, tav_null, tav_null}));
1146 RunTTSTest(type_base_i_object_dynamic, Failure({obj_a2, tav_null, tav_null}));
1147 RunTTSTest(type_base_i_object_dynamic, Failure({obj_b, tav_null, tav_null}));
1148 RunTTSTest(type_base_i_object_dynamic, Failure({obj_b1, tav_null, tav_null}));
1149 RunTTSTest(type_base_i_object_dynamic, Failure({obj_b2, tav_null, tav_null}));
1150
1151 // <...> as Base<T> with T instantiantiator type parameter (T == int)
1152 const auto& tav_baset = TypeArguments::Handle(TypeArguments::New(1));
1153 tav_baset.SetTypeAt(
1154 0, TypeParameter::Handle(GetClassTypeParameter(class_base, 0)));
1155 auto& type_base_t = AbstractType::Handle(Type::New(class_base, tav_baset));
1156 FinalizeAndCanonicalize(&type_base_t);
1157 RunTTSTest(type_base_t, {obj_base_int, tav_int, tav_null});
1158 RunTTSTest(type_base_t, Failure({obj_baseistringdouble, tav_int, tav_null}));
1159
1160 // <...> as Base<B> with B function type parameter
1161 const auto& tav_baseb = TypeArguments::Handle(TypeArguments::New(1));
1162 tav_baseb.SetTypeAt(
1164 auto& type_base_b = AbstractType::Handle(Type::New(class_base, tav_baseb));
1165 FinalizeAndCanonicalize(&type_base_b);
1166 // With B == int
1167 RunTTSTest(type_base_b, {obj_base_int, tav_null, tav_dynamic_int});
1168 RunTTSTest(type_base_b,
1169 Failure({obj_baseistringdouble, tav_null, tav_dynamic_int}));
1170 // With B == dynamic (null vector)
1171 RunTTSTest(type_base_b, {obj_base_int, tav_null, tav_null});
1172 RunTTSTest(type_base_b, Failure({obj_i2, tav_null, tav_null}));
1173
1174 // We generate TTS for implemented classes and uninstantiated types, but
1175 // any class that implements the type class but does not match in both
1176 // instance TAV offset and type argument indices is guaranteed to be a
1177 // false negative.
1178 //
1179 // obj as I<dynamic, String> // I is generic & implemented.
1180 // obj as Base<A2<T>> // A2<T> is not instantiated.
1181 // obj as Base<A2<A1>> // A2<A1> is not a rare type.
1182 //
1183
1184 // <...> as I<dynamic, String>
1185 RELEASE_ASSERT(class_i.is_implemented());
1186 auto& type_i_dynamic_string =
1187 Type::Handle(Type::New(class_i, tav_dynamic_string));
1188 type_i_dynamic_string = type_i_dynamic_string.ToNullability(
1190 FinalizeAndCanonicalize(&type_i_dynamic_string);
1191 RunTTSTest(type_i_dynamic_string, {obj_i, tav_null, tav_null});
1192 RunTTSTest(type_i_dynamic_string,
1193 Failure({obj_base_int, tav_null, tav_null}));
1194
1195 // <...> as Base<A2<T>>
1196 const auto& tav_t = TypeArguments::Handle(TypeArguments::New(1));
1197 tav_t.SetTypeAt(0,
1199 auto& type_a2_t = Type::Handle(Type::New(class_a2, tav_t));
1200 type_a2_t = type_a2_t.ToNullability(Nullability::kLegacy, Heap::kNew);
1201 FinalizeAndCanonicalize(&type_a2_t);
1202 const auto& tav_a2_t = TypeArguments::Handle(TypeArguments::New(1));
1203 tav_a2_t.SetTypeAt(0, type_a2_t);
1204 auto& type_base_a2_t = Type::Handle(Type::New(class_base, tav_a2_t));
1205 type_base_a2_t =
1206 type_base_a2_t.ToNullability(Nullability::kNonNullable, Heap::kNew);
1207 FinalizeAndCanonicalize(&type_base_a2_t);
1208 RunTTSTest(type_base_a2_t,
1209 FalseNegative({obj_basea2int, tav_null, tav_null}));
1210 RunTTSTest(type_base_a2_t, Failure({obj_base_int, tav_null, tav_null}));
1211
1212 // <...> as Base<A2<A1>>
1213 const auto& tav_a1 = TypeArguments::Handle(TypeArguments::New(1));
1214 tav_a1.SetTypeAt(0, type_a1);
1215 auto& type_a2_a1 = Type::Handle(Type::New(class_a2, tav_a1));
1216 type_a2_a1 = type_a2_a1.ToNullability(Nullability::kLegacy, Heap::kNew);
1217 FinalizeAndCanonicalize(&type_a2_a1);
1218 const auto& tav_a2_a1 = TypeArguments::Handle(TypeArguments::New(1));
1219 tav_a2_a1.SetTypeAt(0, type_a2_a1);
1220 auto& type_base_a2_a1 = Type::Handle(Type::New(class_base, tav_a2_a1));
1221 type_base_a2_a1 =
1222 type_base_a2_a1.ToNullability(Nullability::kNonNullable, Heap::kNew);
1223 FinalizeAndCanonicalize(&type_base_a2_a1);
1224 RunTTSTest(type_base_a2_a1,
1225 FalseNegative({obj_basea2a1, tav_null, tav_null}));
1226 RunTTSTest(type_base_a2_a1, Failure({obj_basea2int, tav_null, tav_null}));
1227}
1228
1230 R"(
1231 class A {}
1232 class B extends A {}
1233 class C implements A {}
1234 class D<T> {}
1235
1236 getType<T>() => T;
1237 getRecordType1() => getType<(int, A)>();
1238 getRecordType2() => getType<(A, int, String)>();
1239 getRecordType3() => getType<(int, D)>();
1240
1241 createObj1() => (1, B());
1242 createObj2() => (1, 'bye');
1243 createObj3() => (1, foo: B());
1244 createObj4() => (1, B(), 2);
1245 createObj5() => (C(), 2, 'hi');
1246 createObj6() => (D(), 2, 'hi');
1247 createObj7() => (3, D<int>());
1248 createObj8() => (D<int>(), 3);
1249)";
1250
1251ISOLATE_UNIT_TEST_CASE(TTS_RecordSubtypeRangeCheck) {
1252 const auto& root_library =
1254
1255 const auto& type1 = AbstractType::Cast(
1256 Object::Handle(Invoke(root_library, "getRecordType1")));
1257 const auto& type2 = AbstractType::Cast(
1258 Object::Handle(Invoke(root_library, "getRecordType2")));
1259 const auto& type3 = AbstractType::Cast(
1260 Object::Handle(Invoke(root_library, "getRecordType3")));
1261
1262 const auto& obj1 = Object::Handle(Invoke(root_library, "createObj1"));
1263 const auto& obj2 = Object::Handle(Invoke(root_library, "createObj2"));
1264 const auto& obj3 = Object::Handle(Invoke(root_library, "createObj3"));
1265 const auto& obj4 = Object::Handle(Invoke(root_library, "createObj4"));
1266 const auto& obj5 = Object::Handle(Invoke(root_library, "createObj5"));
1267 const auto& obj6 = Object::Handle(Invoke(root_library, "createObj6"));
1268 const auto& obj7 = Object::Handle(Invoke(root_library, "createObj7"));
1269 const auto& obj8 = Object::Handle(Invoke(root_library, "createObj8"));
1270
1271 const auto& tav_null = TypeArguments::Handle(TypeArguments::null());
1272
1273 // (1, B()) as (int, A)
1274 // (1, 'bye') as (int, A)
1275 // (1, foo: B()) as (int, A)
1276 // (1, B(), 2) as (int, A)
1277 RunTTSTest(type1, {obj1, tav_null, tav_null});
1278 RunTTSTest(type1, Failure({obj2, tav_null, tav_null}));
1279 RunTTSTest(type1, Failure({obj3, tav_null, tav_null}));
1280 RunTTSTest(type1, Failure({obj4, tav_null, tav_null}));
1281
1282 // (C(), 2, 'hi') as (A, int, String)
1283 // (D(), 2, 'hi') as (A, int, String)
1284 RunTTSTest(type2, {obj5, tav_null, tav_null});
1285 RunTTSTest(type2, Failure({obj6, tav_null, tav_null}));
1286
1287 // (3, D<int>()) as (int, D)
1288 // (D<int>(), 3) as (int, D)
1289 RunTTSTest(type3, {obj7, tav_null, tav_null});
1290 RunTTSTest(type3, Failure({obj8, tav_null, tav_null}));
1291}
1292
1293ISOLATE_UNIT_TEST_CASE(TTS_Generic_Implements_Instantiated_Interface) {
1294 const char* kScript =
1295 R"(
1296 abstract class I<T> {}
1297 class B<R> implements I<String> {}
1298
1299 createBInt() => B<int>();
1300)";
1301
1302 const auto& root_library = Library::Handle(LoadTestScript(kScript));
1303 const auto& class_i = Class::Handle(GetClass(root_library, "I"));
1304 const auto& obj_b_int = Object::Handle(Invoke(root_library, "createBInt"));
1305
1306 const auto& tav_null = Object::null_type_arguments();
1307 auto& tav_string = TypeArguments::Handle(TypeArguments::New(1));
1308 tav_string.SetTypeAt(0, Type::Handle(Type::StringType()));
1309 CanonicalizeTAV(&tav_string);
1310
1311 auto& type_i_string = Type::Handle(Type::New(class_i, tav_string));
1312 FinalizeAndCanonicalize(&type_i_string);
1313 const auto& type_i_t = Type::Handle(class_i.DeclarationType());
1314
1315 RunTTSTest(type_i_string, {obj_b_int, tav_null, tav_null});
1316 // Optimized TTSees don't currently handle the case where the implemented
1317 // type is known, but the type being checked requires instantiation at
1318 // runtime.
1319 RunTTSTest(type_i_t, FalseNegative({obj_b_int, tav_string, tav_null}));
1320}
1321
1322ISOLATE_UNIT_TEST_CASE(TTS_Future) {
1323 const char* kScript =
1324 R"(
1325 import "dart:async";
1326
1327 Future<int> createFutureInt() async => 3;
1328 Future<int Function()> createFutureFunction() async => () => 3;
1329 Future<int Function()?> createFutureNullableFunction() async =>
1330 (() => 3) as int Function()?;
1331)";
1332
1334
1335 const auto& class_future =
1336 Class::Handle(IsolateGroup::Current()->object_store()->future_class());
1337
1338 const auto& root_library = Library::Handle(LoadTestScript(kScript));
1339 const auto& class_closure =
1340 Class::Handle(IsolateGroup::Current()->object_store()->closure_class());
1341 const auto& obj_futureint =
1342 Object::Handle(Invoke(root_library, "createFutureInt"));
1343 const auto& obj_futurefunction =
1344 Object::Handle(Invoke(root_library, "createFutureFunction"));
1345 const auto& obj_futurenullablefunction =
1346 Object::Handle(Invoke(root_library, "createFutureNullableFunction"));
1347
1348 const auto& tav_null = Object::null_type_arguments();
1349 const auto& type_object = Type::Handle(
1350 IsolateGroup::Current()->object_store()->non_nullable_object_type());
1351 const auto& type_legacy_object = Type::Handle(
1352 IsolateGroup::Current()->object_store()->legacy_object_type());
1353 const auto& type_nullable_object = Type::Handle(
1354 IsolateGroup::Current()->object_store()->nullable_object_type());
1355 const auto& type_int = Type::Handle(
1356 IsolateGroup::Current()->object_store()->non_nullable_int_type());
1357
1358 auto& type_string = Type::Handle(Type::StringType());
1359 type_string =
1360 type_string.ToNullability(Nullability::kNonNullable, Heap::kNew);
1361 FinalizeAndCanonicalize(&type_string);
1362 auto& type_num = Type::Handle(Type::Number());
1363 type_num = type_num.ToNullability(Nullability::kNonNullable, Heap::kNew);
1364 FinalizeAndCanonicalize(&type_num);
1365
1366 auto& tav_dynamic = TypeArguments::Handle(TypeArguments::New(1));
1367 tav_dynamic.SetTypeAt(0, Object::dynamic_type());
1368 CanonicalizeTAV(&tav_dynamic);
1369 auto& tav_object = TypeArguments::Handle(TypeArguments::New(1));
1370 tav_object.SetTypeAt(0, type_object);
1371 CanonicalizeTAV(&tav_object);
1372 auto& tav_legacy_object = TypeArguments::Handle(TypeArguments::New(1));
1373 tav_legacy_object.SetTypeAt(0, type_legacy_object);
1374 CanonicalizeTAV(&tav_legacy_object);
1375 auto& tav_nullable_object = TypeArguments::Handle(TypeArguments::New(1));
1376 tav_nullable_object.SetTypeAt(0, type_nullable_object);
1377 CanonicalizeTAV(&tav_nullable_object);
1378 auto& tav_int = TypeArguments::Handle(TypeArguments::New(1));
1379 tav_int.SetTypeAt(0, type_int);
1380 CanonicalizeTAV(&tav_int);
1381 auto& tav_num = TypeArguments::Handle(TypeArguments::New(1));
1382 tav_num.SetTypeAt(0, type_num);
1383 CanonicalizeTAV(&tav_num);
1384 auto& tav_string = TypeArguments::Handle(TypeArguments::New(1));
1385 tav_string.SetTypeAt(0, type_string);
1386 CanonicalizeTAV(&tav_string);
1387
1388 auto& type_future = Type::Handle(
1389 Type::New(class_future, tav_null, Nullability::kNonNullable));
1390 FinalizeAndCanonicalize(&type_future);
1391 auto& type_future_dynamic = Type::Handle(
1392 Type::New(class_future, tav_dynamic, Nullability::kNonNullable));
1393 FinalizeAndCanonicalize(&type_future_dynamic);
1394 auto& type_future_object = Type::Handle(
1395 Type::New(class_future, tav_object, Nullability::kNonNullable));
1396 FinalizeAndCanonicalize(&type_future_object);
1397 auto& type_future_legacy_object = Type::Handle(
1398 Type::New(class_future, tav_legacy_object, Nullability::kNonNullable));
1399 FinalizeAndCanonicalize(&type_future_legacy_object);
1400 auto& type_future_nullable_object = Type::Handle(
1401 Type::New(class_future, tav_nullable_object, Nullability::kNonNullable));
1402 FinalizeAndCanonicalize(&type_future_nullable_object);
1403 auto& type_future_int =
1404 Type::Handle(Type::New(class_future, tav_int, Nullability::kNonNullable));
1405 FinalizeAndCanonicalize(&type_future_int);
1406 auto& type_future_string = Type::Handle(
1407 Type::New(class_future, tav_string, Nullability::kNonNullable));
1408 FinalizeAndCanonicalize(&type_future_string);
1409 auto& type_future_num =
1410 Type::Handle(Type::New(class_future, tav_num, Nullability::kNonNullable));
1411 FinalizeAndCanonicalize(&type_future_num);
1412 const auto& type_future_t = Type::Handle(class_future.DeclarationType());
1413
1414 THR_Print("********************************************************\n");
1415 THR_Print(" Testing Future<int>\n");
1416 THR_Print("********************************************************\n\n");
1417
1418 // Some more tests of generic implemented classes, using Future. Here,
1419 // obj is an object of type Future<int>.
1420 //
1421 // True positives from TTS:
1422 // obj as Future : Null type args
1423 // obj as Future<dynamic> : Canonicalized to same as previous case.
1424 // obj as Future<Object?> : Type arg is top type
1425 // obj as Future<Object*> : Type arg is top type
1426 // obj as Future<Object> : Type arg is certain supertype
1427 // obj as Future<int> : Type arg is the same type
1428 // obj as Future<num> : Type arg is a supertype that can be matched
1429 // with cid range
1430 // obj as Future<X>, : Type arg is a type parameter instantiated with
1431 // X = int : ... the same type
1432 //
1433 RunTTSTest(type_future, {obj_futureint, tav_null, tav_null});
1434 RunTTSTest(type_future_dynamic, {obj_futureint, tav_null, tav_null});
1435 RunTTSTest(type_future_object, {obj_futureint, tav_null, tav_null});
1436 RunTTSTest(type_future_legacy_object, {obj_futureint, tav_null, tav_null});
1437 RunTTSTest(type_future_nullable_object, {obj_futureint, tav_null, tav_null});
1438 RunTTSTest(type_future_int, {obj_futureint, tav_null, tav_null});
1439 RunTTSTest(type_future_num, {obj_futureint, tav_null, tav_null});
1440 RunTTSTest(type_future_t, {obj_futureint, tav_int, tav_null});
1441
1442 // False negatives from TTS (caught by STC/runtime):
1443 // obj as Future<X>, : Type arg is a type parameter instantiated with
1444 // X = num : ... a supertype
1445 RunTTSTest(type_future_t, FalseNegative({obj_futureint, tav_num, tav_null}));
1446
1447 // Errors:
1448 // obj as Future<String> : Type arg is not a supertype
1449 // obj as Future<X>, : Type arg is a type parameter instantiated with
1450 // X = String : ... an unrelated type
1451 //
1452 RunTTSTest(type_future_string, Failure({obj_futureint, tav_null, tav_null}));
1453 RunTTSTest(type_future_t, Failure({obj_futureint, tav_string, tav_null}));
1454
1455 auto& type_function = Type::Handle(Type::DartFunctionType());
1456 type_function =
1457 type_function.ToNullability(Nullability::kNonNullable, Heap::kNew);
1458 FinalizeAndCanonicalize(&type_function);
1459 auto& type_legacy_function = Type::Handle(
1460 type_function.ToNullability(Nullability::kLegacy, Heap::kNew));
1461 FinalizeAndCanonicalize(&type_legacy_function);
1462 auto& type_nullable_function = Type::Handle(
1463 type_function.ToNullability(Nullability::kNullable, Heap::kOld));
1464 FinalizeAndCanonicalize(&type_nullable_function);
1465 auto& type_closure = Type::Handle(
1466 Type::New(class_closure, tav_null, Nullability::kNonNullable));
1467 FinalizeAndCanonicalize(&type_closure);
1468 auto& type_legacy_closure = Type::Handle(
1469 type_closure.ToNullability(Nullability::kLegacy, Heap::kOld));
1470 FinalizeAndCanonicalize(&type_legacy_closure);
1471 auto& type_nullable_closure = Type::Handle(
1472 type_closure.ToNullability(Nullability::kNullable, Heap::kOld));
1473 FinalizeAndCanonicalize(&type_nullable_closure);
1474 auto& type_function_int_nullary =
1476 // Testing with a closure, so it has an implicit parameter, and we want a
1477 // type that is canonically equal to the type of the closure.
1478 type_function_int_nullary.set_num_implicit_parameters(1);
1479 type_function_int_nullary.set_num_fixed_parameters(1);
1480 type_function_int_nullary.set_parameter_types(Array::Handle(Array::New(1)));
1481 type_function_int_nullary.SetParameterTypeAt(0, Type::dynamic_type());
1482 type_function_int_nullary.set_result_type(type_int);
1483 FinalizeAndCanonicalize(&type_function_int_nullary);
1484 auto& type_legacy_function_int_nullary =
1485 FunctionType::Handle(type_function_int_nullary.ToNullability(
1487 FinalizeAndCanonicalize(&type_legacy_function_int_nullary);
1488 auto& type_nullable_function_int_nullary =
1489 FunctionType::Handle(type_function_int_nullary.ToNullability(
1491 FinalizeAndCanonicalize(&type_nullable_function_int_nullary);
1492
1493 auto& tav_function = TypeArguments::Handle(TypeArguments::New(1));
1494 tav_function.SetTypeAt(0, type_function);
1495 CanonicalizeTAV(&tav_function);
1496 auto& tav_legacy_function = TypeArguments::Handle(TypeArguments::New(1));
1497 tav_legacy_function.SetTypeAt(0, type_legacy_function);
1498 CanonicalizeTAV(&tav_legacy_function);
1499 auto& tav_nullable_function = TypeArguments::Handle(TypeArguments::New(1));
1500 tav_nullable_function.SetTypeAt(0, type_nullable_function);
1501 CanonicalizeTAV(&tav_nullable_function);
1502 auto& tav_closure = TypeArguments::Handle(TypeArguments::New(1));
1503 tav_closure.SetTypeAt(0, type_closure);
1504 CanonicalizeTAV(&tav_closure);
1505 auto& tav_legacy_closure = TypeArguments::Handle(TypeArguments::New(1));
1506 tav_legacy_closure.SetTypeAt(0, type_legacy_closure);
1507 CanonicalizeTAV(&tav_legacy_closure);
1508 auto& tav_nullable_closure = TypeArguments::Handle(TypeArguments::New(1));
1509 tav_nullable_closure.SetTypeAt(0, type_nullable_closure);
1510 CanonicalizeTAV(&tav_nullable_closure);
1511 auto& tav_function_int_nullary = TypeArguments::Handle(TypeArguments::New(1));
1512 tav_function_int_nullary.SetTypeAt(0, type_function_int_nullary);
1513 CanonicalizeTAV(&tav_function_int_nullary);
1514 auto& tav_legacy_function_int_nullary =
1516 tav_legacy_function_int_nullary.SetTypeAt(0,
1517 type_legacy_function_int_nullary);
1518 CanonicalizeTAV(&tav_legacy_function_int_nullary);
1519 auto& tav_nullable_function_int_nullary =
1521 tav_nullable_function_int_nullary.SetTypeAt(
1522 0, type_nullable_function_int_nullary);
1523 CanonicalizeTAV(&tav_nullable_function_int_nullary);
1524
1525 auto& type_future_function = Type::Handle(
1526 Type::New(class_future, tav_function, Nullability::kNonNullable));
1527 FinalizeAndCanonicalize(&type_future_function);
1528 auto& type_future_legacy_function = Type::Handle(
1529 Type::New(class_future, tav_legacy_function, Nullability::kNonNullable));
1530 FinalizeAndCanonicalize(&type_future_legacy_function);
1531 auto& type_future_nullable_function = Type::Handle(Type::New(
1532 class_future, tav_nullable_function, Nullability::kNonNullable));
1533 FinalizeAndCanonicalize(&type_future_nullable_function);
1534 auto& type_future_closure = Type::Handle(
1535 Type::New(class_future, tav_closure, Nullability::kNonNullable));
1536 FinalizeAndCanonicalize(&type_future_closure);
1537 auto& type_future_legacy_closure = Type::Handle(
1538 Type::New(class_future, tav_legacy_closure, Nullability::kNonNullable));
1539 FinalizeAndCanonicalize(&type_future_legacy_closure);
1540 auto& type_future_nullable_closure = Type::Handle(
1541 Type::New(class_future, tav_nullable_closure, Nullability::kNonNullable));
1542 FinalizeAndCanonicalize(&type_future_nullable_closure);
1543 auto& type_future_function_int_nullary =
1544 Type::Handle(Type::New(class_future, tav_function_int_nullary));
1545 FinalizeAndCanonicalize(&type_future_function_int_nullary);
1546 auto& type_future_legacy_function_int_nullary =
1547 Type::Handle(Type::New(class_future, tav_legacy_function_int_nullary));
1548 FinalizeAndCanonicalize(&type_future_legacy_function_int_nullary);
1549 auto& type_future_nullable_function_int_nullary =
1550 Type::Handle(Type::New(class_future, tav_nullable_function_int_nullary));
1551 FinalizeAndCanonicalize(&type_future_nullable_function_int_nullary);
1552
1553 THR_Print("\n********************************************************\n");
1554 THR_Print(" Testing Future<int Function()>\n");
1555 THR_Print("********************************************************\n\n");
1556
1557 // And here, obj is an object of type Future<int Function()>. Note that
1558 // int Function() <: Function, but int Function() </: _Closure. That is,
1559 // _Closure is a separate subtype of Function from FunctionTypes.
1560 //
1561 // True positive from TTS:
1562 // obj as Future : Null type args
1563 // obj as Future<dynamic> : Canonicalized to same as previous case.
1564 // obj as Future<Object?> : Type arg is top type
1565 // obj as Future<Object*> : Type arg is top typ
1566 // obj as Future<Object> : Type arg is certain supertype
1567 // obj as Future<Function?> : Type arg is certain supertype
1568 // obj as Future<Function*> : Type arg is certain supertype
1569 // obj as Future<Function> : Type arg is certain supertype
1570 // obj as Future<X>, : Type arg is a type parameter instantiated with
1571 // X = dynamic : ... a top type
1572 // X = Object? : ... a top type
1573 // X = Object* : ... a top type
1574 // X = Object : ... a certain supertype
1575 // X = int Function() : ... the same type.
1576 //
1577 RunTTSTest(type_future, {obj_futurefunction, tav_null, tav_null});
1578 RunTTSTest(type_future_dynamic, {obj_futurefunction, tav_null, tav_null});
1579 RunTTSTest(type_future_nullable_object,
1580 {obj_futurefunction, tav_null, tav_null});
1581 RunTTSTest(type_future_legacy_object,
1582 {obj_futurefunction, tav_null, tav_null});
1583 RunTTSTest(type_future_object, {obj_futurefunction, tav_null, tav_null});
1584 RunTTSTest(type_future_nullable_object,
1585 {obj_futurefunction, tav_null, tav_null});
1586 RunTTSTest(type_future_legacy_object,
1587 {obj_futurefunction, tav_null, tav_null});
1588 RunTTSTest(type_future_object, {obj_futurefunction, tav_null, tav_null});
1589 RunTTSTest(type_future_nullable_function,
1590 {obj_futurefunction, tav_null, tav_null});
1591 RunTTSTest(type_future_legacy_function,
1592 {obj_futurefunction, tav_null, tav_null});
1593 RunTTSTest(type_future_function, {obj_futurefunction, tav_null, tav_null});
1594 RunTTSTest(type_future_t, {obj_futurefunction, tav_null, tav_null});
1595 RunTTSTest(type_future_t,
1596 {obj_futurefunction, tav_nullable_object, tav_null});
1597 RunTTSTest(type_future_t, {obj_futurefunction, tav_legacy_object, tav_null});
1598 RunTTSTest(type_future_t, {obj_futurefunction, tav_object, tav_null});
1599 RunTTSTest(type_future_t,
1600 {obj_futurefunction, tav_function_int_nullary, tav_null});
1601
1602 // False negative from TTS (caught by runtime or STC):
1603 // obj as Future<int Function()?> : No specialization.
1604 // obj as Future<int Function()*> : No specialization.
1605 // obj as Future<int Function()> : No specialization.
1606 // obj as Future<X>, : Type arg is a type parameter instantiated with
1607 // X = Function? : ... a certain supertype (but not checked)
1608 // X = Function* : ... a certain supertype (but not checked)
1609 // X = Function : ... a certain supertype (but not checked)
1610 // X = int Function()? : ... a canonically different type.
1611 // X = int Function()* : ... a canonically different type.
1612 //
1613 RunTTSTest(type_future_nullable_function_int_nullary,
1614 FalseNegative({obj_futurefunction, tav_null, tav_null}));
1615 RunTTSTest(type_future_legacy_function_int_nullary,
1616 FalseNegative({obj_futurefunction, tav_null, tav_null}));
1617 RunTTSTest(type_future_function_int_nullary,
1618 FalseNegative({obj_futurefunction, tav_null, tav_null}));
1619 RunTTSTest(type_future_t, FalseNegative({obj_futurefunction,
1620 tav_nullable_function, tav_null}));
1621 RunTTSTest(type_future_t, FalseNegative({obj_futurefunction,
1622 tav_legacy_function, tav_null}));
1623 RunTTSTest(type_future_t,
1624 FalseNegative({obj_futurefunction, tav_function, tav_null}));
1625 RunTTSTest(type_future_t,
1626 FalseNegative({obj_futurefunction,
1627 tav_nullable_function_int_nullary, tav_null}));
1628 RunTTSTest(type_future_t,
1629 FalseNegative({obj_futurefunction, tav_legacy_function_int_nullary,
1630 tav_null}));
1631
1632 // Errors:
1633 // obj as Future<_Closure?> : Type arg is not a supertype
1634 // obj as Future<_Closure*> : Type arg is not a supertype
1635 // obj as Future<_Closure> : Type arg is not a supertype
1636 // obj as Future<X>, : Type arg is a type parameter instantiated with
1637 // X = _Closure? : ... an unrelated type.
1638 // X = _Closure* : ... an unrelated type.
1639 // X = _Closure : ... an unrelated type.
1640 //
1641 RunTTSTest(type_future_nullable_closure,
1642 Failure({obj_futurefunction, tav_null, tav_null}));
1643 RunTTSTest(type_future_legacy_closure,
1644 Failure({obj_futurefunction, tav_null, tav_null}));
1645 RunTTSTest(type_future_closure,
1646 Failure({obj_futurefunction, tav_null, tav_null}));
1647 RunTTSTest(type_future_t,
1648 Failure({obj_futurefunction, tav_nullable_closure, tav_null}));
1649 RunTTSTest(type_future_t,
1650 Failure({obj_futurefunction, tav_legacy_closure, tav_null}));
1651 RunTTSTest(type_future_t,
1652 Failure({obj_futurefunction, tav_closure, tav_null}));
1653
1654 THR_Print("\n********************************************************\n");
1655 THR_Print(" Testing Future<int Function()?>\n");
1656 THR_Print("********************************************************\n\n");
1657
1658 // And here, obj is an object of type Future<int Function()?>.
1659 //
1660 // True positive from TTS:
1661 // obj as Future : Null type args
1662 // obj as Future<dynamic> : Canonicalized to same as previous case.
1663 // obj as Future<Object?> : Type arg is top type
1664 // obj as Future<Object*> : Type arg is top typ
1665 // obj as Future<Function?> : Type arg is certain supertype
1666 // obj as Future<Function*> : Type arg is certain supertype
1667 // obj as Future<X>, : Type arg is a type parameter instantiated with
1668 // X = dynamic : ... a top type
1669 // X = Object? : ... a top type
1670 // X = Object* : ... a top type
1671 // X = int Function()? : ... the same type.
1672 //
1673 // If not null safe:
1674 // obj as Future<Object> : Type arg is certain supertype
1675 // obj as Future<Function> : Type arg is certain supertype
1676 // obj as Future<X>, : Type arg is a type parameter instantiated with
1677 // X = Object : ... a certain supertype
1678 RunTTSTest(type_future, {obj_futurenullablefunction, tav_null, tav_null});
1679 RunTTSTest(type_future_dynamic,
1680 {obj_futurenullablefunction, tav_null, tav_null});
1681 RunTTSTest(type_future_nullable_object,
1682 {obj_futurenullablefunction, tav_null, tav_null});
1683 RunTTSTest(type_future_legacy_object,
1684 {obj_futurenullablefunction, tav_null, tav_null});
1685 RunTTSTest(type_future_nullable_object,
1686 {obj_futurefunction, tav_null, tav_null});
1687 RunTTSTest(type_future_legacy_object,
1688 {obj_futurenullablefunction, tav_null, tav_null});
1689 RunTTSTest(type_future_nullable_function,
1690 {obj_futurenullablefunction, tav_null, tav_null});
1691 RunTTSTest(type_future_legacy_function,
1692 {obj_futurenullablefunction, tav_null, tav_null});
1693 RunTTSTest(type_future_t, {obj_futurenullablefunction, tav_null, tav_null});
1694 RunTTSTest(type_future_t,
1695 {obj_futurenullablefunction, tav_nullable_object, tav_null});
1696 RunTTSTest(type_future_t,
1697 {obj_futurenullablefunction, tav_legacy_object, tav_null});
1698 RunTTSTest(type_future_t, {obj_futurenullablefunction,
1699 tav_nullable_function_int_nullary, tav_null});
1700
1701 // False negative from TTS (caught by runtime or STC):
1702 // obj as Future<int Function()?> : No specialization.
1703 // obj as Future<int Function()*> : No specialization.
1704 // obj as Future<X>, : Type arg is a type parameter instantiated with
1705 // X = Function? : ... a certain supertype (but not checked)
1706 // X = Function* : ... a certain supertype (but not checked)
1707 // X = int Function()* : ... a canonically different type.
1708 //
1709 // If not null safe:
1710 // obj as Future<int Function()> : No specialization.
1711 // obj as Future<X>, : Type arg is a type parameter instantiated with
1712 // X = Function : ... a certain supertype (but not checked)
1713 // X = int Function() : ... a canonically different type.
1714
1715 RunTTSTest(type_future_nullable_function_int_nullary,
1716 FalseNegative({obj_futurenullablefunction, tav_null, tav_null}));
1717 RunTTSTest(type_future_legacy_function_int_nullary,
1718 FalseNegative({obj_futurenullablefunction, tav_null, tav_null}));
1719 RunTTSTest(type_future_t, FalseNegative({obj_futurenullablefunction,
1720 tav_nullable_function, tav_null}));
1721 RunTTSTest(type_future_t, FalseNegative({obj_futurenullablefunction,
1722 tav_legacy_function, tav_null}));
1723 RunTTSTest(type_future_t,
1724 FalseNegative({obj_futurenullablefunction,
1725 tav_legacy_function_int_nullary, tav_null}));
1726
1727 // Errors:
1728 // obj as Future<_Closure?> : Type arg is not a supertype
1729 // obj as Future<_Closure*> : Type arg is not a supertype
1730 // obj as Future<_Closure> : Type arg is not a supertype
1731 // obj as Future<X>, : Type arg is a type parameter instantiated with
1732 // X = _Closure? : ... an unrelated type.
1733 // X = _Closure* : ... an unrelated type.
1734 // X = _Closure : ... an unrelated type.
1735 //
1736 // If null safe:
1737 // obj as Future<int Function()> : Nullable type cannot be subtype of a
1738 // non-nullable type.
1739 // obj as Future<Object> : Nullable type cannot be subtype of a
1740 // non-nullable type.
1741 // obj as Future<Function> : Nullable type cannot be subtype of a
1742 // non-nullable type.
1743 // obj as Future<X>, : Type arg is a type parameter instantiated with
1744 // X = Object : ... a non-nullable type.
1745 // X = Function : ... a non-nullable type.
1746 // X = int Function() : ... a non-nullable type.
1747
1748 RunTTSTest(type_future_nullable_closure,
1749 Failure({obj_futurenullablefunction, tav_null, tav_null}));
1750 RunTTSTest(type_future_legacy_closure,
1751 Failure({obj_futurenullablefunction, tav_null, tav_null}));
1752 RunTTSTest(type_future_closure,
1753 Failure({obj_futurenullablefunction, tav_null, tav_null}));
1754 RunTTSTest(type_future_t, Failure({obj_futurenullablefunction,
1755 tav_nullable_closure, tav_null}));
1756 RunTTSTest(type_future_t, Failure({obj_futurenullablefunction,
1757 tav_legacy_closure, tav_null}));
1758 RunTTSTest(type_future_t,
1759 Failure({obj_futurenullablefunction, tav_closure, tav_null}));
1760
1761 RunTTSTest(type_future_function_int_nullary,
1762 Failure({obj_futurenullablefunction, tav_null, tav_null}));
1763 RunTTSTest(type_future_object,
1764 Failure({obj_futurenullablefunction, tav_null, tav_null}));
1765 RunTTSTest(type_future_function,
1766 Failure({obj_futurenullablefunction, tav_null, tav_null}));
1767 RunTTSTest(type_future_t,
1768 Failure({obj_futurenullablefunction, tav_object, tav_null}));
1769 RunTTSTest(type_future_t,
1770 Failure({obj_futurenullablefunction, tav_function, tav_null}));
1771 RunTTSTest(type_future_t, Failure({obj_futurenullablefunction,
1772 tav_function_int_nullary, tav_null}));
1773}
1774
1775ISOLATE_UNIT_TEST_CASE(TTS_Regress40964) {
1776 const char* kScript =
1777 R"(
1778 class A<T> {
1779 test(x) => x as B<T>;
1780 }
1781 class B<T> {}
1782 class C<T> {}
1783
1784 createACint() => A<C<int>>();
1785 createBCint() => B<C<int>>();
1786 createBCnum() => B<C<num>>();
1787 )";
1788
1789 const auto& root_library = Library::Handle(LoadTestScript(kScript));
1790 const auto& class_b = Class::Handle(GetClass(root_library, "B"));
1792 const auto& acint = Object::Handle(Invoke(root_library, "createACint"));
1793 const auto& bcint = Object::Handle(Invoke(root_library, "createBCint"));
1794 const auto& bcnum = Object::Handle(Invoke(root_library, "createBCnum"));
1795
1796 // dst_type = B<T>
1797 const auto& dst_tav = TypeArguments::Handle(TypeArguments::New(1));
1798 dst_tav.SetTypeAt(0,
1800 auto& dst_type = Type::Handle(Type::New(class_b, dst_tav));
1801 FinalizeAndCanonicalize(&dst_type);
1802 const auto& cint_tav =
1803 TypeArguments::Handle(Instance::Cast(acint).GetTypeArguments());
1804 const auto& function_tav = TypeArguments::Handle();
1805
1806 // a as B<T> -- a==B<C<int>, T==<C<int>>
1807 RunTTSTest(dst_type, {bcint, cint_tav, function_tav});
1808
1809 // a as B<T> -- a==B<C<num>, T==<C<int>>
1810 RunTTSTest(dst_type, Failure({bcnum, cint_tav, function_tav}));
1811}
1812
1813ISOLATE_UNIT_TEST_CASE(TTS_TypeParameter) {
1814 const char* kScript =
1815 R"(
1816 class A<T> {
1817 T test(dynamic x) => x as T;
1818 }
1819 H genericFun<H>(dynamic x) => x as H;
1820
1821 createAInt() => A<int>();
1822 createAString() => A<String>();
1823 )";
1824
1825 const auto& root_library = Library::Handle(LoadTestScript(kScript));
1826 const auto& class_a = Class::Handle(GetClass(root_library, "A"));
1828
1829 const auto& fun_generic =
1830 Function::Handle(GetFunction(root_library, "genericFun"));
1832 const auto& dst_type_t =
1834
1835 const auto& dst_type_h =
1837
1838 const auto& aint = Object::Handle(Invoke(root_library, "createAInt"));
1839 const auto& astring = Object::Handle(Invoke(root_library, "createAString"));
1840
1841 const auto& int_tav =
1842 TypeArguments::Handle(Instance::Cast(aint).GetTypeArguments());
1843 const auto& string_tav =
1844 TypeArguments::Handle(Instance::Cast(astring).GetTypeArguments());
1845
1846 const auto& int_instance = Integer::Handle(Integer::New(1));
1847 const auto& string_instance = String::Handle(String::New("foo"));
1848
1849 THR_Print("Testing int instance, class parameter instantiated to int\n");
1850 RunTTSTest(dst_type_t, {int_instance, int_tav, string_tav});
1851 THR_Print("\nTesting string instance, class parameter instantiated to int\n");
1852 RunTTSTest(dst_type_t, Failure({string_instance, int_tav, string_tav}));
1853
1854 THR_Print(
1855 "\nTesting string instance, function parameter instantiated to string\n");
1856 RunTTSTest(dst_type_h, {string_instance, int_tav, string_tav});
1857 RunTTSTest(dst_type_h, Failure({int_instance, int_tav, string_tav}));
1858}
1859
1860// Check that we generate correct TTS for _Smi type.
1861ISOLATE_UNIT_TEST_CASE(TTS_Smi) {
1862 const auto& type_smi = Type::Handle(Type::SmiType());
1863 const auto& tav_null = Object::null_type_arguments();
1864
1865 // Test on some easy-to-make instances.
1866 RunTTSTest(type_smi, {Smi::Handle(Smi::New(0)), tav_null, tav_null});
1868 tav_null, tav_null}));
1869 RunTTSTest(type_smi,
1870 Failure({Double::Handle(Double::New(1.0)), tav_null, tav_null}));
1871 RunTTSTest(type_smi, Failure({Symbols::Empty(), tav_null, tav_null}));
1872 RunTTSTest(type_smi,
1873 Failure({Array::Handle(Array::New(1)), tav_null, tav_null}));
1874}
1875
1876// Check that we generate correct TTS for int type.
1877ISOLATE_UNIT_TEST_CASE(TTS_Int) {
1878 const auto& type_int = Type::Handle(Type::IntType());
1879 const auto& tav_null = Object::null_type_arguments();
1880
1881 // Test on some easy-to-make instances.
1882 RunTTSTest(type_int, {Smi::Handle(Smi::New(0)), tav_null, tav_null});
1883 RunTTSTest(type_int,
1884 {Integer::Handle(Integer::New(kMaxInt64)), tav_null, tav_null});
1885 RunTTSTest(type_int,
1886 Failure({Double::Handle(Double::New(1.0)), tav_null, tav_null}));
1887 RunTTSTest(type_int, Failure({Symbols::Empty(), tav_null, tav_null}));
1888 RunTTSTest(type_int,
1889 Failure({Array::Handle(Array::New(1)), tav_null, tav_null}));
1890}
1891
1892// Check that we generate correct TTS for num type.
1894 const auto& type_num = Type::Handle(Type::Number());
1895 const auto& tav_null = Object::null_type_arguments();
1896
1897 // Test on some easy-to-make instances.
1898 RunTTSTest(type_num, {Smi::Handle(Smi::New(0)), tav_null, tav_null});
1899 RunTTSTest(type_num,
1900 {Integer::Handle(Integer::New(kMaxInt64)), tav_null, tav_null});
1901 RunTTSTest(type_num, {Double::Handle(Double::New(1.0)), tav_null, tav_null});
1902 RunTTSTest(type_num, Failure({Symbols::Empty(), tav_null, tav_null}));
1903 RunTTSTest(type_num,
1904 Failure({Array::Handle(Array::New(1)), tav_null, tav_null}));
1905}
1906
1907// Check that we generate correct TTS for Double type.
1908ISOLATE_UNIT_TEST_CASE(TTS_Double) {
1909 const auto& type_num = Type::Handle(Type::Double());
1910 const auto& tav_null = Object::null_type_arguments();
1911
1912 // Test on some easy-to-make instances.
1913 RunTTSTest(type_num, Failure({Smi::Handle(Smi::New(0)), tav_null, tav_null}));
1915 tav_null, tav_null}));
1916 RunTTSTest(type_num, {Double::Handle(Double::New(1.0)), tav_null, tav_null});
1917 RunTTSTest(type_num, Failure({Symbols::Empty(), tav_null, tav_null}));
1918 RunTTSTest(type_num,
1919 Failure({Array::Handle(Array::New(1)), tav_null, tav_null}));
1920}
1921
1922// Check that we generate correct TTS for Object type.
1923ISOLATE_UNIT_TEST_CASE(TTS_Object) {
1924 const auto& type_obj =
1925 Type::Handle(IsolateGroup::Current()->object_store()->object_type());
1926 const auto& tav_null = Object::null_type_arguments();
1927
1928 // Non-nullable Object is not a top type,
1929 // so its TTS specializes the first time it is invoked.
1930 const bool should_specialize = true;
1931 auto make_test_case = [&](const Instance& instance) -> TTSTestCase {
1932 return {instance, tav_null, tav_null};
1933 };
1934
1935 // Test on some easy-to-make instances.
1936 RunTTSTest(type_obj, make_test_case(Smi::Handle(Smi::New(0))),
1937 should_specialize);
1938 RunTTSTest(type_obj, make_test_case(Integer::Handle(Integer::New(kMaxInt64))),
1939 should_specialize);
1940 RunTTSTest(type_obj, make_test_case(Double::Handle(Double::New(1.0))),
1941 should_specialize);
1942 RunTTSTest(type_obj, make_test_case(Symbols::Empty()), should_specialize);
1943 RunTTSTest(type_obj, make_test_case(Array::Handle(Array::New(1))),
1944 should_specialize);
1945}
1946
1947// Check that we generate correct TTS for type Function (the non-FunctionType
1948// version).
1949ISOLATE_UNIT_TEST_CASE(TTS_Function) {
1950 const char* kScript =
1951 R"(
1952 class A<T> {}
1953
1954 createF() => (){};
1955 createG() => () => 3;
1956 createH() => (int x, String y, {int z = 0}) => x + z;
1957
1958 createAInt() => A<int>();
1959 createAFunction() => A<Function>();
1960 )";
1961
1962 const auto& root_library = Library::Handle(LoadTestScript(kScript));
1963 const auto& obj_f = Object::Handle(Invoke(root_library, "createF"));
1964 const auto& obj_g = Object::Handle(Invoke(root_library, "createG"));
1965 const auto& obj_h = Object::Handle(Invoke(root_library, "createH"));
1966
1967 const auto& tav_null = TypeArguments::Handle(TypeArguments::null());
1968 const auto& type_function = Type::Handle(Type::DartFunctionType());
1969
1970 RunTTSTest(type_function, {obj_f, tav_null, tav_null});
1971 RunTTSTest(type_function, {obj_g, tav_null, tav_null});
1972 RunTTSTest(type_function, {obj_h, tav_null, tav_null});
1973
1974 const auto& class_a = Class::Handle(GetClass(root_library, "A"));
1975 const auto& obj_a_int = Object::Handle(Invoke(root_library, "createAInt"));
1976 const auto& obj_a_function =
1977 Object::Handle(Invoke(root_library, "createAFunction"));
1978
1979 auto& tav_function = TypeArguments::Handle(TypeArguments::New(1));
1980 tav_function.SetTypeAt(0, type_function);
1981 CanonicalizeTAV(&tav_function);
1982 auto& type_a_function = Type::Handle(Type::New(class_a, tav_function));
1983 FinalizeAndCanonicalize(&type_a_function);
1984
1985 RunTTSTest(type_a_function, {obj_a_function, tav_null, tav_null});
1986 RunTTSTest(type_a_function, Failure({obj_a_int, tav_null, tav_null}));
1987}
1988
1989ISOLATE_UNIT_TEST_CASE(TTS_Partial) {
1990 const char* kScript =
1991 R"(
1992 class B<T> {}
1993
1994 class C {}
1995 class D extends C {}
1996 class E extends D {}
1997
1998 F<A>() {}
1999 createBE() => B<E>();
2000 createBENullable() => B<E?>();
2001 createBNull() => B<Null>();
2002 createBNever() => B<Never>();
2003)";
2004
2005 const auto& root_library = Library::Handle(LoadTestScript(kScript));
2006 const auto& class_b = Class::Handle(GetClass(root_library, "B"));
2007 const auto& class_c = Class::Handle(GetClass(root_library, "C"));
2008 const auto& class_d = Class::Handle(GetClass(root_library, "D"));
2009 const auto& class_e = Class::Handle(GetClass(root_library, "E"));
2010 const auto& fun_f = Function::Handle(GetFunction(root_library, "F"));
2011 const auto& obj_b_e = Object::Handle(Invoke(root_library, "createBE"));
2012 const auto& obj_b_e_nullable =
2013 Object::Handle(Invoke(root_library, "createBENullable"));
2014 const auto& obj_b_null = Object::Handle(Invoke(root_library, "createBNull"));
2015 const auto& obj_b_never =
2016 Object::Handle(Invoke(root_library, "createBNever"));
2017
2018 const auto& tav_null = Object::null_type_arguments();
2019 auto& tav_nullable_object = TypeArguments::Handle(TypeArguments::New(1));
2020 tav_nullable_object.SetTypeAt(
2021 0, Type::Handle(
2022 IsolateGroup::Current()->object_store()->nullable_object_type()));
2023 CanonicalizeTAV(&tav_nullable_object);
2024 auto& tav_legacy_object = TypeArguments::Handle(TypeArguments::New(1));
2025 tav_legacy_object.SetTypeAt(
2026 0, Type::Handle(
2027 IsolateGroup::Current()->object_store()->legacy_object_type()));
2028 CanonicalizeTAV(&tav_legacy_object);
2029 auto& tav_object = TypeArguments::Handle(TypeArguments::New(1));
2030 tav_object.SetTypeAt(
2031 0, Type::Handle(IsolateGroup::Current()->object_store()->object_type()));
2032 CanonicalizeTAV(&tav_object);
2033
2034 auto& type_e =
2036 FinalizeAndCanonicalize(&type_e);
2037 auto& type_d =
2039 FinalizeAndCanonicalize(&type_d);
2040 auto& type_c =
2042 FinalizeAndCanonicalize(&type_c);
2043 auto& type_c_nullable =
2044 Type::Handle(Type::New(class_c, tav_null, Nullability::kNullable));
2045 FinalizeAndCanonicalize(&type_c_nullable);
2046 auto& type_c_legacy =
2047 Type::Handle(Type::New(class_c, tav_null, Nullability::kLegacy));
2048 FinalizeAndCanonicalize(&type_c_legacy);
2049
2050 auto& tav_e = TypeArguments::Handle(TypeArguments::New(1));
2051 tav_e.SetTypeAt(0, type_e);
2052 CanonicalizeTAV(&tav_e);
2053 auto& tav_d = TypeArguments::Handle(TypeArguments::New(1));
2054 tav_d.SetTypeAt(0, type_d);
2055 CanonicalizeTAV(&tav_d);
2056 auto& tav_c = TypeArguments::Handle(TypeArguments::New(1));
2057 tav_c.SetTypeAt(0, type_c);
2058 CanonicalizeTAV(&tav_c);
2059 auto& tav_nullable_c = TypeArguments::Handle(TypeArguments::New(1));
2060 tav_nullable_c.SetTypeAt(0, type_c_nullable);
2061 CanonicalizeTAV(&tav_nullable_c);
2062 auto& tav_legacy_c = TypeArguments::Handle(TypeArguments::New(1));
2063 tav_legacy_c.SetTypeAt(0, type_c_legacy);
2064 CanonicalizeTAV(&tav_legacy_c);
2065
2066 // One case where optimized TTSes can be partial is if the type is
2067 // uninstantiated with a type parameter at the same position as one of the
2068 // class's type parameters. The type parameter in the type is instantiated at
2069 // runtime and compared with the corresponding instance type argument using
2070 // pointer equality, which misses the case where the instantiated type
2071 // parameter in the type is a supertype of the instance type argument.
2072 const auto& type_a =
2074 auto& tav_a = TypeArguments::Handle(TypeArguments::New(1));
2075 tav_a.SetTypeAt(0, type_a);
2076 CanonicalizeTAV(&tav_a);
2077 auto& type_b_a = AbstractType::Handle(
2078 Type::New(class_b, tav_a, Nullability::kNonNullable));
2079 FinalizeAndCanonicalize(&type_b_a);
2080 TTSTestState state(thread, type_b_a);
2081
2082 // Should be checked by the TTS.
2083 TTSTestCase b_e_testcase{obj_b_e, tav_null, tav_e};
2084 // Needs to be cached in the STC and checked there.
2085 TTSTestCase b_d_testcase{obj_b_e, tav_null, tav_d};
2086 TTSTestCase b_c_testcase{obj_b_e, tav_null, tav_c};
2087
2088 // First, test that the positive test case is handled by the TTS.
2089 state.InvokeLazilySpecializedStub(b_e_testcase);
2090 state.InvokeExistingStub(b_e_testcase);
2091
2092 // Now restart, using the false negative test cases.
2093 state.ClearCache();
2094
2095 // This specializes the stub, so no STC update.
2096 state.InvokeLazilySpecializedStub(b_d_testcase);
2097 state.InvokeExistingStub(FalseNegative(b_d_testcase));
2098 // Replacing the stub with an eager version without clearing the STC means
2099 // it's still in the STC to be checked.
2100 state.InvokeEagerlySpecializedStub(STCCheck(b_d_testcase));
2101
2102 state.InvokeExistingStub(b_e_testcase);
2103 state.InvokeExistingStub(FalseNegative(b_c_testcase));
2104 state.InvokeExistingStub(STCCheck(b_d_testcase));
2105 state.InvokeExistingStub(b_e_testcase);
2106
2107 state.InvokeExistingStub({obj_b_never, tav_null, tav_d});
2108 state.InvokeExistingStub({obj_b_null, tav_null, tav_nullable_c});
2109 state.InvokeExistingStub({obj_b_null, tav_null, tav_legacy_c});
2110 state.InvokeExistingStub(Failure({obj_b_null, tav_null, tav_c}));
2111
2112 state.InvokeExistingStub({obj_b_e, tav_null, tav_nullable_object});
2113 state.InvokeExistingStub({obj_b_e_nullable, tav_null, tav_nullable_object});
2114 state.InvokeExistingStub({obj_b_e, tav_null, tav_legacy_object});
2115 state.InvokeExistingStub({obj_b_e_nullable, tav_null, tav_legacy_object});
2116 state.InvokeExistingStub({obj_b_e, tav_null, tav_object});
2117 state.InvokeExistingStub(Failure({obj_b_e_nullable, tav_null, tav_object}));
2118}
2119
2120ISOLATE_UNIT_TEST_CASE(TTS_Partial_Incremental) {
2121#define FILE_RESOLVE_URI(Uri) "file:///" Uri
2122#define FIRST_PARTIAL_LIBRARY_NAME "test-lib"
2123#define SECOND_PARTIAL_LIBRARY_NAME "test-lib-2"
2124#define THIRD_PARTIAL_LIBRARY_NAME "test-lib-3"
2125
2126 // Same test script as TTS_Partial.
2127 const char* kFirstScript =
2128 R"(
2129 class B<T> {}
2130 createB() => B<int>();
2131)";
2132
2133 // A test script which imports the B class and extend it, to test
2134 // respecialization when the hierarchy changes without reloading.
2135 const char* kSecondScript =
2136 R"(
2137 import ")" FIRST_PARTIAL_LIBRARY_NAME R"(";
2138 class B2<T> extends B<T> {}
2139 createB2() => B2<int>();
2140)";
2141
2142 // Another one to test respecialization a second time.
2143 const char* kThirdScript =
2144 R"(
2145 import ")" FIRST_PARTIAL_LIBRARY_NAME R"(";
2146 class B3<T> extends B<T> {}
2147 createB3() => B3<int>();
2148)";
2149
2150 const char* kFirstUri = FILE_RESOLVE_URI(FIRST_PARTIAL_LIBRARY_NAME);
2151 const char* kSecondUri = FILE_RESOLVE_URI(SECOND_PARTIAL_LIBRARY_NAME);
2152 const char* kThirdUri = FILE_RESOLVE_URI(THIRD_PARTIAL_LIBRARY_NAME);
2153
2154#undef THIRD_PARTIAL_LIBRARY_URI
2155#undef SECOND_PARTIAL_LIBRARY_URI
2156#undef FIRST_PARTIAL_LIBRARY_URI
2157#undef FILE_RESOLVE_URI
2158
2159 THR_Print("------------------------------------------------------\n");
2160 THR_Print(" Loading %s\n", kFirstUri);
2161 THR_Print("------------------------------------------------------\n");
2162 const auto& first_library = Library::Handle(
2163 LoadTestScript(kFirstScript, /*resolver=*/nullptr, kFirstUri));
2164
2165 const auto& class_b = Class::Handle(GetClass(first_library, "B"));
2166 const auto& obj_b = Object::Handle(Invoke(first_library, "createB"));
2167
2168 const auto& tav_null = Object::null_type_arguments();
2169 auto& tav_int = TypeArguments::Handle(TypeArguments::New(1));
2170 tav_int.SetTypeAt(0, Type::Handle(Type::IntType()));
2171 CanonicalizeTAV(&tav_int);
2172 auto& tav_num = TypeArguments::Handle(TypeArguments::New(1));
2173 tav_num.SetTypeAt(0, Type::Handle(Type::Number()));
2174 CanonicalizeTAV(&tav_num);
2175
2176 auto& type_b2_t = AbstractType::Handle(class_b.DeclarationType());
2177 FinalizeAndCanonicalize(&type_b2_t);
2178 TTSTestState state(thread, type_b2_t);
2179
2180 TTSTestCase first_positive{obj_b, tav_int, tav_null};
2181 TTSTestCase first_false_negative = {obj_b, tav_num, tav_null};
2182 // No test case should possibly hit the same STC entry as another.
2183 ASSERT(!first_false_negative.HasSameSTCEntry(first_positive));
2184 // The type with the tested stub must be the same in all test cases.
2185 ASSERT(state.TypeToTest(first_positive) ==
2186 state.TypeToTest(first_false_negative));
2187
2188 state.InvokeLazilySpecializedStub(first_false_negative);
2189 state.InvokeExistingStub(FalseNegative(first_false_negative));
2190 state.InvokeEagerlySpecializedStub(STCCheck(first_false_negative));
2191
2192 state.InvokeExistingStub(first_positive);
2193 state.InvokeExistingStub(STCCheck(first_false_negative));
2194
2195 Array& stc_cache = Array::Handle(
2196 state.last_stc().IsNull() ? Array::null() : state.last_stc().cache());
2197 THR_Print("------------------------------------------------------\n");
2198 THR_Print(" Loading %s\n", kSecondUri);
2199 THR_Print("------------------------------------------------------\n");
2200 const auto& second_library = Library::Handle(
2201 LoadTestScript(kSecondScript, /*resolver=*/nullptr, kSecondUri));
2202 // Loading the new library shouldn't invalidate the old STC.
2203 EXPECT(state.last_stc().ptr() == state.current_stc());
2204 // Loading the new library should not reset the STCs, as no respecialization
2205 // should happen yet.
2206 EXPECT((state.last_stc().IsNull() && stc_cache.IsNull()) ||
2207 stc_cache.ptr() == state.last_stc().cache());
2208
2209 const auto& obj_b2 = Object::Handle(Invoke(second_library, "createB2"));
2210
2211 TTSTestCase second_positive{obj_b2, tav_int, tav_null};
2212 TTSTestCase second_false_negative = {obj_b2, tav_num, tav_null};
2213 // No test case should possibly hit the same STC entry as another.
2214 ASSERT(!second_positive.HasSameSTCEntry(second_false_negative));
2215 ASSERT(!second_positive.HasSameSTCEntry(first_positive));
2216 ASSERT(!second_positive.HasSameSTCEntry(first_false_negative));
2217 ASSERT(!second_false_negative.HasSameSTCEntry(first_positive));
2218 ASSERT(!second_false_negative.HasSameSTCEntry(first_false_negative));
2219 // The type with the tested stub must be the same in all test cases.
2220 ASSERT(state.TypeToTest(second_positive) ==
2221 state.TypeToTest(second_false_negative));
2222 ASSERT(state.TypeToTest(first_positive) == state.TypeToTest(second_positive));
2223
2224 // Old positive should still be caught by TTS.
2225 state.InvokeExistingStub(first_positive);
2226 // Same false negative should still be caught by STC and not cause
2227 // respecialization.
2228 state.InvokeExistingStub(STCCheck(first_false_negative));
2229
2230 // The new positive should be a false negative at the TTS level that causes
2231 // respecialization, as the class hierarchy has changed.
2232 state.InvokeExistingStub(Respecialization(second_positive));
2233
2234 // The first false positive is still in the cache.
2235 state.InvokeExistingStub(STCCheck(first_false_negative));
2236
2237 // This false negative is not yet in the cache.
2238 state.InvokeExistingStub(FalseNegative(second_false_negative));
2239
2240 state.InvokeExistingStub(first_positive);
2241 state.InvokeExistingStub(second_positive);
2242
2243 // Now the second false negative is in the cache.
2244 state.InvokeExistingStub(STCCheck(second_false_negative));
2245
2246 stc_cache =
2247 state.last_stc().IsNull() ? Array::null() : state.last_stc().cache();
2248 THR_Print("------------------------------------------------------\n");
2249 THR_Print(" Loading %s\n", kThirdUri);
2250 THR_Print("------------------------------------------------------\n");
2251 const auto& third_library = Library::Handle(
2252 LoadTestScript(kThirdScript, /*resolver=*/nullptr, kThirdUri));
2253 // Loading the new library shouldn't invalidate the old STC.
2254 EXPECT(state.last_stc().ptr() == state.current_stc());
2255 // Loading the new library should not reset the STCs, as no respecialization
2256 // should happen yet.
2257 EXPECT((state.last_stc().IsNull() && stc_cache.IsNull()) ||
2258 stc_cache.ptr() == state.last_stc().cache());
2259
2260 const auto& obj_b3 = Object::Handle(Invoke(third_library, "createB3"));
2261
2262 TTSTestCase third_positive{obj_b3, tav_int, tav_null};
2263 TTSTestCase third_false_negative = {obj_b3, tav_num, tav_null};
2264 // No test case should possibly hit the same STC entry as another.
2265 ASSERT(!third_positive.HasSameSTCEntry(third_false_negative));
2266 ASSERT(!third_positive.HasSameSTCEntry(first_positive));
2267 ASSERT(!third_positive.HasSameSTCEntry(first_false_negative));
2268 ASSERT(!third_positive.HasSameSTCEntry(second_positive));
2269 ASSERT(!third_positive.HasSameSTCEntry(second_false_negative));
2270 ASSERT(!third_false_negative.HasSameSTCEntry(first_positive));
2271 ASSERT(!third_false_negative.HasSameSTCEntry(first_false_negative));
2272 ASSERT(!third_false_negative.HasSameSTCEntry(second_positive));
2273 ASSERT(!third_false_negative.HasSameSTCEntry(second_false_negative));
2274 // The type with the tested stub must be the same in all test cases.
2275 ASSERT(state.TypeToTest(third_positive) ==
2276 state.TypeToTest(third_false_negative));
2277 ASSERT(state.TypeToTest(first_positive) == state.TypeToTest(third_positive));
2278
2279 // Again, cases that have run before should still pass as before without STC
2280 // changes/respecialization.
2281 state.InvokeExistingStub(first_positive);
2282 state.InvokeExistingStub(second_positive);
2283 state.InvokeExistingStub(STCCheck(first_false_negative));
2284 state.InvokeExistingStub(STCCheck(second_false_negative));
2285
2286 // Now we lead with the new false negative, to make sure it also triggers
2287 // respecialization but doesn't get immediately added to the STC.
2288 state.InvokeExistingStub(Respecialization(third_false_negative));
2289
2290 // True positives still work as before.
2291 state.InvokeExistingStub(third_positive);
2292 state.InvokeExistingStub(second_positive);
2293 state.InvokeExistingStub(first_positive);
2294
2295 // No additional checks added by rerunning the previous false negatives.
2296 state.InvokeExistingStub(STCCheck(first_false_negative));
2297 state.InvokeExistingStub(STCCheck(second_false_negative));
2298
2299 // Now a check is recorded when rerunning the third false negative.
2300 state.InvokeExistingStub(FalseNegative(third_false_negative));
2301 state.InvokeExistingStub(STCCheck(third_false_negative));
2302}
2303
2304// TTS deoptimization on reload only happens in non-product mode currently.
2305#if !defined(PRODUCT)
2306static const char* kLoadedScript =
2307 R"(
2308 class A<T> {}
2309
2310 createAInt() => A<int>();
2311 createAString() => A<String>();
2312
2313 (int, int) createRecordIntInt() => (1, 2);
2314 (String, int) createRecordStringInt() => ("foo", 2);
2315 (int, String) createRecordIntString() => (1, "bar");
2316 )";
2317
2318static const char* kReloadedScript =
2319 R"(
2320 class A<T> {}
2321 class A2<T> extends A<T> {}
2322
2323 createAInt() => A<int>();
2324 createAString() => A<String>();
2325 createA2Int() => A2<int>();
2326 createA2String() => A2<String>();
2327
2328 (int, int) createRecordIntInt() => (1, 2);
2329 (String, int) createRecordStringInt() => ("foo", 2);
2330 (int, String) createRecordIntString() => (1, "bar");
2331 )";
2332
2333ISOLATE_UNIT_TEST_CASE(TTS_Reload) {
2334 auto* const zone = thread->zone();
2335
2336 auto& root_library = Library::Handle(LoadTestScript(kLoadedScript));
2337 const auto& class_a = Class::Handle(GetClass(root_library, "A"));
2339
2340 const auto& aint = Object::Handle(Invoke(root_library, "createAInt"));
2341 const auto& astring = Object::Handle(Invoke(root_library, "createAString"));
2342
2343 const auto& record_int_int =
2344 Instance::CheckedHandle(zone, Invoke(root_library, "createRecordIntInt"));
2345 const auto& record_int_string = Instance::CheckedHandle(
2346 zone, Invoke(root_library, "createRecordIntString"));
2347 const auto& record_string_int = Instance::CheckedHandle(
2348 zone, Invoke(root_library, "createRecordStringInt"));
2349
2350 const auto& tav_null = Object::null_type_arguments();
2351 const auto& tav_int =
2352 TypeArguments::Handle(Instance::Cast(aint).GetTypeArguments());
2353 auto& tav_num = TypeArguments::Handle(TypeArguments::New(1));
2354 tav_num.SetTypeAt(0, Type::Handle(Type::Number()));
2355 CanonicalizeTAV(&tav_num);
2356
2357 auto& type_a_int = Type::Handle(Type::New(class_a, tav_int));
2358 FinalizeAndCanonicalize(&type_a_int);
2359
2360 auto& type_record_int_int =
2361 AbstractType::Handle(record_int_int.GetType(Heap::kNew));
2362 FinalizeAndCanonicalize(&type_record_int_int);
2363
2364 TTSTestState state(thread, type_a_int);
2365 state.InvokeLazilySpecializedStub({aint, tav_null, tav_null});
2366 state.InvokeExistingStub(Failure({astring, tav_null, tav_null}));
2367
2368 TTSTestState record_state(thread, type_record_int_int);
2369 record_state.InvokeLazilySpecializedStub(
2370 {record_int_int, tav_null, tav_null});
2371 record_state.InvokeExistingStub(
2372 Failure({record_string_int, tav_null, tav_null}));
2373 record_state.InvokeExistingStub(
2374 Failure({record_int_string, tav_null, tav_null}));
2375
2376 // Make sure the stubs are specialized prior to reload.
2377 EXPECT(type_a_int.type_test_stub() !=
2379 EXPECT(type_record_int_int.type_test_stub() !=
2381
2382 root_library = ReloadTestScript(kReloadedScript);
2383 const auto& a2int = Object::Handle(Invoke(root_library, "createA2Int"));
2384 const auto& a2string = Object::Handle(Invoke(root_library, "createA2String"));
2385
2386 // Reloading resets all type testing stubs to the (possibly lazy specializing)
2387 // default stub for that type.
2388 EXPECT(type_a_int.type_test_stub() ==
2390 EXPECT(type_record_int_int.type_test_stub() ==
2392 // Reloading either removes or resets the type testing cache.
2393 EXPECT(state.current_stc() == SubtypeTestCache::null() ||
2394 (state.current_stc() == state.last_stc().ptr() &&
2395 state.last_stc().NumberOfChecks() == 0));
2396 EXPECT(record_state.current_stc() == SubtypeTestCache::null() ||
2397 (record_state.current_stc() == record_state.last_stc().ptr() &&
2398 record_state.last_stc().NumberOfChecks() == 0));
2399
2400 state.InvokeExistingStub(Respecialization({aint, tav_null, tav_null}));
2401 state.InvokeExistingStub(Failure({astring, tav_null, tav_null}));
2402 state.InvokeExistingStub({a2int, tav_null, tav_null});
2403 state.InvokeExistingStub(Failure({a2string, tav_null, tav_null}));
2404
2405 record_state.InvokeExistingStub(
2406 Respecialization({record_int_int, tav_null, tav_null}));
2407 record_state.InvokeExistingStub(
2408 Failure({record_string_int, tav_null, tav_null}));
2409 record_state.InvokeExistingStub(
2410 Failure({record_int_string, tav_null, tav_null}));
2411}
2412
2413ISOLATE_UNIT_TEST_CASE(TTS_Partial_Reload) {
2414 auto& root_library = Library::Handle(LoadTestScript(kLoadedScript));
2415 const auto& class_a = Class::Handle(GetClass(root_library, "A"));
2417
2418 const auto& aint = Object::Handle(Invoke(root_library, "createAInt"));
2419 const auto& astring = Object::Handle(Invoke(root_library, "createAString"));
2420
2421 const auto& tav_null = Object::null_type_arguments();
2422 const auto& tav_int =
2423 TypeArguments::Handle(Instance::Cast(aint).GetTypeArguments());
2424 const auto& tav_string =
2425 TypeArguments::Handle(Instance::Cast(astring).GetTypeArguments());
2426 auto& tav_num = TypeArguments::Handle(TypeArguments::New(1));
2427 tav_num.SetTypeAt(0, Type::Handle(Type::Number()));
2428 CanonicalizeTAV(&tav_num);
2429
2430 // Create a partial TTS to test resets of STCs with false negatives.
2431 const auto& type_a_t = Type::Handle(class_a.DeclarationType());
2432 TTSTestCase positive1{aint, tav_int, tav_null};
2433 TTSTestCase positive2{astring, tav_string, tav_null};
2434 TTSTestCase negative1 = Failure({astring, tav_int, tav_null});
2435 TTSTestCase negative2 = Failure({aint, tav_string, tav_null});
2436 TTSTestCase false_negative = FalseNegative({aint, tav_num, tav_null});
2437 TTSTestState state(thread, type_a_t);
2438 state.InvokeLazilySpecializedStub(positive1);
2439 state.InvokeExistingStub(positive2);
2440 state.InvokeExistingStub(negative1);
2441 state.InvokeExistingStub(negative2);
2442 state.InvokeExistingStub(false_negative);
2443
2444 root_library = ReloadTestScript(kReloadedScript);
2445 const auto& a2int = Object::Handle(Invoke(root_library, "createA2Int"));
2446 const auto& a2string = Object::Handle(Invoke(root_library, "createA2String"));
2447
2448 // Reloading resets all type testing stubs to the (possibly lazy specializing)
2449 // default stub for that type.
2450 EXPECT(type_a_t.type_test_stub() ==
2452 // Reloading either removes or resets the type testing cache.
2453 EXPECT(state.current_stc() == SubtypeTestCache::null() ||
2454 (state.current_stc() == state.last_stc().ptr() &&
2455 state.last_stc().NumberOfChecks() == 0));
2456
2457 state.InvokeExistingStub(Respecialization(positive1));
2458 state.InvokeExistingStub(positive2);
2459 state.InvokeExistingStub(negative1);
2460 state.InvokeExistingStub(negative2);
2461 state.InvokeExistingStub(false_negative);
2462 state.InvokeExistingStub({a2int, tav_int, tav_null});
2463 state.InvokeExistingStub({a2string, tav_string, tav_null});
2464 state.InvokeExistingStub(Failure({a2string, tav_int, tav_null}));
2465 state.InvokeExistingStub(Failure({a2int, tav_string, tav_null}));
2466 state.InvokeExistingStub(FalseNegative({a2int, tav_num, tav_null}));
2467}
2468#endif // !defined(PRODUCT)
2469
2470// This test checks for a failure due to not reloading the class id between
2471// different uses of GenerateCidRangeChecks when loading the instance type
2472// arguments vector in a TTS for an implemented class. GenerateCidRangeChecks
2473// might clobber the register that holds the class ID to check, hence the need
2474// to reload.
2475//
2476// To ensure that the register is clobbered on all architectures, we set things
2477// up by generating the following classes:
2478// * B<X>, a generic abstract class which is implemented by the others.
2479// * I, implements B<String>, has a single int field x, and is
2480// used to create the checked instance.
2481// * G<Y>, which implements B<Y> and has no fields (so its TAV field
2482// offset will correspond to that of the offset of x in I).
2483// * C and D, consecutively defined non-generic classes which both implement
2484// B<int>.
2485// * U0 - UN, unrelated concrete classes as needed for cid alignment.
2486//
2487// We'll carefully set things up so that the following equation between their
2488// class ids holds:
2489//
2490// G = I - C.
2491//
2492// Thus, when we create a TTS for B<int> and check it against an instance V
2493// of I. The cid for I will be loaded into a register R, and then two
2494// check blocks will be generated:
2495//
2496// * A check for the cid range [C-D], which has the side effect of
2497// subtracting the cid of C from the contents of R (here, the cid of I).
2498//
2499// * A check that R contains the cid for G.
2500//
2501// Thus, if the cid of I is not reloaded into R before the second check, and
2502// the equation earlier holds, we'll get a false positive that V is an instance
2503// of G, so the code will then try to load the instance type arguments from V
2504// as if it was an instance of G. This means the contents of x will be loaded
2505// and attempted to be used as a TypeArgumentsPtr, which will cause a crash
2506// during the checks that the instantiation of Y is int.
2507ISOLATE_UNIT_TEST_CASE(TTS_Regress_CidRangeChecks) {
2508 // We create the classes in this order: B, G, C, D, U..., I. We need
2509 // G = I - C => G + C = I
2510 // => G + C = D + N + 1 (where N is the number of U classes)
2511 // => (B + 1) + C = (C + 1) + N + 1
2512 // => B - 1 = N.
2513 // The cid for B will be the next allocated cid, which is the number of
2514 // non-top-level cids in the current class table.
2515 ClassTable* const class_table = IsolateGroup::Current()->class_table();
2516 const intptr_t kNumUnrelated = class_table->NumCids() - 1;
2517 TextBuffer buffer(1024);
2518 buffer.AddString(R"(
2519 abstract class B<X> {}
2520 class G<Y> implements B<Y> {}
2521 class C implements B<int> {}
2522 class D implements B<int> {}
2523)");
2524 for (intptr_t i = 0; i < kNumUnrelated; i++) {
2525 buffer.Printf(R"(
2526 class U%)" Pd R"( {}
2527)",
2528 i);
2529 }
2530 buffer.AddString(R"(
2531 class I implements B<String> {
2532 final x = 1;
2533 }
2534
2535 createI() => I();
2536)");
2537
2538 const auto& root_library = Library::Handle(LoadTestScript(buffer.buffer()));
2539 const auto& class_b = Class::Handle(GetClass(root_library, "B"));
2540 const auto& class_g = Class::Handle(GetClass(root_library, "G"));
2541 const auto& class_c = Class::Handle(GetClass(root_library, "C"));
2542 const auto& class_d = Class::Handle(GetClass(root_library, "D"));
2543 const auto& class_u0 = Class::Handle(GetClass(root_library, "U0"));
2544 const auto& class_i = Class::Handle(GetClass(root_library, "I"));
2545 const auto& obj_i = Object::Handle(Invoke(root_library, "createI"));
2546 {
2547 SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
2549 }
2550
2551 // Double-check assumptions from calculating kNumUnrelated.
2552 EXPECT_EQ(kNumUnrelated, class_b.id() - 1);
2553 EXPECT_EQ(class_b.id() + 1, class_g.id());
2554 EXPECT_EQ(class_c.id() + 1, class_d.id());
2555 EXPECT_EQ(class_d.id() + 1, class_u0.id());
2556 EXPECT_EQ(class_u0.id() + kNumUnrelated, class_i.id());
2557 EXPECT_EQ(class_g.id(), class_i.id() - class_c.id());
2558
2559 const auto& tav_null = Object::null_type_arguments();
2560 auto& tav_int = TypeArguments::Handle(TypeArguments::New(1));
2561 tav_int.SetTypeAt(0, Type::Handle(Type::IntType()));
2562 CanonicalizeTAV(&tav_int);
2563
2564 auto& type_b_int = Type::Handle(Type::New(class_b, tav_int));
2565 FinalizeAndCanonicalize(&type_b_int);
2566
2567 TTSTestState state(thread, type_b_int);
2568 state.InvokeEagerlySpecializedStub(Failure({obj_i, tav_null, tav_null}));
2569}
2570
2571struct STCTestResults {
2572 bool became_hash_cache = false;
2573 bool cache_capped = false;
2574};
2575
2576static STCTestResults SubtypeTestCacheTest(Thread* thread,
2577 intptr_t num_classes) {
2578 TextBuffer buffer(MB);
2579 buffer.AddString("class D<S> {}\n");
2580 buffer.AddString("D<int> Function() createClosureD() => () => D<int>();\n");
2581 for (intptr_t i = 0; i < num_classes; i++) {
2582 buffer.Printf(R"(class C%)" Pd R"(<S> extends D<S> {}
2583 C%)" Pd R"(<int> Function() createClosureC%)" Pd R"(() => () => C%)" Pd
2584 R"(<int>();
2585)",
2586 i, i, i, i);
2587 }
2588
2589 Dart_Handle api_lib = TestCase::LoadTestScript(buffer.buffer(), nullptr);
2590 EXPECT_VALID(api_lib);
2591
2592 // D + C0...CN, where N = kNumClasses - 1
2593 EXPECT(IsolateGroup::Current()->class_table()->NumCids() > num_classes);
2594
2595 TransitionNativeToVM transition(thread);
2596 Zone* const zone = thread->zone();
2597
2598 const auto& root_lib =
2599 Library::CheckedHandle(zone, Api::UnwrapHandle(api_lib));
2600 EXPECT(!root_lib.IsNull());
2601
2602 const auto& class_d = Class::Handle(zone, GetClass(root_lib, "D"));
2603 ASSERT(!class_d.IsNull());
2604 {
2605 SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
2607 }
2608 const auto& object_d =
2609 Instance::CheckedHandle(zone, Invoke(root_lib, "createClosureD"));
2610 ASSERT(!object_d.IsNull());
2611 auto& type_closure_d_int =
2612 AbstractType::Handle(zone, object_d.GetType(Heap::kNew));
2613 const bool can_be_null = Instance::NullIsAssignableTo(type_closure_d_int);
2614
2615 const auto& tav_null = Object::null_type_arguments();
2616
2617 TTSTestState state(thread, type_closure_d_int);
2618 // Prime the stub before the loop with the null object.
2619 state.InvokeEagerlySpecializedStub(
2620 {Object::null_object(), tav_null, tav_null, can_be_null ? kTTS : kFail});
2621
2622 auto& class_c = Class::Handle(zone);
2623 auto& object_c = Object::Handle(zone);
2624 STCTestResults results;
2625 for (intptr_t i = 0; i < num_classes; ++i) {
2626 auto const class_name = OS::SCreate(zone, "C%" Pd "", i);
2627 class_c = GetClass(root_lib, class_name);
2628 ASSERT(!class_c.IsNull());
2629 {
2630 SafepointWriteRwLocker ml(thread,
2631 thread->isolate_group()->program_lock());
2633 }
2634 auto const function_name = OS::SCreate(zone, "createClosureC%" Pd "", i);
2635 object_c = Invoke(root_lib, function_name);
2636
2637 TTSTestCase base_case = {object_c, tav_null, tav_null};
2638
2639 if (i >= FLAG_max_subtype_cache_entries) {
2640 ASSERT(state.last_stc().NumberOfChecks() >=
2641 FLAG_max_subtype_cache_entries);
2642 state.InvokeExistingStub(RuntimeCheck(base_case));
2643 // Rerunning the test doesn't change the fact we can't add to the STC.
2644 state.InvokeExistingStub(RuntimeCheck(base_case));
2645 results.cache_capped = true;
2646 } else {
2647 const bool was_hash = state.last_stc().IsHash();
2648 // All the rest of the tests should create or modify an STC.
2649 state.InvokeExistingStub(FalseNegative(base_case));
2650 if (i == 0) {
2651 // We should get a linear cache the first time.
2652 EXPECT(!state.last_stc().IsHash());
2653 } else if (was_hash) {
2654 // We should never change from hash back to linear.
2655 EXPECT(state.last_stc().IsHash());
2656 } else if (state.last_stc().IsHash()) {
2657 results.became_hash_cache = true;
2658 }
2659 state.InvokeExistingStub(STCCheck(base_case));
2660 }
2661 }
2662
2663 return results;
2664}
2665
2666// The smallest test that just checks linear caches.
2667TEST_CASE(TTS_STC_LinearOnly) {
2668 const intptr_t num_classes =
2669 Utils::Minimum(static_cast<intptr_t>(FLAG_max_subtype_cache_entries),
2671 EXPECT(num_classes > 0);
2672 const auto& results = SubtypeTestCacheTest(thread, num_classes);
2673 EXPECT(!results.became_hash_cache);
2674 EXPECT(!results.cache_capped);
2675}
2676
2677// A larger test that ensures we convert to a hash table at some point.
2678TEST_CASE(TTS_STC_Hash) {
2679 const intptr_t num_classes =
2680 Utils::Minimum(static_cast<intptr_t>(FLAG_max_subtype_cache_entries),
2683 const auto& results = SubtypeTestCacheTest(thread, num_classes);
2684 EXPECT(results.became_hash_cache);
2685 EXPECT(!results.cache_capped);
2686}
2687
2688// A larger test that ensures that we use enough entries to hit the max STC
2689// size and test what happens when we go above it.
2690TEST_CASE(TTS_STC_Capped) {
2691 const intptr_t num_classes = 1.1 * FLAG_max_subtype_cache_entries;
2692 EXPECT(num_classes > 0);
2693 const auto& results = SubtypeTestCacheTest(thread, num_classes);
2694 EXPECT_EQ(SubtypeTestCache::kMaxLinearCacheEntries < num_classes,
2695 results.became_hash_cache);
2696 EXPECT(results.cache_capped);
2697}
2698
2699} // namespace dart
2700
2701#endif // !defined(TARGET_ARCH_IA32)
AutoreleasePool pool
static void done(const char *config, const char *src, const char *srcOptions, const char *name)
Definition DM.cpp:263
#define EXPECT(type, expectedAlignment, expectedSize)
#define __
#define UNREACHABLE()
Definition assert.h:248
#define ASSERT_EQUAL(expected, actual)
Definition assert.h:309
#define RELEASE_ASSERT(cond)
Definition assert.h:327
void SetTypeTestingStub(const Code &stub) const
Definition object.cc:21822
CodePtr type_test_stub() const
Definition object.h:9284
static ObjectPtr UnwrapHandle(Dart_Handle object)
static ArrayPtr New(intptr_t len, Heap::Space space=Heap::kNew)
Definition object.h:10933
ObjectPtr At(intptr_t index) const
Definition object.h:10854
void SetAt(intptr_t index, const Object &value) const
Definition object.h:10858
static constexpr Register kReturnReg
static AbstractTypePtr FinalizeType(const AbstractType &type, FinalizationKind finalization=kCanonicalize)
static void FinalizeTypesInClass(const Class &cls)
static void FinalizeClass(const Class &cls)
intptr_t NumCids() const
static CodePtr FinalizeCodeAndNotify(const Function &function, FlowGraphCompiler *compiler, compiler::Assembler *assembler, PoolAttachment pool_attachment, bool optimized=false, CodeStatistics *stats=nullptr)
Definition object.cc:18033
static void DisassembleStub(const char *name, const Code &code)
static DoublePtr New(double d, Heap::Space space=Heap::kNew)
Definition object.cc:23481
static ExceptionHandlersPtr New(intptr_t num_handlers)
Definition object.cc:16288
static void GenerateIndirectTTSCall(compiler::Assembler *assembler, Register reg_with_type, intptr_t sub_type_cache_index)
static FunctionTypePtr New(intptr_t num_parent_type_arguments=0, Nullability nullability=Nullability::kLegacy, Heap::Space space=Heap::kOld)
Definition object.cc:11682
static FunctionPtr New(const FunctionType &signature, const String &name, UntaggedFunction::Kind kind, bool is_static, bool is_const, bool is_abstract, bool is_external, bool is_native, const Object &owner, TokenPosition token_pos, Heap::Space space=Heap::kOld)
Definition object.cc:10301
@ kNew
Definition heap.h:38
@ kOld
Definition heap.h:39
static bool NullIsAssignableTo(const AbstractType &other)
Definition object.cc:20715
static IntegerPtr New(const String &str, Heap::Space space=Heap::kNew)
Definition object.cc:23063
static IsolateGroup * Current()
Definition isolate.h:534
ClassTable * class_table() const
Definition isolate.h:491
Mutex * subtype_test_cache_mutex()
Definition isolate.h:511
static char * SCreate(Zone *zone, const char *format,...) PRINTF_ATTRIBUTE(2
ObjectPtr ObjectAt(intptr_t index) const
Definition object.h:5599
void SetObjectAt(intptr_t index, const Object &obj) const
Definition object.h:5605
static ObjectPoolPtr NewFromBuilder(const compiler::ObjectPoolBuilder &builder)
Definition object.cc:15723
static ObjectPtr null()
Definition object.h:433
intptr_t GetClassId() const
Definition object.h:341
ObjectPtr ptr() const
Definition object.h:332
virtual const char * ToCString() const
Definition object.h:366
bool IsNull() const
Definition object.h:363
static Object & Handle()
Definition object.h:407
static ObjectPtr RawCast(ObjectPtr obj)
Definition object.h:325
ClassPtr clazz() const
Definition object.h:13192
static const char * RegisterName(Register reg)
Definition constants.h:46
static SmiPtr New(intptr_t value)
Definition object.h:9985
intptr_t Value() const
Definition object.h:9969
static StringPtr New(const char *cstr, Heap::Space space=Heap::kNew)
Definition object.cc:23777
static const char * NameOfStub(uword entry_point)
Definition stub_code.cc:330
SubtypeTestCachePtr Copy(Thread *thread) const
Definition object.cc:19689
ArrayPtr cache() const
Definition object.cc:18988
intptr_t NumberOfChecks() const
Definition object.cc:19004
static constexpr intptr_t kMaxLinearCacheEntries
Definition object.h:7801
void WriteToBuffer(Zone *zone, BaseTextBuffer *buffer, const char *line_prefix=nullptr) const
Definition object.cc:19557
static const String & Empty()
Definition symbols.h:687
static StringPtr New(Thread *thread, const char *cstr)
Definition symbols.h:722
TTSTestState(Thread *thread, const AbstractType &type)
AbstractTypePtr TypeToTest(const TTSTestCase &test_case) const
const SubtypeTestCachePtr current_stc() const
void InvokeLazilySpecializedStub(const TTSTestCase &test_case, bool should_specialize=true)
void InvokeExistingStub(const TTSTestCase &test_case)
const SubtypeTestCache & last_stc() const
void InvokeEagerlySpecializedStub(const TTSTestCase &test_case)
static Dart_Handle LoadTestScript(const char *script, Dart_NativeEntryResolver resolver, const char *lib_uri=RESOLVED_USER_TEST_URI, bool finalize=true, bool allow_compile_errors=false)
Definition unit_test.cc:422
static bool IsNNBD()
Definition unit_test.cc:217
Zone * zone() const
static Thread * Current()
Definition thread.h:361
IsolateGroup * isolate_group() const
Definition thread.h:540
TypeArgumentsPtr Canonicalize(Thread *thread) const
Definition object.cc:7761
static TypeArgumentsPtr New(intptr_t len, Heap::Space space=Heap::kOld)
Definition object.cc:7733
static CodePtr DefaultCodeForType(const AbstractType &type, bool lazy_specialize=true)
static CodePtr SpecializeStubFor(Thread *thread, const AbstractType &type)
static TypePtr SmiType()
Definition object.cc:21894
static TypePtr New(const Class &clazz, const TypeArguments &arguments, Nullability nullability=Nullability::kLegacy, Heap::Space space=Heap::kOld)
Definition object.cc:22492
static TypePtr Number()
Definition object.cc:21922
static TypePtr ObjectType()
Definition object.cc:21878
static TypePtr Double()
Definition object.cc:21902
static TypePtr DynamicType()
Definition object.cc:21866
static TypePtr DartFunctionType()
Definition object.cc:21938
static TypePtr StringType()
Definition object.cc:21930
static TypePtr IntType()
Definition object.cc:21886
static T Minimum(T x, T y)
Definition utils.h:21
#define THR_Print(format,...)
Definition log.h:20
struct _Dart_Handle * Dart_Handle
Definition dart_api.h:258
#define FAIL(name, result)
#define ASSERT(E)
VkInstance instance
Definition main.cc:48
#define FATAL(error)
AtkStateType state
static const uint8_t buffer[]
const uint8_t uint32_t uint32_t GError ** error
GAsyncResult * result
#define DECLARE_FLAG(type, name)
Definition flags.h:14
#define DEFINE_FLAG(type, name, default_value, comment)
Definition flags.h:16
Dart_NativeFunction function
Definition fuchsia.cc:51
constexpr int64_t kMaxInt64
Definition globals.h:486
constexpr intptr_t MB
Definition globals.h:530
static TTSTestCase FalseNegative(const TTSTestCase &original)
LibraryPtr LoadTestScript(const char *script, Dart_NativeEntryResolver resolver, const char *lib_uri)
const char * kSubtypeRangeCheckScript
static void FinalizeAndCanonicalize(AbstractType *type)
const char *const class_name
const char * kRecordSubtypeRangeCheckScript
ObjectPtr Invoke(const Library &lib, const char *name)
FunctionPtr GetFunction(const Library &lib, const char *name)
LibraryPtr ReloadTestScript(const char *script)
static const char * kTestResultStrings[]
static TTSTestCase Respecialization(const TTSTestCase &original)
static void SubtypeTestCacheTest(Thread *thread, intptr_t num_classes, bool expect_hash)
static void GenerateInvokeTTSStub(compiler::Assembler *assembler)
@ kNumberOfCpuRegisters
static TTSTestCase STCCheck(const TTSTestCase &original)
static constexpr int kCallerSpSlotFromFp
constexpr RegList kDartAvailableCpuRegs
const Register FPREG
ClassPtr GetClass(const Library &lib, const char *name)
const intptr_t cid
TypeParameterPtr GetClassTypeParameter(const Class &klass, intptr_t index)
static void RunTTSTest(const AbstractType &dst_type, const TTSTestCase &test_case, bool should_specialize=true)
void SetupCoreLibrariesForUnitTest()
Definition unit_test.cc:148
TypeParameterPtr GetFunctionTypeParameter(const Function &fun, intptr_t index)
static TTSTestCase Failure(const TTSTestCase &original)
const char *const function_name
static TTSTestCase RuntimeCheck(const TTSTestCase &original)
static void CanonicalizeTAV(TypeArguments *tav)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets Path to the Flutter assets directory enable service port Allow the VM service to fallback to automatic port selection if binding to a specified port fails trace Trace early application lifecycle Automatically switches to an endless trace buffer trace skia Filters out all Skia trace event categories except those that are specified in this comma separated list dump skp on shader Automatically dump the skp that triggers new shader compilations This is useful for writing custom ShaderWarmUp to reduce jank By this is not enabled to reduce the overhead purge persistent cache
Definition switches.h:191
#define Pd
Definition globals.h:408
static constexpr intptr_t kInternalRegisters
const TTSTestResult expected_result
bool HasSTCEntry(const SubtypeTestCache &cache, const AbstractType &dst_type, Bool *out_result=nullptr, intptr_t *out_index=nullptr) const
TTSTestCase(const Object &obj, const TypeArguments &i_tav, const TypeArguments &f_tav, const TTSTestResult result=kTTS)
bool HasSameSTCEntry(const TTSTestCase &other) const
const TypeArguments & function_tav
const TypeArguments & instantiator_tav
static constexpr Register kDstTypeReg
static constexpr Register kInstanceReg
static constexpr Register kFunctionTypeArgumentsReg
static constexpr Register kInstantiatorTypeArgumentsReg
static constexpr intptr_t kPreservedAbiRegisters
static constexpr Register kScratchReg
static constexpr intptr_t kAbiRegisters
#define FILE_RESOLVE_URI(Uri)
#define THIRD_PARTIAL_LIBRARY_NAME
#define FIRST_PARTIAL_LIBRARY_NAME
#define SECOND_PARTIAL_LIBRARY_NAME
#define ISOLATE_UNIT_TEST_CASE(name)
Definition unit_test.h:64
#define TEST_CASE(name)
Definition unit_test.h:85
#define EXPECT_VALID(handle)
Definition unit_test.h:650