[vm/kernel/bytecode] Implement strong mode type checks in bytecode
This includes argument type checks and implicit type checks inserted
by front-end.
Also, lookup of getter functions is fixed for StaticICData constant
pool entry to include non-static getters (StaticICData is used for
all kinds of direct calls including super calls).
Change-Id: Ic265ea6a7fca2bfcc9a96e4ede268ec8e693ff41
Reviewed-on: https://dart-review.googlesource.com/60822
Reviewed-by: Zach Anderson <zra@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
diff --git a/pkg/vm/lib/bytecode/constant_pool.dart b/pkg/vm/lib/bytecode/constant_pool.dart
index b6b0ad1..c0c07ff 100644
--- a/pkg/vm/lib/bytecode/constant_pool.dart
+++ b/pkg/vm/lib/bytecode/constant_pool.dart
@@ -158,6 +158,10 @@
StringReference nativeName;
}
+type ConstantSubtypeTestCache extends ConstantPoolEntry {
+ Byte tag = 24;
+}
+
*/
enum ConstantTag {
@@ -185,6 +189,7 @@
kClosureFunction,
kEndClosureFunctionScope,
kNativeEntry,
+ kSubtypeTestCache,
}
abstract class ConstantPoolEntry {
@@ -251,6 +256,8 @@
return new ConstantEndClosureFunctionScope.readFromBinary(source);
case ConstantTag.kNativeEntry:
return new ConstantNativeEntry.readFromBinary(source);
+ case ConstantTag.kSubtypeTestCache:
+ return new ConstantSubtypeTestCache.readFromBinary(source);
}
throw 'Unexpected constant tag $tag';
}
@@ -1041,6 +1048,31 @@
other is ConstantNativeEntry && this.nativeName == other.nativeName;
}
+class ConstantSubtypeTestCache extends ConstantPoolEntry {
+ ConstantSubtypeTestCache();
+
+ @override
+ ConstantTag get tag => ConstantTag.kSubtypeTestCache;
+
+ @override
+ void writeValueToBinary(BinarySink sink) {}
+
+ ConstantSubtypeTestCache.readFromBinary(BinarySource source);
+
+ @override
+ String toString() => 'SubtypeTestCache';
+
+ // ConstantSubtypeTestCache entries are created per subtype test site and
+ // should not be merged, so ConstantSubtypeTestCache class uses identity
+ // [hashCode] and [operator ==].
+
+ @override
+ int get hashCode => identityHashCode(this);
+
+ @override
+ bool operator ==(other) => identical(this, other);
+}
+
class ConstantPool {
final List<ConstantPoolEntry> entries = <ConstantPoolEntry>[];
final Map<ConstantPoolEntry, int> _canonicalizationCache =
diff --git a/pkg/vm/lib/bytecode/gen_bytecode.dart b/pkg/vm/lib/bytecode/gen_bytecode.dart
index 2eac25e..953ae1c 100644
--- a/pkg/vm/lib/bytecode/gen_bytecode.dart
+++ b/pkg/vm/lib/bytecode/gen_bytecode.dart
@@ -250,8 +250,6 @@
_genPushReceiver();
initializer.accept(this);
- // TODO(alexmarkov): assignability check
-
final int cpIndex = cp.add(new ConstantFieldOffset(field));
asm.emitStoreFieldTOS(cpIndex);
}
@@ -663,10 +661,9 @@
}
asm.emitCheckStack();
- // TODO(alexmarkov): add type checks for parameters
-
final bool isClosure =
node is FunctionDeclaration || node is FunctionExpression;
+
if (isClosure) {
asm.emitPush(locals.closureVarIndexInFrame);
asm.emitLoadFieldTOS(cp.add(new ConstantFieldOffset(closureContext)));
@@ -691,6 +688,10 @@
asm.emitPopLocal(locals.typeArgsVarIndexInFrame);
}
}
+
+ if (isClosure || (node is Procedure && node.isInstanceMember)) {
+ _checkArguments(function);
+ }
}
void _setupInitialContext(FunctionNode function) {
@@ -716,6 +717,44 @@
}
}
+ void _checkArguments(FunctionNode function) {
+ for (var typeParam in function.typeParameters) {
+ if (!typeEnvironment.isTop(typeParam.bound)) {
+ final DartType type = new TypeParameterType(typeParam);
+ _genPushInstantiatorAndFunctionTypeArguments([type, typeParam.bound]);
+ asm.emitPushConstant(cp.add(new ConstantType(type)));
+ asm.emitPushConstant(cp.add(new ConstantType(typeParam.bound)));
+ asm.emitPushConstant(cp.add(new ConstantString(typeParam.name)));
+ asm.emitAssertSubtype();
+ }
+ }
+ function.positionalParameters.forEach(_genArgumentTypeCheck);
+ function.namedParameters.forEach(_genArgumentTypeCheck);
+ }
+
+ void _genArgumentTypeCheck(VariableDeclaration variable) {
+ if (typeEnvironment.isTop(variable.type)) {
+ return;
+ }
+ if (locals.isCaptured(variable)) {
+ asm.emitPush(locals.getOriginalParamSlotIndex(variable));
+ } else {
+ asm.emitPush(locals.getVarIndexInFrame(variable));
+ }
+ _genAssertAssignable(variable.type, name: variable.name);
+ asm.emitDrop1();
+ }
+
+ void _genAssertAssignable(DartType type, {String name = ''}) {
+ assert(!typeEnvironment.isTop(type));
+ _genPushInstantiatorAndFunctionTypeArguments([type]);
+ asm.emitPushConstant(cp.add(new ConstantType(type)));
+ asm.emitPushConstant(cp.add(new ConstantString(name)));
+ bool isIntOk = typeEnvironment.isSubtypeOf(typeEnvironment.intType, type);
+ int subtypeTestCacheCpIndex = cp.add(new ConstantSubtypeTestCache());
+ asm.emitAssertAssignable(isIntOk ? 1 : 0, subtypeTestCacheCpIndex);
+ }
+
void _pushAssemblerState() {
savedAssemblers.add(asm);
asm = new BytecodeAssembler();
@@ -964,25 +1003,20 @@
visitAsExpression(AsExpression node) {
node.operand.accept(this);
- if (node.type == const DynamicType()) {
+ final type = node.type;
+ if (typeEnvironment.isTop(type)) {
return;
}
if (node.isTypeError) {
- // TODO(alexmarkov): type checks
- return;
- }
- if (hasTypeParameters([node.type])) {
- _genPushInstantiatorAndFunctionTypeArguments([node.type]);
+ _genAssertAssignable(type);
} else {
- _genPushNull(); // Instantiator type arguments.
- _genPushNull(); // Function type arguments.
+ _genPushInstantiatorAndFunctionTypeArguments([type]);
+ asm.emitPushConstant(cp.add(new ConstantType(type)));
+ final argDescIndex = cp.add(new ConstantArgDesc(4));
+ final icdataIndex = cp.add(new ConstantICData(
+ InvocationKind.method, objectAs.name, argDescIndex));
+ asm.emitInstanceCall1(4, icdataIndex);
}
- final typeIndex = cp.add(new ConstantType(node.type));
- asm.emitPushConstant(typeIndex);
- final argDescIndex = cp.add(new ConstantArgDesc(4));
- final icdataIndex = cp.add(
- new ConstantICData(InvocationKind.method, objectAs.name, argDescIndex));
- asm.emitInstanceCall1(4, icdataIndex);
}
@override
@@ -1123,7 +1157,6 @@
asm.emitPush(temp);
_genPushInt(i);
node.expressions[i].accept(this);
- // TODO(alexmarkov): assignable check
asm.emitStoreIndexedTOS();
}
@@ -1361,7 +1394,6 @@
_genDupTOS(locals.tempIndexInFrame(node));
final target = node.target;
if (target is Field) {
- // TODO(alexmarkov): assignable check
int cpIndex = cp.add(new ConstantField(target));
asm.emitStoreStaticTOS(cpIndex);
} else {
diff --git a/pkg/vm/testcases/bytecode/closures.dart.expect b/pkg/vm/testcases/bytecode/closures.dart.expect
index 6f72b9f..cd54d28 100644
--- a/pkg/vm/testcases/bytecode/closures.dart.expect
+++ b/pkg/vm/testcases/bytecode/closures.dart.expect
@@ -523,45 +523,45 @@
Push r0
PushConstant CP#4
StoreFieldTOS CP#1
- Allocate CP#20
+ Allocate CP#23
StoreLocal r4
Push r4
- PushConstant CP#18
- StoreFieldTOS CP#21
+ PushConstant CP#7
+ StoreFieldTOS CP#24
Push r4
- PushConstant CP#18
- StoreFieldTOS CP#22
+ PushConstant CP#7
+ StoreFieldTOS CP#25
Push r4
PushConstant CP#5
- StoreFieldTOS CP#23
+ StoreFieldTOS CP#26
Push r4
Push r0
StoreFieldTOS CP#6
PopLocal r3
Push r3
- PushConstant CP#27
- InstanceCall1 2, CP#28
+ PushConstant CP#30
+ InstanceCall1 2, CP#31
Drop1
Push r3
- PushConstant CP#29
- InstanceCall1 2, CP#30
+ PushConstant CP#32
+ InstanceCall1 2, CP#33
Drop1
Push r2
- PushConstant CP#31
- IndirectStaticCall 1, CP#15
+ PushConstant CP#34
+ IndirectStaticCall 1, CP#19
Drop1
Push r0
LoadFieldTOS CP#1
- PushConstant CP#32
- IndirectStaticCall 1, CP#15
+ PushConstant CP#35
+ IndirectStaticCall 1, CP#19
Drop1
Push r0
LoadFieldTOS CP#0
PopLocal r0
Push r0
LoadFieldTOS CP#1
- PushConstant CP#33
- IndirectStaticCall 1, CP#15
+ PushConstant CP#36
+ IndirectStaticCall 1, CP#19
Drop1
Push r0
LoadFieldTOS CP#0
@@ -573,30 +573,30 @@
StoreFieldTOS CP#0
PopLocal r0
Push r0
- PushConstant CP#34
+ PushConstant CP#37
StoreFieldTOS CP#1
- Allocate CP#20
+ Allocate CP#23
StoreLocal r3
Push r3
- PushConstant CP#18
- StoreFieldTOS CP#21
+ PushConstant CP#7
+ StoreFieldTOS CP#24
Push r3
- PushConstant CP#18
- StoreFieldTOS CP#22
+ PushConstant CP#7
+ StoreFieldTOS CP#25
Push r3
- PushConstant CP#35
- StoreFieldTOS CP#23
+ PushConstant CP#38
+ StoreFieldTOS CP#26
Push r3
Push r0
StoreFieldTOS CP#6
PopLocal r2
Push r2
- InstanceCall1 1, CP#38
+ InstanceCall1 1, CP#41
Drop1
Push r0
LoadFieldTOS CP#0
PopLocal r0
- PushConstant CP#18
+ PushConstant CP#7
ReturnTOS
}
ConstantPool {
@@ -607,40 +607,43 @@
[4] = Int 3
[5] = ClosureFunction <anonymous closure> (dart.core::int y) → dart.core::Null;
[6] = FieldOffset dart.core::_Closure::_context
- [7] = ArgDesc num-args 2, num-type-args 0, names []
- [8] = ICData target-name '+', arg-desc CP#7
- [9] = Int 5
- [10] = ICData target-name '>', arg-desc CP#7
- [11] = Bool true
- [12] = Int 4
- [13] = ClosureFunction closure2 () → void;
- [14] = ICData target-name '+', arg-desc CP#7
- [15] = ArgDesc num-args 1, num-type-args 0, names []
- [16] = ICData get target-name 'foo', arg-desc CP#15
- [17] = ICData target-name '+', arg-desc CP#7
- [18] = Null
- [19] = EndClosureFunctionScope
- [20] = Class dart.core::_Closure
- [21] = FieldOffset dart.core::_Closure::_instantiator_type_arguments
- [22] = FieldOffset dart.core::_Closure::_function_type_arguments
- [23] = FieldOffset dart.core::_Closure::_function
- [24] = ICData target-name 'call', arg-desc CP#15
- [25] = StaticICData target 'dart.core::print', arg-desc CP#15
- [26] = EndClosureFunctionScope
- [27] = Int 10
- [28] = ICData target-name 'call', arg-desc CP#7
- [29] = Int 11
- [30] = ICData target-name 'call', arg-desc CP#7
- [31] = StaticICData target 'dart.core::print', arg-desc CP#15
- [32] = StaticICData target 'dart.core::print', arg-desc CP#15
- [33] = StaticICData target 'dart.core::print', arg-desc CP#15
- [34] = Int 42
- [35] = ClosureFunction <anonymous closure> () → dart.core::Null;
- [36] = ICData set target-name 'foo', arg-desc CP#7
- [37] = EndClosureFunctionScope
- [38] = ICData target-name 'call', arg-desc CP#15
+ [7] = Null
+ [8] = Type dart.core::int
+ [9] = String 'y'
+ [10] = SubtypeTestCache
+ [11] = ArgDesc num-args 2, num-type-args 0, names []
+ [12] = ICData target-name '+', arg-desc CP#11
+ [13] = Int 5
+ [14] = ICData target-name '>', arg-desc CP#11
+ [15] = Bool true
+ [16] = Int 4
+ [17] = ClosureFunction closure2 () → void;
+ [18] = ICData target-name '+', arg-desc CP#11
+ [19] = ArgDesc num-args 1, num-type-args 0, names []
+ [20] = ICData get target-name 'foo', arg-desc CP#19
+ [21] = ICData target-name '+', arg-desc CP#11
+ [22] = EndClosureFunctionScope
+ [23] = Class dart.core::_Closure
+ [24] = FieldOffset dart.core::_Closure::_instantiator_type_arguments
+ [25] = FieldOffset dart.core::_Closure::_function_type_arguments
+ [26] = FieldOffset dart.core::_Closure::_function
+ [27] = ICData target-name 'call', arg-desc CP#19
+ [28] = StaticICData target 'dart.core::print', arg-desc CP#19
+ [29] = EndClosureFunctionScope
+ [30] = Int 10
+ [31] = ICData target-name 'call', arg-desc CP#11
+ [32] = Int 11
+ [33] = ICData target-name 'call', arg-desc CP#11
+ [34] = StaticICData target 'dart.core::print', arg-desc CP#19
+ [35] = StaticICData target 'dart.core::print', arg-desc CP#19
+ [36] = StaticICData target 'dart.core::print', arg-desc CP#19
+ [37] = Int 42
+ [38] = ClosureFunction <anonymous closure> () → dart.core::Null;
+ [39] = ICData set target-name 'foo', arg-desc CP#11
+ [40] = EndClosureFunctionScope
+ [41] = ICData target-name 'call', arg-desc CP#19
}
-Closure CP#13 {
+Closure CP#17 {
Entry 3
CheckStack
Push FP[-5]
@@ -655,7 +658,7 @@
LoadFieldTOS CP#0
LoadFieldTOS CP#1
PushConstant CP#3
- InstanceCall1 2, CP#14
+ InstanceCall1 2, CP#18
StoreLocal r2
StoreFieldTOS CP#1
Push r2
@@ -667,16 +670,16 @@
LoadFieldTOS CP#0
LoadFieldTOS CP#0
LoadFieldTOS CP#1
- InstanceCall1 1, CP#16
+ InstanceCall1 1, CP#20
Push r0
LoadFieldTOS CP#0
LoadFieldTOS CP#1
- InstanceCall1 2, CP#17
+ InstanceCall1 2, CP#21
StoreLocal r2
StoreFieldTOS CP#1
Push r2
Drop1
- PushConstant CP#18
+ PushConstant CP#7
ReturnTOS
}
@@ -687,6 +690,13 @@
Push FP[-6]
LoadFieldTOS CP#6
PopLocal r0
+ Push FP[-5]
+ PushConstant CP#7
+ PushConstant CP#7
+ PushConstant CP#8
+ PushConstant CP#9
+ AssertAssignable 1, CP#10
+ Drop1
AllocateContext 1
StoreLocal r1
Push r1
@@ -708,7 +718,7 @@
Push r0
LoadFieldTOS CP#1
PushConstant CP#2
- InstanceCall1 2, CP#8
+ InstanceCall1 2, CP#12
StoreLocal r2
StoreFieldTOS CP#1
Push r2
@@ -717,9 +727,9 @@
LoadFieldTOS CP#0
LoadFieldTOS CP#0
LoadFieldTOS CP#1
- PushConstant CP#9
- InstanceCall1 2, CP#10
- PushConstant CP#11
+ PushConstant CP#13
+ InstanceCall1 2, CP#14
+ PushConstant CP#15
IfNeStrictTOS
Jump L1
AllocateContext 1
@@ -729,41 +739,41 @@
StoreFieldTOS CP#0
PopLocal r0
Push r0
- PushConstant CP#12
+ PushConstant CP#16
StoreFieldTOS CP#1
- Allocate CP#20
+ Allocate CP#23
StoreLocal r2
Push r2
- PushConstant CP#18
- StoreFieldTOS CP#21
+ PushConstant CP#7
+ StoreFieldTOS CP#24
Push r2
- PushConstant CP#18
- StoreFieldTOS CP#22
+ PushConstant CP#7
+ StoreFieldTOS CP#25
Push r2
- PushConstant CP#13
- StoreFieldTOS CP#23
+ PushConstant CP#17
+ StoreFieldTOS CP#26
Push r2
Push r0
StoreFieldTOS CP#6
PopLocal r3
Push r3
- InstanceCall1 1, CP#24
+ InstanceCall1 1, CP#27
Drop1
Push r0
LoadFieldTOS CP#1
- PushConstant CP#25
- IndirectStaticCall 1, CP#15
+ PushConstant CP#28
+ IndirectStaticCall 1, CP#19
Drop1
Push r0
LoadFieldTOS CP#0
PopLocal r0
L1:
- PushConstant CP#18
+ PushConstant CP#7
ReturnTOS
}
-Closure CP#35 {
+Closure CP#38 {
Entry 3
CheckStack
Push FP[-5]
@@ -775,11 +785,11 @@
Push r0
LoadFieldTOS CP#1
StoreLocal r2
- InstanceCall1 2, CP#36
+ InstanceCall1 2, CP#39
Drop1
Push r2
Drop1
- PushConstant CP#18
+ PushConstant CP#7
ReturnTOS
}
@@ -918,7 +928,7 @@
Push r3
Push r0
StoreFieldTOS CP#12
- InstanceCall1 2, CP#24
+ InstanceCall1 2, CP#27
Drop1
Push r0
CloneContext
@@ -926,8 +936,8 @@
Push r0
Push r0
LoadFieldTOS CP#2
- PushConstant CP#25
- InstanceCall1 2, CP#26
+ PushConstant CP#28
+ InstanceCall1 2, CP#29
StoreLocal r3
StoreFieldTOS CP#2
Push r3
@@ -966,11 +976,14 @@
[19] = FieldOffset dart.core::_Closure::_function
[20] = ICData target-name 'add', arg-desc CP#8
[21] = ClosureFunction <anonymous closure> (dart.core::int ii) → dart.core::Null;
- [22] = ICData target-name '+', arg-desc CP#8
- [23] = EndClosureFunctionScope
- [24] = ICData target-name 'add', arg-desc CP#8
- [25] = Int 1
- [26] = ICData target-name '+', arg-desc CP#8
+ [22] = Type dart.core::int
+ [23] = String 'ii'
+ [24] = SubtypeTestCache
+ [25] = ICData target-name '+', arg-desc CP#8
+ [26] = EndClosureFunctionScope
+ [27] = ICData target-name 'add', arg-desc CP#8
+ [28] = Int 1
+ [29] = ICData target-name '+', arg-desc CP#8
}
Closure CP#11 {
Entry 2
@@ -996,12 +1009,19 @@
Push FP[-6]
LoadFieldTOS CP#12
PopLocal r0
+ Push FP[-5]
+ PushConstant CP#14
+ PushConstant CP#14
+ PushConstant CP#22
+ PushConstant CP#23
+ AssertAssignable 1, CP#24
+ Drop1
Push r0
Push FP[-5]
Push r0
LoadFieldTOS CP#0
LoadFieldTOS CP#2
- InstanceCall1 2, CP#22
+ InstanceCall1 2, CP#25
StoreLocal r2
StoreFieldTOS CP#2
Push r2
@@ -1026,94 +1046,104 @@
Entry 5
CheckStack
Push FP[-5]
- InstanceCall1 1, CP#1
+ PushConstant CP#0
+ PushConstant CP#0
+ PushConstant CP#1
+ PushConstant CP#2
+ AssertAssignable 0, CP#3
+ Drop1
+ Push FP[-5]
+ InstanceCall1 1, CP#5
PopLocal r2
L2:
CheckStack
Push r2
- InstanceCall1 1, CP#2
- PushConstant CP#3
+ InstanceCall1 1, CP#6
+ PushConstant CP#7
IfNeStrictTOS
Jump L1
AllocateContext 1
StoreLocal r1
Push r1
Push r0
- StoreFieldTOS CP#4
+ StoreFieldTOS CP#8
PopLocal r0
Push r0
Push r2
- InstanceCall1 1, CP#5
- StoreFieldTOS CP#6
- Allocate CP#14
+ InstanceCall1 1, CP#9
+ StoreFieldTOS CP#10
+ Allocate CP#17
StoreLocal r4
Push r4
- PushConstant CP#12
- StoreFieldTOS CP#15
+ PushConstant CP#0
+ StoreFieldTOS CP#18
Push r4
- PushConstant CP#12
- StoreFieldTOS CP#16
+ PushConstant CP#0
+ StoreFieldTOS CP#19
Push r4
- PushConstant CP#7
- StoreFieldTOS CP#17
+ PushConstant CP#11
+ StoreFieldTOS CP#20
Push r4
Push r0
- StoreFieldTOS CP#8
+ StoreFieldTOS CP#12
PopLocal r3
Push r3
- InstanceCall1 1, CP#18
+ InstanceCall1 1, CP#21
Drop1
Push r0
- LoadFieldTOS CP#6
- PushConstant CP#19
- IndirectStaticCall 1, CP#0
+ LoadFieldTOS CP#10
+ PushConstant CP#22
+ IndirectStaticCall 1, CP#4
Drop1
Push r0
- LoadFieldTOS CP#4
+ LoadFieldTOS CP#8
PopLocal r0
Jump L2
L1:
- PushConstant CP#12
+ PushConstant CP#0
ReturnTOS
}
ConstantPool {
- [0] = ArgDesc num-args 1, num-type-args 0, names []
- [1] = ICData get target-name 'iterator', arg-desc CP#0
- [2] = ICData target-name 'moveNext', arg-desc CP#0
- [3] = Bool true
- [4] = ContextOffset parent
- [5] = ICData get target-name 'current', arg-desc CP#0
- [6] = ContextOffset var [0]
- [7] = ClosureFunction <anonymous closure> () → dart.core::Null;
- [8] = FieldOffset dart.core::_Closure::_context
- [9] = Int 1
- [10] = ArgDesc num-args 2, num-type-args 0, names []
- [11] = ICData target-name '+', arg-desc CP#10
- [12] = Null
- [13] = EndClosureFunctionScope
- [14] = Class dart.core::_Closure
- [15] = FieldOffset dart.core::_Closure::_instantiator_type_arguments
- [16] = FieldOffset dart.core::_Closure::_function_type_arguments
- [17] = FieldOffset dart.core::_Closure::_function
- [18] = ICData target-name 'call', arg-desc CP#0
- [19] = StaticICData target 'dart.core::print', arg-desc CP#0
+ [0] = Null
+ [1] = Type dart.core::List<dart.core::int>
+ [2] = String 'list'
+ [3] = SubtypeTestCache
+ [4] = ArgDesc num-args 1, num-type-args 0, names []
+ [5] = ICData get target-name 'iterator', arg-desc CP#4
+ [6] = ICData target-name 'moveNext', arg-desc CP#4
+ [7] = Bool true
+ [8] = ContextOffset parent
+ [9] = ICData get target-name 'current', arg-desc CP#4
+ [10] = ContextOffset var [0]
+ [11] = ClosureFunction <anonymous closure> () → dart.core::Null;
+ [12] = FieldOffset dart.core::_Closure::_context
+ [13] = Int 1
+ [14] = ArgDesc num-args 2, num-type-args 0, names []
+ [15] = ICData target-name '+', arg-desc CP#14
+ [16] = EndClosureFunctionScope
+ [17] = Class dart.core::_Closure
+ [18] = FieldOffset dart.core::_Closure::_instantiator_type_arguments
+ [19] = FieldOffset dart.core::_Closure::_function_type_arguments
+ [20] = FieldOffset dart.core::_Closure::_function
+ [21] = ICData target-name 'call', arg-desc CP#4
+ [22] = StaticICData target 'dart.core::print', arg-desc CP#4
}
-Closure CP#7 {
+Closure CP#11 {
Entry 3
CheckStack
Push FP[-5]
- LoadFieldTOS CP#8
+ LoadFieldTOS CP#12
PopLocal r0
Push r0
Push r0
- LoadFieldTOS CP#6
- PushConstant CP#9
- InstanceCall1 2, CP#11
+ LoadFieldTOS CP#10
+ PushConstant CP#13
+ InstanceCall1 2, CP#15
StoreLocal r2
- StoreFieldTOS CP#6
+ StoreFieldTOS CP#10
Push r2
Drop1
- PushConstant CP#12
+ PushConstant CP#0
ReturnTOS
}
@@ -1140,24 +1170,24 @@
Push r0
PushConstant CP#1
StoreFieldTOS CP#2
- Allocate CP#9
+ Allocate CP#12
StoreLocal r3
Push r3
- PushConstant CP#7
- StoreFieldTOS CP#10
+ PushConstant CP#5
+ StoreFieldTOS CP#13
Push r3
- PushConstant CP#7
- StoreFieldTOS CP#11
+ PushConstant CP#5
+ StoreFieldTOS CP#14
Push r3
PushConstant CP#3
- StoreFieldTOS CP#12
+ StoreFieldTOS CP#15
Push r3
Push r0
StoreFieldTOS CP#4
PopLocal r2
Push r2
- PushConstant CP#13
- InstanceCall1 2, CP#14
+ PushConstant CP#16
+ InstanceCall1 2, CP#17
Drop1
Push r0
LoadFieldTOS CP#2
@@ -1165,7 +1195,7 @@
Push r0
LoadFieldTOS CP#0
PopLocal r0
- PushConstant CP#7
+ PushConstant CP#5
ReturnTOS
}
ConstantPool {
@@ -1174,16 +1204,19 @@
[2] = ContextOffset var [0]
[3] = ClosureFunction <anonymous closure> (dart.core::int y) → dart.core::Null;
[4] = FieldOffset dart.core::_Closure::_context
- [5] = ArgDesc num-args 2, num-type-args 0, names []
- [6] = ICData target-name '+', arg-desc CP#5
- [7] = Null
- [8] = EndClosureFunctionScope
- [9] = Class dart.core::_Closure
- [10] = FieldOffset dart.core::_Closure::_instantiator_type_arguments
- [11] = FieldOffset dart.core::_Closure::_function_type_arguments
- [12] = FieldOffset dart.core::_Closure::_function
- [13] = Int 3
- [14] = ICData target-name 'call', arg-desc CP#5
+ [5] = Null
+ [6] = Type dart.core::int
+ [7] = String 'y'
+ [8] = SubtypeTestCache
+ [9] = ArgDesc num-args 2, num-type-args 0, names []
+ [10] = ICData target-name '+', arg-desc CP#9
+ [11] = EndClosureFunctionScope
+ [12] = Class dart.core::_Closure
+ [13] = FieldOffset dart.core::_Closure::_instantiator_type_arguments
+ [14] = FieldOffset dart.core::_Closure::_function_type_arguments
+ [15] = FieldOffset dart.core::_Closure::_function
+ [16] = Int 3
+ [17] = ICData target-name 'call', arg-desc CP#9
}
Closure CP#3 {
Entry 3
@@ -1191,16 +1224,23 @@
Push FP[-6]
LoadFieldTOS CP#4
PopLocal r0
+ Push FP[-5]
+ PushConstant CP#5
+ PushConstant CP#5
+ PushConstant CP#6
+ PushConstant CP#7
+ AssertAssignable 1, CP#8
+ Drop1
Push r0
Push r0
LoadFieldTOS CP#2
Push FP[-5]
- InstanceCall1 2, CP#6
+ InstanceCall1 2, CP#10
StoreLocal r2
StoreFieldTOS CP#2
Push r2
Drop1
- PushConstant CP#7
+ PushConstant CP#5
ReturnTOS
}
diff --git a/pkg/vm/testcases/bytecode/type_ops.dart b/pkg/vm/testcases/bytecode/type_ops.dart
index 664dc6e..687d58f 100644
--- a/pkg/vm/testcases/bytecode/type_ops.dart
+++ b/pkg/vm/testcases/bytecode/type_ops.dart
@@ -21,6 +21,8 @@
class D<P, Q> extends C<int, Q, P> {
Map<P, Q> foo;
+ D(tt) : foo = tt;
+
foo2(y) {
if (y is A<P>) {
print('21');
@@ -40,6 +42,22 @@
}
return (z as Map<T2, Q>).values;
}
+
+ Map<P, Q> foo4(w) {
+ List<Map<P, Q>> list = [w];
+ return w;
+ }
+}
+
+List<Iterable> globalVar;
+
+void foo5(x) {
+ globalVar = x;
+}
+
+class E<P extends String> {
+ factory E() => null;
+ void foo6<T extends P, U extends List<T>>(Map<T, U> map) {}
}
main() {}
diff --git a/pkg/vm/testcases/bytecode/type_ops.dart.expect b/pkg/vm/testcases/bytecode/type_ops.dart.expect
index 16f4aaf..eb5a775 100644
--- a/pkg/vm/testcases/bytecode/type_ops.dart.expect
+++ b/pkg/vm/testcases/bytecode/type_ops.dart.expect
@@ -66,25 +66,39 @@
;
}
class D<P extends core::Object = dynamic, Q extends core::Object = dynamic> extends self::C<core::int, self::D::Q, self::D::P> {
- generic-covariant-impl field core::Map<self::D::P, self::D::Q> foo = null;
+ generic-covariant-impl field core::Map<self::D::P, self::D::Q> foo;
[@vm.bytecode=
Bytecode {
Entry 0
CheckStack
+ Push FP[-6]
Push FP[-5]
+ Push FP[-6]
+ LoadFieldTOS CP#0
PushConstant CP#1
- IndirectStaticCall 1, CP#0
- Drop1
PushConstant CP#2
+ PushConstant CP#3
+ AssertAssignable 0, CP#4
+ StoreFieldTOS CP#5
+ Push FP[-6]
+ PushConstant CP#7
+ IndirectStaticCall 1, CP#6
+ Drop1
+ PushConstant CP#1
ReturnTOS
}
ConstantPool {
- [0] = ArgDesc num-args 1, num-type-args 0, names []
- [1] = StaticICData target '#lib::C::', arg-desc CP#0
- [2] = Null
+ [0] = TypeArgumentsFieldOffset #lib::D
+ [1] = Null
+ [2] = Type dart.core::Map<#lib::D::P, #lib::D::Q>
+ [3] = String ''
+ [4] = SubtypeTestCache
+ [5] = FieldOffset #lib::D::foo
+ [6] = ArgDesc num-args 1, num-type-args 0, names []
+ [7] = StaticICData target '#lib::C::', arg-desc CP#6
}
-] synthetic constructor •() → void
- : super self::C::•()
+] constructor •(dynamic tt) → void
+ : self::D::foo = tt as{TypeError} core::Map<self::D::P, self::D::Q>, super self::C::•()
;
[@vm.bytecode=
Bytecode {
@@ -120,8 +134,14 @@
L2:
Push FP[-6]
Push FP[-5]
+ Push FP[-6]
+ LoadFieldTOS CP#0
+ PushConstant CP#1
+ PushConstant CP#13
+ PushConstant CP#14
+ AssertAssignable 0, CP#15
StoreLocal r0
- InstanceCall1 2, CP#14
+ InstanceCall1 2, CP#17
Drop1
Push r0
Drop1
@@ -142,8 +162,11 @@
[10] = ICData target-name 'dart.core::_instanceOf', arg-desc CP#3
[11] = String '22'
[12] = StaticICData target 'dart.core::print', arg-desc CP#7
- [13] = ArgDesc num-args 2, num-type-args 0, names []
- [14] = ICData set target-name 'foo', arg-desc CP#13
+ [13] = Type dart.core::Map<#lib::D::P, #lib::D::Q>
+ [14] = String ''
+ [15] = SubtypeTestCache
+ [16] = ArgDesc num-args 2, num-type-args 0, names []
+ [17] = ICData set target-name 'foo', arg-desc CP#16
}
] method foo2(dynamic y) → dynamic {
if(y is self::A<self::D::P>) {
@@ -223,7 +246,119 @@
}
return (z as core::Map<self::D::foo3::T2, self::D::Q>).{core::Map::values};
}
+[@vm.bytecode=
+Bytecode {
+ Entry 2
+ CheckStack
+ Push FP[-6]
+ LoadFieldTOS CP#0
+ PushConstant CP#1
+ InstantiateTypeArgumentsTOS 1, CP#2
+ StoreLocal r1
+ Push r1
+ PushConstant CP#3
+ CreateArrayTOS
+ StoreLocal r1
+ Push r1
+ PushConstant CP#4
+ Push FP[-5]
+ Push FP[-6]
+ LoadFieldTOS CP#0
+ PushConstant CP#1
+ PushConstant CP#5
+ PushConstant CP#6
+ AssertAssignable 0, CP#7
+ StoreIndexedTOS
+ PushConstant CP#9
+ IndirectStaticCall 2, CP#8
+ PopLocal r0
+ Push FP[-5]
+ Push FP[-6]
+ LoadFieldTOS CP#0
+ PushConstant CP#1
+ PushConstant CP#5
+ PushConstant CP#6
+ AssertAssignable 0, CP#10
+ ReturnTOS
+ PushConstant CP#1
+ ReturnTOS
}
+ConstantPool {
+ [0] = TypeArgumentsFieldOffset #lib::D
+ [1] = Null
+ [2] = TypeArgs [dart.core::Map<#lib::D::P, #lib::D::Q>]
+ [3] = Int 1
+ [4] = Int 0
+ [5] = Type dart.core::Map<#lib::D::P, #lib::D::Q>
+ [6] = String ''
+ [7] = SubtypeTestCache
+ [8] = ArgDesc num-args 1, num-type-args 1, names []
+ [9] = StaticICData target 'dart.core::List::_fromLiteral', arg-desc CP#8
+ [10] = SubtypeTestCache
+}
+] method foo4(dynamic w) → core::Map<self::D::P, self::D::Q> {
+ core::List<core::Map<self::D::P, self::D::Q>> list = <core::Map<self::D::P, self::D::Q>>[w as{TypeError} core::Map<self::D::P, self::D::Q>];
+ return w as{TypeError} core::Map<self::D::P, self::D::Q>;
+ }
+}
+class E<P extends core::String = core::String> extends core::Object {
+[@vm.bytecode=
+Bytecode {
+ Entry 0
+ CheckStack
+ PushConstant CP#0
+ ReturnTOS
+ PushConstant CP#0
+ ReturnTOS
+}
+ConstantPool {
+ [0] = Null
+}
+] static factory •<P extends core::String = dynamic>() → self::E<self::E::•::P>
+ return null;
+[@vm.bytecode=
+Bytecode {
+ Entry 0
+ CheckStack
+ Push FP[-6]
+ LoadFieldTOS CP#0
+ Push FP[-7]
+ PushConstant CP#1
+ PushConstant CP#2
+ PushConstant CP#3
+ AssertSubtype
+ PushConstant CP#4
+ Push FP[-7]
+ PushConstant CP#5
+ PushConstant CP#6
+ PushConstant CP#7
+ AssertSubtype
+ Push FP[-5]
+ PushConstant CP#4
+ Push FP[-7]
+ PushConstant CP#8
+ PushConstant CP#9
+ AssertAssignable 0, CP#10
+ Drop1
+ PushConstant CP#4
+ ReturnTOS
+}
+ConstantPool {
+ [0] = TypeArgumentsFieldOffset #lib::E
+ [1] = Type #lib::E::foo6::T
+ [2] = Type #lib::E::P
+ [3] = String 'T'
+ [4] = Null
+ [5] = Type #lib::E::foo6::U
+ [6] = Type dart.core::List<#lib::E::foo6::T>
+ [7] = String 'U'
+ [8] = Type dart.core::Map<#lib::E::foo6::T, #lib::E::foo6::U>
+ [9] = String 'map'
+ [10] = SubtypeTestCache
+}
+] method foo6<generic-covariant-impl T extends self::E::P = self::E::P, U extends core::List<self::E::foo6::T> = core::List<self::E::P>>(core::Map<self::E::foo6::T, self::E::foo6::U> map) → void {}
+}
+static field core::List<core::Iterable<dynamic>> globalVar;
[@vm.bytecode=
Bytecode {
Entry 0
@@ -290,6 +425,33 @@
}
[@vm.bytecode=
Bytecode {
+ Entry 1
+ CheckStack
+ Push FP[-5]
+ PushConstant CP#0
+ PushConstant CP#0
+ PushConstant CP#1
+ PushConstant CP#2
+ AssertAssignable 0, CP#3
+ StoreLocal r0
+ Push r0
+ StoreStaticTOS CP#4
+ Drop1
+ PushConstant CP#0
+ ReturnTOS
+}
+ConstantPool {
+ [0] = Null
+ [1] = Type dart.core::List<dart.core::Iterable<dynamic>>
+ [2] = String ''
+ [3] = SubtypeTestCache
+ [4] = Field #lib::globalVar
+}
+]static method foo5(dynamic x) → void {
+ self::globalVar = x as{TypeError} core::List<core::Iterable<dynamic>>;
+}
+[@vm.bytecode=
+Bytecode {
Entry 0
CheckStack
PushConstant CP#0
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 68ac2f7..c440292 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -998,6 +998,7 @@
kClosureFunction,
kEndClosureFunctionScope,
kNativeEntry,
+ kSubtypeTestCache,
};
enum InvocationKind {
@@ -1100,7 +1101,7 @@
}
field = H.LookupFieldByKernelField(target);
cls = field.Owner();
- elem = cls.LookupStaticFunction(name);
+ elem = cls.LookupFunctionAllowPrivate(name);
} else {
if ((kind == InvocationKind::method) && H.IsGetter(target)) {
UNIMPLEMENTED(); // TODO(regis): Revisit.
@@ -1340,6 +1341,9 @@
name = H.DartString(builder_->ReadStringReference()).raw();
obj = NativeEntry(function, name);
} break;
+ case ConstantPoolTag::kSubtypeTestCache: {
+ obj = SubtypeTestCache::New();
+ } break;
default:
UNREACHABLE();
}