Skip to content

Commit 824bec5

Browse files
alexmarkovcommit-bot@chromium.org
authored andcommitted
[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 #46893 Issue #46231 Change-Id: Ie24a9f527f66a06118427b7a09e49c03dff93d8e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/210066 Commit-Queue: Alexander Markov <[email protected]> Reviewed-by: Tess Strickland <[email protected]>
1 parent 7760548 commit 824bec5

12 files changed

+206
-40
lines changed

runtime/lib/object.cc

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,14 +101,18 @@ static bool HaveSameRuntimeTypeHelper(Zone* zone,
101101
if (left_cid != right_cid) {
102102
if (IsIntegerClassId(left_cid)) {
103103
return IsIntegerClassId(right_cid);
104-
}
105-
if (IsStringClassId(left_cid)) {
104+
} else if (IsStringClassId(left_cid)) {
106105
return IsStringClassId(right_cid);
107-
}
108-
if (IsTypeClassId(left_cid)) {
106+
} else if (IsTypeClassId(left_cid)) {
109107
return IsTypeClassId(right_cid);
108+
} else if (IsArrayClassId(left_cid)) {
109+
if (!IsArrayClassId(right_cid)) {
110+
return false;
111+
}
112+
// Still need to check type arguments.
113+
} else {
114+
return false;
110115
}
111-
return false;
112116
}
113117

114118
if (left_cid == kClosureCid) {
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// Test for corner cases of 'a.runtimeType == b.runtimeType' pattern
6+
// which is recognized and optimized in AOT mode.
7+
8+
import "package:expect/expect.dart";
9+
10+
@pragma('vm:never-inline')
11+
Object getType(Object obj) => obj.runtimeType;
12+
13+
@pragma('vm:never-inline')
14+
void test(bool expected, Object a, Object b) {
15+
bool result1 = getType(a) == getType(b);
16+
bool result2 = a.runtimeType == b.runtimeType;
17+
Expect.equals(expected, result1);
18+
Expect.equals(expected, result2);
19+
}
20+
21+
typedef Func = void Function();
22+
23+
void main() {
24+
test(true, 0x7fffffffffffffff, int.parse('42'));
25+
test(true, 'hi', String.fromCharCode(1114111));
26+
test(false, 'hi', 1);
27+
test(true, List, Func);
28+
test(true, <int>[1], const <int>[2]);
29+
test(true, const <String>[], List<String>.filled(1, ''));
30+
test(true, <String>[]..add('hi'), List<String>.filled(2, ''));
31+
test(false, <int>[], <String>[]);
32+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// Test for corner cases of 'a.runtimeType == b.runtimeType' pattern
6+
// which is recognized and optimized in AOT mode.
7+
8+
// @dart = 2.9
9+
10+
import "package:expect/expect.dart";
11+
12+
@pragma('vm:never-inline')
13+
Object getType(Object obj) => obj.runtimeType;
14+
15+
@pragma('vm:never-inline')
16+
void test(bool expected, Object a, Object b) {
17+
bool result1 = getType(a) == getType(b);
18+
bool result2 = a.runtimeType == b.runtimeType;
19+
Expect.equals(expected, result1);
20+
Expect.equals(expected, result2);
21+
}
22+
23+
typedef Func = void Function();
24+
25+
void main() {
26+
test(true, 0x7fffffffffffffff, int.parse('42'));
27+
test(true, 'hi', String.fromCharCode(1114111));
28+
test(false, 'hi', 1);
29+
test(true, List, Func);
30+
test(true, <int>[1], const <int>[2]);
31+
test(true, const <String>[], List<String>.filled(1, ''));
32+
test(true, <String>[]..add('hi'), List<String>.filled(2, ''));
33+
test(false, <int>[], <String>[]);
34+
}

runtime/vm/class_id.h

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ typedef uint16_t ClassIdTagType;
7878
V(Mint) \
7979
V(Double) \
8080
V(Bool) \
81-
V(GrowableObjectArray) \
8281
V(Float32x4) \
8382
V(Int32x4) \
8483
V(Float64x2) \
@@ -108,10 +107,14 @@ typedef uint16_t ClassIdTagType;
108107
// TODO(http://dartbug.com/45908): Add ImmutableLinkedHashSet.
109108
#define CLASS_LIST_SETS(V) V(LinkedHashSet)
110109

111-
#define CLASS_LIST_ARRAYS(V) \
110+
#define CLASS_LIST_FIXED_LENGTH_ARRAYS(V) \
112111
V(Array) \
113112
V(ImmutableArray)
114113

114+
#define CLASS_LIST_ARRAYS(V) \
115+
CLASS_LIST_FIXED_LENGTH_ARRAYS(V) \
116+
V(GrowableObjectArray)
117+
115118
#define CLASS_LIST_STRINGS(V) \
116119
V(String) \
117120
V(OneByteString) \
@@ -182,6 +185,7 @@ typedef uint16_t ClassIdTagType;
182185
V(LinkedHashMap) \
183186
V(LinkedHashSet) \
184187
V(Array) \
188+
V(GrowableObjectArray) \
185189
V(String)
186190

187191
#define CLASS_LIST_NO_OBJECT(V) \
@@ -329,11 +333,15 @@ inline bool IsExternalStringClassId(intptr_t index) {
329333
index == kExternalTwoByteStringCid);
330334
}
331335

336+
inline bool IsArrayClassId(intptr_t index) {
337+
COMPILE_ASSERT(kImmutableArrayCid == kArrayCid + 1);
338+
COMPILE_ASSERT(kGrowableObjectArrayCid == kArrayCid + 2);
339+
return (index >= kArrayCid && index <= kGrowableObjectArrayCid);
340+
}
341+
332342
inline bool IsBuiltinListClassId(intptr_t index) {
333343
// Make sure this function is updated when new builtin List types are added.
334-
COMPILE_ASSERT(kImmutableArrayCid == kArrayCid + 1);
335-
return ((index >= kArrayCid && index <= kImmutableArrayCid) ||
336-
(index == kGrowableObjectArrayCid) || IsTypedDataBaseClassId(index) ||
344+
return (IsArrayClassId(index) || IsTypedDataBaseClassId(index) ||
337345
(index == kByteBufferCid));
338346
}
339347

runtime/vm/compiler/asm_intrinsifier_arm.cc

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1198,6 +1198,14 @@ static void JumpIfNotString(Assembler* assembler,
11981198
kIfNotInRange, target);
11991199
}
12001200

1201+
static void JumpIfNotList(Assembler* assembler,
1202+
Register cid,
1203+
Register tmp,
1204+
Label* target) {
1205+
RangeCheck(assembler, cid, tmp, kArrayCid, kGrowableObjectArrayCid,
1206+
kIfNotInRange, target);
1207+
}
1208+
12011209
static void JumpIfType(Assembler* assembler,
12021210
Register cid,
12031211
Register tmp,
@@ -1284,7 +1292,7 @@ static void EquivalentClassIds(Assembler* assembler,
12841292
Register scratch,
12851293
bool testing_instance_cids) {
12861294
Label different_cids, equal_cids_but_generic, not_integer,
1287-
not_integer_or_string;
1295+
not_integer_or_string, not_integer_or_string_or_list;
12881296

12891297
// Check if left hand side is a closure. Closures are handled in the runtime.
12901298
__ CompareImmediate(cid1, kClosureCid);
@@ -1310,7 +1318,8 @@ static void EquivalentClassIds(Assembler* assembler,
13101318
__ b(equal);
13111319

13121320
// Class ids are different. Check if we are comparing two string types (with
1313-
// different representations) or two integer types or two type types.
1321+
// different representations), two integer types, two list types or two type
1322+
// types.
13141323
__ Bind(&different_cids);
13151324
__ CompareImmediate(cid1, kNumPredefinedCids);
13161325
__ b(not_equal, HI);
@@ -1335,9 +1344,20 @@ static void EquivalentClassIds(Assembler* assembler,
13351344

13361345
if (testing_instance_cids) {
13371346
__ Bind(&not_integer_or_string);
1347+
// Check if both are List types.
1348+
JumpIfNotList(assembler, cid1, scratch, &not_integer_or_string_or_list);
1349+
1350+
// First type is a List. Check if the second is a List too.
1351+
JumpIfNotList(assembler, cid2, scratch, not_equal);
1352+
ASSERT(compiler::target::Array::type_arguments_offset() ==
1353+
compiler::target::GrowableObjectArray::type_arguments_offset());
1354+
__ LoadImmediate(scratch, compiler::target::Array::type_arguments_offset());
1355+
__ b(&equal_cids_but_generic);
1356+
1357+
__ Bind(&not_integer_or_string_or_list);
13381358
// Check if the first type is a Type. If it is not then types are not
13391359
// equivalent because they have different class ids and they are not String
1340-
// or integer or Type.
1360+
// or integer or List or Type.
13411361
JumpIfNotType(assembler, cid1, scratch, not_equal);
13421362

13431363
// First type is a Type. Check if the second is a Type too.

runtime/vm/compiler/asm_intrinsifier_arm64.cc

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,6 +1342,14 @@ static void JumpIfNotString(Assembler* assembler,
13421342
kIfNotInRange, target);
13431343
}
13441344

1345+
static void JumpIfNotList(Assembler* assembler,
1346+
Register cid,
1347+
Register tmp,
1348+
Label* target) {
1349+
RangeCheck(assembler, cid, tmp, kArrayCid, kGrowableObjectArrayCid,
1350+
kIfNotInRange, target);
1351+
}
1352+
13451353
static void JumpIfType(Assembler* assembler,
13461354
Register cid,
13471355
Register tmp,
@@ -1432,7 +1440,7 @@ static void EquivalentClassIds(Assembler* assembler,
14321440
Register scratch,
14331441
bool testing_instance_cids) {
14341442
Label different_cids, equal_cids_but_generic, not_integer,
1435-
not_integer_or_string;
1443+
not_integer_or_string, not_integer_or_string_or_list;
14361444

14371445
// Check if left hand side is a closure. Closures are handled in the runtime.
14381446
__ CompareImmediate(cid1, kClosureCid);
@@ -1458,7 +1466,8 @@ static void EquivalentClassIds(Assembler* assembler,
14581466
__ b(equal);
14591467

14601468
// Class ids are different. Check if we are comparing two string types (with
1461-
// different representations) or two integer types or two type types.
1469+
// different representations), two integer types, two list types or two type
1470+
// types.
14621471
__ Bind(&different_cids);
14631472
__ CompareImmediate(cid1, kNumPredefinedCids);
14641473
__ b(not_equal, HI);
@@ -1483,9 +1492,20 @@ static void EquivalentClassIds(Assembler* assembler,
14831492

14841493
if (testing_instance_cids) {
14851494
__ Bind(&not_integer_or_string);
1495+
// Check if both are List types.
1496+
JumpIfNotList(assembler, cid1, scratch, &not_integer_or_string_or_list);
1497+
1498+
// First type is a List. Check if the second is a List too.
1499+
JumpIfNotList(assembler, cid2, scratch, not_equal);
1500+
ASSERT(compiler::target::Array::type_arguments_offset() ==
1501+
compiler::target::GrowableObjectArray::type_arguments_offset());
1502+
__ LoadImmediate(scratch, compiler::target::Array::type_arguments_offset());
1503+
__ b(&equal_cids_but_generic);
1504+
1505+
__ Bind(&not_integer_or_string_or_list);
14861506
// Check if the first type is a Type. If it is not then types are not
14871507
// equivalent because they have different class ids and they are not String
1488-
// or integer or Type.
1508+
// or integer or List or Type.
14891509
JumpIfNotType(assembler, cid1, scratch, not_equal);
14901510

14911511
// First type is a Type. Check if the second is a Type too.

runtime/vm/compiler/asm_intrinsifier_ia32.cc

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,6 +1281,11 @@ static void JumpIfNotString(Assembler* assembler, Register cid, Label* target) {
12811281
kIfNotInRange, target);
12821282
}
12831283

1284+
static void JumpIfNotList(Assembler* assembler, Register cid, Label* target) {
1285+
RangeCheck(assembler, cid, kArrayCid, kGrowableObjectArrayCid, kIfNotInRange,
1286+
target);
1287+
}
1288+
12841289
static void JumpIfType(Assembler* assembler, Register cid, Label* target) {
12851290
RangeCheck(assembler, cid, kTypeCid, kFunctionTypeCid, kIfInRange, target);
12861291
}
@@ -1370,7 +1375,7 @@ static void EquivalentClassIds(Assembler* assembler,
13701375
Register scratch,
13711376
bool testing_instance_cids) {
13721377
Label different_cids, equal_cids_but_generic, not_integer,
1373-
not_integer_or_string;
1378+
not_integer_or_string, not_integer_or_string_or_list;
13741379

13751380
// Check if left hand side is a closure. Closures are handled in the runtime.
13761381
__ cmpl(cid1, Immediate(kClosureCid));
@@ -1392,11 +1397,12 @@ static void EquivalentClassIds(Assembler* assembler,
13921397
scratch,
13931398
target::Class::host_type_arguments_field_offset_in_words_offset()));
13941399
__ cmpl(scratch, Immediate(target::Class::kNoTypeArguments));
1395-
__ j(NOT_EQUAL, &equal_cids_but_generic, Assembler::kNearJump);
1400+
__ j(NOT_EQUAL, &equal_cids_but_generic);
13961401
__ jmp(equal);
13971402

13981403
// Class ids are different. Check if we are comparing two string types (with
1399-
// different representations) or two integer types or two type types.
1404+
// different representations), two integer types, two list types or two type
1405+
// types.
14001406
__ Bind(&different_cids);
14011407
__ cmpl(cid1, Immediate(kNumPredefinedCids));
14021408
__ j(ABOVE_EQUAL, not_equal);
@@ -1406,25 +1412,42 @@ static void EquivalentClassIds(Assembler* assembler,
14061412
JumpIfNotInteger(assembler, scratch, &not_integer);
14071413

14081414
// First type is an integer. Check if the second is an integer too.
1409-
JumpIfInteger(assembler, cid2, equal);
1415+
__ movl(scratch, cid2);
1416+
JumpIfInteger(assembler, scratch, equal);
14101417
// Integer types are only equivalent to other integer types.
14111418
__ jmp(not_equal);
14121419

14131420
__ Bind(&not_integer);
14141421
// Check if both are String types.
1415-
JumpIfNotString(assembler, cid1,
1422+
__ movl(scratch, cid1);
1423+
JumpIfNotString(assembler, scratch,
14161424
testing_instance_cids ? &not_integer_or_string : not_equal);
14171425

14181426
// First type is a String. Check if the second is a String too.
1419-
JumpIfString(assembler, cid2, equal);
1427+
__ movl(scratch, cid2);
1428+
JumpIfString(assembler, scratch, equal);
14201429
// String types are only equivalent to other String types.
14211430
__ jmp(not_equal);
14221431

14231432
if (testing_instance_cids) {
14241433
__ Bind(&not_integer_or_string);
1434+
// Check if both are List types.
1435+
__ movl(scratch, cid1);
1436+
JumpIfNotList(assembler, scratch, &not_integer_or_string_or_list);
1437+
1438+
// First type is a List. Check if the second is a List too.
1439+
__ movl(scratch, cid2);
1440+
JumpIfNotList(assembler, scratch, not_equal);
1441+
ASSERT(compiler::target::Array::type_arguments_offset() ==
1442+
compiler::target::GrowableObjectArray::type_arguments_offset());
1443+
__ movl(scratch,
1444+
Immediate(compiler::target::Array::type_arguments_offset()));
1445+
__ jmp(&equal_cids_but_generic, Assembler::kNearJump);
1446+
1447+
__ Bind(&not_integer_or_string_or_list);
14251448
// Check if the first type is a Type. If it is not then types are not
14261449
// equivalent because they have different class ids and they are not String
1427-
// or integer or Type.
1450+
// or integer or List or Type.
14281451
JumpIfNotType(assembler, cid1, not_equal);
14291452

14301453
// First type is a Type. Check if the second is a Type too.

0 commit comments

Comments
 (0)