mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 13:57:58 +00:00
Reland "[vm/compiler] Add more checks in TTSes for uninstantiated types."
This is a reland of 1016bbadde
The broken benchmarks causing the revert were not due to this CL.
TEST=vm/cc/TTS
Original change's description:
> [vm/compiler] Add more checks in TTSes for uninstantiated types.
>
> In certain cases, optimized type testing stubs for uninstantiated types
> compare the instantiation of the type arguments for the type against the
> corresponding instance type arguments. Previously, only an identity
> check was generated, so the instantiation had to match the instance type
> argument exactly.
>
> This CL adds checks for the following cases:
> * The instantiated type argument is dynamic, void, or Object.
> * The instance type argument is Null or Never.
>
> When strong null safety is enabled, we also check that the the instance
> type argument is legacy or non-nullable when the instantiated type
> argument is non-nullable Object or that the instantiated type argument
> is nullable or legacy when the instance type argument is Null.
>
> This CL also adds handling for the case where the instantiated or
> instance type argument is not a Type, which is necessary for safely
> retrieving the type class id and nullability, and allocates some
> additional registers in TTSInternalRegs for storing type arguments. On
> ARM7, this requires pushes/pops around type argument checking, because
> we've run out of registers there.
>
> Fixes https://github.com/dart-lang/sdk/issues/40736
>
> TEST=vm/cc/TTS
>
> Bug: https://github.com/dart-lang/sdk/issues/46920
> Cq-Include-Trybots: luci.dart.try:vm-kernel-linux-release-x64-try,vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-nnbd-linux-release-x64-try,vm-kernel-nnbd-linux-release-x64-try,vm-kernel-linux-product-x64-try,vm-kernel-precomp-linux-product-x64-try,vm-kernel-linux-release-simarm-try,vm-kernel-linux-release-simarm64-try
> Change-Id: Ib8498fd2b9593b4abb92111f062ed2fc95a45c16
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/210681
> Commit-Queue: Tess Strickland <sstrickl@google.com>
> Reviewed-by: Alexander Markov <alexmarkov@google.com>
Bug: https://github.com/dart-lang/sdk/issues/46920
Change-Id: Id84730b3bb292ea7fdfe9ebd8b1fc6e71c64cf22
Cq-Include-Trybots: luci.dart.try:vm-kernel-linux-release-x64-try,vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-nnbd-linux-release-x64-try,vm-kernel-nnbd-linux-release-x64-try,vm-kernel-linux-product-x64-try,vm-kernel-precomp-linux-product-x64-try,vm-kernel-linux-release-simarm-try,vm-kernel-linux-release-simarm64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/211901
Commit-Queue: Tess Strickland <sstrickl@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
parent
11f497f6ab
commit
bb1640eec2
|
@ -2303,6 +2303,16 @@ void Assembler::Bind(Label* label) {
|
|||
BindARMv7(label);
|
||||
}
|
||||
|
||||
void Assembler::LoadCompressedSmi(Register dest, const Address& slot) {
|
||||
ldr(dest, slot);
|
||||
#if defined(DEBUG)
|
||||
Label done;
|
||||
BranchIfSmi(dest, &done);
|
||||
Stop("Expected Smi");
|
||||
Bind(&done);
|
||||
#endif
|
||||
}
|
||||
|
||||
OperandSize Address::OperandSizeFor(intptr_t cid) {
|
||||
switch (cid) {
|
||||
case kArrayCid:
|
||||
|
|
|
@ -395,6 +395,7 @@ class Assembler : public AssemblerBase {
|
|||
LoadFromOffset(dst, base, offset);
|
||||
}
|
||||
void LoadCompressed(Register dest, const Address& slot) { ldr(dest, slot); }
|
||||
void LoadCompressedSmi(Register dest, const Address& slot);
|
||||
void StoreMemoryValue(Register src, Register base, int32_t offset) {
|
||||
StoreToOffset(src, base, offset);
|
||||
}
|
||||
|
@ -429,7 +430,15 @@ class Assembler : public AssemblerBase {
|
|||
cmp(value, Operand(TMP));
|
||||
}
|
||||
|
||||
void CompareTypeNullabilityWith(Register type, int8_t value) {
|
||||
void CompareFunctionTypeNullabilityWith(Register type,
|
||||
int8_t value) override {
|
||||
EnsureHasClassIdInDEBUG(kFunctionTypeCid, type, TMP);
|
||||
ldrb(TMP, FieldAddress(
|
||||
type, compiler::target::FunctionType::nullability_offset()));
|
||||
cmp(TMP, Operand(value));
|
||||
}
|
||||
void CompareTypeNullabilityWith(Register type, int8_t value) override {
|
||||
EnsureHasClassIdInDEBUG(kTypeCid, type, TMP);
|
||||
ldrb(TMP, FieldAddress(type, compiler::target::Type::nullability_offset()));
|
||||
cmp(TMP, Operand(value));
|
||||
}
|
||||
|
|
|
@ -646,7 +646,17 @@ class Assembler : public AssemblerBase {
|
|||
cmp(value, Operand(TMP), sz);
|
||||
}
|
||||
|
||||
void CompareTypeNullabilityWith(Register type, int8_t value) {
|
||||
void CompareFunctionTypeNullabilityWith(Register type,
|
||||
int8_t value) override {
|
||||
EnsureHasClassIdInDEBUG(kFunctionTypeCid, type, TMP);
|
||||
ldr(TMP,
|
||||
FieldAddress(type, compiler::target::FunctionType::nullability_offset(),
|
||||
kByte),
|
||||
kUnsignedByte);
|
||||
cmp(TMP, Operand(value));
|
||||
}
|
||||
void CompareTypeNullabilityWith(Register type, int8_t value) override {
|
||||
EnsureHasClassIdInDEBUG(kTypeCid, type, TMP);
|
||||
ldr(TMP,
|
||||
FieldAddress(type, compiler::target::Type::nullability_offset(), kByte),
|
||||
kUnsignedByte);
|
||||
|
|
|
@ -718,6 +718,20 @@ class AssemblerBase : public StackResource {
|
|||
Register address,
|
||||
int32_t offset = 0) = 0;
|
||||
|
||||
// Retrieves nullability from a FunctionTypePtr in [type] and compares it
|
||||
// to [value].
|
||||
//
|
||||
// TODO(dartbug.com/47034): Change how nullability is stored so that it
|
||||
// can be accessed without checking the class id first.
|
||||
virtual void CompareFunctionTypeNullabilityWith(Register type,
|
||||
int8_t value) = 0;
|
||||
|
||||
// Retrieves nullability from a TypePtr in [type] and compares it to [value].
|
||||
//
|
||||
// TODO(dartbug.com/47034): Change how nullability is stored so that it
|
||||
// can be accessed without checking the class id first.
|
||||
virtual void CompareTypeNullabilityWith(Register type, int8_t value) = 0;
|
||||
|
||||
virtual void EnsureHasClassIdInDEBUG(intptr_t cid,
|
||||
Register src,
|
||||
Register scratch,
|
||||
|
|
|
@ -782,7 +782,13 @@ class Assembler : public AssemblerBase {
|
|||
cmpxchgl(address, reg);
|
||||
}
|
||||
|
||||
void CompareTypeNullabilityWith(Register type, int8_t value) {
|
||||
void CompareFunctionTypeNullabilityWith(Register type,
|
||||
int8_t value) override {
|
||||
cmpb(FieldAddress(type,
|
||||
compiler::target::FunctionType::nullability_offset()),
|
||||
Immediate(value));
|
||||
}
|
||||
void CompareTypeNullabilityWith(Register type, int8_t value) override {
|
||||
cmpb(FieldAddress(type, compiler::target::Type::nullability_offset()),
|
||||
Immediate(value));
|
||||
}
|
||||
|
|
|
@ -1102,7 +1102,15 @@ class Assembler : public AssemblerBase {
|
|||
OBJ(cmp)(value, FieldAddress(base, offset));
|
||||
}
|
||||
|
||||
void CompareTypeNullabilityWith(Register type, int8_t value) {
|
||||
void CompareFunctionTypeNullabilityWith(Register type,
|
||||
int8_t value) override {
|
||||
EnsureHasClassIdInDEBUG(kFunctionTypeCid, type, TMP);
|
||||
cmpb(FieldAddress(type,
|
||||
compiler::target::FunctionType::nullability_offset()),
|
||||
Immediate(value));
|
||||
}
|
||||
void CompareTypeNullabilityWith(Register type, int8_t value) override {
|
||||
EnsureHasClassIdInDEBUG(kTypeCid, type, TMP);
|
||||
cmpb(FieldAddress(type, compiler::target::Type::nullability_offset()),
|
||||
Immediate(value));
|
||||
}
|
||||
|
|
|
@ -425,14 +425,16 @@ static constexpr dart::compiler::target::word Type_type_class_id_offset = 12;
|
|||
static constexpr dart::compiler::target::word Type_type_state_offset = 24;
|
||||
static constexpr dart::compiler::target::word Type_nullability_offset = 25;
|
||||
static constexpr dart::compiler::target::word FunctionType_hash_offset = 28;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_named_parameter_names_offset = 24;
|
||||
static constexpr dart::compiler::target::word FunctionType_nullability_offset =
|
||||
39;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_packed_parameter_counts_offset = 32;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_packed_type_parameter_counts_offset = 36;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_parameter_types_offset = 20;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_named_parameter_names_offset = 24;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_type_parameters_offset = 12;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -981,14 +983,16 @@ static constexpr dart::compiler::target::word Type_type_class_id_offset = 24;
|
|||
static constexpr dart::compiler::target::word Type_type_state_offset = 48;
|
||||
static constexpr dart::compiler::target::word Type_nullability_offset = 49;
|
||||
static constexpr dart::compiler::target::word FunctionType_hash_offset = 56;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_named_parameter_names_offset = 48;
|
||||
static constexpr dart::compiler::target::word FunctionType_nullability_offset =
|
||||
71;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_packed_parameter_counts_offset = 64;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_packed_type_parameter_counts_offset = 68;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_parameter_types_offset = 40;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_named_parameter_names_offset = 48;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_type_parameters_offset = 24;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -1530,14 +1534,16 @@ static constexpr dart::compiler::target::word Type_type_class_id_offset = 12;
|
|||
static constexpr dart::compiler::target::word Type_type_state_offset = 24;
|
||||
static constexpr dart::compiler::target::word Type_nullability_offset = 25;
|
||||
static constexpr dart::compiler::target::word FunctionType_hash_offset = 28;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_named_parameter_names_offset = 24;
|
||||
static constexpr dart::compiler::target::word FunctionType_nullability_offset =
|
||||
39;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_packed_parameter_counts_offset = 32;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_packed_type_parameter_counts_offset = 36;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_parameter_types_offset = 20;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_named_parameter_names_offset = 24;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_type_parameters_offset = 12;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -2083,14 +2089,16 @@ static constexpr dart::compiler::target::word Type_type_class_id_offset = 24;
|
|||
static constexpr dart::compiler::target::word Type_type_state_offset = 48;
|
||||
static constexpr dart::compiler::target::word Type_nullability_offset = 49;
|
||||
static constexpr dart::compiler::target::word FunctionType_hash_offset = 56;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_named_parameter_names_offset = 48;
|
||||
static constexpr dart::compiler::target::word FunctionType_nullability_offset =
|
||||
71;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_packed_parameter_counts_offset = 64;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_packed_type_parameter_counts_offset = 68;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_parameter_types_offset = 40;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_named_parameter_names_offset = 48;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_type_parameters_offset = 24;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -2639,14 +2647,16 @@ static constexpr dart::compiler::target::word Type_type_class_id_offset = 24;
|
|||
static constexpr dart::compiler::target::word Type_type_state_offset = 36;
|
||||
static constexpr dart::compiler::target::word Type_nullability_offset = 37;
|
||||
static constexpr dart::compiler::target::word FunctionType_hash_offset = 40;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_named_parameter_names_offset = 36;
|
||||
static constexpr dart::compiler::target::word FunctionType_nullability_offset =
|
||||
51;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_packed_parameter_counts_offset = 44;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_packed_type_parameter_counts_offset = 48;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_parameter_types_offset = 32;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_named_parameter_names_offset = 36;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_type_parameters_offset = 24;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -3194,14 +3204,16 @@ static constexpr dart::compiler::target::word Type_type_class_id_offset = 24;
|
|||
static constexpr dart::compiler::target::word Type_type_state_offset = 36;
|
||||
static constexpr dart::compiler::target::word Type_nullability_offset = 37;
|
||||
static constexpr dart::compiler::target::word FunctionType_hash_offset = 40;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_named_parameter_names_offset = 36;
|
||||
static constexpr dart::compiler::target::word FunctionType_nullability_offset =
|
||||
51;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_packed_parameter_counts_offset = 44;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_packed_type_parameter_counts_offset = 48;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_parameter_types_offset = 32;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_named_parameter_names_offset = 36;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_type_parameters_offset = 24;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -3740,14 +3752,16 @@ static constexpr dart::compiler::target::word Type_type_class_id_offset = 12;
|
|||
static constexpr dart::compiler::target::word Type_type_state_offset = 24;
|
||||
static constexpr dart::compiler::target::word Type_nullability_offset = 25;
|
||||
static constexpr dart::compiler::target::word FunctionType_hash_offset = 28;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_named_parameter_names_offset = 24;
|
||||
static constexpr dart::compiler::target::word FunctionType_nullability_offset =
|
||||
39;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_packed_parameter_counts_offset = 32;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_packed_type_parameter_counts_offset = 36;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_parameter_types_offset = 20;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_named_parameter_names_offset = 24;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_type_parameters_offset = 12;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -4290,14 +4304,16 @@ static constexpr dart::compiler::target::word Type_type_class_id_offset = 24;
|
|||
static constexpr dart::compiler::target::word Type_type_state_offset = 48;
|
||||
static constexpr dart::compiler::target::word Type_nullability_offset = 49;
|
||||
static constexpr dart::compiler::target::word FunctionType_hash_offset = 56;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_named_parameter_names_offset = 48;
|
||||
static constexpr dart::compiler::target::word FunctionType_nullability_offset =
|
||||
71;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_packed_parameter_counts_offset = 64;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_packed_type_parameter_counts_offset = 68;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_parameter_types_offset = 40;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_named_parameter_names_offset = 48;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_type_parameters_offset = 24;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -4833,14 +4849,16 @@ static constexpr dart::compiler::target::word Type_type_class_id_offset = 12;
|
|||
static constexpr dart::compiler::target::word Type_type_state_offset = 24;
|
||||
static constexpr dart::compiler::target::word Type_nullability_offset = 25;
|
||||
static constexpr dart::compiler::target::word FunctionType_hash_offset = 28;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_named_parameter_names_offset = 24;
|
||||
static constexpr dart::compiler::target::word FunctionType_nullability_offset =
|
||||
39;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_packed_parameter_counts_offset = 32;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_packed_type_parameter_counts_offset = 36;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_parameter_types_offset = 20;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_named_parameter_names_offset = 24;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_type_parameters_offset = 12;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -5380,14 +5398,16 @@ static constexpr dart::compiler::target::word Type_type_class_id_offset = 24;
|
|||
static constexpr dart::compiler::target::word Type_type_state_offset = 48;
|
||||
static constexpr dart::compiler::target::word Type_nullability_offset = 49;
|
||||
static constexpr dart::compiler::target::word FunctionType_hash_offset = 56;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_named_parameter_names_offset = 48;
|
||||
static constexpr dart::compiler::target::word FunctionType_nullability_offset =
|
||||
71;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_packed_parameter_counts_offset = 64;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_packed_type_parameter_counts_offset = 68;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_parameter_types_offset = 40;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_named_parameter_names_offset = 48;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_type_parameters_offset = 24;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -5930,14 +5950,16 @@ static constexpr dart::compiler::target::word Type_type_class_id_offset = 24;
|
|||
static constexpr dart::compiler::target::word Type_type_state_offset = 36;
|
||||
static constexpr dart::compiler::target::word Type_nullability_offset = 37;
|
||||
static constexpr dart::compiler::target::word FunctionType_hash_offset = 40;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_named_parameter_names_offset = 36;
|
||||
static constexpr dart::compiler::target::word FunctionType_nullability_offset =
|
||||
51;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_packed_parameter_counts_offset = 44;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_packed_type_parameter_counts_offset = 48;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_parameter_types_offset = 32;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_named_parameter_names_offset = 36;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_type_parameters_offset = 24;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -6479,14 +6501,16 @@ static constexpr dart::compiler::target::word Type_type_class_id_offset = 24;
|
|||
static constexpr dart::compiler::target::word Type_type_state_offset = 36;
|
||||
static constexpr dart::compiler::target::word Type_nullability_offset = 37;
|
||||
static constexpr dart::compiler::target::word FunctionType_hash_offset = 40;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_named_parameter_names_offset = 36;
|
||||
static constexpr dart::compiler::target::word FunctionType_nullability_offset =
|
||||
51;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_packed_parameter_counts_offset = 44;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_packed_type_parameter_counts_offset = 48;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_parameter_types_offset = 32;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_named_parameter_names_offset = 36;
|
||||
static constexpr dart::compiler::target::word
|
||||
FunctionType_type_parameters_offset = 24;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -7075,14 +7099,16 @@ static constexpr dart::compiler::target::word AOT_Type_type_class_id_offset =
|
|||
static constexpr dart::compiler::target::word AOT_Type_type_state_offset = 24;
|
||||
static constexpr dart::compiler::target::word AOT_Type_nullability_offset = 25;
|
||||
static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 28;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_named_parameter_names_offset = 24;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_nullability_offset = 39;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_packed_parameter_counts_offset = 32;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_packed_type_parameter_counts_offset = 36;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_parameter_types_offset = 20;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_named_parameter_names_offset = 24;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_type_parameters_offset = 12;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -7691,14 +7717,16 @@ static constexpr dart::compiler::target::word AOT_Type_type_class_id_offset =
|
|||
static constexpr dart::compiler::target::word AOT_Type_type_state_offset = 48;
|
||||
static constexpr dart::compiler::target::word AOT_Type_nullability_offset = 49;
|
||||
static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 56;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_named_parameter_names_offset = 48;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_nullability_offset = 71;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_packed_parameter_counts_offset = 64;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_packed_type_parameter_counts_offset = 68;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_parameter_types_offset = 40;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_named_parameter_names_offset = 48;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_type_parameters_offset = 24;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -8312,14 +8340,16 @@ static constexpr dart::compiler::target::word AOT_Type_type_class_id_offset =
|
|||
static constexpr dart::compiler::target::word AOT_Type_type_state_offset = 48;
|
||||
static constexpr dart::compiler::target::word AOT_Type_nullability_offset = 49;
|
||||
static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 56;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_named_parameter_names_offset = 48;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_nullability_offset = 71;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_packed_parameter_counts_offset = 64;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_packed_type_parameter_counts_offset = 68;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_parameter_types_offset = 40;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_named_parameter_names_offset = 48;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_type_parameters_offset = 24;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -8930,14 +8960,16 @@ static constexpr dart::compiler::target::word AOT_Type_type_class_id_offset =
|
|||
static constexpr dart::compiler::target::word AOT_Type_type_state_offset = 36;
|
||||
static constexpr dart::compiler::target::word AOT_Type_nullability_offset = 37;
|
||||
static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 40;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_named_parameter_names_offset = 36;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_nullability_offset = 51;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_packed_parameter_counts_offset = 44;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_packed_type_parameter_counts_offset = 48;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_parameter_types_offset = 32;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_named_parameter_names_offset = 36;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_type_parameters_offset = 24;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -9547,14 +9579,16 @@ static constexpr dart::compiler::target::word AOT_Type_type_class_id_offset =
|
|||
static constexpr dart::compiler::target::word AOT_Type_type_state_offset = 36;
|
||||
static constexpr dart::compiler::target::word AOT_Type_nullability_offset = 37;
|
||||
static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 40;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_named_parameter_names_offset = 36;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_nullability_offset = 51;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_packed_parameter_counts_offset = 44;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_packed_type_parameter_counts_offset = 48;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_parameter_types_offset = 32;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_named_parameter_names_offset = 36;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_type_parameters_offset = 24;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -10159,14 +10193,16 @@ static constexpr dart::compiler::target::word AOT_Type_type_class_id_offset =
|
|||
static constexpr dart::compiler::target::word AOT_Type_type_state_offset = 24;
|
||||
static constexpr dart::compiler::target::word AOT_Type_nullability_offset = 25;
|
||||
static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 28;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_named_parameter_names_offset = 24;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_nullability_offset = 39;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_packed_parameter_counts_offset = 32;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_packed_type_parameter_counts_offset = 36;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_parameter_types_offset = 20;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_named_parameter_names_offset = 24;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_type_parameters_offset = 12;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -10768,14 +10804,16 @@ static constexpr dart::compiler::target::word AOT_Type_type_class_id_offset =
|
|||
static constexpr dart::compiler::target::word AOT_Type_type_state_offset = 48;
|
||||
static constexpr dart::compiler::target::word AOT_Type_nullability_offset = 49;
|
||||
static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 56;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_named_parameter_names_offset = 48;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_nullability_offset = 71;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_packed_parameter_counts_offset = 64;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_packed_type_parameter_counts_offset = 68;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_parameter_types_offset = 40;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_named_parameter_names_offset = 48;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_type_parameters_offset = 24;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -11382,14 +11420,16 @@ static constexpr dart::compiler::target::word AOT_Type_type_class_id_offset =
|
|||
static constexpr dart::compiler::target::word AOT_Type_type_state_offset = 48;
|
||||
static constexpr dart::compiler::target::word AOT_Type_nullability_offset = 49;
|
||||
static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 56;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_named_parameter_names_offset = 48;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_nullability_offset = 71;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_packed_parameter_counts_offset = 64;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_packed_type_parameter_counts_offset = 68;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_parameter_types_offset = 40;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_named_parameter_names_offset = 48;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_type_parameters_offset = 24;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -11993,14 +12033,16 @@ static constexpr dart::compiler::target::word AOT_Type_type_class_id_offset =
|
|||
static constexpr dart::compiler::target::word AOT_Type_type_state_offset = 36;
|
||||
static constexpr dart::compiler::target::word AOT_Type_nullability_offset = 37;
|
||||
static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 40;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_named_parameter_names_offset = 36;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_nullability_offset = 51;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_packed_parameter_counts_offset = 44;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_packed_type_parameter_counts_offset = 48;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_parameter_types_offset = 32;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_named_parameter_names_offset = 36;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_type_parameters_offset = 24;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -12603,14 +12645,16 @@ static constexpr dart::compiler::target::word AOT_Type_type_class_id_offset =
|
|||
static constexpr dart::compiler::target::word AOT_Type_type_state_offset = 36;
|
||||
static constexpr dart::compiler::target::word AOT_Type_nullability_offset = 37;
|
||||
static constexpr dart::compiler::target::word AOT_FunctionType_hash_offset = 40;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_named_parameter_names_offset = 36;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_nullability_offset = 51;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_packed_parameter_counts_offset = 44;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_packed_type_parameter_counts_offset = 48;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_parameter_types_offset = 32;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_named_parameter_names_offset = 36;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_FunctionType_type_parameters_offset = 24;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
|
|
@ -294,10 +294,11 @@
|
|||
FIELD(Type, type_state_offset) \
|
||||
FIELD(Type, nullability_offset) \
|
||||
FIELD(FunctionType, hash_offset) \
|
||||
FIELD(FunctionType, named_parameter_names_offset) \
|
||||
FIELD(FunctionType, nullability_offset) \
|
||||
FIELD(FunctionType, packed_parameter_counts_offset) \
|
||||
FIELD(FunctionType, packed_type_parameter_counts_offset) \
|
||||
FIELD(FunctionType, parameter_types_offset) \
|
||||
FIELD(FunctionType, named_parameter_names_offset) \
|
||||
FIELD(FunctionType, type_parameters_offset) \
|
||||
FIELD(TypeParameter, parameterized_class_id_offset) \
|
||||
FIELD(TypeParameter, index_offset) \
|
||||
|
|
|
@ -339,9 +339,18 @@ struct InstantiationABI {
|
|||
struct TTSInternalRegs {
|
||||
static const Register kInstanceTypeArgumentsReg = R4;
|
||||
static const Register kScratchReg = R9;
|
||||
static const Register kSubTypeArgumentReg = R3;
|
||||
static const Register kSuperTypeArgumentReg = R8;
|
||||
|
||||
// Must be pushed/popped whenever generic type arguments are being checked as
|
||||
// they overlap with registers in TypeTestABI.
|
||||
static const intptr_t kSavedTypeArgumentRegisters =
|
||||
(1 << kSubTypeArgumentReg) | (1 << kSuperTypeArgumentReg);
|
||||
|
||||
static const intptr_t kInternalRegisters =
|
||||
(1 << kInstanceTypeArgumentsReg) | (1 << kScratchReg);
|
||||
((1 << kInstanceTypeArgumentsReg) | (1 << kScratchReg) |
|
||||
(1 << kSubTypeArgumentReg) | (1 << kSuperTypeArgumentReg)) &
|
||||
~kSavedTypeArgumentRegisters;
|
||||
};
|
||||
|
||||
// Registers in addition to those listed in TypeTestABI used inside the
|
||||
|
|
|
@ -172,9 +172,17 @@ struct InstantiationABI {
|
|||
struct TTSInternalRegs {
|
||||
static const Register kInstanceTypeArgumentsReg = R7;
|
||||
static const Register kScratchReg = R9;
|
||||
static const Register kSubTypeArgumentReg = R5;
|
||||
static const Register kSuperTypeArgumentReg = R6;
|
||||
|
||||
// Must be pushed/popped whenever generic type arguments are being checked as
|
||||
// they overlap with registers in TypeTestABI.
|
||||
static const intptr_t kSavedTypeArgumentRegisters = 0;
|
||||
|
||||
static const intptr_t kInternalRegisters =
|
||||
(1 << kInstanceTypeArgumentsReg) | (1 << kScratchReg);
|
||||
((1 << kInstanceTypeArgumentsReg) | (1 << kScratchReg) |
|
||||
(1 << kSubTypeArgumentReg) | (1 << kSuperTypeArgumentReg)) &
|
||||
~kSavedTypeArgumentRegisters;
|
||||
};
|
||||
|
||||
// Registers in addition to those listed in TypeTestABI used inside the
|
||||
|
|
|
@ -148,9 +148,17 @@ struct InstantiationABI {
|
|||
struct TTSInternalRegs {
|
||||
static const Register kInstanceTypeArgumentsReg = RSI;
|
||||
static const Register kScratchReg = R8;
|
||||
static const Register kSubTypeArgumentReg = R10;
|
||||
static const Register kSuperTypeArgumentReg = R13;
|
||||
|
||||
// Must be pushed/popped whenever generic type arguments are being checked as
|
||||
// they overlap with registers in TypeTestABI.
|
||||
static const intptr_t kSavedTypeArgumentRegisters = 0;
|
||||
|
||||
static const intptr_t kInternalRegisters =
|
||||
(1 << kInstanceTypeArgumentsReg) | (1 << kScratchReg);
|
||||
((1 << kInstanceTypeArgumentsReg) | (1 << kScratchReg) |
|
||||
(1 << kSubTypeArgumentReg) | (1 << kSuperTypeArgumentReg)) &
|
||||
~kSavedTypeArgumentRegisters;
|
||||
};
|
||||
|
||||
// Registers in addition to those listed in TypeTestABI used inside the
|
||||
|
|
|
@ -4,7 +4,10 @@
|
|||
|
||||
#include <functional>
|
||||
|
||||
#include "platform/globals.h"
|
||||
#include "vm/class_id.h"
|
||||
#include "vm/compiler/assembler/disassembler.h"
|
||||
#include "vm/compiler/runtime_api.h"
|
||||
#include "vm/hash_map.h"
|
||||
#include "vm/longjump.h"
|
||||
#include "vm/object_store.h"
|
||||
|
@ -329,7 +332,7 @@ void TypeTestingStubGenerator::BuildOptimizedTypeTestStubFastCases(
|
|||
|
||||
if (type.IsObjectType()) {
|
||||
ASSERT(type.IsNonNullable() &&
|
||||
IsolateGroup::Current()->use_strict_null_safety_checks());
|
||||
hi->thread()->isolate_group()->use_strict_null_safety_checks());
|
||||
compiler::Label is_null;
|
||||
__ CompareObject(TypeTestABI::kInstanceReg, Object::null_object());
|
||||
__ BranchIf(EQUAL, &is_null, compiler::Assembler::kNearJump);
|
||||
|
@ -621,6 +624,11 @@ void TypeTestingStubGenerator::
|
|||
}
|
||||
|
||||
// c) Then we'll check each value of the type argument.
|
||||
compiler::Label pop_saved_registers_on_failure;
|
||||
const RegisterSet saved_registers(
|
||||
TTSInternalRegs::kSavedTypeArgumentRegisters);
|
||||
__ PushRegisters(saved_registers);
|
||||
|
||||
AbstractType& type_arg = AbstractType::Handle();
|
||||
const TypeArguments& ta = TypeArguments::Handle(type.arguments());
|
||||
const intptr_t num_type_parameters = type_class.NumTypeParameters();
|
||||
|
@ -634,10 +642,20 @@ void TypeTestingStubGenerator::
|
|||
ASSERT(type_arg.IsTypeParameter() ||
|
||||
hi->CanUseSubtypeRangeCheckFor(type_arg));
|
||||
|
||||
BuildOptimizedTypeArgumentValueCheck(
|
||||
assembler, hi, type_arg, type_param_value_offset_i, &check_failed);
|
||||
if (type_arg.IsTypeParameter()) {
|
||||
BuildOptimizedTypeParameterArgumentValueCheck(
|
||||
assembler, hi, TypeParameter::Cast(type_arg),
|
||||
type_param_value_offset_i, &pop_saved_registers_on_failure);
|
||||
} else {
|
||||
BuildOptimizedTypeArgumentValueCheck(
|
||||
assembler, hi, Type::Cast(type_arg), type_param_value_offset_i,
|
||||
&pop_saved_registers_on_failure);
|
||||
}
|
||||
}
|
||||
__ PopRegisters(saved_registers);
|
||||
__ Ret();
|
||||
__ Bind(&pop_saved_registers_on_failure);
|
||||
__ PopRegisters(saved_registers);
|
||||
}
|
||||
|
||||
// If anything fails.
|
||||
|
@ -928,110 +946,259 @@ bool TypeTestingStubGenerator::BuildLoadInstanceTypeArguments(
|
|||
return !type_argument_checks.is_empty();
|
||||
}
|
||||
|
||||
// Unwraps TypeRefs, jumping to the appropriate label for the unwrapped type
|
||||
// if that label is not nullptr and otherwise falling through.
|
||||
//
|
||||
// [type_reg] must contain an AbstractTypePtr, and [scratch] must be distinct
|
||||
// from [type_reg]. Clobbers [type_reg] with the unwrapped type.
|
||||
static void UnwrapAbstractType(compiler::Assembler* assembler,
|
||||
Register type_reg,
|
||||
Register scratch,
|
||||
compiler::Label* is_type = nullptr,
|
||||
compiler::Label* is_function_type = nullptr,
|
||||
compiler::Label* is_type_parameter = nullptr) {
|
||||
ASSERT(scratch != type_reg);
|
||||
compiler::Label cid_checks, fall_through;
|
||||
// TypeRefs never wrap other TypeRefs, so we only need to unwrap once.
|
||||
__ LoadClassId(scratch, type_reg);
|
||||
__ CompareImmediate(scratch, kTypeRefCid);
|
||||
__ BranchIf(NOT_EQUAL, &cid_checks, compiler::Assembler::kNearJump);
|
||||
__ LoadCompressedFieldFromOffset(type_reg, type_reg,
|
||||
compiler::target::TypeRef::type_offset());
|
||||
// Only load the class id of the unwrapped type if it will be checked below.
|
||||
if (is_type != nullptr || is_function_type != nullptr ||
|
||||
is_type_parameter != nullptr) {
|
||||
__ LoadClassId(scratch, type_reg);
|
||||
}
|
||||
__ Bind(&cid_checks);
|
||||
if (is_type != nullptr) {
|
||||
__ CompareImmediate(scratch, kTypeCid);
|
||||
__ BranchIf(EQUAL, is_type);
|
||||
}
|
||||
if (is_function_type != nullptr) {
|
||||
__ CompareImmediate(scratch, kFunctionTypeCid);
|
||||
__ BranchIf(EQUAL, is_function_type);
|
||||
}
|
||||
if (is_type_parameter != nullptr) {
|
||||
__ CompareImmediate(scratch, kTypeParameterCid);
|
||||
__ BranchIf(EQUAL, is_type_parameter);
|
||||
}
|
||||
}
|
||||
|
||||
// src must contain a TypePtr. Assumes dst != src. May affect flags.
|
||||
static void LoadTypeClassId(compiler::Assembler* assembler,
|
||||
Register dst,
|
||||
Register src) {
|
||||
ASSERT(src != dst);
|
||||
__ EnsureHasClassIdInDEBUG(kTypeCid, src, dst);
|
||||
__ LoadCompressedSmi(
|
||||
dst, compiler::FieldAddress(
|
||||
src, compiler::target::Type::type_class_id_offset()));
|
||||
__ SmiUntag(dst);
|
||||
}
|
||||
|
||||
void TypeTestingStubGenerator::BuildOptimizedTypeParameterArgumentValueCheck(
|
||||
compiler::Assembler* assembler,
|
||||
HierarchyInfo* hi,
|
||||
const TypeParameter& type_param,
|
||||
intptr_t type_param_value_offset_i,
|
||||
compiler::Label* check_failed) {
|
||||
if (assembler->EmittingComments()) {
|
||||
TextBuffer buffer(128);
|
||||
buffer.Printf("Generating check for type argument %" Pd ": ",
|
||||
type_param_value_offset_i);
|
||||
type_param.PrintName(Object::kScrubbedName, &buffer);
|
||||
__ Comment("%s", buffer.buffer());
|
||||
}
|
||||
|
||||
const Register kTypeArgumentsReg =
|
||||
type_param.IsClassTypeParameter()
|
||||
? TypeTestABI::kInstantiatorTypeArgumentsReg
|
||||
: TypeTestABI::kFunctionTypeArgumentsReg;
|
||||
|
||||
const bool strict_null_safety =
|
||||
hi->thread()->isolate_group()->use_strict_null_safety_checks();
|
||||
compiler::Label is_subtype;
|
||||
// TODO(dartbug.com/46920): Currently only canonical equality (identity)
|
||||
// and some top and bottom types are checked.
|
||||
__ CompareObject(kTypeArgumentsReg, Object::null_object());
|
||||
__ BranchIf(EQUAL, &is_subtype);
|
||||
|
||||
__ LoadCompressedFieldFromOffset(
|
||||
TTSInternalRegs::kSuperTypeArgumentReg, kTypeArgumentsReg,
|
||||
compiler::target::TypeArguments::type_at_offset(type_param.index()));
|
||||
__ LoadCompressedFieldFromOffset(
|
||||
TTSInternalRegs::kSubTypeArgumentReg,
|
||||
TTSInternalRegs::kInstanceTypeArgumentsReg,
|
||||
compiler::target::TypeArguments::type_at_offset(
|
||||
type_param_value_offset_i));
|
||||
__ CompareRegisters(TTSInternalRegs::kSuperTypeArgumentReg,
|
||||
TTSInternalRegs::kSubTypeArgumentReg);
|
||||
__ BranchIf(EQUAL, &is_subtype);
|
||||
|
||||
__ Comment("Checking instantiated type parameter for possible top types");
|
||||
compiler::Label check_subtype_type_class_ids;
|
||||
UnwrapAbstractType(assembler, TTSInternalRegs::kSuperTypeArgumentReg,
|
||||
TTSInternalRegs::kScratchReg, /*is_type=*/nullptr,
|
||||
&check_subtype_type_class_ids);
|
||||
LoadTypeClassId(assembler, TTSInternalRegs::kScratchReg,
|
||||
TTSInternalRegs::kSuperTypeArgumentReg);
|
||||
__ CompareImmediate(TTSInternalRegs::kScratchReg, kDynamicCid);
|
||||
__ BranchIf(EQUAL, &is_subtype);
|
||||
__ CompareImmediate(TTSInternalRegs::kScratchReg, kVoidCid);
|
||||
__ BranchIf(EQUAL, &is_subtype);
|
||||
__ CompareImmediate(TTSInternalRegs::kScratchReg, kInstanceCid);
|
||||
if (strict_null_safety) {
|
||||
__ BranchIf(NOT_EQUAL, &check_subtype_type_class_ids);
|
||||
// If non-nullable Object, then the subtype must be legacy or non-nullable.
|
||||
__ CompareTypeNullabilityWith(
|
||||
TTSInternalRegs::kSuperTypeArgumentReg,
|
||||
static_cast<int8_t>(Nullability::kNonNullable));
|
||||
__ BranchIf(NOT_EQUAL, &is_subtype);
|
||||
__ Comment("Checking for legacy or non-nullable instance type argument");
|
||||
compiler::Label subtype_is_type;
|
||||
UnwrapAbstractType(assembler, TTSInternalRegs::kSubTypeArgumentReg,
|
||||
TTSInternalRegs::kScratchReg, &subtype_is_type);
|
||||
__ CompareFunctionTypeNullabilityWith(
|
||||
TTSInternalRegs::kSubTypeArgumentReg,
|
||||
static_cast<int8_t>(Nullability::kNullable));
|
||||
__ BranchIf(EQUAL, check_failed);
|
||||
__ Jump(&is_subtype);
|
||||
__ Bind(&subtype_is_type);
|
||||
__ CompareTypeNullabilityWith(TTSInternalRegs::kSubTypeArgumentReg,
|
||||
static_cast<int8_t>(Nullability::kNullable));
|
||||
__ BranchIf(EQUAL, check_failed);
|
||||
__ Jump(&is_subtype);
|
||||
} else {
|
||||
__ BranchIf(EQUAL, &is_subtype, compiler::Assembler::kNearJump);
|
||||
}
|
||||
|
||||
__ Bind(&check_subtype_type_class_ids);
|
||||
__ Comment("Checking instance type argument for possible bottom types");
|
||||
// Nothing else to check for non-Types, so fall back to the slow stub.
|
||||
UnwrapAbstractType(assembler, TTSInternalRegs::kSubTypeArgumentReg,
|
||||
TTSInternalRegs::kScratchReg, /*is_type=*/nullptr,
|
||||
check_failed);
|
||||
LoadTypeClassId(assembler, TTSInternalRegs::kScratchReg,
|
||||
TTSInternalRegs::kSubTypeArgumentReg);
|
||||
__ CompareImmediate(TTSInternalRegs::kScratchReg, kNeverCid);
|
||||
__ BranchIf(EQUAL, &is_subtype);
|
||||
__ CompareImmediate(TTSInternalRegs::kScratchReg, kNullCid);
|
||||
// Last possible check, so fall back to slow stub on failure.
|
||||
__ BranchIf(NOT_EQUAL, check_failed);
|
||||
if (strict_null_safety) {
|
||||
// Only nullable or legacy types can be a supertype of Null.
|
||||
__ Comment("Checking for legacy or nullable instantiated type parameter");
|
||||
compiler::Label supertype_is_type;
|
||||
UnwrapAbstractType(assembler, TTSInternalRegs::kSuperTypeArgumentReg,
|
||||
TTSInternalRegs::kScratchReg, &supertype_is_type);
|
||||
__ CompareFunctionTypeNullabilityWith(
|
||||
TTSInternalRegs::kSuperTypeArgumentReg,
|
||||
static_cast<int8_t>(Nullability::kNonNullable));
|
||||
__ BranchIf(EQUAL, check_failed);
|
||||
__ Jump(&is_subtype, compiler::Assembler::kNearJump);
|
||||
__ Bind(&supertype_is_type);
|
||||
__ CompareTypeNullabilityWith(
|
||||
TTSInternalRegs::kSuperTypeArgumentReg,
|
||||
static_cast<int8_t>(Nullability::kNonNullable));
|
||||
__ BranchIf(EQUAL, check_failed);
|
||||
}
|
||||
|
||||
__ Bind(&is_subtype);
|
||||
}
|
||||
|
||||
// Generate code to verify that instance's type argument is a subtype of
|
||||
// 'type_arg'.
|
||||
void TypeTestingStubGenerator::BuildOptimizedTypeArgumentValueCheck(
|
||||
compiler::Assembler* assembler,
|
||||
HierarchyInfo* hi,
|
||||
const AbstractType& type_arg,
|
||||
const Type& type,
|
||||
intptr_t type_param_value_offset_i,
|
||||
compiler::Label* check_failed) {
|
||||
if (type_arg.IsTopTypeForSubtyping()) {
|
||||
ASSERT(type.IsInstantiated());
|
||||
if (type.IsTopTypeForSubtyping()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool strict_null_safety =
|
||||
hi->thread()->isolate_group()->use_strict_null_safety_checks();
|
||||
ASSERT(!type.IsObjectType() || (strict_null_safety && type.IsNonNullable()));
|
||||
|
||||
if (assembler->EmittingComments()) {
|
||||
TextBuffer buffer(128);
|
||||
buffer.Printf("Generating check for type argument %" Pd ": ",
|
||||
type_param_value_offset_i);
|
||||
type_arg.PrintName(Object::kScrubbedName, &buffer);
|
||||
type.PrintName(Object::kScrubbedName, &buffer);
|
||||
__ Comment("%s", buffer.buffer());
|
||||
}
|
||||
if (type_arg.IsTypeParameter()) {
|
||||
const TypeParameter& type_param = TypeParameter::Cast(type_arg);
|
||||
const Register kTypeArgumentsReg =
|
||||
type_param.IsClassTypeParameter()
|
||||
? TypeTestABI::kInstantiatorTypeArgumentsReg
|
||||
: TypeTestABI::kFunctionTypeArgumentsReg;
|
||||
|
||||
compiler::Label is_dynamic;
|
||||
__ CompareObject(kTypeArgumentsReg, Object::null_object());
|
||||
__ BranchIf(EQUAL, &is_dynamic, compiler::Assembler::kNearJump);
|
||||
|
||||
// TODO(dartbug.com/46920): Currently only canonical equality (identity)
|
||||
// is checked.
|
||||
__ LoadCompressedFieldFromOffset(
|
||||
TTSInternalRegs::kScratchReg, kTypeArgumentsReg,
|
||||
compiler::target::TypeArguments::type_at_offset(type_param.index()));
|
||||
__ CompareWithCompressedFieldFromOffset(
|
||||
TTSInternalRegs::kScratchReg,
|
||||
TTSInternalRegs::kInstanceTypeArgumentsReg,
|
||||
compiler::target::TypeArguments::type_at_offset(
|
||||
type_param_value_offset_i));
|
||||
__ BranchIf(NOT_EQUAL, check_failed);
|
||||
__ Bind(&is_dynamic);
|
||||
} else {
|
||||
__ LoadCompressedFieldFromOffset(
|
||||
TTSInternalRegs::kScratchReg,
|
||||
TTSInternalRegs::kInstanceTypeArgumentsReg,
|
||||
compiler::target::TypeArguments::type_at_offset(
|
||||
type_param_value_offset_i));
|
||||
if (type_arg.IsObjectType()) {
|
||||
// Just check the nullability, since this must be non-nullable Object
|
||||
// and we must be in null safe mode.
|
||||
ASSERT(IsolateGroup::Current()->use_strict_null_safety_checks() &&
|
||||
type_arg.IsNonNullable());
|
||||
__ CompareTypeNullabilityWith(TTSInternalRegs::kScratchReg,
|
||||
compiler::target::Nullability::kNullable);
|
||||
compiler::Label is_subtype, check_subtype_cid, sub_is_function_type,
|
||||
sub_is_type;
|
||||
__ LoadCompressedFieldFromOffset(
|
||||
TTSInternalRegs::kSubTypeArgumentReg,
|
||||
TTSInternalRegs::kInstanceTypeArgumentsReg,
|
||||
compiler::target::TypeArguments::type_at_offset(
|
||||
type_param_value_offset_i));
|
||||
__ Bind(&check_subtype_cid);
|
||||
UnwrapAbstractType(assembler, TTSInternalRegs::kSubTypeArgumentReg,
|
||||
TTSInternalRegs::kScratchReg, &sub_is_type);
|
||||
__ Comment("Checks for FunctionType");
|
||||
__ EnsureHasClassIdInDEBUG(kFunctionTypeCid,
|
||||
TTSInternalRegs::kSubTypeArgumentReg,
|
||||
TTSInternalRegs::kScratchReg);
|
||||
if (type.IsObjectType() || type.IsDartFunctionType()) {
|
||||
if (strict_null_safety && type.IsNonNullable()) {
|
||||
// Nullable types cannot be a subtype of a non-nullable type.
|
||||
__ CompareFunctionTypeNullabilityWith(
|
||||
TTSInternalRegs::kSubTypeArgumentReg,
|
||||
compiler::target::Nullability::kNullable);
|
||||
__ BranchIf(EQUAL, check_failed);
|
||||
} else {
|
||||
__ LoadCompressedFieldFromOffset(
|
||||
TTSInternalRegs::kScratchReg, TTSInternalRegs::kScratchReg,
|
||||
compiler::target::Type::type_class_id_offset());
|
||||
|
||||
const Class& type_class = Class::Handle(type_arg.type_class());
|
||||
const bool null_is_assignable = Instance::NullIsAssignableTo(type_arg);
|
||||
const CidRangeVector& ranges =
|
||||
hi->SubtypeRangesForClass(type_class,
|
||||
/*include_abstract=*/true,
|
||||
/*exclude_null=*/!null_is_assignable);
|
||||
|
||||
compiler::Label is_subtype;
|
||||
__ SmiUntag(TTSInternalRegs::kScratchReg);
|
||||
// Never is a bottom type.
|
||||
__ CompareImmediate(TTSInternalRegs::kScratchReg, kNeverCid);
|
||||
__ BranchIf(EQUAL, &is_subtype);
|
||||
if (null_is_assignable) {
|
||||
__ CompareImmediate(TTSInternalRegs::kScratchReg, kNullCid);
|
||||
__ BranchIf(EQUAL, &is_subtype);
|
||||
}
|
||||
compiler::Label check_nullability;
|
||||
BuildOptimizedSubtypeRangeCheck(assembler, ranges,
|
||||
TTSInternalRegs::kScratchReg,
|
||||
&check_nullability, check_failed);
|
||||
__ Bind(&check_nullability);
|
||||
|
||||
// Weak NNBD mode uses LEGACY_SUBTYPE which ignores nullability.
|
||||
// We don't need to check nullability of LHS for nullable and legacy RHS
|
||||
// ("Right Legacy", "Right Nullable" rules).
|
||||
if (IsolateGroup::Current()->use_strict_null_safety_checks() &&
|
||||
type_arg.IsNonNullable()) {
|
||||
// Nullable type is not a subtype of non-nullable type.
|
||||
// TODO(dartbug.com/40736): Allocate a register for instance type
|
||||
// argument and avoid reloading it.
|
||||
__ LoadCompressedFieldFromOffset(
|
||||
TTSInternalRegs::kScratchReg,
|
||||
TTSInternalRegs::kInstanceTypeArgumentsReg,
|
||||
compiler::target::TypeArguments::type_at_offset(
|
||||
type_param_value_offset_i));
|
||||
__ CompareTypeNullabilityWith(TTSInternalRegs::kScratchReg,
|
||||
compiler::target::Nullability::kNullable);
|
||||
__ BranchIf(EQUAL, check_failed);
|
||||
}
|
||||
|
||||
__ Bind(&is_subtype);
|
||||
}
|
||||
// No further checks needed for non-nullable Object or Function.
|
||||
__ Jump(&is_subtype, compiler::Assembler::kNearJump);
|
||||
} else {
|
||||
// _Closure <: Function, and T <: Function for any FunctionType T, but
|
||||
// T </: _Closure, so we _don't_ want to fall back to cid tests. Instead,
|
||||
// just let the STC/runtime handle any possible false negatives here.
|
||||
__ Jump(check_failed);
|
||||
}
|
||||
|
||||
__ Comment("Checks for Type");
|
||||
__ Bind(&sub_is_type);
|
||||
if (strict_null_safety && type.IsNonNullable()) {
|
||||
// Nullable types cannot be a subtype of a non-nullable type in strict mode.
|
||||
__ CompareTypeNullabilityWith(TTSInternalRegs::kSubTypeArgumentReg,
|
||||
compiler::target::Nullability::kNullable);
|
||||
__ BranchIf(EQUAL, check_failed);
|
||||
// Fall through to bottom type checks.
|
||||
}
|
||||
|
||||
// No further checks needed for non-nullable object.
|
||||
if (!type.IsObjectType()) {
|
||||
LoadTypeClassId(assembler, TTSInternalRegs::kScratchReg,
|
||||
TTSInternalRegs::kSubTypeArgumentReg);
|
||||
|
||||
const bool null_is_assignable = Instance::NullIsAssignableTo(type);
|
||||
// Check bottom types.
|
||||
__ CompareImmediate(TTSInternalRegs::kScratchReg, kNeverCid);
|
||||
__ BranchIf(EQUAL, &is_subtype);
|
||||
if (null_is_assignable) {
|
||||
__ CompareImmediate(TTSInternalRegs::kScratchReg, kNullCid);
|
||||
__ BranchIf(EQUAL, &is_subtype);
|
||||
}
|
||||
|
||||
// Not a bottom type, so check cid ranges.
|
||||
const Class& type_class = Class::Handle(type.type_class());
|
||||
const CidRangeVector& ranges =
|
||||
hi->SubtypeRangesForClass(type_class,
|
||||
/*include_abstract=*/true,
|
||||
/*exclude_null=*/!null_is_assignable);
|
||||
BuildOptimizedSubtypeRangeCheck(assembler, ranges,
|
||||
TTSInternalRegs::kScratchReg, &is_subtype,
|
||||
check_failed);
|
||||
}
|
||||
|
||||
__ Bind(&is_subtype);
|
||||
}
|
||||
|
||||
void RegisterTypeArgumentsUse(const Function& function,
|
||||
|
|
|
@ -103,10 +103,17 @@ class TypeTestingStubGenerator {
|
|||
compiler::Label* load_succeeded,
|
||||
compiler::Label* load_failed);
|
||||
|
||||
static void BuildOptimizedTypeParameterArgumentValueCheck(
|
||||
compiler::Assembler* assembler,
|
||||
HierarchyInfo* hi,
|
||||
const TypeParameter& type_param,
|
||||
intptr_t type_param_value_offset_i,
|
||||
compiler::Label* check_failed);
|
||||
|
||||
static void BuildOptimizedTypeArgumentValueCheck(
|
||||
compiler::Assembler* assembler,
|
||||
HierarchyInfo* hi,
|
||||
const AbstractType& type_arg,
|
||||
const Type& type,
|
||||
intptr_t type_param_value_offset_i,
|
||||
compiler::Label* check_failed);
|
||||
|
||||
|
|
|
@ -20,16 +20,16 @@
|
|||
|
||||
namespace dart {
|
||||
|
||||
// FLAG_trace_type_checks is only a non-constant in DEBUG mode, so we only
|
||||
// allow tracing of type testing stub tests there.
|
||||
#if defined(DEBUG)
|
||||
// Note that flags that this affects may only mutable in some modes, e.g.,
|
||||
// tracing type checks can only be done in DEBUG mode.
|
||||
DEFINE_FLAG(bool,
|
||||
trace_type_testing_stub_tests,
|
||||
false,
|
||||
"Trace type checks performed in type testing stub tests");
|
||||
#else
|
||||
const bool FLAG_trace_type_testing_stub_tests = false;
|
||||
#endif
|
||||
"Trace type testing stub tests");
|
||||
DEFINE_FLAG(bool,
|
||||
print_type_testing_stub_test_headers,
|
||||
true,
|
||||
"Print headers for executed type testing stub tests");
|
||||
|
||||
class TraceStubInvocationScope : public ValueObject {
|
||||
public:
|
||||
|
@ -351,7 +351,9 @@ class TTSTestState : public ValueObject {
|
|||
new_tts_stub_(Code::Handle(zone())),
|
||||
last_stc_(SubtypeTestCache::Handle(zone())),
|
||||
last_result_(Object::Handle(zone())) {
|
||||
THR_Print("Creating test state for type %s\n", type.ToCString());
|
||||
if (FLAG_print_type_testing_stub_test_headers) {
|
||||
THR_Print("Creating test state for type %s\n", type.ToCString());
|
||||
}
|
||||
}
|
||||
|
||||
Zone* zone() const { return thread_->zone(); }
|
||||
|
@ -379,17 +381,20 @@ class TTSTestState : public ValueObject {
|
|||
const auto& default_stub =
|
||||
Code::Handle(zone(), TypeTestingStubGenerator::DefaultCodeForType(
|
||||
last_tested_type_, /*lazy_specialize=*/false));
|
||||
previous_tts_stub_ =
|
||||
TypeTestingStubGenerator::SpecializeStubFor(thread_, last_tested_type_);
|
||||
{
|
||||
// To make sure we output the disassembled stub if desired.
|
||||
TraceStubInvocationScope scope;
|
||||
previous_tts_stub_ = TypeTestingStubGenerator::SpecializeStubFor(
|
||||
thread_, last_tested_type_);
|
||||
}
|
||||
EXPECT_EQ(test_case.should_specialize,
|
||||
previous_tts_stub_.ptr() != default_stub.ptr());
|
||||
last_tested_type_.SetTypeTestingStub(previous_tts_stub_);
|
||||
PrintInvocationHeader(test_case);
|
||||
PrintInvocationHeader("eagerly specialized", test_case);
|
||||
InvokeStubHelper(test_case);
|
||||
// Treat it as a failure if the stub respecializes, since we're attempting
|
||||
// to simulate AOT mode.
|
||||
EXPECT(previous_tts_stub_.ptr() == new_tts_stub_.ptr());
|
||||
ReportUnexpectedSTCChanges(test_case);
|
||||
}
|
||||
|
||||
void InvokeLazilySpecializedStub(const TTSTestCase& test_case) {
|
||||
|
@ -401,8 +406,8 @@ class TTSTestState : public ValueObject {
|
|||
const auto& specializing_stub =
|
||||
Code::Handle(zone(), TypeTestingStubGenerator::DefaultCodeForType(
|
||||
last_tested_type_, /*lazy_specialize=*/true));
|
||||
PrintInvocationHeader(test_case);
|
||||
last_tested_type_.SetTypeTestingStub(specializing_stub);
|
||||
PrintInvocationHeader("lazy specialized", test_case);
|
||||
InvokeStubHelper(test_case,
|
||||
/*is_lazy_specialization=*/test_case.should_specialize);
|
||||
if (test_case.should_fail || test_case.instance.IsNull()) {
|
||||
|
@ -417,18 +422,15 @@ class TTSTestState : public ValueObject {
|
|||
// Non-specializing test cases should result in a default TTS.
|
||||
EXPECT(new_tts_stub_.ptr() == default_stub.ptr());
|
||||
}
|
||||
ReportUnexpectedSTCChanges(
|
||||
test_case, /*is_lazy_specialization=*/test_case.should_specialize);
|
||||
}
|
||||
|
||||
void InvokeExistingStub(const TTSTestCase& test_case) {
|
||||
last_tested_type_ = TypeToTest(test_case);
|
||||
PrintInvocationHeader(test_case);
|
||||
PrintInvocationHeader("existing", test_case);
|
||||
InvokeStubHelper(test_case);
|
||||
// Only respecialization should result in a new stub.
|
||||
EXPECT_EQ(test_case.should_respecialize,
|
||||
previous_tts_stub_.ptr() != new_tts_stub_.ptr());
|
||||
ReportUnexpectedSTCChanges(test_case);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -443,12 +445,14 @@ class TTSTestState : public ValueObject {
|
|||
return Smi::RawCast(modified_rest_regs_box_.At(0));
|
||||
}
|
||||
|
||||
void PrintInvocationHeader(const TTSTestCase& test_case) {
|
||||
void PrintInvocationHeader(const char* stub_type,
|
||||
const TTSTestCase& test_case) {
|
||||
if (!FLAG_print_type_testing_stub_test_headers) return;
|
||||
LogBlock lb;
|
||||
const auto& tts = Code::Handle(zone(), last_tested_type_.type_test_stub());
|
||||
auto* const stub_name = StubCode::NameOfStub(tts.EntryPoint());
|
||||
THR_Print("Testing %s stub for type %s\n",
|
||||
stub_name == nullptr ? "optimized" : stub_name,
|
||||
THR_Print("Testing %s %s stub for type %s\n",
|
||||
stub_name == nullptr ? "optimized" : stub_name, stub_type,
|
||||
last_tested_type_.ToCString());
|
||||
if (last_tested_type_.ptr() != type_.ptr()) {
|
||||
THR_Print(" Original type: %s\n", type_.ToCString());
|
||||
|
@ -523,8 +527,8 @@ class TTSTestState : public ValueObject {
|
|||
}
|
||||
new_tts_stub_ = last_tested_type_.type_test_stub();
|
||||
last_stc_ = current_stc();
|
||||
EXPECT_EQ(test_case.should_fail, !last_result_.IsNull());
|
||||
if (test_case.should_fail) {
|
||||
EXPECT(!last_result_.IsNull());
|
||||
EXPECT(last_result_.IsError());
|
||||
EXPECT(last_result_.IsUnhandledException());
|
||||
if (last_result_.IsUnhandledException()) {
|
||||
|
@ -533,16 +537,28 @@ class TTSTestState : public ValueObject {
|
|||
EXPECT(strstr(error.ToCString(), "_TypeError"));
|
||||
}
|
||||
} else {
|
||||
EXPECT(new_tts_stub_.ptr() != StubCode::LazySpecializeTypeTest().ptr());
|
||||
ReportModifiedRegisters(modified_abi_regs());
|
||||
// If we shouldn't go to the runtime, report any unexpected changes in
|
||||
// non-ABI registers.
|
||||
if (!is_lazy_specialization && !test_case.should_respecialize &&
|
||||
(!test_case.should_be_false_negative ||
|
||||
test_case.HasSTCEntry(previous_stc_, type_))) {
|
||||
ReportModifiedRegisters(modified_rest_regs());
|
||||
EXPECT(last_result_.IsNull());
|
||||
if (!last_result_.IsNull()) {
|
||||
EXPECT(last_result_.IsError());
|
||||
EXPECT(last_result_.IsUnhandledException());
|
||||
if (last_result_.IsUnhandledException()) {
|
||||
const auto& exception = UnhandledException::Cast(last_result_);
|
||||
dart::Expect(__FILE__, __LINE__)
|
||||
.Fail("%s", exception.ToErrorCString());
|
||||
}
|
||||
} else {
|
||||
EXPECT(new_tts_stub_.ptr() != StubCode::LazySpecializeTypeTest().ptr());
|
||||
ReportModifiedRegisters(modified_abi_regs());
|
||||
// If we shouldn't go to the runtime, report any unexpected changes in
|
||||
// non-ABI registers.
|
||||
if (!is_lazy_specialization && !test_case.should_respecialize &&
|
||||
(!test_case.should_be_false_negative ||
|
||||
test_case.HasSTCEntry(previous_stc_, type_))) {
|
||||
ReportModifiedRegisters(modified_rest_regs());
|
||||
}
|
||||
}
|
||||
}
|
||||
ReportUnexpectedSTCChanges(test_case, is_lazy_specialization);
|
||||
}
|
||||
|
||||
static void ReportModifiedRegisters(SmiPtr encoded_reg_mask) {
|
||||
|
@ -595,6 +611,7 @@ class TTSTestState : public ValueObject {
|
|||
|
||||
void ReportUnexpectedSTCChanges(const TTSTestCase& test_case,
|
||||
bool is_lazy_specialization = false) {
|
||||
// Make sure should_be_false_negative is not set if respecialization is.
|
||||
ASSERT(!test_case.should_be_false_negative ||
|
||||
!test_case.should_respecialize);
|
||||
const bool had_stc_entry = test_case.HasSTCEntry(previous_stc_, type_);
|
||||
|
@ -872,20 +889,22 @@ ISOLATE_UNIT_TEST_CASE(TTS_SubtypeRangeCheck) {
|
|||
RunTTSTest(type_i_object_dynamic, Failure({obj_b1, tav_null, tav_null}));
|
||||
RunTTSTest(type_i_object_dynamic, {obj_b2, tav_null, tav_null});
|
||||
|
||||
// We do not generate TTS for uninstantiated types if we would need to use
|
||||
// subtype range checks for the class of the interface type.
|
||||
// We do generate TTSes for uninstantiated types when we need to use
|
||||
// subtype range checks for the class of the interface type, but the TTS
|
||||
// may be partial (returns a false negative in some cases that means going
|
||||
// to the STC/runtime).
|
||||
//
|
||||
// obj as I<dynamic, T>
|
||||
//
|
||||
auto& type_dynamic_t =
|
||||
AbstractType::Handle(Type::New(class_i, tav_dynamic_t));
|
||||
FinalizeAndCanonicalize(&type_dynamic_t);
|
||||
RunTTSTest(type_dynamic_t, FalseNegative({obj_i, tav_object, tav_null}));
|
||||
RunTTSTest(type_dynamic_t, {obj_i, tav_object, tav_null});
|
||||
RunTTSTest(type_dynamic_t, Failure({obj_i2, tav_object, tav_null}));
|
||||
RunTTSTest(type_dynamic_t, Failure({obj_base_int, tav_object, tav_null}));
|
||||
RunTTSTest(type_dynamic_t, Failure({obj_a, tav_object, tav_null}));
|
||||
RunTTSTest(type_dynamic_t, Failure({obj_a1, tav_object, tav_null}));
|
||||
RunTTSTest(type_dynamic_t, FalseNegative({obj_a2, tav_object, tav_null}));
|
||||
RunTTSTest(type_dynamic_t, {obj_a2, tav_object, tav_null});
|
||||
RunTTSTest(type_dynamic_t, Failure({obj_b, tav_object, tav_null}));
|
||||
RunTTSTest(type_dynamic_t, Failure({obj_b1, tav_object, tav_null}));
|
||||
RunTTSTest(type_dynamic_t, FalseNegative({obj_b2, tav_object, tav_null}));
|
||||
|
@ -1116,24 +1135,52 @@ ISOLATE_UNIT_TEST_CASE(TTS_Future) {
|
|||
R"(
|
||||
import "dart:async";
|
||||
|
||||
createFutureInt() => (() async => 3)();
|
||||
Future<int> createFutureInt() async => 3;
|
||||
Future<int Function()> createFutureFunction() async => () => 3;
|
||||
Future<int Function()?> createFutureNullableFunction() async =>
|
||||
(() => 3) as int Function()?;
|
||||
)";
|
||||
|
||||
const auto& class_future =
|
||||
Class::Handle(IsolateGroup::Current()->object_store()->future_class());
|
||||
|
||||
const auto& root_library = Library::Handle(LoadTestScript(kScript));
|
||||
const auto& class_future = Class::Handle(GetClass(root_library, "Future"));
|
||||
const auto& class_closure =
|
||||
Class::Handle(IsolateGroup::Current()->object_store()->closure_class());
|
||||
const auto& obj_futureint =
|
||||
Object::Handle(Invoke(root_library, "createFutureInt"));
|
||||
|
||||
const auto& type_nullable_object = Type::Handle(
|
||||
IsolateGroup::Current()->object_store()->nullable_object_type());
|
||||
const auto& type_int = Type::Handle(Type::IntType());
|
||||
const auto& type_string = Type::Handle(Type::StringType());
|
||||
const auto& type_num = Type::Handle(Type::Number());
|
||||
const auto& obj_futurefunction =
|
||||
Object::Handle(Invoke(root_library, "createFutureFunction"));
|
||||
const auto& obj_futurenullablefunction =
|
||||
Object::Handle(Invoke(root_library, "createFutureNullableFunction"));
|
||||
|
||||
const auto& tav_null = Object::null_type_arguments();
|
||||
const auto& type_object = Type::Handle(
|
||||
IsolateGroup::Current()->object_store()->non_nullable_object_type());
|
||||
const auto& type_legacy_object = Type::Handle(
|
||||
IsolateGroup::Current()->object_store()->legacy_object_type());
|
||||
const auto& type_nullable_object = Type::Handle(
|
||||
IsolateGroup::Current()->object_store()->nullable_object_type());
|
||||
const auto& type_int = Type::Handle(
|
||||
IsolateGroup::Current()->object_store()->non_nullable_int_type());
|
||||
|
||||
auto& type_string = Type::Handle(Type::StringType());
|
||||
type_string =
|
||||
type_string.ToNullability(Nullability::kNonNullable, Heap::kNew);
|
||||
FinalizeAndCanonicalize(&type_string);
|
||||
auto& type_num = Type::Handle(Type::Number());
|
||||
type_num = type_num.ToNullability(Nullability::kNonNullable, Heap::kNew);
|
||||
FinalizeAndCanonicalize(&type_num);
|
||||
|
||||
auto& tav_dynamic = TypeArguments::Handle(TypeArguments::New(1));
|
||||
tav_dynamic.SetTypeAt(0, Object::dynamic_type());
|
||||
CanonicalizeTAV(&tav_dynamic);
|
||||
auto& tav_object = TypeArguments::Handle(TypeArguments::New(1));
|
||||
tav_object.SetTypeAt(0, type_object);
|
||||
CanonicalizeTAV(&tav_object);
|
||||
auto& tav_legacy_object = TypeArguments::Handle(TypeArguments::New(1));
|
||||
tav_legacy_object.SetTypeAt(0, type_legacy_object);
|
||||
CanonicalizeTAV(&tav_legacy_object);
|
||||
auto& tav_nullable_object = TypeArguments::Handle(TypeArguments::New(1));
|
||||
tav_nullable_object.SetTypeAt(0, type_nullable_object);
|
||||
CanonicalizeTAV(&tav_nullable_object);
|
||||
|
@ -1147,45 +1194,422 @@ ISOLATE_UNIT_TEST_CASE(TTS_Future) {
|
|||
tav_string.SetTypeAt(0, type_string);
|
||||
CanonicalizeTAV(&tav_string);
|
||||
|
||||
auto& type_future = Type::Handle(Type::New(class_future, tav_null));
|
||||
auto& type_future = Type::Handle(
|
||||
Type::New(class_future, tav_null, Nullability::kNonNullable));
|
||||
FinalizeAndCanonicalize(&type_future);
|
||||
auto& type_future_dynamic =
|
||||
Type::Handle(Type::New(class_future, tav_dynamic));
|
||||
auto& type_future_dynamic = Type::Handle(
|
||||
Type::New(class_future, tav_dynamic, Nullability::kNonNullable));
|
||||
FinalizeAndCanonicalize(&type_future_dynamic);
|
||||
auto& type_future_nullable_object =
|
||||
Type::Handle(Type::New(class_future, tav_nullable_object));
|
||||
auto& type_future_object = Type::Handle(
|
||||
Type::New(class_future, tav_object, Nullability::kNonNullable));
|
||||
FinalizeAndCanonicalize(&type_future_object);
|
||||
auto& type_future_legacy_object = Type::Handle(
|
||||
Type::New(class_future, tav_legacy_object, Nullability::kNonNullable));
|
||||
FinalizeAndCanonicalize(&type_future_legacy_object);
|
||||
auto& type_future_nullable_object = Type::Handle(
|
||||
Type::New(class_future, tav_nullable_object, Nullability::kNonNullable));
|
||||
FinalizeAndCanonicalize(&type_future_nullable_object);
|
||||
auto& type_future_int = Type::Handle(Type::New(class_future, tav_int));
|
||||
auto& type_future_int =
|
||||
Type::Handle(Type::New(class_future, tav_int, Nullability::kNonNullable));
|
||||
FinalizeAndCanonicalize(&type_future_int);
|
||||
auto& type_future_string = Type::Handle(Type::New(class_future, tav_string));
|
||||
auto& type_future_string = Type::Handle(
|
||||
Type::New(class_future, tav_string, Nullability::kNonNullable));
|
||||
FinalizeAndCanonicalize(&type_future_string);
|
||||
auto& type_future_num = Type::Handle(Type::New(class_future, tav_num));
|
||||
auto& type_future_num =
|
||||
Type::Handle(Type::New(class_future, tav_num, Nullability::kNonNullable));
|
||||
FinalizeAndCanonicalize(&type_future_num);
|
||||
auto& type_future_t = Type::Handle(class_future.DeclarationType());
|
||||
const auto& type_future_t = Type::Handle(class_future.DeclarationType());
|
||||
|
||||
THR_Print("********************************************************\n");
|
||||
THR_Print(" Testing Future<int>\n");
|
||||
THR_Print("********************************************************\n\n");
|
||||
|
||||
// Some more tests of generic implemented classes, using Future. Here,
|
||||
// obj is an object of type Future<int>.
|
||||
//
|
||||
// obj as Future : Null type args (caught by TTS)
|
||||
// True positives from TTS:
|
||||
// obj as Future : Null type args
|
||||
// obj as Future<dynamic> : Canonicalized to same as previous case.
|
||||
// obj as Future<Object?> : Type arg is top type (caught by TTS)
|
||||
// obj as Future<int> : Type arg is the same type (caught by TTS)
|
||||
// obj as Future<String> : Type arg is not a subtype (error via runtime)
|
||||
// obj as Future<Object?> : Type arg is top type
|
||||
// obj as Future<Object*> : Type arg is top type
|
||||
// obj as Future<Object> : Type arg is certain supertype
|
||||
// obj as Future<int> : Type arg is the same type
|
||||
// obj as Future<num> : Type arg is a supertype that can be matched
|
||||
// with cid range (caught by TTS)
|
||||
// with cid range
|
||||
// obj as Future<X>, : Type arg is a type parameter instantiated with
|
||||
// X = int : ... the same type (caught by TTS)
|
||||
// X = String : ... an unrelated type (error via runtime)
|
||||
// X = num : ... a supertype (caught by STC/runtime)
|
||||
// X = int : ... the same type
|
||||
//
|
||||
RunTTSTest(type_future, {obj_futureint, tav_null, tav_null});
|
||||
RunTTSTest(type_future_dynamic, {obj_futureint, tav_null, tav_null});
|
||||
RunTTSTest(type_future_object, {obj_futureint, tav_null, tav_null});
|
||||
RunTTSTest(type_future_legacy_object, {obj_futureint, tav_null, tav_null});
|
||||
RunTTSTest(type_future_nullable_object, {obj_futureint, tav_null, tav_null});
|
||||
RunTTSTest(type_future_int, {obj_futureint, tav_null, tav_null});
|
||||
RunTTSTest(type_future_string, Failure({obj_futureint, tav_null, tav_null}));
|
||||
RunTTSTest(type_future_num, {obj_futureint, tav_null, tav_null});
|
||||
RunTTSTest(type_future_t, {obj_futureint, tav_int, tav_null});
|
||||
RunTTSTest(type_future_t, Failure({obj_futureint, tav_string, tav_null}));
|
||||
|
||||
// False negatives from TTS (caught by STC/runtime):
|
||||
// obj as Future<X>, : Type arg is a type parameter instantiated with
|
||||
// X = num : ... a supertype
|
||||
RunTTSTest(type_future_t, FalseNegative({obj_futureint, tav_num, tav_null}));
|
||||
|
||||
// Errors:
|
||||
// obj as Future<String> : Type arg is not a supertype
|
||||
// obj as Future<X>, : Type arg is a type parameter instantiated with
|
||||
// X = String : ... an unrelated type
|
||||
//
|
||||
RunTTSTest(type_future_string, Failure({obj_futureint, tav_null, tav_null}));
|
||||
RunTTSTest(type_future_t, Failure({obj_futureint, tav_string, tav_null}));
|
||||
|
||||
auto& type_function = Type::Handle(Type::DartFunctionType());
|
||||
type_function =
|
||||
type_function.ToNullability(Nullability::kNonNullable, Heap::kNew);
|
||||
FinalizeAndCanonicalize(&type_function);
|
||||
auto& type_legacy_function = Type::Handle(
|
||||
type_function.ToNullability(Nullability::kLegacy, Heap::kNew));
|
||||
FinalizeAndCanonicalize(&type_legacy_function);
|
||||
auto& type_nullable_function = Type::Handle(
|
||||
type_function.ToNullability(Nullability::kNullable, Heap::kOld));
|
||||
FinalizeAndCanonicalize(&type_nullable_function);
|
||||
auto& type_closure = Type::Handle(
|
||||
Type::New(class_closure, tav_null, Nullability::kNonNullable));
|
||||
FinalizeAndCanonicalize(&type_closure);
|
||||
auto& type_legacy_closure = Type::Handle(
|
||||
type_closure.ToNullability(Nullability::kLegacy, Heap::kOld));
|
||||
FinalizeAndCanonicalize(&type_legacy_closure);
|
||||
auto& type_nullable_closure = Type::Handle(
|
||||
type_closure.ToNullability(Nullability::kNullable, Heap::kOld));
|
||||
FinalizeAndCanonicalize(&type_nullable_closure);
|
||||
auto& type_function_int_nullary =
|
||||
FunctionType::Handle(FunctionType::New(0, Nullability::kNonNullable));
|
||||
// Testing with a closure, so it has an implicit parameter, and we want a
|
||||
// type that is canonically equal to the type of the closure.
|
||||
type_function_int_nullary.set_num_implicit_parameters(1);
|
||||
type_function_int_nullary.set_num_fixed_parameters(1);
|
||||
type_function_int_nullary.set_parameter_types(Array::Handle(Array::New(1)));
|
||||
type_function_int_nullary.SetParameterTypeAt(0, Type::dynamic_type());
|
||||
type_function_int_nullary.set_result_type(type_int);
|
||||
FinalizeAndCanonicalize(&type_function_int_nullary);
|
||||
auto& type_legacy_function_int_nullary =
|
||||
FunctionType::Handle(type_function_int_nullary.ToNullability(
|
||||
Nullability::kLegacy, Heap::kOld));
|
||||
FinalizeAndCanonicalize(&type_legacy_function_int_nullary);
|
||||
auto& type_nullable_function_int_nullary =
|
||||
FunctionType::Handle(type_function_int_nullary.ToNullability(
|
||||
Nullability::kNullable, Heap::kOld));
|
||||
FinalizeAndCanonicalize(&type_nullable_function_int_nullary);
|
||||
|
||||
auto& tav_function = TypeArguments::Handle(TypeArguments::New(1));
|
||||
tav_function.SetTypeAt(0, type_function);
|
||||
CanonicalizeTAV(&tav_function);
|
||||
auto& tav_legacy_function = TypeArguments::Handle(TypeArguments::New(1));
|
||||
tav_legacy_function.SetTypeAt(0, type_legacy_function);
|
||||
CanonicalizeTAV(&tav_legacy_function);
|
||||
auto& tav_nullable_function = TypeArguments::Handle(TypeArguments::New(1));
|
||||
tav_nullable_function.SetTypeAt(0, type_nullable_function);
|
||||
CanonicalizeTAV(&tav_nullable_function);
|
||||
auto& tav_closure = TypeArguments::Handle(TypeArguments::New(1));
|
||||
tav_closure.SetTypeAt(0, type_closure);
|
||||
CanonicalizeTAV(&tav_closure);
|
||||
auto& tav_legacy_closure = TypeArguments::Handle(TypeArguments::New(1));
|
||||
tav_legacy_closure.SetTypeAt(0, type_legacy_closure);
|
||||
CanonicalizeTAV(&tav_legacy_closure);
|
||||
auto& tav_nullable_closure = TypeArguments::Handle(TypeArguments::New(1));
|
||||
tav_nullable_closure.SetTypeAt(0, type_nullable_closure);
|
||||
CanonicalizeTAV(&tav_nullable_closure);
|
||||
auto& tav_function_int_nullary = TypeArguments::Handle(TypeArguments::New(1));
|
||||
tav_function_int_nullary.SetTypeAt(0, type_function_int_nullary);
|
||||
CanonicalizeTAV(&tav_function_int_nullary);
|
||||
auto& tav_legacy_function_int_nullary =
|
||||
TypeArguments::Handle(TypeArguments::New(1));
|
||||
tav_legacy_function_int_nullary.SetTypeAt(0,
|
||||
type_legacy_function_int_nullary);
|
||||
CanonicalizeTAV(&tav_legacy_function_int_nullary);
|
||||
auto& tav_nullable_function_int_nullary =
|
||||
TypeArguments::Handle(TypeArguments::New(1));
|
||||
tav_nullable_function_int_nullary.SetTypeAt(
|
||||
0, type_nullable_function_int_nullary);
|
||||
CanonicalizeTAV(&tav_nullable_function_int_nullary);
|
||||
|
||||
auto& type_future_function = Type::Handle(
|
||||
Type::New(class_future, tav_function, Nullability::kNonNullable));
|
||||
FinalizeAndCanonicalize(&type_future_function);
|
||||
auto& type_future_legacy_function = Type::Handle(
|
||||
Type::New(class_future, tav_legacy_function, Nullability::kNonNullable));
|
||||
FinalizeAndCanonicalize(&type_future_legacy_function);
|
||||
auto& type_future_nullable_function = Type::Handle(Type::New(
|
||||
class_future, tav_nullable_function, Nullability::kNonNullable));
|
||||
FinalizeAndCanonicalize(&type_future_nullable_function);
|
||||
auto& type_future_closure = Type::Handle(
|
||||
Type::New(class_future, tav_closure, Nullability::kNonNullable));
|
||||
FinalizeAndCanonicalize(&type_future_closure);
|
||||
auto& type_future_legacy_closure = Type::Handle(
|
||||
Type::New(class_future, tav_legacy_closure, Nullability::kNonNullable));
|
||||
FinalizeAndCanonicalize(&type_future_legacy_closure);
|
||||
auto& type_future_nullable_closure = Type::Handle(
|
||||
Type::New(class_future, tav_nullable_closure, Nullability::kNonNullable));
|
||||
FinalizeAndCanonicalize(&type_future_nullable_closure);
|
||||
auto& type_future_function_int_nullary =
|
||||
Type::Handle(Type::New(class_future, tav_function_int_nullary));
|
||||
FinalizeAndCanonicalize(&type_future_function_int_nullary);
|
||||
auto& type_future_legacy_function_int_nullary =
|
||||
Type::Handle(Type::New(class_future, tav_legacy_function_int_nullary));
|
||||
FinalizeAndCanonicalize(&type_future_legacy_function_int_nullary);
|
||||
auto& type_future_nullable_function_int_nullary =
|
||||
Type::Handle(Type::New(class_future, tav_nullable_function_int_nullary));
|
||||
FinalizeAndCanonicalize(&type_future_nullable_function_int_nullary);
|
||||
|
||||
THR_Print("\n********************************************************\n");
|
||||
THR_Print(" Testing Future<int Function()>\n");
|
||||
THR_Print("********************************************************\n\n");
|
||||
|
||||
// And here, obj is an object of type Future<int Function()>. Note that
|
||||
// int Function() <: Function, but int Function() </: _Closure. That is,
|
||||
// _Closure is a separate subtype of Function from FunctionTypes.
|
||||
//
|
||||
// True positive from TTS:
|
||||
// obj as Future : Null type args
|
||||
// obj as Future<dynamic> : Canonicalized to same as previous case.
|
||||
// obj as Future<Object?> : Type arg is top type
|
||||
// obj as Future<Object*> : Type arg is top typ
|
||||
// obj as Future<Object> : Type arg is certain supertype
|
||||
// obj as Future<Function?> : Type arg is certain supertype
|
||||
// obj as Future<Function*> : Type arg is certain supertype
|
||||
// obj as Future<Function> : Type arg is certain supertype
|
||||
// obj as Future<X>, : Type arg is a type parameter instantiated with
|
||||
// X = dynamic : ... a top type
|
||||
// X = Object? : ... a top type
|
||||
// X = Object* : ... a top type
|
||||
// X = Object : ... a certain supertype
|
||||
// X = int Function() : ... the same type.
|
||||
//
|
||||
RunTTSTest(type_future, {obj_futurefunction, tav_null, tav_null});
|
||||
RunTTSTest(type_future_dynamic, {obj_futurefunction, tav_null, tav_null});
|
||||
RunTTSTest(type_future_nullable_object,
|
||||
{obj_futurefunction, tav_null, tav_null});
|
||||
RunTTSTest(type_future_legacy_object,
|
||||
{obj_futurefunction, tav_null, tav_null});
|
||||
RunTTSTest(type_future_object, {obj_futurefunction, tav_null, tav_null});
|
||||
RunTTSTest(type_future_nullable_object,
|
||||
{obj_futurefunction, tav_null, tav_null});
|
||||
RunTTSTest(type_future_legacy_object,
|
||||
{obj_futurefunction, tav_null, tav_null});
|
||||
RunTTSTest(type_future_object, {obj_futurefunction, tav_null, tav_null});
|
||||
RunTTSTest(type_future_nullable_function,
|
||||
{obj_futurefunction, tav_null, tav_null});
|
||||
RunTTSTest(type_future_legacy_function,
|
||||
{obj_futurefunction, tav_null, tav_null});
|
||||
RunTTSTest(type_future_function, {obj_futurefunction, tav_null, tav_null});
|
||||
RunTTSTest(type_future_t, {obj_futurefunction, tav_null, tav_null});
|
||||
RunTTSTest(type_future_t,
|
||||
{obj_futurefunction, tav_nullable_object, tav_null});
|
||||
RunTTSTest(type_future_t, {obj_futurefunction, tav_legacy_object, tav_null});
|
||||
RunTTSTest(type_future_t, {obj_futurefunction, tav_object, tav_null});
|
||||
RunTTSTest(type_future_t,
|
||||
{obj_futurefunction, tav_function_int_nullary, tav_null});
|
||||
|
||||
// False negative from TTS (caught by runtime or STC):
|
||||
// obj as Future<int Function()?> : No specialization.
|
||||
// obj as Future<int Function()*> : No specialization.
|
||||
// obj as Future<int Function()> : No specialization.
|
||||
// obj as Future<X>, : Type arg is a type parameter instantiated with
|
||||
// X = Function? : ... a certain supertype (but not checked)
|
||||
// X = Function* : ... a certain supertype (but not checked)
|
||||
// X = Function : ... a certain supertype (but not checked)
|
||||
// X = int Function()? : ... a canonically different type.
|
||||
// X = int Function()* : ... a canonically different type.
|
||||
//
|
||||
RunTTSTest(type_future_nullable_function_int_nullary,
|
||||
FalseNegative({obj_futurefunction, tav_null, tav_null,
|
||||
/*should_specialize=*/false}));
|
||||
RunTTSTest(type_future_legacy_function_int_nullary,
|
||||
FalseNegative({obj_futurefunction, tav_null, tav_null,
|
||||
/*should_specialize=*/false}));
|
||||
RunTTSTest(type_future_function_int_nullary,
|
||||
FalseNegative({obj_futurefunction, tav_null, tav_null,
|
||||
/*should_specialize=*/false}));
|
||||
RunTTSTest(type_future_t, FalseNegative({obj_futurefunction,
|
||||
tav_nullable_function, tav_null}));
|
||||
RunTTSTest(type_future_t, FalseNegative({obj_futurefunction,
|
||||
tav_legacy_function, tav_null}));
|
||||
RunTTSTest(type_future_t,
|
||||
FalseNegative({obj_futurefunction, tav_function, tav_null}));
|
||||
RunTTSTest(type_future_t,
|
||||
FalseNegative({obj_futurefunction,
|
||||
tav_nullable_function_int_nullary, tav_null}));
|
||||
RunTTSTest(type_future_t,
|
||||
FalseNegative({obj_futurefunction, tav_legacy_function_int_nullary,
|
||||
tav_null}));
|
||||
|
||||
// Errors:
|
||||
// obj as Future<_Closure?> : Type arg is not a supertype
|
||||
// obj as Future<_Closure*> : Type arg is not a supertype
|
||||
// obj as Future<_Closure> : Type arg is not a supertype
|
||||
// obj as Future<X>, : Type arg is a type parameter instantiated with
|
||||
// X = _Closure? : ... an unrelated type.
|
||||
// X = _Closure* : ... an unrelated type.
|
||||
// X = _Closure : ... an unrelated type.
|
||||
//
|
||||
RunTTSTest(type_future_nullable_closure,
|
||||
Failure({obj_futurefunction, tav_null, tav_null}));
|
||||
RunTTSTest(type_future_legacy_closure,
|
||||
Failure({obj_futurefunction, tav_null, tav_null}));
|
||||
RunTTSTest(type_future_closure,
|
||||
Failure({obj_futurefunction, tav_null, tav_null}));
|
||||
RunTTSTest(type_future_t,
|
||||
Failure({obj_futurefunction, tav_nullable_closure, tav_null}));
|
||||
RunTTSTest(type_future_t,
|
||||
Failure({obj_futurefunction, tav_legacy_closure, tav_null}));
|
||||
RunTTSTest(type_future_t,
|
||||
Failure({obj_futurefunction, tav_closure, tav_null}));
|
||||
|
||||
THR_Print("\n********************************************************\n");
|
||||
THR_Print(" Testing Future<int Function()?>\n");
|
||||
THR_Print("********************************************************\n\n");
|
||||
|
||||
const bool strict_null_safety =
|
||||
thread->isolate_group()->use_strict_null_safety_checks();
|
||||
|
||||
// And here, obj is an object of type Future<int Function()?>.
|
||||
//
|
||||
// True positive from TTS:
|
||||
// obj as Future : Null type args
|
||||
// obj as Future<dynamic> : Canonicalized to same as previous case.
|
||||
// obj as Future<Object?> : Type arg is top type
|
||||
// obj as Future<Object*> : Type arg is top typ
|
||||
// obj as Future<Function?> : Type arg is certain supertype
|
||||
// obj as Future<Function*> : Type arg is certain supertype
|
||||
// obj as Future<X>, : Type arg is a type parameter instantiated with
|
||||
// X = dynamic : ... a top type
|
||||
// X = Object? : ... a top type
|
||||
// X = Object* : ... a top type
|
||||
// X = int Function()? : ... the same type.
|
||||
//
|
||||
// If not null safe:
|
||||
// obj as Future<Object> : Type arg is certain supertype
|
||||
// obj as Future<Function> : Type arg is certain supertype
|
||||
// obj as Future<X>, : Type arg is a type parameter instantiated with
|
||||
// X = Object : ... a certain supertype
|
||||
RunTTSTest(type_future, {obj_futurenullablefunction, tav_null, tav_null});
|
||||
RunTTSTest(type_future_dynamic,
|
||||
{obj_futurenullablefunction, tav_null, tav_null});
|
||||
RunTTSTest(type_future_nullable_object,
|
||||
{obj_futurenullablefunction, tav_null, tav_null});
|
||||
RunTTSTest(type_future_legacy_object,
|
||||
{obj_futurenullablefunction, tav_null, tav_null});
|
||||
RunTTSTest(type_future_nullable_object,
|
||||
{obj_futurefunction, tav_null, tav_null});
|
||||
RunTTSTest(type_future_legacy_object,
|
||||
{obj_futurenullablefunction, tav_null, tav_null});
|
||||
RunTTSTest(type_future_nullable_function,
|
||||
{obj_futurenullablefunction, tav_null, tav_null});
|
||||
RunTTSTest(type_future_legacy_function,
|
||||
{obj_futurenullablefunction, tav_null, tav_null});
|
||||
RunTTSTest(type_future_t, {obj_futurenullablefunction, tav_null, tav_null});
|
||||
RunTTSTest(type_future_t,
|
||||
{obj_futurenullablefunction, tav_nullable_object, tav_null});
|
||||
RunTTSTest(type_future_t,
|
||||
{obj_futurenullablefunction, tav_legacy_object, tav_null});
|
||||
RunTTSTest(type_future_t, {obj_futurenullablefunction,
|
||||
tav_nullable_function_int_nullary, tav_null});
|
||||
|
||||
if (!strict_null_safety) {
|
||||
RunTTSTest(type_future_object,
|
||||
{obj_futurenullablefunction, tav_null, tav_null});
|
||||
RunTTSTest(type_future_function,
|
||||
{obj_futurenullablefunction, tav_null, tav_null});
|
||||
RunTTSTest(type_future_t,
|
||||
{obj_futurenullablefunction, tav_object, tav_null});
|
||||
}
|
||||
|
||||
// False negative from TTS (caught by runtime or STC):
|
||||
// obj as Future<int Function()?> : No specialization.
|
||||
// obj as Future<int Function()*> : No specialization.
|
||||
// obj as Future<X>, : Type arg is a type parameter instantiated with
|
||||
// X = Function? : ... a certain supertype (but not checked)
|
||||
// X = Function* : ... a certain supertype (but not checked)
|
||||
// X = int Function()* : ... a canonically different type.
|
||||
//
|
||||
// If not null safe:
|
||||
// obj as Future<int Function()> : No specialization.
|
||||
// obj as Future<X>, : Type arg is a type parameter instantiated with
|
||||
// X = Function : ... a certain supertype (but not checked)
|
||||
// X = int Function() : ... a canonically different type.
|
||||
|
||||
RunTTSTest(type_future_nullable_function_int_nullary,
|
||||
FalseNegative({obj_futurenullablefunction, tav_null, tav_null,
|
||||
/*should_specialize=*/false}));
|
||||
RunTTSTest(type_future_legacy_function_int_nullary,
|
||||
FalseNegative({obj_futurenullablefunction, tav_null, tav_null,
|
||||
/*should_specialize=*/false}));
|
||||
RunTTSTest(type_future_t, FalseNegative({obj_futurenullablefunction,
|
||||
tav_nullable_function, tav_null}));
|
||||
RunTTSTest(type_future_t, FalseNegative({obj_futurenullablefunction,
|
||||
tav_legacy_function, tav_null}));
|
||||
RunTTSTest(type_future_t,
|
||||
FalseNegative({obj_futurenullablefunction,
|
||||
tav_legacy_function_int_nullary, tav_null}));
|
||||
|
||||
if (!strict_null_safety) {
|
||||
RunTTSTest(type_future_function_int_nullary,
|
||||
FalseNegative({obj_futurenullablefunction, tav_null, tav_null,
|
||||
/*should_specialize=*/false}));
|
||||
RunTTSTest(type_future_t, FalseNegative({obj_futurenullablefunction,
|
||||
tav_function, tav_null}));
|
||||
RunTTSTest(type_future_t,
|
||||
FalseNegative({obj_futurenullablefunction,
|
||||
tav_function_int_nullary, tav_null}));
|
||||
}
|
||||
|
||||
// Errors:
|
||||
// obj as Future<_Closure?> : Type arg is not a supertype
|
||||
// obj as Future<_Closure*> : Type arg is not a supertype
|
||||
// obj as Future<_Closure> : Type arg is not a supertype
|
||||
// obj as Future<X>, : Type arg is a type parameter instantiated with
|
||||
// X = _Closure? : ... an unrelated type.
|
||||
// X = _Closure* : ... an unrelated type.
|
||||
// X = _Closure : ... an unrelated type.
|
||||
//
|
||||
// If null safe:
|
||||
// obj as Future<int Function()> : Nullable type cannot be subtype of a
|
||||
// non-nullable type.
|
||||
// obj as Future<Object> : Nullable type cannot be subtype of a
|
||||
// non-nullable type.
|
||||
// obj as Future<Function> : Nullable type cannot be subtype of a
|
||||
// non-nullable type.
|
||||
// obj as Future<X>, : Type arg is a type parameter instantiated with
|
||||
// X = Object : ... a non-nullable type.
|
||||
// X = Function : ... a non-nullable type.
|
||||
// X = int Function() : ... a non-nullable type.
|
||||
|
||||
RunTTSTest(type_future_nullable_closure,
|
||||
Failure({obj_futurenullablefunction, tav_null, tav_null}));
|
||||
RunTTSTest(type_future_legacy_closure,
|
||||
Failure({obj_futurenullablefunction, tav_null, tav_null}));
|
||||
RunTTSTest(type_future_closure,
|
||||
Failure({obj_futurenullablefunction, tav_null, tav_null}));
|
||||
RunTTSTest(type_future_t, Failure({obj_futurenullablefunction,
|
||||
tav_nullable_closure, tav_null}));
|
||||
RunTTSTest(type_future_t, Failure({obj_futurenullablefunction,
|
||||
tav_legacy_closure, tav_null}));
|
||||
RunTTSTest(type_future_t,
|
||||
Failure({obj_futurenullablefunction, tav_closure, tav_null}));
|
||||
|
||||
if (strict_null_safety) {
|
||||
RunTTSTest(type_future_function_int_nullary,
|
||||
Failure({obj_futurenullablefunction, tav_null, tav_null,
|
||||
/*should_specialize=*/false}));
|
||||
RunTTSTest(type_future_object,
|
||||
Failure({obj_futurenullablefunction, tav_null, tav_null}));
|
||||
RunTTSTest(type_future_function,
|
||||
Failure({obj_futurenullablefunction, tav_null, tav_null}));
|
||||
RunTTSTest(type_future_t,
|
||||
Failure({obj_futurenullablefunction, tav_object, tav_null}));
|
||||
RunTTSTest(type_future_t,
|
||||
Failure({obj_futurenullablefunction, tav_function, tav_null}));
|
||||
RunTTSTest(type_future_t, Failure({obj_futurenullablefunction,
|
||||
tav_function_int_nullary, tav_null}));
|
||||
}
|
||||
}
|
||||
|
||||
ISOLATE_UNIT_TEST_CASE(TTS_Regress40964) {
|
||||
|
@ -1408,25 +1832,78 @@ ISOLATE_UNIT_TEST_CASE(TTS_Partial) {
|
|||
const char* kScript =
|
||||
R"(
|
||||
class B<T> {}
|
||||
|
||||
class C {}
|
||||
class D extends C {}
|
||||
class E extends D {}
|
||||
|
||||
F<A>() {}
|
||||
createB() => B<int>();
|
||||
createBE() => B<E>();
|
||||
createBENullable() => B<E?>();
|
||||
createBNull() => B<Null>();
|
||||
createBNever() => B<Never>();
|
||||
)";
|
||||
|
||||
const auto& root_library = Library::Handle(LoadTestScript(kScript));
|
||||
const auto& class_b = Class::Handle(GetClass(root_library, "B"));
|
||||
const auto& class_c = Class::Handle(GetClass(root_library, "C"));
|
||||
const auto& class_d = Class::Handle(GetClass(root_library, "D"));
|
||||
const auto& class_e = Class::Handle(GetClass(root_library, "E"));
|
||||
const auto& fun_f = Function::Handle(GetFunction(root_library, "F"));
|
||||
const auto& obj_b = Object::Handle(Invoke(root_library, "createB"));
|
||||
const auto& obj_b_e = Object::Handle(Invoke(root_library, "createBE"));
|
||||
const auto& obj_b_e_nullable =
|
||||
Object::Handle(Invoke(root_library, "createBENullable"));
|
||||
const auto& obj_b_null = Object::Handle(Invoke(root_library, "createBNull"));
|
||||
const auto& obj_b_never =
|
||||
Object::Handle(Invoke(root_library, "createBNever"));
|
||||
|
||||
const auto& tav_null = Object::null_type_arguments();
|
||||
auto& tav_int = TypeArguments::Handle(TypeArguments::New(1));
|
||||
tav_int.SetTypeAt(0, Type::Handle(Type::IntType()));
|
||||
CanonicalizeTAV(&tav_int);
|
||||
auto& tav_nullable_object = TypeArguments::Handle(TypeArguments::New(1));
|
||||
tav_nullable_object.SetTypeAt(
|
||||
0, Type::Handle(
|
||||
IsolateGroup::Current()->object_store()->nullable_object_type()));
|
||||
CanonicalizeTAV(&tav_nullable_object);
|
||||
auto& tav_legacy_object = TypeArguments::Handle(TypeArguments::New(1));
|
||||
tav_legacy_object.SetTypeAt(
|
||||
0, Type::Handle(
|
||||
IsolateGroup::Current()->object_store()->legacy_object_type()));
|
||||
CanonicalizeTAV(&tav_legacy_object);
|
||||
auto& tav_object = TypeArguments::Handle(TypeArguments::New(1));
|
||||
tav_object.SetTypeAt(0, Type::Handle(Type::ObjectType()));
|
||||
tav_object.SetTypeAt(
|
||||
0, Type::Handle(IsolateGroup::Current()->object_store()->object_type()));
|
||||
CanonicalizeTAV(&tav_object);
|
||||
auto& tav_num = TypeArguments::Handle(TypeArguments::New(1));
|
||||
tav_num.SetTypeAt(0, Type::Handle(Type::Number()));
|
||||
CanonicalizeTAV(&tav_num);
|
||||
|
||||
auto& type_e =
|
||||
Type::Handle(Type::New(class_e, tav_null, Nullability::kNonNullable));
|
||||
FinalizeAndCanonicalize(&type_e);
|
||||
auto& type_d =
|
||||
Type::Handle(Type::New(class_d, tav_null, Nullability::kNonNullable));
|
||||
FinalizeAndCanonicalize(&type_d);
|
||||
auto& type_c =
|
||||
Type::Handle(Type::New(class_c, tav_null, Nullability::kNonNullable));
|
||||
FinalizeAndCanonicalize(&type_c);
|
||||
auto& type_c_nullable =
|
||||
Type::Handle(Type::New(class_c, tav_null, Nullability::kNullable));
|
||||
FinalizeAndCanonicalize(&type_c_nullable);
|
||||
auto& type_c_legacy =
|
||||
Type::Handle(Type::New(class_c, tav_null, Nullability::kLegacy));
|
||||
FinalizeAndCanonicalize(&type_c_legacy);
|
||||
|
||||
auto& tav_e = TypeArguments::Handle(TypeArguments::New(1));
|
||||
tav_e.SetTypeAt(0, type_e);
|
||||
CanonicalizeTAV(&tav_e);
|
||||
auto& tav_d = TypeArguments::Handle(TypeArguments::New(1));
|
||||
tav_d.SetTypeAt(0, type_d);
|
||||
CanonicalizeTAV(&tav_d);
|
||||
auto& tav_c = TypeArguments::Handle(TypeArguments::New(1));
|
||||
tav_c.SetTypeAt(0, type_c);
|
||||
CanonicalizeTAV(&tav_c);
|
||||
auto& tav_nullable_c = TypeArguments::Handle(TypeArguments::New(1));
|
||||
tav_nullable_c.SetTypeAt(0, type_c_nullable);
|
||||
CanonicalizeTAV(&tav_nullable_c);
|
||||
auto& tav_legacy_c = TypeArguments::Handle(TypeArguments::New(1));
|
||||
tav_legacy_c.SetTypeAt(0, type_c_legacy);
|
||||
CanonicalizeTAV(&tav_legacy_c);
|
||||
|
||||
// One case where optimized TTSes can be partial is if the type is
|
||||
// uninstantiated with a type parameter at the same position as one of the
|
||||
|
@ -1438,41 +1915,51 @@ ISOLATE_UNIT_TEST_CASE(TTS_Partial) {
|
|||
TypeParameter::Handle(GetFunctionTypeParameter(fun_f, 0));
|
||||
auto& tav_a = TypeArguments::Handle(TypeArguments::New(1));
|
||||
tav_a.SetTypeAt(0, type_a);
|
||||
auto& type_b2_a = AbstractType::Handle(Type::New(class_b, tav_a));
|
||||
FinalizeAndCanonicalize(&type_b2_a);
|
||||
TTSTestState state(thread, type_b2_a);
|
||||
CanonicalizeTAV(&tav_a);
|
||||
auto& type_b_a = AbstractType::Handle(
|
||||
Type::New(class_b, tav_a, Nullability::kNonNullable));
|
||||
FinalizeAndCanonicalize(&type_b_a);
|
||||
TTSTestState state(thread, type_b_a);
|
||||
|
||||
TTSTestCase positive_test_case{obj_b, tav_null, tav_int};
|
||||
TTSTestCase first_false_negative_test_case =
|
||||
FalseNegative({obj_b, tav_null, tav_object});
|
||||
TTSTestCase second_false_negative_test_case =
|
||||
FalseNegative({obj_b, tav_null, tav_num});
|
||||
// No test case should possibly hit the same STC entry as another.
|
||||
ASSERT(!first_false_negative_test_case.HasSameSTCEntry(positive_test_case));
|
||||
ASSERT(!second_false_negative_test_case.HasSameSTCEntry(positive_test_case));
|
||||
ASSERT(!second_false_negative_test_case.HasSameSTCEntry(
|
||||
first_false_negative_test_case));
|
||||
// The type with the tested stub must be the same in all test cases.
|
||||
ASSERT(state.TypeToTest(positive_test_case) ==
|
||||
state.TypeToTest(first_false_negative_test_case));
|
||||
ASSERT(state.TypeToTest(positive_test_case) ==
|
||||
state.TypeToTest(second_false_negative_test_case));
|
||||
TTSTestCase b_e_testcase{obj_b_e, tav_null, tav_e};
|
||||
TTSTestCase b_d_testcase = FalseNegative({obj_b_e, tav_null, tav_d});
|
||||
TTSTestCase b_c_testcase = FalseNegative({obj_b_e, tav_null, tav_c});
|
||||
|
||||
// First, test that the positive test case is handled by the TTS.
|
||||
state.InvokeLazilySpecializedStub(positive_test_case);
|
||||
state.InvokeExistingStub(positive_test_case);
|
||||
state.InvokeLazilySpecializedStub(b_e_testcase);
|
||||
state.InvokeExistingStub(b_e_testcase);
|
||||
|
||||
// Now restart, using the false negative test cases.
|
||||
state.ClearCache();
|
||||
|
||||
state.InvokeLazilySpecializedStub(first_false_negative_test_case);
|
||||
state.InvokeExistingStub(first_false_negative_test_case);
|
||||
state.InvokeEagerlySpecializedStub(first_false_negative_test_case);
|
||||
state.InvokeLazilySpecializedStub(b_d_testcase);
|
||||
state.InvokeExistingStub(b_d_testcase);
|
||||
state.InvokeEagerlySpecializedStub(b_d_testcase);
|
||||
|
||||
state.InvokeExistingStub(positive_test_case);
|
||||
state.InvokeExistingStub(second_false_negative_test_case);
|
||||
state.InvokeExistingStub(first_false_negative_test_case);
|
||||
state.InvokeExistingStub(positive_test_case);
|
||||
state.InvokeExistingStub(b_e_testcase);
|
||||
state.InvokeExistingStub(b_c_testcase);
|
||||
state.InvokeExistingStub(b_d_testcase);
|
||||
state.InvokeExistingStub(b_e_testcase);
|
||||
|
||||
state.InvokeExistingStub({obj_b_never, tav_null, tav_d});
|
||||
state.InvokeExistingStub({obj_b_null, tav_null, tav_nullable_c});
|
||||
state.InvokeExistingStub({obj_b_null, tav_null, tav_legacy_c});
|
||||
if (IsolateGroup::Current()->use_strict_null_safety_checks()) {
|
||||
state.InvokeExistingStub(Failure({obj_b_null, tav_null, tav_c}));
|
||||
} else {
|
||||
state.InvokeExistingStub({obj_b_null, tav_null, tav_c});
|
||||
}
|
||||
|
||||
state.InvokeExistingStub({obj_b_e, tav_null, tav_nullable_object});
|
||||
state.InvokeExistingStub({obj_b_e_nullable, tav_null, tav_nullable_object});
|
||||
state.InvokeExistingStub({obj_b_e, tav_null, tav_legacy_object});
|
||||
state.InvokeExistingStub({obj_b_e_nullable, tav_null, tav_legacy_object});
|
||||
state.InvokeExistingStub({obj_b_e, tav_null, tav_object});
|
||||
if (IsolateGroup::Current()->use_strict_null_safety_checks()) {
|
||||
state.InvokeExistingStub(Failure({obj_b_e_nullable, tav_null, tav_object}));
|
||||
} else {
|
||||
state.InvokeExistingStub({obj_b_e_nullable, tav_null, tav_object});
|
||||
}
|
||||
}
|
||||
|
||||
ISOLATE_UNIT_TEST_CASE(TTS_Partial_Incremental) {
|
||||
|
|
Loading…
Reference in a new issue