Version 2.14.0-78.0.dev

Merge commit 'df8024189956eb428876b47f8837072617dce08b' into 'dev'
diff --git a/pkg/analysis_server/test/lsp/code_actions_assists_test.dart b/pkg/analysis_server/test/lsp/code_actions_assists_test.dart
index 65eaa13..67323f9 100644
--- a/pkg/analysis_server/test/lsp/code_actions_assists_test.dart
+++ b/pkg/analysis_server/test/lsp/code_actions_assists_test.dart
@@ -2,6 +2,8 @@
 // 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.
 
+import 'dart:convert';
+
 import 'package:analysis_server/lsp_protocol/protocol_custom_generated.dart';
 import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
 import 'package:analysis_server/lsp_protocol/protocol_special.dart';
@@ -108,6 +110,50 @@
     expect(contents[mainFilePath], equals(expectedContent));
   }
 
+  Future<void> test_errorMessage_invalidIntegers() async {
+    // A VS Code code coverage extension has been seen to use Number.MAX_VALUE
+    // for the character position and resulted in:
+    //
+    //     type 'double' is not a subtype of type 'int'
+    //
+    // This test ensures the error message for these invalid params is clearer,
+    // indicating this is not a valid (Dart) int.
+    // https://github.com/dart-lang/sdk/issues/42786
+
+    newFile(mainFilePath);
+    await initialize();
+
+    final request = makeRequest(
+      Method.textDocument_codeAction,
+      _RawParams('''
+      {
+        "textDocument": {
+          "uri": "${mainFileUri.toString()}"
+        },
+        "range": {
+          "start": {
+            "line": 3,
+            "character": 2
+          },
+          "end": {
+            "line": 3,
+            "character": 1.7976931348623157e+308
+          }
+        }
+      }
+      '''),
+    );
+    final resp = await sendRequestToServer(request);
+    final error = resp.error!;
+    expect(error.code, equals(ErrorCodes.InvalidParams));
+    expect(
+        error.message,
+        allOf([
+          contains('Invalid params for textDocument/codeAction'),
+          contains('params.range.end.character must be of type int'),
+        ]));
+  }
+
   Future<void> test_nonDartFile() async {
     newFile(pubspecFilePath, content: simplePubspecContent);
     await initialize(
@@ -288,3 +334,12 @@
             .toList(),
       );
 }
+
+class _RawParams extends ToJsonable {
+  final String _json;
+
+  _RawParams(this._json);
+
+  @override
+  Object toJson() => jsonDecode(_json);
+}
diff --git a/pkg/kernel/lib/transformations/async.dart b/pkg/kernel/lib/transformations/async.dart
index f7b74c7..d80784a 100644
--- a/pkg/kernel/lib/transformations/async.dart
+++ b/pkg/kernel/lib/transformations/async.dart
@@ -159,6 +159,7 @@
   TreeNode visitInvalidExpression(InvalidExpression expr) => nullary(expr);
   TreeNode visitSuperPropertyGet(SuperPropertyGet expr) => nullary(expr);
   TreeNode visitStaticGet(StaticGet expr) => nullary(expr);
+  TreeNode visitStaticTearOff(StaticTearOff expr) => nullary(expr);
   TreeNode visitRethrow(Rethrow expr) => nullary(expr);
 
   // Getting a final or const variable is not an effect so it can be evaluated
@@ -211,15 +212,32 @@
     });
   }
 
+  @override
   TreeNode visitVariableSet(VariableSet expr) => unary(expr);
+  @override
   TreeNode visitPropertyGet(PropertyGet expr) => unary(expr);
+  @override
+  TreeNode visitInstanceGet(InstanceGet expr) => unary(expr);
+  @override
+  TreeNode visitDynamicGet(DynamicGet expr) => unary(expr);
+  @override
+  TreeNode visitInstanceTearOff(InstanceTearOff expr) => unary(expr);
+  @override
+  TreeNode visitFunctionTearOff(FunctionTearOff expr) => unary(expr);
+  @override
   TreeNode visitSuperPropertySet(SuperPropertySet expr) => unary(expr);
+  @override
   TreeNode visitStaticSet(StaticSet expr) => unary(expr);
+  @override
   TreeNode visitNot(Not expr) => unary(expr);
+  @override
   TreeNode visitIsExpression(IsExpression expr) => unary(expr);
+  @override
   TreeNode visitAsExpression(AsExpression expr) => unary(expr);
+  @override
   TreeNode visitThrow(Throw expr) => unary(expr);
 
+  @override
   TreeNode visitPropertySet(PropertySet expr) {
     return transformTreeNode(expr, () {
       expr.value = transform(expr.value)..parent = expr;
@@ -227,6 +245,22 @@
     });
   }
 
+  @override
+  TreeNode visitInstanceSet(InstanceSet expr) {
+    return transformTreeNode(expr, () {
+      expr.value = transform(expr.value)..parent = expr;
+      expr.receiver = transform(expr.receiver)..parent = expr;
+    });
+  }
+
+  @override
+  TreeNode visitDynamicSet(DynamicSet expr) {
+    return transformTreeNode(expr, () {
+      expr.value = transform(expr.value)..parent = expr;
+      expr.receiver = transform(expr.receiver)..parent = expr;
+    });
+  }
+
   TreeNode visitArguments(Arguments args) {
     for (var named in args.named.reversed) {
       named.value = transform(named.value)..parent = named;
@@ -240,6 +274,7 @@
     return args;
   }
 
+  @override
   TreeNode visitMethodInvocation(MethodInvocation expr) {
     return transformTreeNode(expr, () {
       visitArguments(expr.arguments);
@@ -247,6 +282,48 @@
     });
   }
 
+  @override
+  TreeNode visitInstanceInvocation(InstanceInvocation expr) {
+    return transformTreeNode(expr, () {
+      visitArguments(expr.arguments);
+      expr.receiver = transform(expr.receiver)..parent = expr;
+    });
+  }
+
+  @override
+  TreeNode visitLocalFunctionInvocation(LocalFunctionInvocation expr) {
+    return transformTreeNode(expr, () {
+      visitArguments(expr.arguments);
+    });
+  }
+
+  @override
+  TreeNode visitDynamicInvocation(DynamicInvocation expr) {
+    return transformTreeNode(expr, () {
+      visitArguments(expr.arguments);
+      expr.receiver = transform(expr.receiver)..parent = expr;
+    });
+  }
+
+  @override
+  TreeNode visitFunctionInvocation(FunctionInvocation expr) {
+    return transformTreeNode(expr, () {
+      visitArguments(expr.arguments);
+      expr.receiver = transform(expr.receiver)..parent = expr;
+    });
+  }
+
+  @override
+  TreeNode visitEqualsNull(EqualsNull expr) => unary(expr);
+
+  @override
+  TreeNode visitEqualsCall(EqualsCall expr) {
+    return transformTreeNode(expr, () {
+      expr.right = transform(expr.right)..parent = expr;
+      expr.left = transform(expr.left)..parent = expr;
+    });
+  }
+
   TreeNode visitSuperMethodInvocation(SuperMethodInvocation expr) {
     return transformTreeNode(expr, () {
       visitArguments(expr.arguments);
diff --git a/pkg/vm/lib/transformations/call_site_annotator.dart b/pkg/vm/lib/transformations/call_site_annotator.dart
index e3722a1..c843506 100644
--- a/pkg/vm/lib/transformations/call_site_annotator.dart
+++ b/pkg/vm/lib/transformations/call_site_annotator.dart
@@ -55,6 +55,11 @@
         receiverType: receiver.getStaticType(_staticTypeContext!));
   }
 
+  void annotateWithFunctionType(TreeNode node, FunctionType type) {
+    _metadata.mapping[node] =
+        new CallSiteAttributesMetadata(receiverType: type);
+  }
+
   @override
   visitPropertySet(PropertySet node) {
     super.visitPropertySet(node);
@@ -65,6 +70,15 @@
   }
 
   @override
+  visitInstanceSet(InstanceSet node) {
+    super.visitInstanceSet(node);
+
+    if (hasGenericCovariantParameters(node.interfaceTarget)) {
+      annotateWithType(node, node.receiver);
+    }
+  }
+
+  @override
   visitMethodInvocation(MethodInvocation node) {
     super.visitMethodInvocation(node);
 
@@ -76,6 +90,46 @@
     }
   }
 
+  @override
+  visitInstanceInvocation(InstanceInvocation node) {
+    super.visitInstanceInvocation(node);
+
+    // TODO(34162): We don't need to save the type here for calls, just whether
+    // or not it's a statically-checked call.
+    if (hasGenericCovariantParameters(node.interfaceTarget)) {
+      annotateWithType(node, node.receiver);
+    }
+  }
+
+  @override
+  visitLocalFunctionInvocation(LocalFunctionInvocation node) {
+    super.visitLocalFunctionInvocation(node);
+
+    // TODO(34162): We don't need to save the type here for calls, just whether
+    // or not it's a statically-checked call.
+    annotateWithFunctionType(node, node.functionType);
+  }
+
+  @override
+  visitFunctionInvocation(FunctionInvocation node) {
+    super.visitFunctionInvocation(node);
+
+    // TODO(34162): We don't need to save the type here for calls, just whether
+    // or not it's a statically-checked call.
+    annotateWithType(node, node.receiver);
+  }
+
+  @override
+  visitEqualsCall(EqualsCall node) {
+    super.visitEqualsCall(node);
+
+    // TODO(34162): We don't need to save the type here for calls, just whether
+    // or not it's a statically-checked call.
+    if (hasGenericCovariantParameters(node.interfaceTarget)) {
+      annotateWithType(node, node.left);
+    }
+  }
+
   /// Return [true] if the given list of [VariableDeclaration] contains
   /// any annotated with generic-covariant-impl.
   static bool containsGenericCovariantImpl(List<VariableDeclaration> decls) =>
diff --git a/pkg/vm/lib/transformations/devirtualization.dart b/pkg/vm/lib/transformations/devirtualization.dart
index a81d089..16189aa 100644
--- a/pkg/vm/lib/transformations/devirtualization.dart
+++ b/pkg/vm/lib/transformations/devirtualization.dart
@@ -101,11 +101,8 @@
     super.visitLibrary(node);
   }
 
-  @override
-  visitMethodInvocation(MethodInvocation node) {
-    super.visitMethodInvocation(node);
-
-    final Member target = node.interfaceTarget;
+  void _handleMethodInvocation(
+      TreeNode node, Member target, Arguments arguments) {
     if (target != null && !isMethod(target)) {
       return;
     }
@@ -116,17 +113,42 @@
     // check into an assertion once front-end implements all override checks.
     if ((directCall != null) &&
         isMethod(directCall.target) &&
-        isLegalTargetForMethodInvocation(directCall.target, node.arguments) &&
+        isLegalTargetForMethodInvocation(directCall.target, arguments) &&
         !hasExtraTargetForNull(directCall)) {
       makeDirectCall(node, target, directCall);
     }
   }
 
   @override
-  visitPropertyGet(PropertyGet node) {
-    super.visitPropertyGet(node);
+  visitMethodInvocation(MethodInvocation node) {
+    super.visitMethodInvocation(node);
+    _handleMethodInvocation(node, node.interfaceTarget, node.arguments);
+  }
 
-    final Member target = node.interfaceTarget;
+  @override
+  visitInstanceInvocation(InstanceInvocation node) {
+    super.visitInstanceInvocation(node);
+    _handleMethodInvocation(node, node.interfaceTarget, node.arguments);
+  }
+
+  @override
+  visitDynamicInvocation(DynamicInvocation node) {
+    super.visitDynamicInvocation(node);
+    _handleMethodInvocation(node, null, node.arguments);
+  }
+
+  @override
+  visitEqualsCall(EqualsCall node) {
+    super.visitEqualsCall(node);
+
+    final target = node.interfaceTarget;
+    final DirectCallMetadata directCall = getDirectCall(node, target);
+    if (directCall != null && !directCall.checkReceiverForNull) {
+      makeDirectCall(node, target, directCall);
+    }
+  }
+
+  void _handlePropertyGet(TreeNode node, Member target) {
     if (target != null && !isFieldOrGetter(target)) {
       return;
     }
@@ -141,16 +163,48 @@
   }
 
   @override
-  visitPropertySet(PropertySet node) {
-    super.visitPropertySet(node);
+  visitPropertyGet(PropertyGet node) {
+    super.visitPropertyGet(node);
+    _handlePropertyGet(node, node.interfaceTarget);
+  }
 
-    final Member target = node.interfaceTarget;
+  @override
+  visitInstanceGet(InstanceGet node) {
+    super.visitInstanceGet(node);
+    _handlePropertyGet(node, node.interfaceTarget);
+  }
+
+  @override
+  visitDynamicGet(DynamicGet node) {
+    super.visitDynamicGet(node);
+    _handlePropertyGet(node, null);
+  }
+
+  void _handlePropertySet(TreeNode node, Member target) {
     final DirectCallMetadata directCall =
         getDirectCall(node, target, setter: true);
     if (directCall != null) {
       makeDirectCall(node, target, directCall);
     }
   }
+
+  @override
+  visitPropertySet(PropertySet node) {
+    super.visitPropertySet(node);
+    _handlePropertySet(node, node.interfaceTarget);
+  }
+
+  @override
+  visitInstanceSet(InstanceSet node) {
+    super.visitInstanceSet(node);
+    _handlePropertySet(node, node.interfaceTarget);
+  }
+
+  @override
+  visitDynamicSet(DynamicSet node) {
+    super.visitDynamicSet(node);
+    _handlePropertySet(node, null);
+  }
 }
 
 /// Devirtualization based on the closed-world class hierarchy analysis.
diff --git a/pkg/vm/lib/transformations/ffi_use_sites.dart b/pkg/vm/lib/transformations/ffi_use_sites.dart
index a094f53..153a07a 100644
--- a/pkg/vm/lib/transformations/ffi_use_sites.dart
+++ b/pkg/vm/lib/transformations/ffi_use_sites.dart
@@ -704,6 +704,44 @@
     return node;
   }
 
+  @override
+  visitInstanceInvocation(InstanceInvocation node) {
+    super.visitInstanceInvocation(node);
+
+    final Member target = node.interfaceTarget;
+    try {
+      if (target == elementAtMethod) {
+        final DartType pointerType =
+            node.receiver.getStaticType(_staticTypeContext);
+        final DartType nativeType = _pointerTypeGetTypeArg(pointerType);
+
+        _ensureNativeTypeValid(nativeType, node, allowCompounds: true);
+
+        Expression inlineSizeOf = _inlineSizeOf(nativeType);
+        if (inlineSizeOf != null) {
+          // Generates `receiver.offsetBy(inlineSizeOfExpression)`.
+          return MethodInvocation(
+              node.receiver,
+              offsetByMethod.name,
+              Arguments([
+                MethodInvocation(
+                    node.arguments.positional.single,
+                    numMultiplication.name,
+                    Arguments([inlineSizeOf]),
+                    numMultiplication)
+              ]),
+              offsetByMethod);
+        }
+      }
+    } on _FfiStaticTypeError {
+      // It's OK to swallow the exception because the diagnostics issued will
+      // cause compilation to fail. By continuing, we can report more
+      // diagnostics before compilation ends.
+    }
+
+    return node;
+  }
+
   DartType _pointerTypeGetTypeArg(DartType pointerType) {
     return pointerType is InterfaceType ? pointerType.typeArguments[0] : null;
   }
diff --git a/pkg/vm/lib/transformations/mixin_deduplication.dart b/pkg/vm/lib/transformations/mixin_deduplication.dart
index a328961..8cfc503 100644
--- a/pkg/vm/lib/transformations/mixin_deduplication.dart
+++ b/pkg/vm/lib/transformations/mixin_deduplication.dart
@@ -172,18 +172,51 @@
   }
 
   @override
+  visitInstanceGet(InstanceGet node) {
+    node.interfaceTarget = _resolveNewInterfaceTarget(node.interfaceTarget)!;
+    super.visitInstanceGet(node);
+  }
+
+  @override
+  visitInstanceTearOff(InstanceTearOff node) {
+    node.interfaceTarget =
+        _resolveNewInterfaceTarget(node.interfaceTarget) as Procedure;
+    super.visitInstanceTearOff(node);
+  }
+
+  @override
   visitPropertySet(PropertySet node) {
     node.interfaceTarget = _resolveNewInterfaceTarget(node.interfaceTarget);
     super.visitPropertySet(node);
   }
 
   @override
+  visitInstanceSet(InstanceSet node) {
+    node.interfaceTarget = _resolveNewInterfaceTarget(node.interfaceTarget)!;
+    super.visitInstanceSet(node);
+  }
+
+  @override
   visitMethodInvocation(MethodInvocation node) {
     node.interfaceTarget = _resolveNewInterfaceTarget(node.interfaceTarget);
     super.visitMethodInvocation(node);
   }
 
   @override
+  visitInstanceInvocation(InstanceInvocation node) {
+    node.interfaceTarget =
+        _resolveNewInterfaceTarget(node.interfaceTarget) as Procedure;
+    super.visitInstanceInvocation(node);
+  }
+
+  @override
+  visitEqualsCall(EqualsCall node) {
+    node.interfaceTarget =
+        _resolveNewInterfaceTarget(node.interfaceTarget) as Procedure;
+    super.visitEqualsCall(node);
+  }
+
+  @override
   visitSuperPropertyGet(SuperPropertyGet node) {
     node.interfaceTarget = _resolveNewInterfaceTarget(node.interfaceTarget);
     super.visitSuperPropertyGet(node);
diff --git a/pkg/vm/lib/transformations/no_dynamic_invocations_annotator.dart b/pkg/vm/lib/transformations/no_dynamic_invocations_annotator.dart
index 9b7e27c..0f9284b 100644
--- a/pkg/vm/lib/transformations/no_dynamic_invocations_annotator.dart
+++ b/pkg/vm/lib/transformations/no_dynamic_invocations_annotator.dart
@@ -190,6 +190,28 @@
   }
 
   @override
+  visitInstanceInvocation(InstanceInvocation node) {
+    super.visitInstanceInvocation(node);
+    if (node.receiver is! ThisExpression) {
+      nonThisSelectors.add(new Selector.doInvoke(node.name));
+    }
+  }
+
+  @override
+  visitDynamicInvocation(DynamicInvocation node) {
+    super.visitDynamicInvocation(node);
+    dynamicSelectors.add(new Selector.doInvoke(node.name));
+  }
+
+  @override
+  visitEqualsCall(EqualsCall node) {
+    super.visitEqualsCall(node);
+    if (node.left is! ThisExpression) {
+      nonThisSelectors.add(new Selector.doInvoke(Name('==')));
+    }
+  }
+
+  @override
   visitPropertyGet(PropertyGet node) {
     super.visitPropertyGet(node);
 
@@ -209,6 +231,29 @@
   }
 
   @override
+  visitInstanceGet(InstanceGet node) {
+    super.visitInstanceGet(node);
+    if (node.receiver is! ThisExpression) {
+      nonThisSelectors.add(new Selector.doGet(node.name));
+    }
+  }
+
+  @override
+  visitDynamicGet(DynamicGet node) {
+    super.visitDynamicGet(node);
+    dynamicSelectors.add(new Selector.doGet(node.name));
+  }
+
+  @override
+  visitInstanceTearOff(InstanceTearOff node) {
+    super.visitInstanceTearOff(node);
+    if (node.receiver is! ThisExpression) {
+      nonThisSelectors.add(new Selector.doGet(node.name));
+    }
+    tearOffSelectors.add(new Selector.doInvoke(node.name));
+  }
+
+  @override
   visitPropertySet(PropertySet node) {
     super.visitPropertySet(node);
 
@@ -221,4 +266,18 @@
       }
     }
   }
+
+  @override
+  visitInstanceSet(InstanceSet node) {
+    super.visitInstanceSet(node);
+    if (node.receiver is! ThisExpression) {
+      nonThisSelectors.add(new Selector.doSet(node.name));
+    }
+  }
+
+  @override
+  visitDynamicSet(DynamicSet node) {
+    super.visitDynamicSet(node);
+    dynamicSelectors.add(new Selector.doSet(node.name));
+  }
 }
diff --git a/pkg/vm/lib/transformations/type_flow/protobuf_handler.dart b/pkg/vm/lib/transformations/type_flow/protobuf_handler.dart
index 7643e45..865f56d 100644
--- a/pkg/vm/lib/transformations/type_flow/protobuf_handler.dart
+++ b/pkg/vm/lib/transformations/type_flow/protobuf_handler.dart
@@ -143,7 +143,8 @@
     Statistics.protobufMetadataFieldsPruned += cls.numberOfFieldsPruned;
   }
 
-  bool _isUnusedMetadata(_MessageClass cls, MethodInvocation node) {
+  bool _isUnusedMetadataMethodInvocation(
+      _MessageClass cls, MethodInvocation node) {
     if (node.interfaceTarget != null &&
         node.interfaceTarget.enclosingClass == _builderInfoClass &&
         fieldAddingMethods.contains(node.name.text)) {
@@ -152,6 +153,15 @@
     }
     return false;
   }
+
+  bool _isUnusedMetadata(_MessageClass cls, InstanceInvocation node) {
+    if (node.interfaceTarget.enclosingClass == _builderInfoClass &&
+        fieldAddingMethods.contains(node.name.text)) {
+      final tagNumber = (node.arguments.positional[0] as IntLiteral).value;
+      return !cls._usedTags.contains(tagNumber);
+    }
+    return false;
+  }
 }
 
 class _MessageClass {
@@ -170,7 +180,7 @@
 
   @override
   TreeNode visitMethodInvocation(MethodInvocation node) {
-    if (!ph._isUnusedMetadata(cls, node)) {
+    if (!ph._isUnusedMetadataMethodInvocation(cls, node)) {
       super.visitMethodInvocation(node);
       return node;
     }
@@ -197,4 +207,34 @@
         ph._builderInfoAddMethod)
       ..fileOffset = node.fileOffset;
   }
+
+  @override
+  TreeNode visitInstanceInvocation(InstanceInvocation node) {
+    if (!ph._isUnusedMetadata(cls, node)) {
+      super.visitInstanceInvocation(node);
+      return node;
+    }
+    // Replace the field metadata method with a dummy call to
+    // `BuilderInfo.add`. This is to preserve the index calculations when
+    // removing a field.
+    // Change the tag-number to 0. Otherwise the decoder will get confused.
+    ++numberOfFieldsPruned;
+    return MethodInvocation(
+        node.receiver,
+        ph._builderInfoAddMethod.name,
+        Arguments(
+          <Expression>[
+            IntLiteral(0), // tagNumber
+            NullLiteral(), // name
+            NullLiteral(), // fieldType
+            NullLiteral(), // defaultOrMaker
+            NullLiteral(), // subBuilder
+            NullLiteral(), // valueOf
+            NullLiteral(), // enumValues
+          ],
+          types: <DartType>[const NullType()],
+        ),
+        ph._builderInfoAddMethod)
+      ..fileOffset = node.fileOffset;
+  }
 }
diff --git a/pkg/vm/lib/transformations/type_flow/signature_shaking.dart b/pkg/vm/lib/transformations/type_flow/signature_shaking.dart
index c6c7608..709604c 100644
--- a/pkg/vm/lib/transformations/type_flow/signature_shaking.dart
+++ b/pkg/vm/lib/transformations/type_flow/signature_shaking.dart
@@ -233,7 +233,8 @@
         shaker.typeFlowAnalysis.nativeCodeOracle
             .isMemberReferencedFromNativeCode(member) ||
         shaker.typeFlowAnalysis.nativeCodeOracle.isRecognized(member) ||
-        getExternalName(member) != null) {
+        getExternalName(member) != null ||
+        member.name.text == '==') {
       info.eligible = false;
     }
   }
@@ -302,6 +303,12 @@
   }
 
   @override
+  void visitInstanceInvocation(InstanceInvocation node) {
+    collectCall(node.interfaceTarget, node.arguments);
+    super.visitInstanceInvocation(node);
+  }
+
+  @override
   void visitSuperMethodInvocation(SuperMethodInvocation node) {
     collectCall(node.interfaceTarget, node.arguments);
     super.visitSuperMethodInvocation(node);
@@ -639,6 +646,12 @@
   }
 
   @override
+  void visitInstanceInvocation(InstanceInvocation node) {
+    super.visitInstanceInvocation(node);
+    transformCall(node.interfaceTarget, node, node.receiver, node.arguments);
+  }
+
+  @override
   void visitSuperMethodInvocation(SuperMethodInvocation node) {
     super.visitSuperMethodInvocation(node);
     transformCall(node.interfaceTarget, node, null, node.arguments);
diff --git a/pkg/vm/lib/transformations/type_flow/summary_collector.dart b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
index 34c375b..a20c840 100644
--- a/pkg/vm/lib/transformations/type_flow/summary_collector.dart
+++ b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
@@ -1131,7 +1131,8 @@
 
   TypeExpr _makeNarrowNotNull(TreeNode node, TypeExpr arg) {
     assert(node is NullCheck ||
-        node is MethodInvocation && isComparisonWithNull(node));
+        node is MethodInvocation && isComparisonWithNull(node) ||
+        node is EqualsNull);
     if (arg is NarrowNotNull) {
       nullTests[node] = arg;
       return arg;
@@ -1388,6 +1389,40 @@
         _variableValues = null;
         return;
       }
+    } else if (node is EqualsCall && node.left is VariableGet) {
+      final lhs = node.left as VariableGet;
+      final rhs = node.right;
+      if ((rhs is IntLiteral &&
+              _isSubtype(lhs.variable.type,
+                  _environment.coreTypes.intLegacyRawType)) ||
+          (rhs is StringLiteral &&
+              _isSubtype(lhs.variable.type,
+                  _environment.coreTypes.stringLegacyRawType)) ||
+          (rhs is ConstantExpression &&
+              !_hasOverriddenEquals(lhs.variable.type))) {
+        // 'x == c', where x is a variable and c is a constant.
+        _addUse(_visit(node));
+        final int varIndex = _variablesInfo.varIndex[lhs.variable];
+        if (_variableCells[varIndex] == null) {
+          trueState[varIndex] = _visit(rhs);
+        }
+        _variableValues = null;
+        return;
+      }
+    } else if (node is EqualsNull && node.expression is VariableGet) {
+      final lhs = node.expression as VariableGet;
+      // 'x == null', where x is a variable.
+      final expr = _visit(lhs);
+      _makeCall(node, DirectSelector(_environment.coreTypes.objectEquals),
+          Args<TypeExpr>([expr, _nullType]));
+      final narrowedNotNull = _makeNarrowNotNull(node, expr);
+      final int varIndex = _variablesInfo.varIndex[lhs.variable];
+      if (_variableCells[varIndex] == null) {
+        trueState[varIndex] = _nullType;
+        falseState[varIndex] = narrowedNotNull;
+      }
+      _variableValues = null;
+      return;
     } else if (node is IsExpression && node.operand is VariableGet) {
       // Handle 'x is T', where x is a variable.
       final operand = node.operand as VariableGet;
@@ -1630,6 +1665,30 @@
     return result;
   }
 
+  @override
+  TypeExpr visitInstanceInvocation(InstanceInvocation node) {
+    final receiverNode = node.receiver;
+    final receiver = _visit(receiverNode);
+    final args = _visitArguments(receiver, node.arguments);
+    final target = node.interfaceTarget;
+    if (receiverNode is ConstantExpression && node.name.text == '[]') {
+      Constant constant = receiverNode.constant;
+      if (constant is ListConstant) {
+        return _handleIndexingIntoListConstant(constant);
+      }
+    }
+    assert(target is Procedure && !target.isGetter);
+    // TODO(alexmarkov): overloaded arithmetic operators
+    final result = _makeCall(
+        node,
+        (node.receiver is ThisExpression)
+            ? new VirtualSelector(target)
+            : new InterfaceSelector(target),
+        args);
+    _updateReceiverAfterCall(receiverNode, receiver, node.name);
+    return result;
+  }
+
   TypeExpr _handleIndexingIntoListConstant(ListConstant list) {
     final elementTypes = new Set<Type>();
     for (var element in list.entries) {
@@ -1649,27 +1708,103 @@
   }
 
   @override
-  TypeExpr visitPropertyGet(PropertyGet node) {
-    var receiver = _visit(node.receiver);
-    var args = new Args<TypeExpr>([receiver]);
+  TypeExpr visitDynamicInvocation(DynamicInvocation node) {
+    final receiverNode = node.receiver;
+    final receiver = _visit(receiverNode);
+    final args = _visitArguments(receiver, node.arguments);
+    final result =
+        _makeCall(node, new DynamicSelector(CallKind.Method, node.name), args);
+    _updateReceiverAfterCall(receiverNode, receiver, node.name);
+    return result;
+  }
+
+  @override
+  TypeExpr visitLocalFunctionInvocation(LocalFunctionInvocation node) {
+    _visitArguments(null, node.arguments);
+    return _staticType(node);
+  }
+
+  @override
+  TypeExpr visitFunctionInvocation(FunctionInvocation node) {
+    final receiverNode = node.receiver;
+    final receiver = _visit(receiverNode);
+    _visitArguments(receiver, node.arguments);
+    final result = _staticType(node);
+    _updateReceiverAfterCall(receiverNode, receiver, Name('call'));
+    return result;
+  }
+
+  @override
+  TypeExpr visitEqualsCall(EqualsCall node) {
+    final left = _visit(node.left);
+    final right = _visit(node.right);
     final target = node.interfaceTarget;
+    return _makeCall(
+        node,
+        (node.left is ThisExpression)
+            ? new VirtualSelector(target)
+            : new InterfaceSelector(target),
+        Args<TypeExpr>([left, right]));
+  }
+
+  @override
+  TypeExpr visitEqualsNull(EqualsNull node) {
+    final arg = _visit(node.expression);
+    _makeNarrowNotNull(node, arg);
+    _makeCall(node, DirectSelector(_environment.coreTypes.objectEquals),
+        Args<TypeExpr>([arg, _nullType]));
+    return _boolType;
+  }
+
+  TypeExpr _handlePropertyGet(
+      TreeNode node, Expression receiverNode, Member target, Name selector) {
+    var receiver = _visit(receiverNode);
+    var args = new Args<TypeExpr>([receiver]);
     TypeExpr result;
     if (target == null) {
       result = _makeCall(
-          node, new DynamicSelector(CallKind.PropertyGet, node.name), args);
+          node, new DynamicSelector(CallKind.PropertyGet, selector), args);
     } else {
       result = _makeCall(
           node,
-          (node.receiver is ThisExpression)
+          (receiverNode is ThisExpression)
               ? new VirtualSelector(target, callKind: CallKind.PropertyGet)
               : new InterfaceSelector(target, callKind: CallKind.PropertyGet),
           args);
     }
-    _updateReceiverAfterCall(node.receiver, receiver, node.name);
+    _updateReceiverAfterCall(receiverNode, receiver, selector);
     return result;
   }
 
   @override
+  TypeExpr visitPropertyGet(PropertyGet node) {
+    return _handlePropertyGet(
+        node, node.receiver, node.interfaceTarget, node.name);
+  }
+
+  @override
+  TypeExpr visitInstanceGet(InstanceGet node) {
+    return _handlePropertyGet(
+        node, node.receiver, node.interfaceTarget, node.name);
+  }
+
+  @override
+  TypeExpr visitInstanceTearOff(InstanceTearOff node) {
+    return _handlePropertyGet(
+        node, node.receiver, node.interfaceTarget, node.name);
+  }
+
+  @override
+  TypeExpr visitFunctionTearOff(FunctionTearOff node) {
+    return _handlePropertyGet(node, node.receiver, null, Name('call'));
+  }
+
+  @override
+  TypeExpr visitDynamicGet(DynamicGet node) {
+    return _handlePropertyGet(node, node.receiver, null, node.name);
+  }
+
+  @override
   TypeExpr visitPropertySet(PropertySet node) {
     var receiver = _visit(node.receiver);
     var value = _visit(node.value);
@@ -1693,6 +1828,35 @@
   }
 
   @override
+  TypeExpr visitInstanceSet(InstanceSet node) {
+    var receiver = _visit(node.receiver);
+    var value = _visit(node.value);
+    var args = new Args<TypeExpr>([receiver, value]);
+    final target = node.interfaceTarget;
+    assert((target is Field) || ((target is Procedure) && target.isSetter));
+    _makeCall(
+        node,
+        (node.receiver is ThisExpression)
+            ? new VirtualSelector(target, callKind: CallKind.PropertySet)
+            : new InterfaceSelector(target, callKind: CallKind.PropertySet),
+        args);
+    _updateReceiverAfterCall(node.receiver, receiver, node.name,
+        isSetter: true);
+    return value;
+  }
+
+  @override
+  TypeExpr visitDynamicSet(DynamicSet node) {
+    var receiver = _visit(node.receiver);
+    var value = _visit(node.value);
+    var args = new Args<TypeExpr>([receiver, value]);
+    _makeCall(node, new DynamicSelector(CallKind.PropertySet, node.name), args);
+    _updateReceiverAfterCall(node.receiver, receiver, node.name,
+        isSetter: true);
+    return value;
+  }
+
+  @override
   TypeExpr visitSuperMethodInvocation(SuperMethodInvocation node) {
     assert(kPartialMixinResolution);
     assert(_receiver != null, "Should have receiver. Node: $node");
diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart
index 427af73..e7ac652 100644
--- a/pkg/vm/lib/transformations/type_flow/transformer.dart
+++ b/pkg/vm/lib/transformations/type_flow/transformer.dart
@@ -256,7 +256,13 @@
     }
 
     final bool markSkipCheck = !callSite.useCheckedEntry &&
-        (node is MethodInvocation || node is PropertySet);
+        (node is MethodInvocation ||
+            node is InstanceInvocation ||
+            node is DynamicInvocation ||
+            node is EqualsCall ||
+            node is PropertySet ||
+            node is InstanceSet ||
+            node is DynamicSet);
 
     bool markReceiverNotInt = false;
 
@@ -264,7 +270,8 @@
       // No information is needed for static calls.
       if (node is! StaticInvocation &&
           node is! StaticSet &&
-          node is! StaticGet) {
+          node is! StaticGet &&
+          node is! StaticTearOff) {
         // The compiler uses another heuristic in addition to the call-site
         // annotation: if the call-site is non-dynamic and the interface target does
         // not exist in the parent chain of _Smi (int is used as an approxmiation
@@ -298,11 +305,17 @@
     // Tell the table selector assigner about the callsite.
     final Selector selector = callSite.selector;
     if (selector is InterfaceSelector && !_callSiteUsesDirectCall(node)) {
-      if (node is PropertyGet) {
+      if (node is PropertyGet ||
+          node is InstanceGet ||
+          node is InstanceTearOff) {
         _tableSelectorAssigner.registerGetterCall(
             selector.member, callSite.isNullableReceiver);
       } else {
-        assert(node is MethodInvocation || node is PropertySet);
+        assert(node is MethodInvocation ||
+            node is InstanceInvocation ||
+            node is EqualsCall ||
+            node is PropertySet ||
+            node is InstanceSet);
         _tableSelectorAssigner.registerMethodOrSetterCall(
             selector.member, callSite.isNullableReceiver);
       }
@@ -428,18 +441,84 @@
   }
 
   @override
+  visitInstanceInvocation(InstanceInvocation node) {
+    _annotateCallSite(node, node.interfaceTarget);
+    super.visitInstanceInvocation(node);
+  }
+
+  @override
+  visitDynamicInvocation(DynamicInvocation node) {
+    _annotateCallSite(node, null);
+    super.visitDynamicInvocation(node);
+  }
+
+  @override
+  visitLocalFunctionInvocation(LocalFunctionInvocation node) {
+    _annotateCallSite(node, null);
+    super.visitLocalFunctionInvocation(node);
+  }
+
+  @override
+  visitFunctionInvocation(FunctionInvocation node) {
+    _annotateCallSite(node, null);
+    super.visitFunctionInvocation(node);
+  }
+
+  @override
+  visitEqualsCall(EqualsCall node) {
+    _annotateCallSite(node, null);
+    super.visitEqualsCall(node);
+  }
+
+  @override
   visitPropertyGet(PropertyGet node) {
     _annotateCallSite(node, node.interfaceTarget);
     super.visitPropertyGet(node);
   }
 
   @override
+  visitInstanceGet(InstanceGet node) {
+    _annotateCallSite(node, node.interfaceTarget);
+    super.visitInstanceGet(node);
+  }
+
+  @override
+  visitInstanceTearOff(InstanceTearOff node) {
+    _annotateCallSite(node, node.interfaceTarget);
+    super.visitInstanceTearOff(node);
+  }
+
+  @override
+  visitFunctionTearOff(FunctionTearOff node) {
+    _annotateCallSite(node, null);
+    super.visitFunctionTearOff(node);
+  }
+
+  @override
+  visitDynamicGet(DynamicGet node) {
+    _annotateCallSite(node, null);
+    super.visitDynamicGet(node);
+  }
+
+  @override
   visitPropertySet(PropertySet node) {
     _annotateCallSite(node, node.interfaceTarget);
     super.visitPropertySet(node);
   }
 
   @override
+  visitInstanceSet(InstanceSet node) {
+    _annotateCallSite(node, node.interfaceTarget);
+    super.visitInstanceSet(node);
+  }
+
+  @override
+  visitDynamicSet(DynamicSet node) {
+    _annotateCallSite(node, null);
+    super.visitDynamicSet(node);
+  }
+
+  @override
   visitSuperMethodInvocation(SuperMethodInvocation node) {
     _annotateCallSite(node, node.interfaceTarget);
     super.visitSuperMethodInvocation(node);
@@ -999,6 +1078,78 @@
   }
 
   @override
+  TreeNode visitInstanceInvocation(
+      InstanceInvocation node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
+    if (_isUnreachable(node)) {
+      return _makeUnreachableCall(
+          _flattenArguments(node.arguments, receiver: node.receiver));
+    }
+    node.interfaceTarget =
+        fieldMorpher.adjustInstanceCallTarget(node.interfaceTarget);
+    shaker.addUsedMember(node.interfaceTarget);
+    return node;
+  }
+
+  @override
+  TreeNode visitDynamicInvocation(
+      DynamicInvocation node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
+    if (_isUnreachable(node)) {
+      return _makeUnreachableCall(
+          _flattenArguments(node.arguments, receiver: node.receiver));
+    }
+    return node;
+  }
+
+  @override
+  TreeNode visitLocalFunctionInvocation(
+      LocalFunctionInvocation node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
+    if (_isUnreachable(node)) {
+      return _makeUnreachableCall(_flattenArguments(node.arguments));
+    }
+    return node;
+  }
+
+  @override
+  TreeNode visitFunctionInvocation(
+      FunctionInvocation node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
+    if (_isUnreachable(node)) {
+      return _makeUnreachableCall(
+          _flattenArguments(node.arguments, receiver: node.receiver));
+    }
+    return node;
+  }
+
+  @override
+  TreeNode visitEqualsCall(EqualsCall node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
+    if (_isUnreachable(node)) {
+      return _makeUnreachableCall([node.left, node.right]);
+    }
+    node.interfaceTarget =
+        fieldMorpher.adjustInstanceCallTarget(node.interfaceTarget);
+    shaker.addUsedMember(node.interfaceTarget);
+    return node;
+  }
+
+  @override
+  TreeNode visitEqualsNull(EqualsNull node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
+    if (_isUnreachable(node)) {
+      return _makeUnreachableCall([node.expression]);
+    }
+    final nullTest = _getNullTest(node);
+    if (nullTest.isAlwaysNull || nullTest.isAlwaysNotNull) {
+      return _evaluateArguments(
+          [node.expression], BoolLiteral(nullTest.isAlwaysNull));
+    }
+    return node;
+  }
+
+  @override
   TreeNode visitPropertyGet(PropertyGet node, TreeNode removalSentinel) {
     node.transformOrRemoveChildren(this);
     if (_isUnreachable(node)) {
@@ -1014,6 +1165,54 @@
   }
 
   @override
+  TreeNode visitInstanceGet(InstanceGet node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
+    if (_isUnreachable(node)) {
+      return _makeUnreachableCall([node.receiver]);
+    } else {
+      node.interfaceTarget =
+          fieldMorpher.adjustInstanceCallTarget(node.interfaceTarget);
+      shaker.addUsedMember(node.interfaceTarget);
+      return node;
+    }
+  }
+
+  @override
+  TreeNode visitInstanceTearOff(
+      InstanceTearOff node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
+    if (_isUnreachable(node)) {
+      return _makeUnreachableCall([node.receiver]);
+    } else {
+      node.interfaceTarget =
+          fieldMorpher.adjustInstanceCallTarget(node.interfaceTarget);
+      shaker.addUsedMember(node.interfaceTarget);
+      return node;
+    }
+  }
+
+  @override
+  TreeNode visitDynamicGet(DynamicGet node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
+    if (_isUnreachable(node)) {
+      return _makeUnreachableCall([node.receiver]);
+    } else {
+      return node;
+    }
+  }
+
+  @override
+  TreeNode visitFunctionTearOff(
+      FunctionTearOff node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
+    if (_isUnreachable(node)) {
+      return _makeUnreachableCall([node.receiver]);
+    } else {
+      return node;
+    }
+  }
+
+  @override
   TreeNode visitPropertySet(PropertySet node, TreeNode removalSentinel) {
     node.transformOrRemoveChildren(this);
     if (_isUnreachable(node)) {
@@ -1029,6 +1228,29 @@
   }
 
   @override
+  TreeNode visitInstanceSet(InstanceSet node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
+    if (_isUnreachable(node)) {
+      return _makeUnreachableCall([node.receiver, node.value]);
+    } else {
+      node.interfaceTarget = fieldMorpher
+          .adjustInstanceCallTarget(node.interfaceTarget, isSetter: true);
+      shaker.addUsedMember(node.interfaceTarget);
+      return node;
+    }
+  }
+
+  @override
+  TreeNode visitDynamicSet(DynamicSet node, TreeNode removalSentinel) {
+    node.transformOrRemoveChildren(this);
+    if (_isUnreachable(node)) {
+      return _makeUnreachableCall([node.receiver, node.value]);
+    } else {
+      return node;
+    }
+  }
+
+  @override
   TreeNode visitSuperMethodInvocation(
       SuperMethodInvocation node, TreeNode removalSentinel) {
     node.transformOrRemoveChildren(this);
diff --git a/runtime/tests/vm/dart/regress_45898_test.dart b/runtime/tests/vm/dart/regress_45898_test.dart
new file mode 100644
index 0000000..fef076b
--- /dev/null
+++ b/runtime/tests/vm/dart/regress_45898_test.dart
@@ -0,0 +1,98 @@
+// 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.
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:path/path.dart' as path;
+
+import 'use_flag_test_helper.dart' show withTempDir, run;
+
+final buildDir = path.dirname(Platform.executable);
+final platformDill = path.join(buildDir, 'vm_platform_strong.dill');
+final genSnapshot1 = path.join(buildDir, 'gen_snapshot');
+final genSnapshot2 = path.join('${buildDir}_X64', 'gen_snapshot');
+final genSnapshot =
+    File(genSnapshot1).existsSync() ? genSnapshot1 : genSnapshot2;
+
+const classCount = 10000;
+const subclassCount = 5000;
+
+// Generates an example that causes generation of a TypeTestingStub checking for
+// 10000 classes - thereby making the TTS larger than 32 KB.
+//
+// We alternate classes to be subclasses of I0 and I1 to ensure that subclasses
+// of I0 do not have consecutive class ids.
+String generateExample() {
+  final sb = StringBuffer()..writeln('''
+class I0 {}
+class I1 {}
+  ''');
+  for (int i = 0; i < classCount; ++i) {
+    sb.writeln('class S$i extends I${i % 2} {}');
+  }
+  sb.writeln('final all = <Object>[');
+  for (int i = 0; i < classCount; ++i) {
+    sb.writeln('  S$i(),');
+  }
+  sb.writeln('];');
+  sb.writeln('''
+main() {
+  int succeeded = 0;
+  int failed = 0;
+  for (dynamic obj in all) {
+    try {
+      obj as I0;
+      succeeded++;
+    } on TypeError catch (e, s) {
+      failed++;
+    }
+  }
+  if (succeeded != $subclassCount ||
+      failed != $subclassCount) {
+    throw 'Error: succeeded: \$succeeded, failed: \$failed';
+  }
+}
+  ''');
+  return sb.toString();
+}
+
+void main(List<String> args) async {
+  if (!Platform.isLinux) {
+    // We want this test to run in (sim)arm, (sim)arm64 on Linux in JIT/AOT.
+    // As written it wouldn't run on Windows / Android due to testing setup.
+    return;
+  }
+
+  final bool isAot = Platform.executable.contains('dart_precompiled_runtime');
+
+  await withTempDir('tts', (String temp) async {
+    final script = path.join(temp, 'script.dart');
+    await File(script).writeAsString(generateExample());
+
+    // We always compile to .dill file because simarm/simarm64 runs really slow
+    // from source (and this dart2kernel compilation happens with checked-in
+    // binaries).
+    final scriptDill = path.join(temp, 'script.dart.dill');
+    await run('pkg/vm/tool/gen_kernel', <String>[
+      isAot ? '--aot' : '--no-aot',
+      '--platform=$platformDill',
+      '-o',
+      scriptDill,
+      script,
+    ]);
+
+    String mainFile = scriptDill;
+    if (isAot) {
+      final elfFile = path.join(temp, 'script.dart.dill.elf');
+      await run(genSnapshot, <String>[
+        '--snapshot-kind=app-aot-elf',
+        '--elf=$elfFile',
+        scriptDill,
+      ]);
+      mainFile = elfFile;
+    }
+    await run(Platform.executable, [mainFile]);
+  });
+}
diff --git a/runtime/tests/vm/dart_2/regress_45898_test.dart b/runtime/tests/vm/dart_2/regress_45898_test.dart
new file mode 100644
index 0000000..fef076b
--- /dev/null
+++ b/runtime/tests/vm/dart_2/regress_45898_test.dart
@@ -0,0 +1,98 @@
+// 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.
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:path/path.dart' as path;
+
+import 'use_flag_test_helper.dart' show withTempDir, run;
+
+final buildDir = path.dirname(Platform.executable);
+final platformDill = path.join(buildDir, 'vm_platform_strong.dill');
+final genSnapshot1 = path.join(buildDir, 'gen_snapshot');
+final genSnapshot2 = path.join('${buildDir}_X64', 'gen_snapshot');
+final genSnapshot =
+    File(genSnapshot1).existsSync() ? genSnapshot1 : genSnapshot2;
+
+const classCount = 10000;
+const subclassCount = 5000;
+
+// Generates an example that causes generation of a TypeTestingStub checking for
+// 10000 classes - thereby making the TTS larger than 32 KB.
+//
+// We alternate classes to be subclasses of I0 and I1 to ensure that subclasses
+// of I0 do not have consecutive class ids.
+String generateExample() {
+  final sb = StringBuffer()..writeln('''
+class I0 {}
+class I1 {}
+  ''');
+  for (int i = 0; i < classCount; ++i) {
+    sb.writeln('class S$i extends I${i % 2} {}');
+  }
+  sb.writeln('final all = <Object>[');
+  for (int i = 0; i < classCount; ++i) {
+    sb.writeln('  S$i(),');
+  }
+  sb.writeln('];');
+  sb.writeln('''
+main() {
+  int succeeded = 0;
+  int failed = 0;
+  for (dynamic obj in all) {
+    try {
+      obj as I0;
+      succeeded++;
+    } on TypeError catch (e, s) {
+      failed++;
+    }
+  }
+  if (succeeded != $subclassCount ||
+      failed != $subclassCount) {
+    throw 'Error: succeeded: \$succeeded, failed: \$failed';
+  }
+}
+  ''');
+  return sb.toString();
+}
+
+void main(List<String> args) async {
+  if (!Platform.isLinux) {
+    // We want this test to run in (sim)arm, (sim)arm64 on Linux in JIT/AOT.
+    // As written it wouldn't run on Windows / Android due to testing setup.
+    return;
+  }
+
+  final bool isAot = Platform.executable.contains('dart_precompiled_runtime');
+
+  await withTempDir('tts', (String temp) async {
+    final script = path.join(temp, 'script.dart');
+    await File(script).writeAsString(generateExample());
+
+    // We always compile to .dill file because simarm/simarm64 runs really slow
+    // from source (and this dart2kernel compilation happens with checked-in
+    // binaries).
+    final scriptDill = path.join(temp, 'script.dart.dill');
+    await run('pkg/vm/tool/gen_kernel', <String>[
+      isAot ? '--aot' : '--no-aot',
+      '--platform=$platformDill',
+      '-o',
+      scriptDill,
+      script,
+    ]);
+
+    String mainFile = scriptDill;
+    if (isAot) {
+      final elfFile = path.join(temp, 'script.dart.dill.elf');
+      await run(genSnapshot, <String>[
+        '--snapshot-kind=app-aot-elf',
+        '--elf=$elfFile',
+        scriptDill,
+      ]);
+      mainFile = elfFile;
+    }
+    await run(Platform.executable, [mainFile]);
+  });
+}
diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status
index 436bad9..6003995 100644
--- a/runtime/tests/vm/vm.status
+++ b/runtime/tests/vm/vm.status
@@ -284,7 +284,9 @@
 
 [ $compiler == dartkp && ($arch == simarm || $arch == simarm64 || $arch == simarm64c) ]
 dart/causal_stacks/async_throws_stack_lazy_non_symbolic_test: Pass, Slow
+dart/regress_45898_test: Pass, Slow
 dart_2/causal_stacks/async_throws_stack_lazy_non_symbolic_test: Pass, Slow
+dart_2/regress_45898_test: Pass, Slow
 
 [ $compiler == dartkp && ($runtime == dart_precompiled || $runtime == vm) ]
 dart/redirection_type_shuffling_test: SkipByDesign # Includes dart:mirrors.
diff --git a/runtime/vm/heap/scavenger.cc b/runtime/vm/heap/scavenger.cc
index f0dee56..fa584f9 100644
--- a/runtime/vm/heap/scavenger.cc
+++ b/runtime/vm/heap/scavenger.cc
@@ -305,16 +305,7 @@
     ObjectPtr new_obj = ScavengeObject(raw_obj);
 
     // Update the reference.
-    if (!new_obj->IsNewObject()) {
-      // Setting the mark bit above must not be ordered after a publishing store
-      // of this object. Note this could be a publishing store even if the
-      // object was promoted by an early invocation of ScavengePointer. Compare
-      // Object::Allocate.
-      reinterpret_cast<std::atomic<ObjectPtr>*>(p)->store(
-          new_obj, std::memory_order_release);
-    } else {
-      *p = new_obj;
-    }
+    *p = new_obj;
 
     // Update the store buffer as needed.
     if (visiting_old_object_ != nullptr) {
@@ -412,7 +403,9 @@
         // push it to the mark stack after forwarding its slots.
         tags = UntaggedObject::OldAndNotMarkedBit::update(
             !thread_->is_marking(), tags);
-        new_obj->untag()->tags_ = tags;
+        // release: Setting the mark bit above must not be ordered after a
+        // publishing store of this object. Compare Object::Allocate.
+        new_obj->untag()->tags_.store(tags, std::memory_order_release);
       }
 
       intptr_t cid = UntaggedObject::ClassIdTag::decode(header);
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index d7c6b01..384b853 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -2648,11 +2648,9 @@
     // concurrent marker on ARM and ARM64 (the marker may observe a
     // publishing store of this object before the stores that initialize its
     // slots), and helps the collection to finish sooner.
-    raw_obj->untag()->SetMarkBitUnsynchronized();
-    // Setting the mark bit must not be ordered after a publishing store of
-    // this object. Adding a barrier here is cheaper than making every store
-    // into the heap a store-release. Compare Scavenger::ScavengePointer.
-    std::atomic_thread_fence(std::memory_order_release);
+    // release: Setting the mark bit must not be ordered after a publishing
+    // store of this object. Compare Scavenger::ScavengePointer.
+    raw_obj->untag()->SetMarkBitRelease();
     heap->old_space()->AllocateBlack(size);
   }
 #ifndef PRODUCT
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 50f35b8..7c62065 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -219,13 +219,13 @@
     Tags() : tags_(0) {}
 
     operator T() const { return tags_.load(std::memory_order_relaxed); }
-
     T operator=(T tags) {
       tags_.store(tags, std::memory_order_relaxed);
       return tags;
     }
 
     T load(std::memory_order order) const { return tags_.load(order); }
+    void store(T value, std::memory_order order) { tags_.store(value, order); }
 
     bool compare_exchange_weak(T old_tags,
                                T new_tags,
@@ -243,12 +243,13 @@
       return TagBitField::decode(*reinterpret_cast<const T*>(&tags_));
     }
 
-    template <class TagBitField>
+    template <class TagBitField,
+              std::memory_order order = std::memory_order_relaxed>
     void UpdateBool(bool value) {
       if (value) {
-        tags_.fetch_or(TagBitField::encode(true), std::memory_order_relaxed);
+        tags_.fetch_or(TagBitField::encode(true), order);
       } else {
-        tags_.fetch_and(~TagBitField::encode(true), std::memory_order_relaxed);
+        tags_.fetch_and(~TagBitField::encode(true), order);
       }
     }
 
@@ -320,6 +321,11 @@
     ASSERT(!IsMarked());
     tags_.UpdateUnsynchronized<OldAndNotMarkedBit>(false);
   }
+  void SetMarkBitRelease() {
+    ASSERT(IsOldObject());
+    ASSERT(!IsMarked());
+    tags_.UpdateBool<OldAndNotMarkedBit, std::memory_order_release>(false);
+  }
   void ClearMarkBit() {
     ASSERT(IsOldObject());
     ASSERT(IsMarked());
diff --git a/runtime/vm/type_testing_stubs.cc b/runtime/vm/type_testing_stubs.cc
index d0ff2d8..260517b 100644
--- a/runtime/vm/type_testing_stubs.cc
+++ b/runtime/vm/type_testing_stubs.cc
@@ -2,11 +2,14 @@
 // 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.
 
-#include "vm/type_testing_stubs.h"
+#include <functional>
+
 #include "vm/compiler/assembler/disassembler.h"
+#include "vm/longjump.h"
 #include "vm/object_store.h"
 #include "vm/stub_code.h"
 #include "vm/timeline.h"
+#include "vm/type_testing_stubs.h"
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
 #include "vm/compiler/backend/flow_graph_compiler.h"
@@ -195,6 +198,37 @@
 #if !defined(TARGET_ARCH_IA32)
 #if !defined(DART_PRECOMPILED_RUNTIME)
 
+#if defined(TARGET_ARCH_ARM) || defined(TARGET_ARCH_ARM64)
+#define ONLY_ON_ARM(...) __VA_ARGS__
+#else
+#define ONLY_ON_ARM(...)
+#endif
+
+static CodePtr RetryCompilationWithFarBranches(
+    Thread* thread,
+    std::function<CodePtr(compiler::Assembler&)> fun) {
+  bool use_far_branches = false;
+  while (true) {
+    LongJumpScope jump;
+    if (setjmp(*jump.Set()) == 0) {
+      // To use the already-defined __ Macro !
+      compiler::Assembler assembler(nullptr ONLY_ON_ARM(, use_far_branches));
+      return fun(assembler);
+    } else {
+      // We bailed out or we encountered an error.
+      const Error& error = Error::Handle(thread->StealStickyError());
+      if (error.ptr() == Object::branch_offset_error().ptr()) {
+        ASSERT(!use_far_branches);
+        use_far_branches = true;
+      } else {
+        UNREACHABLE();
+      }
+    }
+  }
+}
+
+#undef ONLY_ON_ARM
+
 CodePtr TypeTestingStubGenerator::BuildCodeForType(const Type& type) {
   auto thread = Thread::Current();
   auto zone = thread->zone();
@@ -214,55 +248,64 @@
     slow_tts_stub = thread->isolate_group()->object_store()->slow_tts_stub();
   }
 
-  // To use the already-defined __ Macro !
-  compiler::Assembler assembler(nullptr);
-  compiler::UnresolvedPcRelativeCalls unresolved_calls;
-  BuildOptimizedTypeTestStub(&assembler, &unresolved_calls, slow_tts_stub, hi,
-                             type, type_class);
+  const Code& code = Code::Handle(
+      thread->zone(),
+      RetryCompilationWithFarBranches(
+          thread, [&](compiler::Assembler& assembler) {
+            compiler::UnresolvedPcRelativeCalls unresolved_calls;
+            BuildOptimizedTypeTestStub(&assembler, &unresolved_calls,
+                                       slow_tts_stub, hi, type, type_class);
 
-  const auto& static_calls_table =
-      Array::Handle(zone, compiler::StubCodeCompiler::BuildStaticCallsTable(
-                              zone, &unresolved_calls));
+            const auto& static_calls_table = Array::Handle(
+                zone, compiler::StubCodeCompiler::BuildStaticCallsTable(
+                          zone, &unresolved_calls));
 
-  const char* name = namer_.StubNameForType(type);
-  const auto pool_attachment = FLAG_use_bare_instructions
-                                   ? Code::PoolAttachment::kNotAttachPool
-                                   : Code::PoolAttachment::kAttachPool;
+            const char* name = namer_.StubNameForType(type);
+            const auto pool_attachment =
+                FLAG_use_bare_instructions
+                    ? Code::PoolAttachment::kNotAttachPool
+                    : Code::PoolAttachment::kAttachPool;
 
-  Code& code = Code::Handle(thread->zone());
-  auto install_code_fun = [&]() {
-    code = Code::FinalizeCode(nullptr, &assembler, pool_attachment,
-                              /*optimized=*/false, /*stats=*/nullptr);
-    if (!static_calls_table.IsNull()) {
-      code.set_static_calls_target_table(static_calls_table);
-    }
-  };
+            Code& code = Code::Handle(thread->zone());
+            auto install_code_fun = [&]() {
+              code = Code::FinalizeCode(nullptr, &assembler, pool_attachment,
+                                        /*optimized=*/false, /*stats=*/nullptr);
+              if (!static_calls_table.IsNull()) {
+                code.set_static_calls_target_table(static_calls_table);
+              }
+            };
 
-  // We have to ensure no mutators are running, because:
-  //
-  //   a) We allocate an instructions object, which might cause us to
-  //      temporarily flip page protections from (RX -> RW -> RX).
-  //
-  SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
-  thread->isolate_group()->RunWithStoppedMutators(install_code_fun,
-                                                  /*use_force_growth=*/true);
+            // We have to ensure no mutators are running, because:
+            //
+            //   a) We allocate an instructions object, which might cause us to
+            //      temporarily flip page protections from (RX -> RW -> RX).
+            //
+            SafepointWriteRwLocker ml(thread,
+                                      thread->isolate_group()->program_lock());
+            thread->isolate_group()->RunWithStoppedMutators(
+                install_code_fun,
+                /*use_force_growth=*/true);
 
-  Code::NotifyCodeObservers(name, code, /*optimized=*/false);
+            Code::NotifyCodeObservers(name, code, /*optimized=*/false);
 
-  code.set_owner(type);
+            code.set_owner(type);
 #ifndef PRODUCT
-  if (FLAG_support_disassembler && FLAG_disassemble_stubs) {
-    LogBlock lb;
-    THR_Print("Code for stub '%s' (type = %s): {\n", name, type.ToCString());
-    DisassembleToStdout formatter;
-    code.Disassemble(&formatter);
-    THR_Print("}\n");
-    const ObjectPool& object_pool = ObjectPool::Handle(code.object_pool());
-    if (!object_pool.IsNull()) {
-      object_pool.DebugPrint();
-    }
-  }
+            if (FLAG_support_disassembler && FLAG_disassemble_stubs) {
+              LogBlock lb;
+              THR_Print("Code for stub '%s' (type = %s): {\n", name,
+                        type.ToCString());
+              DisassembleToStdout formatter;
+              code.Disassemble(&formatter);
+              THR_Print("}\n");
+              const ObjectPool& object_pool =
+                  ObjectPool::Handle(code.object_pool());
+              if (!object_pool.IsNull()) {
+                object_pool.DebugPrint();
+              }
+            }
 #endif  // !PRODUCT
+            return code.ptr();
+          }));
 
   return code.ptr();
 }
diff --git a/tools/VERSION b/tools/VERSION
index 547d936c..0fb429d 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 77
+PRERELEASE 78
 PRERELEASE_PATCH 0
\ No newline at end of file