Flutter Engine
The Flutter Engine
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 =
87 const intptr_t inst_type_args_offset =
89 const intptr_t fun_type_args_offset =
91 const intptr_t dst_type_offset =
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,
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,
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
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 =
970 type_nullable_int.ToNullability(Nullability::kNullable, Heap::kNew);
971 auto& tav_nullable_int = TypeArguments::Handle(TypeArguments::New(1));
972 tav_nullable_int.SetTypeAt(0, type_nullable_int);
973 CanonicalizeTAV(&tav_nullable_int);
974 auto& type_base_nullable_int =
975 AbstractType::Handle(Type::New(class_base, tav_nullable_int));
976 FinalizeAndCanonicalize(&type_base_nullable_int);
977 RunTTSTest(type_base_nullable_int, {obj_base_null, tav_null, tav_null});
978 RunTTSTest(type_base_nullable_int, {obj_base_never, tav_null, tav_null});
979
980 // Base<Null|Never> as Base<int>
981 auto& type_int = Type::Handle(Type::IntType());
982 type_int = type_int.ToNullability(Nullability::kNonNullable, Heap::kNew);
983 auto& tav_int = TypeArguments::Handle(TypeArguments::New(1));
984 tav_int.SetTypeAt(0, type_int);
985 CanonicalizeTAV(&tav_int);
986 auto& type_base_int = Type::Handle(Type::New(class_base, tav_int));
987 type_base_int =
988 type_base_int.ToNullability(Nullability::kNonNullable, Heap::kNew);
989 FinalizeAndCanonicalize(&type_base_int);
990 RunTTSTest(type_base_int, Failure({obj_base_null, tav_null, tav_null}));
991 RunTTSTest(type_base_int, {obj_base_never, tav_null, tav_null});
992
993 // <...> as I2
994 const auto& type_i2 = AbstractType::Handle(class_i2.RareType());
995 RunTTSTest(type_i2, Failure({obj_i, tav_null, tav_null}));
996 RunTTSTest(type_i2, {obj_i2, tav_null, tav_null});
997 RunTTSTest(type_i2, Failure({obj_base_int, tav_null, tav_null}));
998 RunTTSTest(type_i2, Failure({obj_a, tav_null, tav_null}));
999 RunTTSTest(type_i2, {obj_a1, tav_null, tav_null});
1000 RunTTSTest(type_i2, Failure({obj_a2, tav_null, tav_null}));
1001 RunTTSTest(type_i2, Failure({obj_b, tav_null, tav_null}));
1002 RunTTSTest(type_i2, {obj_b1, tav_null, tav_null});
1003 RunTTSTest(type_i2, Failure({obj_b2, tav_null, tav_null}));
1004
1005 // <...> as I<Object, dynamic>
1006 auto& type_i_object_dynamic =
1007 AbstractType::Handle(Type::New(class_i, tav_object_dynamic));
1008 FinalizeAndCanonicalize(&type_i_object_dynamic);
1009 RunTTSTest(type_i_object_dynamic, {obj_i, tav_null, tav_null});
1010 RunTTSTest(type_i_object_dynamic, Failure({obj_i2, tav_null, tav_null}));
1011 RunTTSTest(type_i_object_dynamic,
1012 Failure({obj_base_int, tav_null, tav_null}));
1013 RunTTSTest(type_i_object_dynamic, Failure({obj_a, tav_null, tav_null}));
1014 RunTTSTest(type_i_object_dynamic, Failure({obj_a1, tav_null, tav_null}));
1015 RunTTSTest(type_i_object_dynamic, {obj_a2, tav_null, tav_null});
1016 RunTTSTest(type_i_object_dynamic, Failure({obj_b, tav_null, tav_null}));
1017 RunTTSTest(type_i_object_dynamic, Failure({obj_b1, tav_null, tav_null}));
1018 RunTTSTest(type_i_object_dynamic, {obj_b2, tav_null, tav_null});
1019
1020 // We do generate TTSes for uninstantiated types when we need to use
1021 // subtype range checks for the class of the interface type, but the TTS
1022 // may be partial (returns a false negative in some cases that means going
1023 // to the STC/runtime).
1024 //
1025 // obj as I<dynamic, T>
1026 //
1027 auto& type_dynamic_t =
1028 AbstractType::Handle(Type::New(class_i, tav_dynamic_t));
1029 FinalizeAndCanonicalize(&type_dynamic_t);
1030 RunTTSTest(type_dynamic_t, {obj_i, tav_object, tav_null});
1031 RunTTSTest(type_dynamic_t, Failure({obj_i2, tav_object, tav_null}));
1032 RunTTSTest(type_dynamic_t, Failure({obj_base_int, tav_object, tav_null}));
1033 RunTTSTest(type_dynamic_t, Failure({obj_a, tav_object, tav_null}));
1034 RunTTSTest(type_dynamic_t, Failure({obj_a1, tav_object, tav_null}));
1035 RunTTSTest(type_dynamic_t, {obj_a2, tav_object, tav_null});
1036 RunTTSTest(type_dynamic_t, Failure({obj_b, tav_object, tav_null}));
1037 RunTTSTest(type_dynamic_t, Failure({obj_b1, tav_object, tav_null}));
1038 RunTTSTest(type_dynamic_t, FalseNegative({obj_b2, tav_object, tav_null}));
1039
1040 // obj as Object (with null safety)
1041 auto isolate_group = IsolateGroup::Current();
1042 auto& type_non_nullable_object =
1043 Type::Handle(isolate_group->object_store()->non_nullable_object_type());
1044 RunTTSTest(type_non_nullable_object, {obj_a, tav_null, tav_null});
1045}
1046
1047ISOLATE_UNIT_TEST_CASE(TTS_GenericSubtypeRangeCheck) {
1048 const auto& root_library =
1050 const auto& class_a1 = Class::Handle(GetClass(root_library, "A1"));
1051 const auto& class_a2 = Class::Handle(GetClass(root_library, "A2"));
1052 const auto& class_base = Class::Handle(GetClass(root_library, "Base"));
1053 const auto& class_i = Class::Handle(GetClass(root_library, "I"));
1054 const auto& fun_generic =
1055 Function::Handle(GetFunction(root_library, "genericFun"));
1056
1057 const auto& obj_i = Object::Handle(Invoke(root_library, "createI"));
1058 const auto& obj_i2 = Object::Handle(Invoke(root_library, "createI2"));
1059 const auto& obj_base_int =
1060 Object::Handle(Invoke(root_library, "createBaseInt"));
1061 const auto& obj_a = Object::Handle(Invoke(root_library, "createA"));
1062 const auto& obj_a1 = Object::Handle(Invoke(root_library, "createA1"));
1063 const auto& obj_a2 = Object::Handle(Invoke(root_library, "createA2"));
1064 const auto& obj_b = Object::Handle(Invoke(root_library, "createB"));
1065 const auto& obj_b1 = Object::Handle(Invoke(root_library, "createB1"));
1066 const auto& obj_b2 = Object::Handle(Invoke(root_library, "createB2"));
1067 const auto& obj_basea2int =
1068 Object::Handle(Invoke(root_library, "createBaseA2Int"));
1069 const auto& obj_basea2a1 =
1070 Object::Handle(Invoke(root_library, "createBaseA2A1"));
1071 const auto& obj_baseb2int =
1072 Object::Handle(Invoke(root_library, "createBaseB2Int"));
1073 const auto& obj_baseistringdouble =
1074 Object::Handle(Invoke(root_library, "createBaseIStringDouble"));
1075
1076 const auto& type_dynamic = Type::Handle(Type::DynamicType());
1077 auto& type_int = Type::Handle(Type::IntType());
1078 auto& type_string = Type::Handle(Type::StringType());
1079 auto& type_object = Type::Handle(Type::ObjectType());
1080 type_object = type_object.ToNullability(Nullability::kNullable, Heap::kNew);
1081 auto& type_a1 = Type::Handle(class_a1.DeclarationType());
1082 FinalizeAndCanonicalize(&type_a1);
1083
1084 const auto& tav_null = TypeArguments::Handle(TypeArguments::null());
1085
1086 auto& tav_object_dynamic = TypeArguments::Handle(TypeArguments::New(2));
1087 tav_object_dynamic.SetTypeAt(0, type_object);
1088 tav_object_dynamic.SetTypeAt(1, type_dynamic);
1089 CanonicalizeTAV(&tav_object_dynamic);
1090
1091 auto& tav_dynamic_int = TypeArguments::Handle(TypeArguments::New(2));
1092 tav_dynamic_int.SetTypeAt(0, type_dynamic);
1093 tav_dynamic_int.SetTypeAt(1, type_int);
1094 CanonicalizeTAV(&tav_dynamic_int);
1095
1096 auto& tav_dynamic_string = TypeArguments::Handle(TypeArguments::New(2));
1097 tav_dynamic_string.SetTypeAt(0, type_dynamic);
1098 tav_dynamic_string.SetTypeAt(1, type_string);
1099 CanonicalizeTAV(&tav_dynamic_string);
1100
1101 auto& tav_int = TypeArguments::Handle(TypeArguments::New(1));
1102 tav_int.SetTypeAt(0, type_int);
1103 CanonicalizeTAV(&tav_int);
1104
1105 auto& type_i_object_dynamic =
1106 AbstractType::Handle(Type::New(class_i, tav_object_dynamic));
1107 FinalizeAndCanonicalize(&type_i_object_dynamic);
1108 const auto& tav_iod = TypeArguments::Handle(TypeArguments::New(1));
1109 tav_iod.SetTypeAt(0, type_i_object_dynamic);
1110
1111 // We will generate specialized TTS for instantiated interface types
1112 // where there are no type arguments or the type arguments are top
1113 // types.
1114 //
1115 // obj as Base<I<Object, dynamic>> // Subclass ranges for Base, subtype
1116 // // ranges tav arguments.
1117 // obj as Base<T> // Subclass ranges for Base, type
1118 // // equality for instantiator type arg T
1119 // obj as Base<B> // Subclass ranges for Base, type
1120 // // equality for function type arg B.
1121 //
1122
1123 // <...> as Base<I<Object, dynamic>>
1124 auto& type_base_i_object_dynamic =
1125 AbstractType::Handle(Type::New(class_base, tav_iod));
1126 FinalizeAndCanonicalize(&type_base_i_object_dynamic);
1127 RunTTSTest(type_base_i_object_dynamic, {obj_baseb2int, tav_null, tav_null});
1128 RunTTSTest(type_base_i_object_dynamic,
1129 {obj_baseistringdouble, tav_null, tav_null});
1130 RunTTSTest(type_base_i_object_dynamic, Failure({obj_a, tav_null, tav_null}));
1131 RunTTSTest(type_base_i_object_dynamic, Failure({obj_a1, tav_null, tav_null}));
1132 RunTTSTest(type_base_i_object_dynamic, Failure({obj_a2, tav_null, tav_null}));
1133 RunTTSTest(type_base_i_object_dynamic, Failure({obj_b, tav_null, tav_null}));
1134 RunTTSTest(type_base_i_object_dynamic, Failure({obj_b1, tav_null, tav_null}));
1135 RunTTSTest(type_base_i_object_dynamic, Failure({obj_b2, tav_null, tav_null}));
1136
1137 // <...> as Base<T> with T instantiantiator type parameter (T == int)
1138 const auto& tav_baset = TypeArguments::Handle(TypeArguments::New(1));
1139 tav_baset.SetTypeAt(
1140 0, TypeParameter::Handle(GetClassTypeParameter(class_base, 0)));
1141 auto& type_base_t = AbstractType::Handle(Type::New(class_base, tav_baset));
1142 FinalizeAndCanonicalize(&type_base_t);
1143 RunTTSTest(type_base_t, {obj_base_int, tav_int, tav_null});
1144 RunTTSTest(type_base_t, Failure({obj_baseistringdouble, tav_int, tav_null}));
1145
1146 // <...> as Base<B> with B function type parameter
1147 const auto& tav_baseb = TypeArguments::Handle(TypeArguments::New(1));
1148 tav_baseb.SetTypeAt(
1150 auto& type_base_b = AbstractType::Handle(Type::New(class_base, tav_baseb));
1151 FinalizeAndCanonicalize(&type_base_b);
1152 // With B == int
1153 RunTTSTest(type_base_b, {obj_base_int, tav_null, tav_dynamic_int});
1154 RunTTSTest(type_base_b,
1155 Failure({obj_baseistringdouble, tav_null, tav_dynamic_int}));
1156 // With B == dynamic (null vector)
1157 RunTTSTest(type_base_b, {obj_base_int, tav_null, tav_null});
1158 RunTTSTest(type_base_b, Failure({obj_i2, tav_null, tav_null}));
1159
1160 // We generate TTS for implemented classes and uninstantiated types, but
1161 // any class that implements the type class but does not match in both
1162 // instance TAV offset and type argument indices is guaranteed to be a
1163 // false negative.
1164 //
1165 // obj as I<dynamic, String> // I is generic & implemented.
1166 // obj as Base<A2<T>> // A2<T> is not instantiated.
1167 // obj as Base<A2<A1>> // A2<A1> is not a rare type.
1168 //
1169
1170 // <...> as I<dynamic, String>
1171 RELEASE_ASSERT(class_i.is_implemented());
1172 auto& type_i_dynamic_string =
1173 Type::Handle(Type::New(class_i, tav_dynamic_string));
1174 type_i_dynamic_string = type_i_dynamic_string.ToNullability(
1176 FinalizeAndCanonicalize(&type_i_dynamic_string);
1177 RunTTSTest(type_i_dynamic_string, {obj_i, tav_null, tav_null});
1178 RunTTSTest(type_i_dynamic_string,
1179 Failure({obj_base_int, tav_null, tav_null}));
1180
1181 // <...> as Base<A2<T>>
1182 const auto& tav_t = TypeArguments::Handle(TypeArguments::New(1));
1183 tav_t.SetTypeAt(0,
1185 auto& type_a2_t = Type::Handle(Type::New(class_a2, tav_t));
1186 FinalizeAndCanonicalize(&type_a2_t);
1187 const auto& tav_a2_t = TypeArguments::Handle(TypeArguments::New(1));
1188 tav_a2_t.SetTypeAt(0, type_a2_t);
1189 auto& type_base_a2_t = Type::Handle(Type::New(class_base, tav_a2_t));
1190 type_base_a2_t =
1191 type_base_a2_t.ToNullability(Nullability::kNonNullable, Heap::kNew);
1192 FinalizeAndCanonicalize(&type_base_a2_t);
1193 RunTTSTest(type_base_a2_t,
1194 FalseNegative({obj_basea2int, tav_null, tav_null}));
1195 RunTTSTest(type_base_a2_t, Failure({obj_base_int, tav_null, tav_null}));
1196
1197 // <...> as Base<A2<A1>>
1198 const auto& tav_a1 = TypeArguments::Handle(TypeArguments::New(1));
1199 tav_a1.SetTypeAt(0, type_a1);
1200 auto& type_a2_a1 = Type::Handle(Type::New(class_a2, tav_a1));
1201 FinalizeAndCanonicalize(&type_a2_a1);
1202 const auto& tav_a2_a1 = TypeArguments::Handle(TypeArguments::New(1));
1203 tav_a2_a1.SetTypeAt(0, type_a2_a1);
1204 auto& type_base_a2_a1 = Type::Handle(Type::New(class_base, tav_a2_a1));
1205 type_base_a2_a1 =
1206 type_base_a2_a1.ToNullability(Nullability::kNonNullable, Heap::kNew);
1207 FinalizeAndCanonicalize(&type_base_a2_a1);
1208 RunTTSTest(type_base_a2_a1,
1209 FalseNegative({obj_basea2a1, tav_null, tav_null}));
1210 RunTTSTest(type_base_a2_a1, Failure({obj_basea2int, tav_null, tav_null}));
1211}
1212
1214 R"(
1215 class A {}
1216 class B extends A {}
1217 class C implements A {}
1218 class D<T> {}
1219
1220 getType<T>() => T;
1221 getRecordType1() => getType<(int, A)>();
1222 getRecordType2() => getType<(A, int, String)>();
1223 getRecordType3() => getType<(int, D)>();
1224
1225 createObj1() => (1, B());
1226 createObj2() => (1, 'bye');
1227 createObj3() => (1, foo: B());
1228 createObj4() => (1, B(), 2);
1229 createObj5() => (C(), 2, 'hi');
1230 createObj6() => (D(), 2, 'hi');
1231 createObj7() => (3, D<int>());
1232 createObj8() => (D<int>(), 3);
1233)";
1234
1235ISOLATE_UNIT_TEST_CASE(TTS_RecordSubtypeRangeCheck) {
1236 const auto& root_library =
1238
1239 const auto& type1 = AbstractType::Cast(
1240 Object::Handle(Invoke(root_library, "getRecordType1")));
1241 const auto& type2 = AbstractType::Cast(
1242 Object::Handle(Invoke(root_library, "getRecordType2")));
1243 const auto& type3 = AbstractType::Cast(
1244 Object::Handle(Invoke(root_library, "getRecordType3")));
1245
1246 const auto& obj1 = Object::Handle(Invoke(root_library, "createObj1"));
1247 const auto& obj2 = Object::Handle(Invoke(root_library, "createObj2"));
1248 const auto& obj3 = Object::Handle(Invoke(root_library, "createObj3"));
1249 const auto& obj4 = Object::Handle(Invoke(root_library, "createObj4"));
1250 const auto& obj5 = Object::Handle(Invoke(root_library, "createObj5"));
1251 const auto& obj6 = Object::Handle(Invoke(root_library, "createObj6"));
1252 const auto& obj7 = Object::Handle(Invoke(root_library, "createObj7"));
1253 const auto& obj8 = Object::Handle(Invoke(root_library, "createObj8"));
1254
1255 const auto& tav_null = TypeArguments::Handle(TypeArguments::null());
1256
1257 // (1, B()) as (int, A)
1258 // (1, 'bye') as (int, A)
1259 // (1, foo: B()) as (int, A)
1260 // (1, B(), 2) as (int, A)
1261 RunTTSTest(type1, {obj1, tav_null, tav_null});
1262 RunTTSTest(type1, Failure({obj2, tav_null, tav_null}));
1263 RunTTSTest(type1, Failure({obj3, tav_null, tav_null}));
1264 RunTTSTest(type1, Failure({obj4, tav_null, tav_null}));
1265
1266 // (C(), 2, 'hi') as (A, int, String)
1267 // (D(), 2, 'hi') as (A, int, String)
1268 RunTTSTest(type2, {obj5, tav_null, tav_null});
1269 RunTTSTest(type2, Failure({obj6, tav_null, tav_null}));
1270
1271 // (3, D<int>()) as (int, D)
1272 // (D<int>(), 3) as (int, D)
1273 RunTTSTest(type3, {obj7, tav_null, tav_null});
1274 RunTTSTest(type3, Failure({obj8, tav_null, tav_null}));
1275}
1276
1277ISOLATE_UNIT_TEST_CASE(TTS_Generic_Implements_Instantiated_Interface) {
1278 const char* kScript =
1279 R"(
1280 abstract class I<T> {}
1281 class B<R> implements I<String> {}
1282
1283 createBInt() => B<int>();
1284)";
1285
1286 const auto& root_library = Library::Handle(LoadTestScript(kScript));
1287 const auto& class_i = Class::Handle(GetClass(root_library, "I"));
1288 const auto& obj_b_int = Object::Handle(Invoke(root_library, "createBInt"));
1289
1290 const auto& tav_null = Object::null_type_arguments();
1291 auto& tav_string = TypeArguments::Handle(TypeArguments::New(1));
1292 tav_string.SetTypeAt(0, Type::Handle(Type::StringType()));
1293 CanonicalizeTAV(&tav_string);
1294
1295 auto& type_i_string = Type::Handle(Type::New(class_i, tav_string));
1296 FinalizeAndCanonicalize(&type_i_string);
1297 const auto& type_i_t = Type::Handle(class_i.DeclarationType());
1298
1299 RunTTSTest(type_i_string, {obj_b_int, tav_null, tav_null});
1300 // Optimized TTSees don't currently handle the case where the implemented
1301 // type is known, but the type being checked requires instantiation at
1302 // runtime.
1303 RunTTSTest(type_i_t, FalseNegative({obj_b_int, tav_string, tav_null}));
1304}
1305
1306ISOLATE_UNIT_TEST_CASE(TTS_Future) {
1307 const char* kScript =
1308 R"(
1309 import "dart:async";
1310
1311 Future<int> createFutureInt() async => 3;
1312 Future<int Function()> createFutureFunction() async => () => 3;
1313 Future<int Function()?> createFutureNullableFunction() async =>
1314 (() => 3) as int Function()?;
1315)";
1316
1318
1319 const auto& class_future =
1320 Class::Handle(IsolateGroup::Current()->object_store()->future_class());
1321
1322 const auto& root_library = Library::Handle(LoadTestScript(kScript));
1323 const auto& class_closure =
1324 Class::Handle(IsolateGroup::Current()->object_store()->closure_class());
1325 const auto& obj_futureint =
1326 Object::Handle(Invoke(root_library, "createFutureInt"));
1327 const auto& obj_futurefunction =
1328 Object::Handle(Invoke(root_library, "createFutureFunction"));
1329 const auto& obj_futurenullablefunction =
1330 Object::Handle(Invoke(root_library, "createFutureNullableFunction"));
1331
1332 const auto& tav_null = Object::null_type_arguments();
1333 const auto& type_object = Type::Handle(
1334 IsolateGroup::Current()->object_store()->non_nullable_object_type());
1335 const auto& type_nullable_object = Type::Handle(
1336 IsolateGroup::Current()->object_store()->nullable_object_type());
1337 const auto& type_int = Type::Handle(
1338 IsolateGroup::Current()->object_store()->non_nullable_int_type());
1339
1340 auto& type_string = Type::Handle(Type::StringType());
1341 type_string =
1342 type_string.ToNullability(Nullability::kNonNullable, Heap::kNew);
1343 FinalizeAndCanonicalize(&type_string);
1344 auto& type_num = Type::Handle(Type::Number());
1345 type_num = type_num.ToNullability(Nullability::kNonNullable, Heap::kNew);
1346 FinalizeAndCanonicalize(&type_num);
1347
1348 auto& tav_dynamic = TypeArguments::Handle(TypeArguments::New(1));
1349 tav_dynamic.SetTypeAt(0, Object::dynamic_type());
1350 CanonicalizeTAV(&tav_dynamic);
1351 auto& tav_object = TypeArguments::Handle(TypeArguments::New(1));
1352 tav_object.SetTypeAt(0, type_object);
1353 CanonicalizeTAV(&tav_object);
1354 auto& tav_nullable_object = TypeArguments::Handle(TypeArguments::New(1));
1355 tav_nullable_object.SetTypeAt(0, type_nullable_object);
1356 CanonicalizeTAV(&tav_nullable_object);
1357 auto& tav_int = TypeArguments::Handle(TypeArguments::New(1));
1358 tav_int.SetTypeAt(0, type_int);
1359 CanonicalizeTAV(&tav_int);
1360 auto& tav_num = TypeArguments::Handle(TypeArguments::New(1));
1361 tav_num.SetTypeAt(0, type_num);
1362 CanonicalizeTAV(&tav_num);
1363 auto& tav_string = TypeArguments::Handle(TypeArguments::New(1));
1364 tav_string.SetTypeAt(0, type_string);
1365 CanonicalizeTAV(&tav_string);
1366
1367 auto& type_future = Type::Handle(
1368 Type::New(class_future, tav_null, Nullability::kNonNullable));
1369 FinalizeAndCanonicalize(&type_future);
1370 auto& type_future_dynamic = Type::Handle(
1371 Type::New(class_future, tav_dynamic, Nullability::kNonNullable));
1372 FinalizeAndCanonicalize(&type_future_dynamic);
1373 auto& type_future_object = Type::Handle(
1374 Type::New(class_future, tav_object, Nullability::kNonNullable));
1375 FinalizeAndCanonicalize(&type_future_object);
1376 auto& type_future_nullable_object = Type::Handle(
1377 Type::New(class_future, tav_nullable_object, Nullability::kNonNullable));
1378 FinalizeAndCanonicalize(&type_future_nullable_object);
1379 auto& type_future_int =
1380 Type::Handle(Type::New(class_future, tav_int, Nullability::kNonNullable));
1381 FinalizeAndCanonicalize(&type_future_int);
1382 auto& type_future_string = Type::Handle(
1383 Type::New(class_future, tav_string, Nullability::kNonNullable));
1384 FinalizeAndCanonicalize(&type_future_string);
1385 auto& type_future_num =
1386 Type::Handle(Type::New(class_future, tav_num, Nullability::kNonNullable));
1387 FinalizeAndCanonicalize(&type_future_num);
1388 const auto& type_future_t = Type::Handle(class_future.DeclarationType());
1389
1390 THR_Print("********************************************************\n");
1391 THR_Print(" Testing Future<int>\n");
1392 THR_Print("********************************************************\n\n");
1393
1394 // Some more tests of generic implemented classes, using Future. Here,
1395 // obj is an object of type Future<int>.
1396 //
1397 // True positives from TTS:
1398 // obj as Future : Null type args
1399 // obj as Future<dynamic> : Canonicalized to same as previous case.
1400 // obj as Future<Object?> : Type arg is top type
1401 // obj as Future<Object*> : Type arg is top type
1402 // obj as Future<Object> : Type arg is certain supertype
1403 // obj as Future<int> : Type arg is the same type
1404 // obj as Future<num> : Type arg is a supertype that can be matched
1405 // with cid range
1406 // obj as Future<X>, : Type arg is a type parameter instantiated with
1407 // X = int : ... the same type
1408 //
1409 RunTTSTest(type_future, {obj_futureint, tav_null, tav_null});
1410 RunTTSTest(type_future_dynamic, {obj_futureint, tav_null, tav_null});
1411 RunTTSTest(type_future_object, {obj_futureint, tav_null, tav_null});
1412 RunTTSTest(type_future_nullable_object, {obj_futureint, tav_null, tav_null});
1413 RunTTSTest(type_future_int, {obj_futureint, tav_null, tav_null});
1414 RunTTSTest(type_future_num, {obj_futureint, tav_null, tav_null});
1415 RunTTSTest(type_future_t, {obj_futureint, tav_int, tav_null});
1416
1417 // False negatives from TTS (caught by STC/runtime):
1418 // obj as Future<X>, : Type arg is a type parameter instantiated with
1419 // X = num : ... a supertype
1420 RunTTSTest(type_future_t, FalseNegative({obj_futureint, tav_num, tav_null}));
1421
1422 // Errors:
1423 // obj as Future<String> : Type arg is not a supertype
1424 // obj as Future<X>, : Type arg is a type parameter instantiated with
1425 // X = String : ... an unrelated type
1426 //
1427 RunTTSTest(type_future_string, Failure({obj_futureint, tav_null, tav_null}));
1428 RunTTSTest(type_future_t, Failure({obj_futureint, tav_string, tav_null}));
1429
1430 auto& type_function = Type::Handle(Type::DartFunctionType());
1431 type_function =
1432 type_function.ToNullability(Nullability::kNonNullable, Heap::kNew);
1433 FinalizeAndCanonicalize(&type_function);
1434 auto& type_nullable_function = Type::Handle(
1435 type_function.ToNullability(Nullability::kNullable, Heap::kOld));
1436 FinalizeAndCanonicalize(&type_nullable_function);
1437 auto& type_closure = Type::Handle(
1438 Type::New(class_closure, tav_null, Nullability::kNonNullable));
1439 FinalizeAndCanonicalize(&type_closure);
1440 auto& type_nullable_closure = Type::Handle(
1441 type_closure.ToNullability(Nullability::kNullable, Heap::kOld));
1442 FinalizeAndCanonicalize(&type_nullable_closure);
1443 auto& type_function_int_nullary =
1445 // Testing with a closure, so it has an implicit parameter, and we want a
1446 // type that is canonically equal to the type of the closure.
1447 type_function_int_nullary.set_num_implicit_parameters(1);
1448 type_function_int_nullary.set_num_fixed_parameters(1);
1449 type_function_int_nullary.set_parameter_types(Array::Handle(Array::New(1)));
1450 type_function_int_nullary.SetParameterTypeAt(0, Type::dynamic_type());
1451 type_function_int_nullary.set_result_type(type_int);
1452 FinalizeAndCanonicalize(&type_function_int_nullary);
1453 auto& type_nullable_function_int_nullary =
1454 FunctionType::Handle(type_function_int_nullary.ToNullability(
1456 FinalizeAndCanonicalize(&type_nullable_function_int_nullary);
1457
1458 auto& tav_function = TypeArguments::Handle(TypeArguments::New(1));
1459 tav_function.SetTypeAt(0, type_function);
1460 CanonicalizeTAV(&tav_function);
1461 auto& tav_nullable_function = TypeArguments::Handle(TypeArguments::New(1));
1462 tav_nullable_function.SetTypeAt(0, type_nullable_function);
1463 CanonicalizeTAV(&tav_nullable_function);
1464 auto& tav_closure = TypeArguments::Handle(TypeArguments::New(1));
1465 tav_closure.SetTypeAt(0, type_closure);
1466 CanonicalizeTAV(&tav_closure);
1467 auto& tav_nullable_closure = TypeArguments::Handle(TypeArguments::New(1));
1468 tav_nullable_closure.SetTypeAt(0, type_nullable_closure);
1469 CanonicalizeTAV(&tav_nullable_closure);
1470 auto& tav_function_int_nullary = TypeArguments::Handle(TypeArguments::New(1));
1471 tav_function_int_nullary.SetTypeAt(0, type_function_int_nullary);
1472 CanonicalizeTAV(&tav_function_int_nullary);
1473 auto& tav_nullable_function_int_nullary =
1475 tav_nullable_function_int_nullary.SetTypeAt(
1476 0, type_nullable_function_int_nullary);
1477 CanonicalizeTAV(&tav_nullable_function_int_nullary);
1478
1479 auto& type_future_function = Type::Handle(
1480 Type::New(class_future, tav_function, Nullability::kNonNullable));
1481 FinalizeAndCanonicalize(&type_future_function);
1482 auto& type_future_nullable_function = Type::Handle(Type::New(
1483 class_future, tav_nullable_function, Nullability::kNonNullable));
1484 FinalizeAndCanonicalize(&type_future_nullable_function);
1485 auto& type_future_closure = Type::Handle(
1486 Type::New(class_future, tav_closure, Nullability::kNonNullable));
1487 FinalizeAndCanonicalize(&type_future_closure);
1488 auto& type_future_nullable_closure = Type::Handle(
1489 Type::New(class_future, tav_nullable_closure, Nullability::kNonNullable));
1490 FinalizeAndCanonicalize(&type_future_nullable_closure);
1491 auto& type_future_function_int_nullary =
1492 Type::Handle(Type::New(class_future, tav_function_int_nullary));
1493 FinalizeAndCanonicalize(&type_future_function_int_nullary);
1494 auto& type_future_nullable_function_int_nullary =
1495 Type::Handle(Type::New(class_future, tav_nullable_function_int_nullary));
1496 FinalizeAndCanonicalize(&type_future_nullable_function_int_nullary);
1497
1498 THR_Print("\n********************************************************\n");
1499 THR_Print(" Testing Future<int Function()>\n");
1500 THR_Print("********************************************************\n\n");
1501
1502 // And here, obj is an object of type Future<int Function()>. Note that
1503 // int Function() <: Function, but int Function() </: _Closure. That is,
1504 // _Closure is a separate subtype of Function from FunctionTypes.
1505 //
1506 // True positive from TTS:
1507 // obj as Future : Null type args
1508 // obj as Future<dynamic> : Canonicalized to same as previous case.
1509 // obj as Future<Object?> : Type arg is top type
1510 // obj as Future<Object*> : Type arg is top typ
1511 // obj as Future<Object> : Type arg is certain supertype
1512 // obj as Future<Function?> : Type arg is certain supertype
1513 // obj as Future<Function*> : Type arg is certain supertype
1514 // obj as Future<Function> : Type arg is certain supertype
1515 // obj as Future<X>, : Type arg is a type parameter instantiated with
1516 // X = dynamic : ... a top type
1517 // X = Object? : ... a top type
1518 // X = Object* : ... a top type
1519 // X = Object : ... a certain supertype
1520 // X = int Function() : ... the same type.
1521 //
1522 RunTTSTest(type_future, {obj_futurefunction, tav_null, tav_null});
1523 RunTTSTest(type_future_dynamic, {obj_futurefunction, tav_null, tav_null});
1524 RunTTSTest(type_future_nullable_object,
1525 {obj_futurefunction, tav_null, tav_null});
1526 RunTTSTest(type_future_object, {obj_futurefunction, tav_null, tav_null});
1527 RunTTSTest(type_future_nullable_object,
1528 {obj_futurefunction, tav_null, tav_null});
1529 RunTTSTest(type_future_object, {obj_futurefunction, tav_null, tav_null});
1530 RunTTSTest(type_future_nullable_function,
1531 {obj_futurefunction, tav_null, tav_null});
1532 RunTTSTest(type_future_function, {obj_futurefunction, tav_null, tav_null});
1533 RunTTSTest(type_future_t, {obj_futurefunction, tav_null, tav_null});
1534 RunTTSTest(type_future_t,
1535 {obj_futurefunction, tav_nullable_object, tav_null});
1536 RunTTSTest(type_future_t, {obj_futurefunction, tav_object, tav_null});
1537 RunTTSTest(type_future_t,
1538 {obj_futurefunction, tav_function_int_nullary, tav_null});
1539
1540 // False negative from TTS (caught by runtime or STC):
1541 // obj as Future<int Function()?> : No specialization.
1542 // obj as Future<int Function()*> : No specialization.
1543 // obj as Future<int Function()> : No specialization.
1544 // obj as Future<X>, : Type arg is a type parameter instantiated with
1545 // X = Function? : ... a certain supertype (but not checked)
1546 // X = Function* : ... a certain supertype (but not checked)
1547 // X = Function : ... a certain supertype (but not checked)
1548 // X = int Function()? : ... a canonically different type.
1549 // X = int Function()* : ... a canonically different type.
1550 //
1551 RunTTSTest(type_future_nullable_function_int_nullary,
1552 FalseNegative({obj_futurefunction, tav_null, tav_null}));
1553 RunTTSTest(type_future_function_int_nullary,
1554 FalseNegative({obj_futurefunction, tav_null, tav_null}));
1555 RunTTSTest(type_future_t, FalseNegative({obj_futurefunction,
1556 tav_nullable_function, tav_null}));
1557 RunTTSTest(type_future_t,
1558 FalseNegative({obj_futurefunction, tav_function, tav_null}));
1559 RunTTSTest(type_future_t,
1560 FalseNegative({obj_futurefunction,
1561 tav_nullable_function_int_nullary, tav_null}));
1562
1563 // Errors:
1564 // obj as Future<_Closure?> : Type arg is not a supertype
1565 // obj as Future<_Closure*> : Type arg is not a supertype
1566 // obj as Future<_Closure> : Type arg is not a supertype
1567 // obj as Future<X>, : Type arg is a type parameter instantiated with
1568 // X = _Closure? : ... an unrelated type.
1569 // X = _Closure* : ... an unrelated type.
1570 // X = _Closure : ... an unrelated type.
1571 //
1572 RunTTSTest(type_future_nullable_closure,
1573 Failure({obj_futurefunction, tav_null, tav_null}));
1574 RunTTSTest(type_future_closure,
1575 Failure({obj_futurefunction, tav_null, tav_null}));
1576 RunTTSTest(type_future_t,
1577 Failure({obj_futurefunction, tav_nullable_closure, tav_null}));
1578 RunTTSTest(type_future_t,
1579 Failure({obj_futurefunction, tav_closure, tav_null}));
1580
1581 THR_Print("\n********************************************************\n");
1582 THR_Print(" Testing Future<int Function()?>\n");
1583 THR_Print("********************************************************\n\n");
1584
1585 // And here, obj is an object of type Future<int Function()?>.
1586 //
1587 // True positive from TTS:
1588 // obj as Future : Null type args
1589 // obj as Future<dynamic> : Canonicalized to same as previous case.
1590 // obj as Future<Object?> : Type arg is top type
1591 // obj as Future<Object*> : Type arg is top typ
1592 // obj as Future<Function?> : Type arg is certain supertype
1593 // obj as Future<Function*> : Type arg is certain supertype
1594 // obj as Future<X>, : Type arg is a type parameter instantiated with
1595 // X = dynamic : ... a top type
1596 // X = Object? : ... a top type
1597 // X = Object* : ... a top type
1598 // X = int Function()? : ... the same type.
1599 //
1600 // If not null safe:
1601 // obj as Future<Object> : Type arg is certain supertype
1602 // obj as Future<Function> : Type arg is certain supertype
1603 // obj as Future<X>, : Type arg is a type parameter instantiated with
1604 // X = Object : ... a certain supertype
1605 RunTTSTest(type_future, {obj_futurenullablefunction, tav_null, tav_null});
1606 RunTTSTest(type_future_dynamic,
1607 {obj_futurenullablefunction, tav_null, tav_null});
1608 RunTTSTest(type_future_nullable_object,
1609 {obj_futurenullablefunction, tav_null, tav_null});
1610 RunTTSTest(type_future_nullable_object,
1611 {obj_futurefunction, tav_null, tav_null});
1612 RunTTSTest(type_future_nullable_function,
1613 {obj_futurenullablefunction, tav_null, tav_null});
1614 RunTTSTest(type_future_t, {obj_futurenullablefunction, tav_null, tav_null});
1615 RunTTSTest(type_future_t,
1616 {obj_futurenullablefunction, tav_nullable_object, tav_null});
1617 RunTTSTest(type_future_t, {obj_futurenullablefunction,
1618 tav_nullable_function_int_nullary, tav_null});
1619
1620 // False negative from TTS (caught by runtime or STC):
1621 // obj as Future<int Function()?> : No specialization.
1622 // obj as Future<int Function()*> : No specialization.
1623 // obj as Future<X>, : Type arg is a type parameter instantiated with
1624 // X = Function? : ... a certain supertype (but not checked)
1625 // X = Function* : ... a certain supertype (but not checked)
1626 // X = int Function()* : ... a canonically different type.
1627 //
1628 // If not null safe:
1629 // obj as Future<int Function()> : No specialization.
1630 // obj as Future<X>, : Type arg is a type parameter instantiated with
1631 // X = Function : ... a certain supertype (but not checked)
1632 // X = int Function() : ... a canonically different type.
1633
1634 RunTTSTest(type_future_nullable_function_int_nullary,
1635 FalseNegative({obj_futurenullablefunction, tav_null, tav_null}));
1636 RunTTSTest(type_future_t, FalseNegative({obj_futurenullablefunction,
1637 tav_nullable_function, tav_null}));
1638
1639 // Errors:
1640 // obj as Future<_Closure?> : Type arg is not a supertype
1641 // obj as Future<_Closure*> : Type arg is not a supertype
1642 // obj as Future<_Closure> : Type arg is not a supertype
1643 // obj as Future<X>, : Type arg is a type parameter instantiated with
1644 // X = _Closure? : ... an unrelated type.
1645 // X = _Closure* : ... an unrelated type.
1646 // X = _Closure : ... an unrelated type.
1647 //
1648 // If null safe:
1649 // obj as Future<int Function()> : Nullable type cannot be subtype of a
1650 // non-nullable type.
1651 // obj as Future<Object> : Nullable type cannot be subtype of a
1652 // non-nullable type.
1653 // obj as Future<Function> : Nullable type cannot be subtype of a
1654 // non-nullable type.
1655 // obj as Future<X>, : Type arg is a type parameter instantiated with
1656 // X = Object : ... a non-nullable type.
1657 // X = Function : ... a non-nullable type.
1658 // X = int Function() : ... a non-nullable type.
1659
1660 RunTTSTest(type_future_nullable_closure,
1661 Failure({obj_futurenullablefunction, tav_null, tav_null}));
1662 RunTTSTest(type_future_closure,
1663 Failure({obj_futurenullablefunction, tav_null, tav_null}));
1664 RunTTSTest(type_future_t, Failure({obj_futurenullablefunction,
1665 tav_nullable_closure, tav_null}));
1666 RunTTSTest(type_future_t,
1667 Failure({obj_futurenullablefunction, tav_closure, tav_null}));
1668
1669 RunTTSTest(type_future_function_int_nullary,
1670 Failure({obj_futurenullablefunction, tav_null, tav_null}));
1671 RunTTSTest(type_future_object,
1672 Failure({obj_futurenullablefunction, tav_null, tav_null}));
1673 RunTTSTest(type_future_function,
1674 Failure({obj_futurenullablefunction, tav_null, tav_null}));
1675 RunTTSTest(type_future_t,
1676 Failure({obj_futurenullablefunction, tav_object, tav_null}));
1677 RunTTSTest(type_future_t,
1678 Failure({obj_futurenullablefunction, tav_function, tav_null}));
1679 RunTTSTest(type_future_t, Failure({obj_futurenullablefunction,
1680 tav_function_int_nullary, tav_null}));
1681}
1682
1683ISOLATE_UNIT_TEST_CASE(TTS_Regress40964) {
1684 const char* kScript =
1685 R"(
1686 class A<T> {
1687 test(x) => x as B<T>;
1688 }
1689 class B<T> {}
1690 class C<T> {}
1691
1692 createACint() => A<C<int>>();
1693 createBCint() => B<C<int>>();
1694 createBCnum() => B<C<num>>();
1695 )";
1696
1697 const auto& root_library = Library::Handle(LoadTestScript(kScript));
1698 const auto& class_b = Class::Handle(GetClass(root_library, "B"));
1700 const auto& acint = Object::Handle(Invoke(root_library, "createACint"));
1701 const auto& bcint = Object::Handle(Invoke(root_library, "createBCint"));
1702 const auto& bcnum = Object::Handle(Invoke(root_library, "createBCnum"));
1703
1704 // dst_type = B<T>
1705 const auto& dst_tav = TypeArguments::Handle(TypeArguments::New(1));
1706 dst_tav.SetTypeAt(0,
1708 auto& dst_type = Type::Handle(Type::New(class_b, dst_tav));
1709 FinalizeAndCanonicalize(&dst_type);
1710 const auto& cint_tav =
1711 TypeArguments::Handle(Instance::Cast(acint).GetTypeArguments());
1712 const auto& function_tav = TypeArguments::Handle();
1713
1714 // a as B<T> -- a==B<C<int>, T==<C<int>>
1715 RunTTSTest(dst_type, {bcint, cint_tav, function_tav});
1716
1717 // a as B<T> -- a==B<C<num>, T==<C<int>>
1718 RunTTSTest(dst_type, Failure({bcnum, cint_tav, function_tav}));
1719}
1720
1721ISOLATE_UNIT_TEST_CASE(TTS_TypeParameter) {
1722 const char* kScript =
1723 R"(
1724 class A<T> {
1725 T test(dynamic x) => x as T;
1726 }
1727 H genericFun<H>(dynamic x) => x as H;
1728
1729 createAInt() => A<int>();
1730 createAString() => A<String>();
1731 )";
1732
1733 const auto& root_library = Library::Handle(LoadTestScript(kScript));
1734 const auto& class_a = Class::Handle(GetClass(root_library, "A"));
1736
1737 const auto& fun_generic =
1738 Function::Handle(GetFunction(root_library, "genericFun"));
1740 const auto& dst_type_t =
1742
1743 const auto& dst_type_h =
1745
1746 const auto& aint = Object::Handle(Invoke(root_library, "createAInt"));
1747 const auto& astring = Object::Handle(Invoke(root_library, "createAString"));
1748
1749 const auto& int_tav =
1750 TypeArguments::Handle(Instance::Cast(aint).GetTypeArguments());
1751 const auto& string_tav =
1752 TypeArguments::Handle(Instance::Cast(astring).GetTypeArguments());
1753
1754 const auto& int_instance = Integer::Handle(Integer::New(1));
1755 const auto& string_instance = String::Handle(String::New("foo"));
1756
1757 THR_Print("Testing int instance, class parameter instantiated to int\n");
1758 RunTTSTest(dst_type_t, {int_instance, int_tav, string_tav});
1759 THR_Print("\nTesting string instance, class parameter instantiated to int\n");
1760 RunTTSTest(dst_type_t, Failure({string_instance, int_tav, string_tav}));
1761
1762 THR_Print(
1763 "\nTesting string instance, function parameter instantiated to string\n");
1764 RunTTSTest(dst_type_h, {string_instance, int_tav, string_tav});
1765 RunTTSTest(dst_type_h, Failure({int_instance, int_tav, string_tav}));
1766}
1767
1768// Check that we generate correct TTS for _Smi type.
1769ISOLATE_UNIT_TEST_CASE(TTS_Smi) {
1770 const auto& type_smi = Type::Handle(Type::SmiType());
1771 const auto& tav_null = Object::null_type_arguments();
1772
1773 // Test on some easy-to-make instances.
1774 RunTTSTest(type_smi, {Smi::Handle(Smi::New(0)), tav_null, tav_null});
1776 tav_null, tav_null}));
1777 RunTTSTest(type_smi,
1778 Failure({Double::Handle(Double::New(1.0)), tav_null, tav_null}));
1779 RunTTSTest(type_smi, Failure({Symbols::Empty(), tav_null, tav_null}));
1780 RunTTSTest(type_smi,
1781 Failure({Array::Handle(Array::New(1)), tav_null, tav_null}));
1782}
1783
1784// Check that we generate correct TTS for int type.
1785ISOLATE_UNIT_TEST_CASE(TTS_Int) {
1786 const auto& type_int = Type::Handle(Type::IntType());
1787 const auto& tav_null = Object::null_type_arguments();
1788
1789 // Test on some easy-to-make instances.
1790 RunTTSTest(type_int, {Smi::Handle(Smi::New(0)), tav_null, tav_null});
1791 RunTTSTest(type_int,
1792 {Integer::Handle(Integer::New(kMaxInt64)), tav_null, tav_null});
1793 RunTTSTest(type_int,
1794 Failure({Double::Handle(Double::New(1.0)), tav_null, tav_null}));
1795 RunTTSTest(type_int, Failure({Symbols::Empty(), tav_null, tav_null}));
1796 RunTTSTest(type_int,
1797 Failure({Array::Handle(Array::New(1)), tav_null, tav_null}));
1798}
1799
1800// Check that we generate correct TTS for num type.
1802 const auto& type_num = Type::Handle(Type::Number());
1803 const auto& tav_null = Object::null_type_arguments();
1804
1805 // Test on some easy-to-make instances.
1806 RunTTSTest(type_num, {Smi::Handle(Smi::New(0)), tav_null, tav_null});
1807 RunTTSTest(type_num,
1808 {Integer::Handle(Integer::New(kMaxInt64)), tav_null, tav_null});
1809 RunTTSTest(type_num, {Double::Handle(Double::New(1.0)), tav_null, tav_null});
1810 RunTTSTest(type_num, Failure({Symbols::Empty(), tav_null, tav_null}));
1811 RunTTSTest(type_num,
1812 Failure({Array::Handle(Array::New(1)), tav_null, tav_null}));
1813}
1814
1815// Check that we generate correct TTS for Double type.
1816ISOLATE_UNIT_TEST_CASE(TTS_Double) {
1817 const auto& type_num = Type::Handle(Type::Double());
1818 const auto& tav_null = Object::null_type_arguments();
1819
1820 // Test on some easy-to-make instances.
1821 RunTTSTest(type_num, Failure({Smi::Handle(Smi::New(0)), tav_null, tav_null}));
1823 tav_null, tav_null}));
1824 RunTTSTest(type_num, {Double::Handle(Double::New(1.0)), tav_null, tav_null});
1825 RunTTSTest(type_num, Failure({Symbols::Empty(), tav_null, tav_null}));
1826 RunTTSTest(type_num,
1827 Failure({Array::Handle(Array::New(1)), tav_null, tav_null}));
1828}
1829
1830// Check that we generate correct TTS for Object type.
1831ISOLATE_UNIT_TEST_CASE(TTS_Object) {
1832 const auto& type_obj =
1833 Type::Handle(IsolateGroup::Current()->object_store()->object_type());
1834 const auto& tav_null = Object::null_type_arguments();
1835
1836 // Non-nullable Object is not a top type,
1837 // so its TTS specializes the first time it is invoked.
1838 const bool should_specialize = true;
1839 auto make_test_case = [&](const Instance& instance) -> TTSTestCase {
1840 return {instance, tav_null, tav_null};
1841 };
1842
1843 // Test on some easy-to-make instances.
1844 RunTTSTest(type_obj, make_test_case(Smi::Handle(Smi::New(0))),
1845 should_specialize);
1846 RunTTSTest(type_obj, make_test_case(Integer::Handle(Integer::New(kMaxInt64))),
1847 should_specialize);
1848 RunTTSTest(type_obj, make_test_case(Double::Handle(Double::New(1.0))),
1849 should_specialize);
1850 RunTTSTest(type_obj, make_test_case(Symbols::Empty()), should_specialize);
1851 RunTTSTest(type_obj, make_test_case(Array::Handle(Array::New(1))),
1852 should_specialize);
1853}
1854
1855// Check that we generate correct TTS for type Function (the non-FunctionType
1856// version).
1857ISOLATE_UNIT_TEST_CASE(TTS_Function) {
1858 const char* kScript =
1859 R"(
1860 class A<T> {}
1861
1862 createF() => (){};
1863 createG() => () => 3;
1864 createH() => (int x, String y, {int z = 0}) => x + z;
1865
1866 createAInt() => A<int>();
1867 createAFunction() => A<Function>();
1868 )";
1869
1870 const auto& root_library = Library::Handle(LoadTestScript(kScript));
1871 const auto& obj_f = Object::Handle(Invoke(root_library, "createF"));
1872 const auto& obj_g = Object::Handle(Invoke(root_library, "createG"));
1873 const auto& obj_h = Object::Handle(Invoke(root_library, "createH"));
1874
1875 const auto& tav_null = TypeArguments::Handle(TypeArguments::null());
1876 const auto& type_function = Type::Handle(Type::DartFunctionType());
1877
1878 RunTTSTest(type_function, {obj_f, tav_null, tav_null});
1879 RunTTSTest(type_function, {obj_g, tav_null, tav_null});
1880 RunTTSTest(type_function, {obj_h, tav_null, tav_null});
1881
1882 const auto& class_a = Class::Handle(GetClass(root_library, "A"));
1883 const auto& obj_a_int = Object::Handle(Invoke(root_library, "createAInt"));
1884 const auto& obj_a_function =
1885 Object::Handle(Invoke(root_library, "createAFunction"));
1886
1887 auto& tav_function = TypeArguments::Handle(TypeArguments::New(1));
1888 tav_function.SetTypeAt(0, type_function);
1889 CanonicalizeTAV(&tav_function);
1890 auto& type_a_function = Type::Handle(Type::New(class_a, tav_function));
1891 FinalizeAndCanonicalize(&type_a_function);
1892
1893 RunTTSTest(type_a_function, {obj_a_function, tav_null, tav_null});
1894 RunTTSTest(type_a_function, Failure({obj_a_int, tav_null, tav_null}));
1895}
1896
1897ISOLATE_UNIT_TEST_CASE(TTS_Partial) {
1898 const char* kScript =
1899 R"(
1900 class B<T> {}
1901
1902 class C {}
1903 class D extends C {}
1904 class E extends D {}
1905
1906 F<A>() {}
1907 createBE() => B<E>();
1908 createBENullable() => B<E?>();
1909 createBNull() => B<Null>();
1910 createBNever() => B<Never>();
1911)";
1912
1913 const auto& root_library = Library::Handle(LoadTestScript(kScript));
1914 const auto& class_b = Class::Handle(GetClass(root_library, "B"));
1915 const auto& class_c = Class::Handle(GetClass(root_library, "C"));
1916 const auto& class_d = Class::Handle(GetClass(root_library, "D"));
1917 const auto& class_e = Class::Handle(GetClass(root_library, "E"));
1918 const auto& fun_f = Function::Handle(GetFunction(root_library, "F"));
1919 const auto& obj_b_e = Object::Handle(Invoke(root_library, "createBE"));
1920 const auto& obj_b_e_nullable =
1921 Object::Handle(Invoke(root_library, "createBENullable"));
1922 const auto& obj_b_null = Object::Handle(Invoke(root_library, "createBNull"));
1923 const auto& obj_b_never =
1924 Object::Handle(Invoke(root_library, "createBNever"));
1925
1926 const auto& tav_null = Object::null_type_arguments();
1927 auto& tav_nullable_object = TypeArguments::Handle(TypeArguments::New(1));
1928 tav_nullable_object.SetTypeAt(
1929 0, Type::Handle(
1930 IsolateGroup::Current()->object_store()->nullable_object_type()));
1931 CanonicalizeTAV(&tav_nullable_object);
1932 auto& tav_object = TypeArguments::Handle(TypeArguments::New(1));
1933 tav_object.SetTypeAt(
1934 0, Type::Handle(IsolateGroup::Current()->object_store()->object_type()));
1935 CanonicalizeTAV(&tav_object);
1936
1937 auto& type_e =
1939 FinalizeAndCanonicalize(&type_e);
1940 auto& type_d =
1942 FinalizeAndCanonicalize(&type_d);
1943 auto& type_c =
1945 FinalizeAndCanonicalize(&type_c);
1946 auto& type_c_nullable =
1947 Type::Handle(Type::New(class_c, tav_null, Nullability::kNullable));
1948 FinalizeAndCanonicalize(&type_c_nullable);
1949
1950 auto& tav_e = TypeArguments::Handle(TypeArguments::New(1));
1951 tav_e.SetTypeAt(0, type_e);
1952 CanonicalizeTAV(&tav_e);
1953 auto& tav_d = TypeArguments::Handle(TypeArguments::New(1));
1954 tav_d.SetTypeAt(0, type_d);
1955 CanonicalizeTAV(&tav_d);
1956 auto& tav_c = TypeArguments::Handle(TypeArguments::New(1));
1957 tav_c.SetTypeAt(0, type_c);
1958 CanonicalizeTAV(&tav_c);
1959 auto& tav_nullable_c = TypeArguments::Handle(TypeArguments::New(1));
1960 tav_nullable_c.SetTypeAt(0, type_c_nullable);
1961 CanonicalizeTAV(&tav_nullable_c);
1962
1963 // One case where optimized TTSes can be partial is if the type is
1964 // uninstantiated with a type parameter at the same position as one of the
1965 // class's type parameters. The type parameter in the type is instantiated at
1966 // runtime and compared with the corresponding instance type argument using
1967 // pointer equality, which misses the case where the instantiated type
1968 // parameter in the type is a supertype of the instance type argument.
1969 const auto& type_a =
1971 auto& tav_a = TypeArguments::Handle(TypeArguments::New(1));
1972 tav_a.SetTypeAt(0, type_a);
1973 CanonicalizeTAV(&tav_a);
1974 auto& type_b_a = AbstractType::Handle(
1975 Type::New(class_b, tav_a, Nullability::kNonNullable));
1976 FinalizeAndCanonicalize(&type_b_a);
1977 TTSTestState state(thread, type_b_a);
1978
1979 // Should be checked by the TTS.
1980 TTSTestCase b_e_testcase{obj_b_e, tav_null, tav_e};
1981 // Needs to be cached in the STC and checked there.
1982 TTSTestCase b_d_testcase{obj_b_e, tav_null, tav_d};
1983 TTSTestCase b_c_testcase{obj_b_e, tav_null, tav_c};
1984
1985 // First, test that the positive test case is handled by the TTS.
1986 state.InvokeLazilySpecializedStub(b_e_testcase);
1987 state.InvokeExistingStub(b_e_testcase);
1988
1989 // Now restart, using the false negative test cases.
1990 state.ClearCache();
1991
1992 // This specializes the stub, so no STC update.
1993 state.InvokeLazilySpecializedStub(b_d_testcase);
1994 state.InvokeExistingStub(FalseNegative(b_d_testcase));
1995 // Replacing the stub with an eager version without clearing the STC means
1996 // it's still in the STC to be checked.
1997 state.InvokeEagerlySpecializedStub(STCCheck(b_d_testcase));
1998
1999 state.InvokeExistingStub(b_e_testcase);
2000 state.InvokeExistingStub(FalseNegative(b_c_testcase));
2001 state.InvokeExistingStub(STCCheck(b_d_testcase));
2002 state.InvokeExistingStub(b_e_testcase);
2003
2004 state.InvokeExistingStub({obj_b_never, tav_null, tav_d});
2005 state.InvokeExistingStub({obj_b_null, tav_null, tav_nullable_c});
2006 state.InvokeExistingStub(Failure({obj_b_null, tav_null, tav_c}));
2007
2008 state.InvokeExistingStub({obj_b_e, tav_null, tav_nullable_object});
2009 state.InvokeExistingStub({obj_b_e_nullable, tav_null, tav_nullable_object});
2010 state.InvokeExistingStub({obj_b_e, tav_null, tav_object});
2011 state.InvokeExistingStub(Failure({obj_b_e_nullable, tav_null, tav_object}));
2012}
2013
2014ISOLATE_UNIT_TEST_CASE(TTS_Partial_Incremental) {
2015#define FILE_RESOLVE_URI(Uri) "file:///" Uri
2016#define FIRST_PARTIAL_LIBRARY_NAME "test-lib"
2017#define SECOND_PARTIAL_LIBRARY_NAME "test-lib-2"
2018#define THIRD_PARTIAL_LIBRARY_NAME "test-lib-3"
2019
2020 // Same test script as TTS_Partial.
2021 const char* kFirstScript =
2022 R"(
2023 class B<T> {}
2024 createB() => B<int>();
2025)";
2026
2027 // A test script which imports the B class and extend it, to test
2028 // respecialization when the hierarchy changes without reloading.
2029 const char* kSecondScript =
2030 R"(
2031 import ")" FIRST_PARTIAL_LIBRARY_NAME R"(";
2032 class B2<T> extends B<T> {}
2033 createB2() => B2<int>();
2034)";
2035
2036 // Another one to test respecialization a second time.
2037 const char* kThirdScript =
2038 R"(
2039 import ")" FIRST_PARTIAL_LIBRARY_NAME R"(";
2040 class B3<T> extends B<T> {}
2041 createB3() => B3<int>();
2042)";
2043
2044 const char* kFirstUri = FILE_RESOLVE_URI(FIRST_PARTIAL_LIBRARY_NAME);
2045 const char* kSecondUri = FILE_RESOLVE_URI(SECOND_PARTIAL_LIBRARY_NAME);
2046 const char* kThirdUri = FILE_RESOLVE_URI(THIRD_PARTIAL_LIBRARY_NAME);
2047
2048#undef THIRD_PARTIAL_LIBRARY_URI
2049#undef SECOND_PARTIAL_LIBRARY_URI
2050#undef FIRST_PARTIAL_LIBRARY_URI
2051#undef FILE_RESOLVE_URI
2052
2053 THR_Print("------------------------------------------------------\n");
2054 THR_Print(" Loading %s\n", kFirstUri);
2055 THR_Print("------------------------------------------------------\n");
2056 const auto& first_library = Library::Handle(
2057 LoadTestScript(kFirstScript, /*resolver=*/nullptr, kFirstUri));
2058
2059 const auto& class_b = Class::Handle(GetClass(first_library, "B"));
2060 const auto& obj_b = Object::Handle(Invoke(first_library, "createB"));
2061
2062 const auto& tav_null = Object::null_type_arguments();
2063 auto& tav_int = TypeArguments::Handle(TypeArguments::New(1));
2064 tav_int.SetTypeAt(0, Type::Handle(Type::IntType()));
2065 CanonicalizeTAV(&tav_int);
2066 auto& tav_num = TypeArguments::Handle(TypeArguments::New(1));
2067 tav_num.SetTypeAt(0, Type::Handle(Type::Number()));
2068 CanonicalizeTAV(&tav_num);
2069
2070 auto& type_b2_t = AbstractType::Handle(class_b.DeclarationType());
2071 FinalizeAndCanonicalize(&type_b2_t);
2072 TTSTestState state(thread, type_b2_t);
2073
2074 TTSTestCase first_positive{obj_b, tav_int, tav_null};
2075 TTSTestCase first_false_negative = {obj_b, tav_num, tav_null};
2076 // No test case should possibly hit the same STC entry as another.
2077 ASSERT(!first_false_negative.HasSameSTCEntry(first_positive));
2078 // The type with the tested stub must be the same in all test cases.
2079 ASSERT(state.TypeToTest(first_positive) ==
2080 state.TypeToTest(first_false_negative));
2081
2082 state.InvokeLazilySpecializedStub(first_false_negative);
2083 state.InvokeExistingStub(FalseNegative(first_false_negative));
2084 state.InvokeEagerlySpecializedStub(STCCheck(first_false_negative));
2085
2086 state.InvokeExistingStub(first_positive);
2087 state.InvokeExistingStub(STCCheck(first_false_negative));
2088
2089 Array& stc_cache = Array::Handle(
2090 state.last_stc().IsNull() ? Array::null() : state.last_stc().cache());
2091 THR_Print("------------------------------------------------------\n");
2092 THR_Print(" Loading %s\n", kSecondUri);
2093 THR_Print("------------------------------------------------------\n");
2094 const auto& second_library = Library::Handle(
2095 LoadTestScript(kSecondScript, /*resolver=*/nullptr, kSecondUri));
2096 // Loading the new library shouldn't invalidate the old STC.
2097 EXPECT(state.last_stc().ptr() == state.current_stc());
2098 // Loading the new library should not reset the STCs, as no respecialization
2099 // should happen yet.
2100 EXPECT((state.last_stc().IsNull() && stc_cache.IsNull()) ||
2101 stc_cache.ptr() == state.last_stc().cache());
2102
2103 const auto& obj_b2 = Object::Handle(Invoke(second_library, "createB2"));
2104
2105 TTSTestCase second_positive{obj_b2, tav_int, tav_null};
2106 TTSTestCase second_false_negative = {obj_b2, tav_num, tav_null};
2107 // No test case should possibly hit the same STC entry as another.
2108 ASSERT(!second_positive.HasSameSTCEntry(second_false_negative));
2109 ASSERT(!second_positive.HasSameSTCEntry(first_positive));
2110 ASSERT(!second_positive.HasSameSTCEntry(first_false_negative));
2111 ASSERT(!second_false_negative.HasSameSTCEntry(first_positive));
2112 ASSERT(!second_false_negative.HasSameSTCEntry(first_false_negative));
2113 // The type with the tested stub must be the same in all test cases.
2114 ASSERT(state.TypeToTest(second_positive) ==
2115 state.TypeToTest(second_false_negative));
2116 ASSERT(state.TypeToTest(first_positive) == state.TypeToTest(second_positive));
2117
2118 // Old positive should still be caught by TTS.
2119 state.InvokeExistingStub(first_positive);
2120 // Same false negative should still be caught by STC and not cause
2121 // respecialization.
2122 state.InvokeExistingStub(STCCheck(first_false_negative));
2123
2124 // The new positive should be a false negative at the TTS level that causes
2125 // respecialization, as the class hierarchy has changed.
2126 state.InvokeExistingStub(Respecialization(second_positive));
2127
2128 // The first false positive is still in the cache.
2129 state.InvokeExistingStub(STCCheck(first_false_negative));
2130
2131 // This false negative is not yet in the cache.
2132 state.InvokeExistingStub(FalseNegative(second_false_negative));
2133
2134 state.InvokeExistingStub(first_positive);
2135 state.InvokeExistingStub(second_positive);
2136
2137 // Now the second false negative is in the cache.
2138 state.InvokeExistingStub(STCCheck(second_false_negative));
2139
2140 stc_cache =
2141 state.last_stc().IsNull() ? Array::null() : state.last_stc().cache();
2142 THR_Print("------------------------------------------------------\n");
2143 THR_Print(" Loading %s\n", kThirdUri);
2144 THR_Print("------------------------------------------------------\n");
2145 const auto& third_library = Library::Handle(
2146 LoadTestScript(kThirdScript, /*resolver=*/nullptr, kThirdUri));
2147 // Loading the new library shouldn't invalidate the old STC.
2148 EXPECT(state.last_stc().ptr() == state.current_stc());
2149 // Loading the new library should not reset the STCs, as no respecialization
2150 // should happen yet.
2151 EXPECT((state.last_stc().IsNull() && stc_cache.IsNull()) ||
2152 stc_cache.ptr() == state.last_stc().cache());
2153
2154 const auto& obj_b3 = Object::Handle(Invoke(third_library, "createB3"));
2155
2156 TTSTestCase third_positive{obj_b3, tav_int, tav_null};
2157 TTSTestCase third_false_negative = {obj_b3, tav_num, tav_null};
2158 // No test case should possibly hit the same STC entry as another.
2159 ASSERT(!third_positive.HasSameSTCEntry(third_false_negative));
2160 ASSERT(!third_positive.HasSameSTCEntry(first_positive));
2161 ASSERT(!third_positive.HasSameSTCEntry(first_false_negative));
2162 ASSERT(!third_positive.HasSameSTCEntry(second_positive));
2163 ASSERT(!third_positive.HasSameSTCEntry(second_false_negative));
2164 ASSERT(!third_false_negative.HasSameSTCEntry(first_positive));
2165 ASSERT(!third_false_negative.HasSameSTCEntry(first_false_negative));
2166 ASSERT(!third_false_negative.HasSameSTCEntry(second_positive));
2167 ASSERT(!third_false_negative.HasSameSTCEntry(second_false_negative));
2168 // The type with the tested stub must be the same in all test cases.
2169 ASSERT(state.TypeToTest(third_positive) ==
2170 state.TypeToTest(third_false_negative));
2171 ASSERT(state.TypeToTest(first_positive) == state.TypeToTest(third_positive));
2172
2173 // Again, cases that have run before should still pass as before without STC
2174 // changes/respecialization.
2175 state.InvokeExistingStub(first_positive);
2176 state.InvokeExistingStub(second_positive);
2177 state.InvokeExistingStub(STCCheck(first_false_negative));
2178 state.InvokeExistingStub(STCCheck(second_false_negative));
2179
2180 // Now we lead with the new false negative, to make sure it also triggers
2181 // respecialization but doesn't get immediately added to the STC.
2182 state.InvokeExistingStub(Respecialization(third_false_negative));
2183
2184 // True positives still work as before.
2185 state.InvokeExistingStub(third_positive);
2186 state.InvokeExistingStub(second_positive);
2187 state.InvokeExistingStub(first_positive);
2188
2189 // No additional checks added by rerunning the previous false negatives.
2190 state.InvokeExistingStub(STCCheck(first_false_negative));
2191 state.InvokeExistingStub(STCCheck(second_false_negative));
2192
2193 // Now a check is recorded when rerunning the third false negative.
2194 state.InvokeExistingStub(FalseNegative(third_false_negative));
2195 state.InvokeExistingStub(STCCheck(third_false_negative));
2196}
2197
2198// TTS deoptimization on reload only happens in non-product mode currently.
2199#if !defined(PRODUCT)
2200static const char* kLoadedScript =
2201 R"(
2202 class A<T> {}
2203
2204 createAInt() => A<int>();
2205 createAString() => A<String>();
2206
2207 (int, int) createRecordIntInt() => (1, 2);
2208 (String, int) createRecordStringInt() => ("foo", 2);
2209 (int, String) createRecordIntString() => (1, "bar");
2210 )";
2211
2212static const char* kReloadedScript =
2213 R"(
2214 class A<T> {}
2215 class A2<T> extends A<T> {}
2216
2217 createAInt() => A<int>();
2218 createAString() => A<String>();
2219 createA2Int() => A2<int>();
2220 createA2String() => A2<String>();
2221
2222 (int, int) createRecordIntInt() => (1, 2);
2223 (String, int) createRecordStringInt() => ("foo", 2);
2224 (int, String) createRecordIntString() => (1, "bar");
2225 )";
2226
2227ISOLATE_UNIT_TEST_CASE(TTS_Reload) {
2228 auto* const zone = thread->zone();
2229
2230 auto& root_library = Library::Handle(LoadTestScript(kLoadedScript));
2231 const auto& class_a = Class::Handle(GetClass(root_library, "A"));
2233
2234 const auto& aint = Object::Handle(Invoke(root_library, "createAInt"));
2235 const auto& astring = Object::Handle(Invoke(root_library, "createAString"));
2236
2237 const auto& record_int_int =
2238 Instance::CheckedHandle(zone, Invoke(root_library, "createRecordIntInt"));
2239 const auto& record_int_string = Instance::CheckedHandle(
2240 zone, Invoke(root_library, "createRecordIntString"));
2241 const auto& record_string_int = Instance::CheckedHandle(
2242 zone, Invoke(root_library, "createRecordStringInt"));
2243
2244 const auto& tav_null = Object::null_type_arguments();
2245 const auto& tav_int =
2246 TypeArguments::Handle(Instance::Cast(aint).GetTypeArguments());
2247 auto& tav_num = TypeArguments::Handle(TypeArguments::New(1));
2248 tav_num.SetTypeAt(0, Type::Handle(Type::Number()));
2249 CanonicalizeTAV(&tav_num);
2250
2251 auto& type_a_int = Type::Handle(Type::New(class_a, tav_int));
2252 FinalizeAndCanonicalize(&type_a_int);
2253
2254 auto& type_record_int_int =
2255 AbstractType::Handle(record_int_int.GetType(Heap::kNew));
2256 FinalizeAndCanonicalize(&type_record_int_int);
2257
2258 TTSTestState state(thread, type_a_int);
2259 state.InvokeLazilySpecializedStub({aint, tav_null, tav_null});
2260 state.InvokeExistingStub(Failure({astring, tav_null, tav_null}));
2261
2262 TTSTestState record_state(thread, type_record_int_int);
2263 record_state.InvokeLazilySpecializedStub(
2264 {record_int_int, tav_null, tav_null});
2265 record_state.InvokeExistingStub(
2266 Failure({record_string_int, tav_null, tav_null}));
2267 record_state.InvokeExistingStub(
2268 Failure({record_int_string, tav_null, tav_null}));
2269
2270 // Make sure the stubs are specialized prior to reload.
2271 EXPECT(type_a_int.type_test_stub() !=
2273 EXPECT(type_record_int_int.type_test_stub() !=
2275
2276 root_library = ReloadTestScript(kReloadedScript);
2277 const auto& a2int = Object::Handle(Invoke(root_library, "createA2Int"));
2278 const auto& a2string = Object::Handle(Invoke(root_library, "createA2String"));
2279
2280 // Reloading resets all type testing stubs to the (possibly lazy specializing)
2281 // default stub for that type.
2282 EXPECT(type_a_int.type_test_stub() ==
2284 EXPECT(type_record_int_int.type_test_stub() ==
2286 // Reloading either removes or resets the type testing cache.
2287 EXPECT(state.current_stc() == SubtypeTestCache::null() ||
2288 (state.current_stc() == state.last_stc().ptr() &&
2289 state.last_stc().NumberOfChecks() == 0));
2290 EXPECT(record_state.current_stc() == SubtypeTestCache::null() ||
2291 (record_state.current_stc() == record_state.last_stc().ptr() &&
2292 record_state.last_stc().NumberOfChecks() == 0));
2293
2294 state.InvokeExistingStub(Respecialization({aint, tav_null, tav_null}));
2295 state.InvokeExistingStub(Failure({astring, tav_null, tav_null}));
2296 state.InvokeExistingStub({a2int, tav_null, tav_null});
2297 state.InvokeExistingStub(Failure({a2string, tav_null, tav_null}));
2298
2299 record_state.InvokeExistingStub(
2300 Respecialization({record_int_int, tav_null, tav_null}));
2301 record_state.InvokeExistingStub(
2302 Failure({record_string_int, tav_null, tav_null}));
2303 record_state.InvokeExistingStub(
2304 Failure({record_int_string, tav_null, tav_null}));
2305}
2306
2307ISOLATE_UNIT_TEST_CASE(TTS_Partial_Reload) {
2308 auto& root_library = Library::Handle(LoadTestScript(kLoadedScript));
2309 const auto& class_a = Class::Handle(GetClass(root_library, "A"));
2311
2312 const auto& aint = Object::Handle(Invoke(root_library, "createAInt"));
2313 const auto& astring = Object::Handle(Invoke(root_library, "createAString"));
2314
2315 const auto& tav_null = Object::null_type_arguments();
2316 const auto& tav_int =
2317 TypeArguments::Handle(Instance::Cast(aint).GetTypeArguments());
2318 const auto& tav_string =
2319 TypeArguments::Handle(Instance::Cast(astring).GetTypeArguments());
2320 auto& tav_num = TypeArguments::Handle(TypeArguments::New(1));
2321 tav_num.SetTypeAt(0, Type::Handle(Type::Number()));
2322 CanonicalizeTAV(&tav_num);
2323
2324 // Create a partial TTS to test resets of STCs with false negatives.
2325 const auto& type_a_t = Type::Handle(class_a.DeclarationType());
2326 TTSTestCase positive1{aint, tav_int, tav_null};
2327 TTSTestCase positive2{astring, tav_string, tav_null};
2328 TTSTestCase negative1 = Failure({astring, tav_int, tav_null});
2329 TTSTestCase negative2 = Failure({aint, tav_string, tav_null});
2330 TTSTestCase false_negative = FalseNegative({aint, tav_num, tav_null});
2331 TTSTestState state(thread, type_a_t);
2332 state.InvokeLazilySpecializedStub(positive1);
2333 state.InvokeExistingStub(positive2);
2334 state.InvokeExistingStub(negative1);
2335 state.InvokeExistingStub(negative2);
2336 state.InvokeExistingStub(false_negative);
2337
2338 root_library = ReloadTestScript(kReloadedScript);
2339 const auto& a2int = Object::Handle(Invoke(root_library, "createA2Int"));
2340 const auto& a2string = Object::Handle(Invoke(root_library, "createA2String"));
2341
2342 // Reloading resets all type testing stubs to the (possibly lazy specializing)
2343 // default stub for that type.
2344 EXPECT(type_a_t.type_test_stub() ==
2346 // Reloading either removes or resets the type testing cache.
2347 EXPECT(state.current_stc() == SubtypeTestCache::null() ||
2348 (state.current_stc() == state.last_stc().ptr() &&
2349 state.last_stc().NumberOfChecks() == 0));
2350
2351 state.InvokeExistingStub(Respecialization(positive1));
2352 state.InvokeExistingStub(positive2);
2353 state.InvokeExistingStub(negative1);
2354 state.InvokeExistingStub(negative2);
2355 state.InvokeExistingStub(false_negative);
2356 state.InvokeExistingStub({a2int, tav_int, tav_null});
2357 state.InvokeExistingStub({a2string, tav_string, tav_null});
2358 state.InvokeExistingStub(Failure({a2string, tav_int, tav_null}));
2359 state.InvokeExistingStub(Failure({a2int, tav_string, tav_null}));
2360 state.InvokeExistingStub(FalseNegative({a2int, tav_num, tav_null}));
2361}
2362#endif // !defined(PRODUCT)
2363
2364// This test checks for a failure due to not reloading the class id between
2365// different uses of GenerateCidRangeChecks when loading the instance type
2366// arguments vector in a TTS for an implemented class. GenerateCidRangeChecks
2367// might clobber the register that holds the class ID to check, hence the need
2368// to reload.
2369//
2370// To ensure that the register is clobbered on all architectures, we set things
2371// up by generating the following classes:
2372// * B<X>, a generic abstract class which is implemented by the others.
2373// * I, implements B<String>, has a single int field x, and is
2374// used to create the checked instance.
2375// * G<Y>, which implements B<Y> and has no fields (so its TAV field
2376// offset will correspond to that of the offset of x in I).
2377// * C and D, consecutively defined non-generic classes which both implement
2378// B<int>.
2379// * U0 - UN, unrelated concrete classes as needed for cid alignment.
2380//
2381// We'll carefully set things up so that the following equation between their
2382// class ids holds:
2383//
2384// G = I - C.
2385//
2386// Thus, when we create a TTS for B<int> and check it against an instance V
2387// of I. The cid for I will be loaded into a register R, and then two
2388// check blocks will be generated:
2389//
2390// * A check for the cid range [C-D], which has the side effect of
2391// subtracting the cid of C from the contents of R (here, the cid of I).
2392//
2393// * A check that R contains the cid for G.
2394//
2395// Thus, if the cid of I is not reloaded into R before the second check, and
2396// the equation earlier holds, we'll get a false positive that V is an instance
2397// of G, so the code will then try to load the instance type arguments from V
2398// as if it was an instance of G. This means the contents of x will be loaded
2399// and attempted to be used as a TypeArgumentsPtr, which will cause a crash
2400// during the checks that the instantiation of Y is int.
2401ISOLATE_UNIT_TEST_CASE(TTS_Regress_CidRangeChecks) {
2402 // We create the classes in this order: B, G, C, D, U..., I. We need
2403 // G = I - C => G + C = I
2404 // => G + C = D + N + 1 (where N is the number of U classes)
2405 // => (B + 1) + C = (C + 1) + N + 1
2406 // => B - 1 = N.
2407 // The cid for B will be the next allocated cid, which is the number of
2408 // non-top-level cids in the current class table.
2409 ClassTable* const class_table = IsolateGroup::Current()->class_table();
2410 const intptr_t kNumUnrelated = class_table->NumCids() - 1;
2411 TextBuffer buffer(1024);
2412 buffer.AddString(R"(
2413 abstract class B<X> {}
2414 class G<Y> implements B<Y> {}
2415 class C implements B<int> {}
2416 class D implements B<int> {}
2417)");
2418 for (intptr_t i = 0; i < kNumUnrelated; i++) {
2419 buffer.Printf(R"(
2420 class U%)" Pd R"( {}
2421)",
2422 i);
2423 }
2424 buffer.AddString(R"(
2425 class I implements B<String> {
2426 final x = 1;
2427 }
2428
2429 createI() => I();
2430)");
2431
2432 const auto& root_library = Library::Handle(LoadTestScript(buffer.buffer()));
2433 const auto& class_b = Class::Handle(GetClass(root_library, "B"));
2434 const auto& class_g = Class::Handle(GetClass(root_library, "G"));
2435 const auto& class_c = Class::Handle(GetClass(root_library, "C"));
2436 const auto& class_d = Class::Handle(GetClass(root_library, "D"));
2437 const auto& class_u0 = Class::Handle(GetClass(root_library, "U0"));
2438 const auto& class_i = Class::Handle(GetClass(root_library, "I"));
2439 const auto& obj_i = Object::Handle(Invoke(root_library, "createI"));
2440 {
2441 SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
2443 }
2444
2445 // Double-check assumptions from calculating kNumUnrelated.
2446 EXPECT_EQ(kNumUnrelated, class_b.id() - 1);
2447 EXPECT_EQ(class_b.id() + 1, class_g.id());
2448 EXPECT_EQ(class_c.id() + 1, class_d.id());
2449 EXPECT_EQ(class_d.id() + 1, class_u0.id());
2450 EXPECT_EQ(class_u0.id() + kNumUnrelated, class_i.id());
2451 EXPECT_EQ(class_g.id(), class_i.id() - class_c.id());
2452
2453 const auto& tav_null = Object::null_type_arguments();
2454 auto& tav_int = TypeArguments::Handle(TypeArguments::New(1));
2455 tav_int.SetTypeAt(0, Type::Handle(Type::IntType()));
2456 CanonicalizeTAV(&tav_int);
2457
2458 auto& type_b_int = Type::Handle(Type::New(class_b, tav_int));
2459 FinalizeAndCanonicalize(&type_b_int);
2460
2461 TTSTestState state(thread, type_b_int);
2462 state.InvokeEagerlySpecializedStub(Failure({obj_i, tav_null, tav_null}));
2463}
2464
2465struct STCTestResults {
2466 bool became_hash_cache = false;
2467 bool cache_capped = false;
2468};
2469
2470static STCTestResults SubtypeTestCacheTest(Thread* thread,
2471 intptr_t num_classes) {
2472 TextBuffer buffer(MB);
2473 buffer.AddString("class D<S> {}\n");
2474 buffer.AddString("D<int> Function() createClosureD() => () => D<int>();\n");
2475 for (intptr_t i = 0; i < num_classes; i++) {
2476 buffer.Printf(R"(class C%)" Pd R"(<S> extends D<S> {}
2477 C%)" Pd R"(<int> Function() createClosureC%)" Pd R"(() => () => C%)" Pd
2478 R"(<int>();
2479)",
2480 i, i, i, i);
2481 }
2482
2483 Dart_Handle api_lib = TestCase::LoadTestScript(buffer.buffer(), nullptr);
2484 EXPECT_VALID(api_lib);
2485
2486 // D + C0...CN, where N = kNumClasses - 1
2487 EXPECT(IsolateGroup::Current()->class_table()->NumCids() > num_classes);
2488
2489 TransitionNativeToVM transition(thread);
2490 Zone* const zone = thread->zone();
2491
2492 const auto& root_lib =
2493 Library::CheckedHandle(zone, Api::UnwrapHandle(api_lib));
2494 EXPECT(!root_lib.IsNull());
2495
2496 const auto& class_d = Class::Handle(zone, GetClass(root_lib, "D"));
2497 ASSERT(!class_d.IsNull());
2498 {
2499 SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
2501 }
2502 const auto& object_d =
2503 Instance::CheckedHandle(zone, Invoke(root_lib, "createClosureD"));
2504 ASSERT(!object_d.IsNull());
2505 auto& type_closure_d_int =
2506 AbstractType::Handle(zone, object_d.GetType(Heap::kNew));
2507 const bool can_be_null = Instance::NullIsAssignableTo(type_closure_d_int);
2508
2509 const auto& tav_null = Object::null_type_arguments();
2510
2511 TTSTestState state(thread, type_closure_d_int);
2512 // Prime the stub before the loop with the null object.
2513 state.InvokeEagerlySpecializedStub(
2514 {Object::null_object(), tav_null, tav_null, can_be_null ? kTTS : kFail});
2515
2516 auto& class_c = Class::Handle(zone);
2517 auto& object_c = Object::Handle(zone);
2518 STCTestResults results;
2519 for (intptr_t i = 0; i < num_classes; ++i) {
2520 auto const class_name = OS::SCreate(zone, "C%" Pd "", i);
2521 class_c = GetClass(root_lib, class_name);
2522 ASSERT(!class_c.IsNull());
2523 {
2524 SafepointWriteRwLocker ml(thread,
2525 thread->isolate_group()->program_lock());
2527 }
2528 auto const function_name = OS::SCreate(zone, "createClosureC%" Pd "", i);
2529 object_c = Invoke(root_lib, function_name);
2530
2531 TTSTestCase base_case = {object_c, tav_null, tav_null};
2532
2533 if (i >= FLAG_max_subtype_cache_entries) {
2534 ASSERT(state.last_stc().NumberOfChecks() >=
2535 FLAG_max_subtype_cache_entries);
2536 state.InvokeExistingStub(RuntimeCheck(base_case));
2537 // Rerunning the test doesn't change the fact we can't add to the STC.
2538 state.InvokeExistingStub(RuntimeCheck(base_case));
2539 results.cache_capped = true;
2540 } else {
2541 const bool was_hash = state.last_stc().IsHash();
2542 // All the rest of the tests should create or modify an STC.
2543 state.InvokeExistingStub(FalseNegative(base_case));
2544 if (i == 0) {
2545 // We should get a linear cache the first time.
2546 EXPECT(!state.last_stc().IsHash());
2547 } else if (was_hash) {
2548 // We should never change from hash back to linear.
2549 EXPECT(state.last_stc().IsHash());
2550 } else if (state.last_stc().IsHash()) {
2551 results.became_hash_cache = true;
2552 }
2553 state.InvokeExistingStub(STCCheck(base_case));
2554 }
2555 }
2556
2557 return results;
2558}
2559
2560// The smallest test that just checks linear caches.
2561TEST_CASE(TTS_STC_LinearOnly) {
2562 const intptr_t num_classes =
2563 Utils::Minimum(static_cast<intptr_t>(FLAG_max_subtype_cache_entries),
2565 EXPECT(num_classes > 0);
2566 const auto& results = SubtypeTestCacheTest(thread, num_classes);
2567 EXPECT(!results.became_hash_cache);
2568 EXPECT(!results.cache_capped);
2569}
2570
2571// A larger test that ensures we convert to a hash table at some point.
2572TEST_CASE(TTS_STC_Hash) {
2573 const intptr_t num_classes =
2574 Utils::Minimum(static_cast<intptr_t>(FLAG_max_subtype_cache_entries),
2577 const auto& results = SubtypeTestCacheTest(thread, num_classes);
2578 EXPECT(results.became_hash_cache);
2579 EXPECT(!results.cache_capped);
2580}
2581
2582// A larger test that ensures that we use enough entries to hit the max STC
2583// size and test what happens when we go above it.
2584TEST_CASE(TTS_STC_Capped) {
2585 const intptr_t num_classes = 1.1 * FLAG_max_subtype_cache_entries;
2586 EXPECT(num_classes > 0);
2587 const auto& results = SubtypeTestCacheTest(thread, num_classes);
2588 EXPECT_EQ(SubtypeTestCache::kMaxLinearCacheEntries < num_classes,
2589 results.became_hash_cache);
2590 EXPECT(results.cache_capped);
2591}
2592
2593} // namespace dart
2594
2595#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 UNREACHABLE()
Definition: assert.h:248
#define ASSERT_EQUAL(expected, actual)
Definition: assert.h:309
#define RELEASE_ASSERT(cond)
Definition: assert.h:327
GLenum type
void SetTypeTestingStub(const Code &stub) const
Definition: object.cc:21761
CodePtr type_test_stub() const
Definition: object.h:9304
static ObjectPtr UnwrapHandle(Dart_Handle object)
static ArrayPtr New(intptr_t len, Heap::Space space=Heap::kNew)
Definition: object.h:10959
ObjectPtr At(intptr_t index) const
Definition: object.h:10875
void SetAt(intptr_t index, const Object &value) const
Definition: object.h:10880
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
Definition: class_table.h:447
static CodePtr FinalizeCodeAndNotify(const Function &function, FlowGraphCompiler *compiler, compiler::Assembler *assembler, PoolAttachment pool_attachment, bool optimized=false, CodeStatistics *stats=nullptr)
Definition: object.cc:17983
static void DisassembleStub(const char *name, const Code &code)
static DoublePtr New(double d, Heap::Space space=Heap::kNew)
Definition: object.cc:23402
static ExceptionHandlersPtr New(intptr_t num_handlers)
Definition: object.cc:16241
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::kNonNullable, Heap::Space space=Heap::kOld)
Definition: object.cc:11631
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:10243
@ kNew
Definition: heap.h:38
@ kOld
Definition: heap.h:39
static bool NullIsAssignableTo(const AbstractType &other)
Definition: object.cc:20674
static IntegerPtr New(const String &str, Heap::Space space=Heap::kNew)
Definition: object.cc:22984
static IsolateGroup * Current()
Definition: isolate.h:539
ClassTable * class_table() const
Definition: isolate.h:496
Mutex * subtype_test_cache_mutex()
Definition: isolate.h:516
static char * SCreate(Zone *zone, const char *format,...) PRINTF_ATTRIBUTE(2
ObjectPtr ObjectAt(intptr_t index) const
Definition: object.h:5628
void SetObjectAt(intptr_t index, const Object &obj) const
Definition: object.h:5634
static ObjectPoolPtr NewFromBuilder(const compiler::ObjectPoolBuilder &builder)
Definition: object.cc:15676
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:13218
static const char * RegisterName(Register reg)
Definition: constants.h:46
static SmiPtr New(intptr_t value)
Definition: object.h:10006
intptr_t Value() const
Definition: object.h:9990
static StringPtr New(const char *cstr, Heap::Space space=Heap::kNew)
Definition: object.cc:23698
static const char * NameOfStub(uword entry_point)
Definition: stub_code.cc:330
SubtypeTestCachePtr Copy(Thread *thread) const
Definition: object.cc:19639
ArrayPtr cache() const
Definition: object.cc:18938
intptr_t NumberOfChecks() const
Definition: object.cc:18954
static constexpr intptr_t kMaxLinearCacheEntries
Definition: object.h:7830
void WriteToBuffer(Zone *zone, BaseTextBuffer *buffer, const char *line_prefix=nullptr) const
Definition: object.cc:19507
static const String & Empty()
Definition: symbols.h:688
static StringPtr New(Thread *thread, const char *cstr)
Definition: symbols.h:723
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:436
Zone * zone() const
Definition: thread_state.h:37
static Thread * Current()
Definition: thread.h:362
IsolateGroup * isolate_group() const
Definition: thread.h:541
TypeArgumentsPtr Canonicalize(Thread *thread) const
Definition: object.cc:7703
static TypeArgumentsPtr New(intptr_t len, Heap::Space space=Heap::kOld)
Definition: object.cc:7675
static CodePtr DefaultCodeForType(const AbstractType &type, bool lazy_specialize=true)
static CodePtr SpecializeStubFor(Thread *thread, const AbstractType &type)
static TypePtr IntType()
static TypePtr Double()
static TypePtr Number()
static TypePtr StringType()
static TypePtr DartFunctionType()
static TypePtr ObjectType()
static TypePtr SmiType()
static TypePtr DynamicType()
static TypePtr New(const Class &clazz, const TypeArguments &arguments, Nullability nullability=Nullability::kNonNullable, Heap::Space space=Heap::kOld)
static T Minimum(T x, T y)
Definition: utils.h:36
static word element_offset(intptr_t index)
#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
const uint8_t uint32_t uint32_t GError ** error
GAsyncResult * result
Dart_NativeFunction function
Definition: fuchsia.cc:51
static constexpr intptr_t kWordSize
Definition: runtime_api.h:274
Definition: dart_vm.cc:33
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
Definition: constants_arm.h:98
static TTSTestCase STCCheck(const TTSTestCase &original)
static constexpr int kCallerSpSlotFromFp
ISOLATE_UNIT_TEST_CASE(StackAllocatedDestruction)
constexpr RegList kDartAvailableCpuRegs
DEFINE_FLAG(bool, print_cluster_information, false, "Print information about clusters written to snapshot")
const Register FPREG
ClassPtr GetClass(const Library &lib, const char *name)
const intptr_t cid
TEST_CASE(DirectoryCurrent)
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:149
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)
DECLARE_FLAG(bool, show_invisible_frames)
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
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 buffer
Definition: switches.h:126
std::function< void()> closure
Definition: closure.h:14
static bool Bind(PassBindingsCacheMTL &pass, ShaderStage stage, size_t bind_index, const BufferView &view)
#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 __
#define FILE_RESOLVE_URI(Uri)
#define THIRD_PARTIAL_LIBRARY_NAME
#define FIRST_PARTIAL_LIBRARY_NAME
#define SECOND_PARTIAL_LIBRARY_NAME
#define EXPECT_VALID(handle)
Definition: unit_test.h:643