[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();
     }