mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 08:24:39 +00:00
Reland "[vm] Hide internal implementation List types and expose them as List"
This is a reland of 824bec596f
Original change's description:
> [vm] Hide internal implementation List types and expose them as List
>
> When taking a type of an instance with x.runtimeType we can map
> internal classes _List, _ImmutableList and _GrowableList to a
> user-visible List class. This is similar to what we do for
> implementation classes of int, String and Type.
> After that, result of x.runtimeType for built-in lists would be
> compatible with List<T> type literals.
>
> Also, both intrinsic and native implementations of _haveSameRuntimeType
> are updated to agree with new semantic of runtimeType.
>
> TEST=co19/LanguageFeatures/Constructor-tear-offs/type_literal_A01_t01
> TEST=runtime/tests/vm/dart/have_same_runtime_type_test
>
> Fixes https://github.com/dart-lang/sdk/issues/46893
> Issue https://github.com/dart-lang/sdk/issues/46231
>
> Change-Id: Ie24a9f527f66a06118427b7a09e49c03dff93d8e
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/210066
> Commit-Queue: Alexander Markov <alexmarkov@google.com>
> Reviewed-by: Tess Strickland <sstrickl@google.com>
TEST=co19/LanguageFeatures/Constructor-tear-offs/type_literal_A01_t01
TEST=runtime/tests/vm/dart/have_same_runtime_type_test
TEST=lib/mirrors/regress_b196606044_test
Fixes https://github.com/dart-lang/sdk/issues/46893
Issue https://github.com/dart-lang/sdk/issues/46231
Change-Id: I79b587540338808bd73a6554f00a5eed042f4c26
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/210201
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Tess Strickland <sstrickl@google.com>
This commit is contained in:
parent
6fdf8f1c78
commit
cfb057ddca
|
@ -88,7 +88,18 @@ DEFINE_NATIVE_ENTRY(Object_runtimeType, 0, 1) {
|
|||
return Type::Double();
|
||||
} else if (instance.IsType() || instance.IsFunctionType()) {
|
||||
return Type::DartTypeType();
|
||||
} else if (IsArrayClassId(instance.GetClassId())) {
|
||||
const auto& cls = Class::Handle(
|
||||
zone, thread->isolate_group()->object_store()->list_class());
|
||||
const auto& type_arguments =
|
||||
TypeArguments::Handle(zone, instance.GetTypeArguments());
|
||||
const auto& type = Type::Handle(
|
||||
zone,
|
||||
Type::New(cls, type_arguments, Nullability::kNonNullable, Heap::kNew));
|
||||
type.SetIsFinalized();
|
||||
return type.Canonicalize(thread, nullptr);
|
||||
}
|
||||
|
||||
return instance.GetType(Heap::kNew);
|
||||
}
|
||||
|
||||
|
@ -101,14 +112,18 @@ static bool HaveSameRuntimeTypeHelper(Zone* zone,
|
|||
if (left_cid != right_cid) {
|
||||
if (IsIntegerClassId(left_cid)) {
|
||||
return IsIntegerClassId(right_cid);
|
||||
}
|
||||
if (IsStringClassId(left_cid)) {
|
||||
} else if (IsStringClassId(left_cid)) {
|
||||
return IsStringClassId(right_cid);
|
||||
}
|
||||
if (IsTypeClassId(left_cid)) {
|
||||
} else if (IsTypeClassId(left_cid)) {
|
||||
return IsTypeClassId(right_cid);
|
||||
} else if (IsArrayClassId(left_cid)) {
|
||||
if (!IsArrayClassId(right_cid)) {
|
||||
return false;
|
||||
}
|
||||
// Still need to check type arguments.
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (left_cid == kClosureCid) {
|
||||
|
|
32
runtime/tests/vm/dart/have_same_runtime_type_test.dart
Normal file
32
runtime/tests/vm/dart/have_same_runtime_type_test.dart
Normal file
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// Test for corner cases of 'a.runtimeType == b.runtimeType' pattern
|
||||
// which is recognized and optimized in AOT mode.
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
Object getType(Object obj) => obj.runtimeType;
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
void test(bool expected, Object a, Object b) {
|
||||
bool result1 = getType(a) == getType(b);
|
||||
bool result2 = a.runtimeType == b.runtimeType;
|
||||
Expect.equals(expected, result1);
|
||||
Expect.equals(expected, result2);
|
||||
}
|
||||
|
||||
typedef Func = void Function();
|
||||
|
||||
void main() {
|
||||
test(true, 0x7fffffffffffffff, int.parse('42'));
|
||||
test(true, 'hi', String.fromCharCode(1114111));
|
||||
test(false, 'hi', 1);
|
||||
test(true, List, Func);
|
||||
test(true, <int>[1], const <int>[2]);
|
||||
test(true, const <String>[], List<String>.filled(1, ''));
|
||||
test(true, <String>[]..add('hi'), List<String>.filled(2, ''));
|
||||
test(false, <int>[], <String>[]);
|
||||
}
|
34
runtime/tests/vm/dart_2/have_same_runtime_type_test.dart
Normal file
34
runtime/tests/vm/dart_2/have_same_runtime_type_test.dart
Normal file
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// Test for corner cases of 'a.runtimeType == b.runtimeType' pattern
|
||||
// which is recognized and optimized in AOT mode.
|
||||
|
||||
// @dart = 2.9
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
Object getType(Object obj) => obj.runtimeType;
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
void test(bool expected, Object a, Object b) {
|
||||
bool result1 = getType(a) == getType(b);
|
||||
bool result2 = a.runtimeType == b.runtimeType;
|
||||
Expect.equals(expected, result1);
|
||||
Expect.equals(expected, result2);
|
||||
}
|
||||
|
||||
typedef Func = void Function();
|
||||
|
||||
void main() {
|
||||
test(true, 0x7fffffffffffffff, int.parse('42'));
|
||||
test(true, 'hi', String.fromCharCode(1114111));
|
||||
test(false, 'hi', 1);
|
||||
test(true, List, Func);
|
||||
test(true, <int>[1], const <int>[2]);
|
||||
test(true, const <String>[], List<String>.filled(1, ''));
|
||||
test(true, <String>[]..add('hi'), List<String>.filled(2, ''));
|
||||
test(false, <int>[], <String>[]);
|
||||
}
|
|
@ -78,7 +78,6 @@ typedef uint16_t ClassIdTagType;
|
|||
V(Mint) \
|
||||
V(Double) \
|
||||
V(Bool) \
|
||||
V(GrowableObjectArray) \
|
||||
V(Float32x4) \
|
||||
V(Int32x4) \
|
||||
V(Float64x2) \
|
||||
|
@ -108,10 +107,14 @@ typedef uint16_t ClassIdTagType;
|
|||
// TODO(http://dartbug.com/45908): Add ImmutableLinkedHashSet.
|
||||
#define CLASS_LIST_SETS(V) V(LinkedHashSet)
|
||||
|
||||
#define CLASS_LIST_ARRAYS(V) \
|
||||
#define CLASS_LIST_FIXED_LENGTH_ARRAYS(V) \
|
||||
V(Array) \
|
||||
V(ImmutableArray)
|
||||
|
||||
#define CLASS_LIST_ARRAYS(V) \
|
||||
CLASS_LIST_FIXED_LENGTH_ARRAYS(V) \
|
||||
V(GrowableObjectArray)
|
||||
|
||||
#define CLASS_LIST_STRINGS(V) \
|
||||
V(String) \
|
||||
V(OneByteString) \
|
||||
|
@ -182,6 +185,7 @@ typedef uint16_t ClassIdTagType;
|
|||
V(LinkedHashMap) \
|
||||
V(LinkedHashSet) \
|
||||
V(Array) \
|
||||
V(GrowableObjectArray) \
|
||||
V(String)
|
||||
|
||||
#define CLASS_LIST_NO_OBJECT(V) \
|
||||
|
@ -327,11 +331,15 @@ inline bool IsExternalStringClassId(intptr_t index) {
|
|||
index == kExternalTwoByteStringCid);
|
||||
}
|
||||
|
||||
inline bool IsArrayClassId(intptr_t index) {
|
||||
COMPILE_ASSERT(kImmutableArrayCid == kArrayCid + 1);
|
||||
COMPILE_ASSERT(kGrowableObjectArrayCid == kArrayCid + 2);
|
||||
return (index >= kArrayCid && index <= kGrowableObjectArrayCid);
|
||||
}
|
||||
|
||||
inline bool IsBuiltinListClassId(intptr_t index) {
|
||||
// Make sure this function is updated when new builtin List types are added.
|
||||
COMPILE_ASSERT(kImmutableArrayCid == kArrayCid + 1);
|
||||
return ((index >= kArrayCid && index <= kImmutableArrayCid) ||
|
||||
(index == kGrowableObjectArrayCid) || IsTypedDataBaseClassId(index) ||
|
||||
return (IsArrayClassId(index) || IsTypedDataBaseClassId(index) ||
|
||||
(index == kByteBufferCid));
|
||||
}
|
||||
|
||||
|
|
|
@ -1198,6 +1198,14 @@ static void JumpIfNotString(Assembler* assembler,
|
|||
kIfNotInRange, target);
|
||||
}
|
||||
|
||||
static void JumpIfNotList(Assembler* assembler,
|
||||
Register cid,
|
||||
Register tmp,
|
||||
Label* target) {
|
||||
RangeCheck(assembler, cid, tmp, kArrayCid, kGrowableObjectArrayCid,
|
||||
kIfNotInRange, target);
|
||||
}
|
||||
|
||||
static void JumpIfType(Assembler* assembler,
|
||||
Register cid,
|
||||
Register tmp,
|
||||
|
@ -1284,7 +1292,7 @@ static void EquivalentClassIds(Assembler* assembler,
|
|||
Register scratch,
|
||||
bool testing_instance_cids) {
|
||||
Label different_cids, equal_cids_but_generic, not_integer,
|
||||
not_integer_or_string;
|
||||
not_integer_or_string, not_integer_or_string_or_list;
|
||||
|
||||
// Check if left hand side is a closure. Closures are handled in the runtime.
|
||||
__ CompareImmediate(cid1, kClosureCid);
|
||||
|
@ -1310,7 +1318,8 @@ static void EquivalentClassIds(Assembler* assembler,
|
|||
__ b(equal);
|
||||
|
||||
// Class ids are different. Check if we are comparing two string types (with
|
||||
// different representations) or two integer types or two type types.
|
||||
// different representations), two integer types, two list types or two type
|
||||
// types.
|
||||
__ Bind(&different_cids);
|
||||
__ CompareImmediate(cid1, kNumPredefinedCids);
|
||||
__ b(not_equal, HI);
|
||||
|
@ -1335,9 +1344,20 @@ static void EquivalentClassIds(Assembler* assembler,
|
|||
|
||||
if (testing_instance_cids) {
|
||||
__ Bind(¬_integer_or_string);
|
||||
// Check if both are List types.
|
||||
JumpIfNotList(assembler, cid1, scratch, ¬_integer_or_string_or_list);
|
||||
|
||||
// First type is a List. Check if the second is a List too.
|
||||
JumpIfNotList(assembler, cid2, scratch, not_equal);
|
||||
ASSERT(compiler::target::Array::type_arguments_offset() ==
|
||||
compiler::target::GrowableObjectArray::type_arguments_offset());
|
||||
__ LoadImmediate(scratch, compiler::target::Array::type_arguments_offset());
|
||||
__ b(&equal_cids_but_generic);
|
||||
|
||||
__ Bind(¬_integer_or_string_or_list);
|
||||
// Check if the first type is a Type. If it is not then types are not
|
||||
// equivalent because they have different class ids and they are not String
|
||||
// or integer or Type.
|
||||
// or integer or List or Type.
|
||||
JumpIfNotType(assembler, cid1, scratch, not_equal);
|
||||
|
||||
// First type is a Type. Check if the second is a Type too.
|
||||
|
|
|
@ -1342,6 +1342,14 @@ static void JumpIfNotString(Assembler* assembler,
|
|||
kIfNotInRange, target);
|
||||
}
|
||||
|
||||
static void JumpIfNotList(Assembler* assembler,
|
||||
Register cid,
|
||||
Register tmp,
|
||||
Label* target) {
|
||||
RangeCheck(assembler, cid, tmp, kArrayCid, kGrowableObjectArrayCid,
|
||||
kIfNotInRange, target);
|
||||
}
|
||||
|
||||
static void JumpIfType(Assembler* assembler,
|
||||
Register cid,
|
||||
Register tmp,
|
||||
|
@ -1432,7 +1440,7 @@ static void EquivalentClassIds(Assembler* assembler,
|
|||
Register scratch,
|
||||
bool testing_instance_cids) {
|
||||
Label different_cids, equal_cids_but_generic, not_integer,
|
||||
not_integer_or_string;
|
||||
not_integer_or_string, not_integer_or_string_or_list;
|
||||
|
||||
// Check if left hand side is a closure. Closures are handled in the runtime.
|
||||
__ CompareImmediate(cid1, kClosureCid);
|
||||
|
@ -1458,7 +1466,8 @@ static void EquivalentClassIds(Assembler* assembler,
|
|||
__ b(equal);
|
||||
|
||||
// Class ids are different. Check if we are comparing two string types (with
|
||||
// different representations) or two integer types or two type types.
|
||||
// different representations), two integer types, two list types or two type
|
||||
// types.
|
||||
__ Bind(&different_cids);
|
||||
__ CompareImmediate(cid1, kNumPredefinedCids);
|
||||
__ b(not_equal, HI);
|
||||
|
@ -1483,9 +1492,20 @@ static void EquivalentClassIds(Assembler* assembler,
|
|||
|
||||
if (testing_instance_cids) {
|
||||
__ Bind(¬_integer_or_string);
|
||||
// Check if both are List types.
|
||||
JumpIfNotList(assembler, cid1, scratch, ¬_integer_or_string_or_list);
|
||||
|
||||
// First type is a List. Check if the second is a List too.
|
||||
JumpIfNotList(assembler, cid2, scratch, not_equal);
|
||||
ASSERT(compiler::target::Array::type_arguments_offset() ==
|
||||
compiler::target::GrowableObjectArray::type_arguments_offset());
|
||||
__ LoadImmediate(scratch, compiler::target::Array::type_arguments_offset());
|
||||
__ b(&equal_cids_but_generic);
|
||||
|
||||
__ Bind(¬_integer_or_string_or_list);
|
||||
// Check if the first type is a Type. If it is not then types are not
|
||||
// equivalent because they have different class ids and they are not String
|
||||
// or integer or Type.
|
||||
// or integer or List or Type.
|
||||
JumpIfNotType(assembler, cid1, scratch, not_equal);
|
||||
|
||||
// First type is a Type. Check if the second is a Type too.
|
||||
|
|
|
@ -1281,6 +1281,11 @@ static void JumpIfNotString(Assembler* assembler, Register cid, Label* target) {
|
|||
kIfNotInRange, target);
|
||||
}
|
||||
|
||||
static void JumpIfNotList(Assembler* assembler, Register cid, Label* target) {
|
||||
RangeCheck(assembler, cid, kArrayCid, kGrowableObjectArrayCid, kIfNotInRange,
|
||||
target);
|
||||
}
|
||||
|
||||
static void JumpIfType(Assembler* assembler, Register cid, Label* target) {
|
||||
RangeCheck(assembler, cid, kTypeCid, kFunctionTypeCid, kIfInRange, target);
|
||||
}
|
||||
|
@ -1370,7 +1375,7 @@ static void EquivalentClassIds(Assembler* assembler,
|
|||
Register scratch,
|
||||
bool testing_instance_cids) {
|
||||
Label different_cids, equal_cids_but_generic, not_integer,
|
||||
not_integer_or_string;
|
||||
not_integer_or_string, not_integer_or_string_or_list;
|
||||
|
||||
// Check if left hand side is a closure. Closures are handled in the runtime.
|
||||
__ cmpl(cid1, Immediate(kClosureCid));
|
||||
|
@ -1392,11 +1397,12 @@ static void EquivalentClassIds(Assembler* assembler,
|
|||
scratch,
|
||||
target::Class::host_type_arguments_field_offset_in_words_offset()));
|
||||
__ cmpl(scratch, Immediate(target::Class::kNoTypeArguments));
|
||||
__ j(NOT_EQUAL, &equal_cids_but_generic, Assembler::kNearJump);
|
||||
__ j(NOT_EQUAL, &equal_cids_but_generic);
|
||||
__ jmp(equal);
|
||||
|
||||
// Class ids are different. Check if we are comparing two string types (with
|
||||
// different representations) or two integer types or two type types.
|
||||
// different representations), two integer types, two list types or two type
|
||||
// types.
|
||||
__ Bind(&different_cids);
|
||||
__ cmpl(cid1, Immediate(kNumPredefinedCids));
|
||||
__ j(ABOVE_EQUAL, not_equal);
|
||||
|
@ -1406,25 +1412,42 @@ static void EquivalentClassIds(Assembler* assembler,
|
|||
JumpIfNotInteger(assembler, scratch, ¬_integer);
|
||||
|
||||
// First type is an integer. Check if the second is an integer too.
|
||||
JumpIfInteger(assembler, cid2, equal);
|
||||
__ movl(scratch, cid2);
|
||||
JumpIfInteger(assembler, scratch, equal);
|
||||
// Integer types are only equivalent to other integer types.
|
||||
__ jmp(not_equal);
|
||||
|
||||
__ Bind(¬_integer);
|
||||
// Check if both are String types.
|
||||
JumpIfNotString(assembler, cid1,
|
||||
__ movl(scratch, cid1);
|
||||
JumpIfNotString(assembler, scratch,
|
||||
testing_instance_cids ? ¬_integer_or_string : not_equal);
|
||||
|
||||
// First type is a String. Check if the second is a String too.
|
||||
JumpIfString(assembler, cid2, equal);
|
||||
__ movl(scratch, cid2);
|
||||
JumpIfString(assembler, scratch, equal);
|
||||
// String types are only equivalent to other String types.
|
||||
__ jmp(not_equal);
|
||||
|
||||
if (testing_instance_cids) {
|
||||
__ Bind(¬_integer_or_string);
|
||||
// Check if both are List types.
|
||||
__ movl(scratch, cid1);
|
||||
JumpIfNotList(assembler, scratch, ¬_integer_or_string_or_list);
|
||||
|
||||
// First type is a List. Check if the second is a List too.
|
||||
__ movl(scratch, cid2);
|
||||
JumpIfNotList(assembler, scratch, not_equal);
|
||||
ASSERT(compiler::target::Array::type_arguments_offset() ==
|
||||
compiler::target::GrowableObjectArray::type_arguments_offset());
|
||||
__ movl(scratch,
|
||||
Immediate(compiler::target::Array::type_arguments_offset()));
|
||||
__ jmp(&equal_cids_but_generic, Assembler::kNearJump);
|
||||
|
||||
__ Bind(¬_integer_or_string_or_list);
|
||||
// Check if the first type is a Type. If it is not then types are not
|
||||
// equivalent because they have different class ids and they are not String
|
||||
// or integer or Type.
|
||||
// or integer or List or Type.
|
||||
JumpIfNotType(assembler, cid1, not_equal);
|
||||
|
||||
// First type is a Type. Check if the second is a Type too.
|
||||
|
|
|
@ -1184,6 +1184,11 @@ static void JumpIfNotString(Assembler* assembler, Register cid, Label* target) {
|
|||
kIfNotInRange, target);
|
||||
}
|
||||
|
||||
static void JumpIfNotList(Assembler* assembler, Register cid, Label* target) {
|
||||
RangeCheck(assembler, cid, kArrayCid, kGrowableObjectArrayCid, kIfNotInRange,
|
||||
target);
|
||||
}
|
||||
|
||||
static void JumpIfType(Assembler* assembler, Register cid, Label* target) {
|
||||
RangeCheck(assembler, cid, kTypeCid, kFunctionTypeCid, kIfInRange, target);
|
||||
}
|
||||
|
@ -1275,7 +1280,7 @@ static void EquivalentClassIds(Assembler* assembler,
|
|||
Register scratch,
|
||||
bool testing_instance_cids) {
|
||||
Label different_cids, equal_cids_but_generic, not_integer,
|
||||
not_integer_or_string;
|
||||
not_integer_or_string, not_integer_or_string_or_list;
|
||||
|
||||
// Check if left hand side is a closure. Closures are handled in the runtime.
|
||||
__ cmpq(cid1, Immediate(kClosureCid));
|
||||
|
@ -1297,11 +1302,12 @@ static void EquivalentClassIds(Assembler* assembler,
|
|||
scratch,
|
||||
target::Class::host_type_arguments_field_offset_in_words_offset()));
|
||||
__ cmpl(scratch, Immediate(target::Class::kNoTypeArguments));
|
||||
__ j(NOT_EQUAL, &equal_cids_but_generic, Assembler::kNearJump);
|
||||
__ j(NOT_EQUAL, &equal_cids_but_generic);
|
||||
__ jmp(equal);
|
||||
|
||||
// Class ids are different. Check if we are comparing two string types (with
|
||||
// different representations) or two integer types or two type types.
|
||||
// different representations), two integer types, two list types or two type
|
||||
// types.
|
||||
__ Bind(&different_cids);
|
||||
__ cmpq(cid1, Immediate(kNumPredefinedCids));
|
||||
__ j(ABOVE_EQUAL, not_equal);
|
||||
|
@ -1311,25 +1317,42 @@ static void EquivalentClassIds(Assembler* assembler,
|
|||
JumpIfNotInteger(assembler, scratch, ¬_integer);
|
||||
|
||||
// First type is an integer. Check if the second is an integer too.
|
||||
JumpIfInteger(assembler, cid2, equal);
|
||||
__ movq(scratch, cid2);
|
||||
JumpIfInteger(assembler, scratch, equal);
|
||||
// Integer types are only equivalent to other integer types.
|
||||
__ jmp(not_equal);
|
||||
|
||||
__ Bind(¬_integer);
|
||||
// Check if both are String types.
|
||||
JumpIfNotString(assembler, cid1,
|
||||
__ movq(scratch, cid1);
|
||||
JumpIfNotString(assembler, scratch,
|
||||
testing_instance_cids ? ¬_integer_or_string : not_equal);
|
||||
|
||||
// First type is a String. Check if the second is a String too.
|
||||
JumpIfString(assembler, cid2, equal);
|
||||
__ movq(scratch, cid2);
|
||||
JumpIfString(assembler, scratch, equal);
|
||||
// String types are only equivalent to other String types.
|
||||
__ jmp(not_equal);
|
||||
|
||||
if (testing_instance_cids) {
|
||||
__ Bind(¬_integer_or_string);
|
||||
// Check if both are List types.
|
||||
__ movq(scratch, cid1);
|
||||
JumpIfNotList(assembler, scratch, ¬_integer_or_string_or_list);
|
||||
|
||||
// First type is a List. Check if the second is a List too.
|
||||
__ movq(scratch, cid2);
|
||||
JumpIfNotList(assembler, scratch, not_equal);
|
||||
ASSERT(compiler::target::Array::type_arguments_offset() ==
|
||||
compiler::target::GrowableObjectArray::type_arguments_offset());
|
||||
__ movq(scratch,
|
||||
Immediate(compiler::target::Array::type_arguments_offset()));
|
||||
__ jmp(&equal_cids_but_generic, Assembler::kNearJump);
|
||||
|
||||
__ Bind(¬_integer_or_string_or_list);
|
||||
// Check if the first type is a Type. If it is not then types are not
|
||||
// equivalent because they have different class ids and they are not String
|
||||
// or integer or Type.
|
||||
// or integer or List or Type.
|
||||
JumpIfNotType(assembler, cid1, not_equal);
|
||||
|
||||
// First type is a Type. Check if the second is a Type too.
|
||||
|
|
|
@ -1531,13 +1531,11 @@ void FlowGraphCompiler::GenerateListTypeCheck(
|
|||
Register class_id_reg,
|
||||
compiler::Label* is_instance_lbl) {
|
||||
assembler()->Comment("ListTypeCheck");
|
||||
compiler::Label unknown;
|
||||
GrowableArray<intptr_t> args;
|
||||
args.Add(kArrayCid);
|
||||
args.Add(kGrowableObjectArrayCid);
|
||||
args.Add(kImmutableArrayCid);
|
||||
CheckClassIds(class_id_reg, args, is_instance_lbl, &unknown);
|
||||
assembler()->Bind(&unknown);
|
||||
COMPILE_ASSERT((kImmutableArrayCid == kArrayCid + 1) &&
|
||||
(kGrowableObjectArrayCid == kArrayCid + 2));
|
||||
CidRangeVector ranges;
|
||||
ranges.Add({kArrayCid, kGrowableObjectArrayCid});
|
||||
GenerateCidRangesCheck(assembler(), class_id_reg, ranges, is_instance_lbl);
|
||||
}
|
||||
|
||||
void FlowGraphCompiler::EmitComment(Instruction* instr) {
|
||||
|
|
|
@ -607,6 +607,7 @@ void Object::InitVtables() {
|
|||
builtin_vtables_[k##clazz##Cid] = fake_handle.vtable(); \
|
||||
}
|
||||
CLASS_LIST_NO_OBJECT_NOR_STRING_NOR_ARRAY_NOR_MAP(INIT_VTABLE)
|
||||
INIT_VTABLE(GrowableObjectArray)
|
||||
#undef INIT_VTABLE
|
||||
|
||||
#define INIT_VTABLE(clazz) \
|
||||
|
@ -630,7 +631,7 @@ void Object::InitVtables() {
|
|||
Array fake_handle; \
|
||||
builtin_vtables_[k##clazz##Cid] = fake_handle.vtable(); \
|
||||
}
|
||||
CLASS_LIST_ARRAYS(INIT_VTABLE)
|
||||
CLASS_LIST_FIXED_LENGTH_ARRAYS(INIT_VTABLE)
|
||||
#undef INIT_VTABLE
|
||||
|
||||
#define INIT_VTABLE(clazz) \
|
||||
|
|
|
@ -1009,6 +1009,7 @@ class ObjectCopy : public Base {
|
|||
|
||||
CLASS_LIST_NO_OBJECT_NOR_STRING_NOR_ARRAY_NOR_MAP(COPY_TO)
|
||||
COPY_TO(Array)
|
||||
COPY_TO(GrowableObjectArray)
|
||||
COPY_TO(LinkedHashMap)
|
||||
COPY_TO(LinkedHashSet)
|
||||
#undef COPY_TO
|
||||
|
|
|
@ -5208,7 +5208,6 @@ static void GetDefaultClassesAliases(Thread* thread, JSONStream* js) {
|
|||
{
|
||||
JSONArray internals(&map, "List");
|
||||
CLASS_LIST_ARRAYS(DEFINE_ADD_VALUE_F_CID)
|
||||
DEFINE_ADD_VALUE_F_CID(GrowableObjectArray)
|
||||
DEFINE_ADD_VALUE_F_CID(ByteBuffer)
|
||||
}
|
||||
{
|
||||
|
|
23
tests/lib/mirrors/regress_b196606044_test.dart
Normal file
23
tests/lib/mirrors/regress_b196606044_test.dart
Normal file
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// Regression test for b/196606044.
|
||||
//
|
||||
// Verifies that instance.type.instanceMembers contains 'isNotEmpty'
|
||||
// member for a List literal.
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
import 'dart:mirrors';
|
||||
|
||||
dynamic object = <int>[1, 2, 3];
|
||||
String name = 'isNotEmpty';
|
||||
|
||||
main() {
|
||||
var instance = reflect(object);
|
||||
var member = instance.type.instanceMembers[new Symbol(name)];
|
||||
Expect.isNotNull(member);
|
||||
var invocation = instance.getField(member!.simpleName);
|
||||
Expect.isNotNull(invocation);
|
||||
Expect.equals(true, invocation.reflectee);
|
||||
}
|
25
tests/lib_2/mirrors/regress_b196606044_test.dart
Normal file
25
tests/lib_2/mirrors/regress_b196606044_test.dart
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// @dart = 2.9
|
||||
|
||||
// Regression test for b/196606044.
|
||||
//
|
||||
// Verifies that instance.type.instanceMembers contains 'isNotEmpty'
|
||||
// member for a List literal.
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
import 'dart:mirrors';
|
||||
|
||||
dynamic object = <int>[1, 2, 3];
|
||||
String name = 'isNotEmpty';
|
||||
|
||||
main() {
|
||||
var instance = reflect(object);
|
||||
var member = instance.type.instanceMembers[new Symbol(name)];
|
||||
Expect.isNotNull(member);
|
||||
var invocation = instance.getField(member.simpleName);
|
||||
Expect.isNotNull(invocation);
|
||||
Expect.equals(true, invocation.reflectee);
|
||||
}
|
Loading…
Reference in a new issue