Version 2.14.0-38.0.dev

Merge commit '90c309e9e436e3a9187225cd6c2d0dc95cfb7702' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index 6709196..761daab 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -11,7 +11,7 @@
     "constraint, update this by running tools/generate_package_config.dart."
   ],
   "configVersion": 2,
-  "generated": "2021-04-22T20:51:04.585975",
+  "generated": "2021-04-23T17:16:06.111697",
   "generator": "tools/generate_package_config.dart",
   "packages": [
     {
@@ -381,7 +381,7 @@
       "name": "json_rpc_2",
       "rootUri": "../third_party/pkg/json_rpc_2",
       "packageUri": "lib/",
-      "languageVersion": "2.2"
+      "languageVersion": "2.12"
     },
     {
       "name": "kernel",
diff --git a/DEPS b/DEPS
index dab41b0..a7cc76e 100644
--- a/DEPS
+++ b/DEPS
@@ -123,7 +123,7 @@
   "idl_parser_rev": "5fb1ebf49d235b5a70c9f49047e83b0654031eb7",
   "intl_tag": "0.17.0-nullsafety",
   "jinja2_rev": "2222b31554f03e62600cd7e383376a7c187967a1",
-  "json_rpc_2_rev": "b8dfe403fd8528fd14399dee3a6527b55802dd4d",
+  "json_rpc_2_rev": "5ec32a2e0e99dedcef5b3237f93167cd22c2da50",
   "linter_tag": "1.3.0",
   "logging_rev": "e2f633b543ef89c54688554b15ca3d7e425b86a2",
   "markupsafe_rev": "8f45f5cfa0009d2a70589bcda0349b8cb2b72783",
diff --git a/pkg/_fe_analyzer_shared/lib/src/scanner/token.dart b/pkg/_fe_analyzer_shared/lib/src/scanner/token.dart
index 4cc7473..74a310a 100644
--- a/pkg/_fe_analyzer_shared/lib/src/scanner/token.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/scanner/token.dart
@@ -1269,11 +1269,11 @@
 
   static const TokenType AMPERSAND = const TokenType(
       '&', 'AMPERSAND', BITWISE_AND_PRECEDENCE, AMPERSAND_TOKEN,
-      isOperator: true, isUserDefinableOperator: true);
+      isOperator: true, isBinaryOperator: true, isUserDefinableOperator: true);
 
   static const TokenType AMPERSAND_AMPERSAND = const TokenType('&&',
       'AMPERSAND_AMPERSAND', LOGICAL_AND_PRECEDENCE, AMPERSAND_AMPERSAND_TOKEN,
-      isOperator: true);
+      isOperator: true, isBinaryOperator: true);
 
   // This is not yet part of the language and not supported by fasta
   static const TokenType AMPERSAND_AMPERSAND_EQ = const TokenType(
@@ -1303,11 +1303,11 @@
 
   static const TokenType BAR = const TokenType(
       '|', 'BAR', BITWISE_OR_PRECEDENCE, BAR_TOKEN,
-      isOperator: true, isUserDefinableOperator: true);
+      isOperator: true, isBinaryOperator: true, isUserDefinableOperator: true);
 
   static const TokenType BAR_BAR = const TokenType(
       '||', 'BAR_BAR', LOGICAL_OR_PRECEDENCE, BAR_BAR_TOKEN,
-      isOperator: true);
+      isOperator: true, isBinaryOperator: true);
 
   // This is not yet part of the language and not supported by fasta
   static const TokenType BAR_BAR_EQ = const TokenType(
@@ -1326,7 +1326,7 @@
 
   static const TokenType CARET = const TokenType(
       '^', 'CARET', BITWISE_XOR_PRECEDENCE, CARET_TOKEN,
-      isOperator: true, isUserDefinableOperator: true);
+      isOperator: true, isBinaryOperator: true, isUserDefinableOperator: true);
 
   static const TokenType CARET_EQ = const TokenType(
       '^=', 'CARET_EQ', ASSIGNMENT_PRECEDENCE, CARET_EQ_TOKEN,
@@ -1347,7 +1347,7 @@
 
   static const TokenType EQ_EQ = const TokenType(
       '==', 'EQ_EQ', EQUALITY_PRECEDENCE, EQ_EQ_TOKEN,
-      isOperator: true, isUserDefinableOperator: true);
+      isOperator: true, isBinaryOperator: true, isUserDefinableOperator: true);
 
   /// The `===` operator is not supported in the Dart language
   /// but is parsed as such by the scanner to support better recovery
@@ -1360,15 +1360,15 @@
 
   static const TokenType GT = const TokenType(
       '>', 'GT', RELATIONAL_PRECEDENCE, GT_TOKEN,
-      isOperator: true, isUserDefinableOperator: true);
+      isOperator: true, isBinaryOperator: true, isUserDefinableOperator: true);
 
   static const TokenType GT_EQ = const TokenType(
       '>=', 'GT_EQ', RELATIONAL_PRECEDENCE, GT_EQ_TOKEN,
-      isOperator: true, isUserDefinableOperator: true);
+      isOperator: true, isBinaryOperator: true, isUserDefinableOperator: true);
 
   static const TokenType GT_GT = const TokenType(
       '>>', 'GT_GT', SHIFT_PRECEDENCE, GT_GT_TOKEN,
-      isOperator: true, isUserDefinableOperator: true);
+      isOperator: true, isBinaryOperator: true, isUserDefinableOperator: true);
 
   static const TokenType GT_GT_EQ = const TokenType(
       '>>=', 'GT_GT_EQ', ASSIGNMENT_PRECEDENCE, GT_GT_EQ_TOKEN,
@@ -1376,7 +1376,7 @@
 
   static const TokenType GT_GT_GT = const TokenType(
       '>>>', 'GT_GT_GT', SHIFT_PRECEDENCE, GT_GT_GT_TOKEN,
-      isOperator: true, isUserDefinableOperator: true);
+      isOperator: true, isBinaryOperator: true, isUserDefinableOperator: true);
 
   static const TokenType GT_GT_GT_EQ = const TokenType(
       '>>>=', 'GT_GT_GT_EQ', ASSIGNMENT_PRECEDENCE, GT_GT_GT_EQ_TOKEN,
@@ -1395,15 +1395,15 @@
 
   static const TokenType LT = const TokenType(
       '<', 'LT', RELATIONAL_PRECEDENCE, LT_TOKEN,
-      isOperator: true, isUserDefinableOperator: true);
+      isOperator: true, isBinaryOperator: true, isUserDefinableOperator: true);
 
   static const TokenType LT_EQ = const TokenType(
       '<=', 'LT_EQ', RELATIONAL_PRECEDENCE, LT_EQ_TOKEN,
-      isOperator: true, isUserDefinableOperator: true);
+      isOperator: true, isBinaryOperator: true, isUserDefinableOperator: true);
 
   static const TokenType LT_LT = const TokenType(
       '<<', 'LT_LT', SHIFT_PRECEDENCE, LT_LT_TOKEN,
-      isOperator: true, isUserDefinableOperator: true);
+      isOperator: true, isBinaryOperator: true, isUserDefinableOperator: true);
 
   static const TokenType LT_LT_EQ = const TokenType(
       '<<=', 'LT_LT_EQ', ASSIGNMENT_PRECEDENCE, LT_LT_EQ_TOKEN,
@@ -1411,7 +1411,7 @@
 
   static const TokenType MINUS = const TokenType(
       '-', 'MINUS', ADDITIVE_PRECEDENCE, MINUS_TOKEN,
-      isOperator: true, isUserDefinableOperator: true);
+      isOperator: true, isBinaryOperator: true, isUserDefinableOperator: true);
 
   static const TokenType MINUS_EQ = const TokenType(
       '-=', 'MINUS_EQ', ASSIGNMENT_PRECEDENCE, MINUS_EQ_TOKEN,
@@ -1432,7 +1432,7 @@
 
   static const TokenType PERCENT = const TokenType(
       '%', 'PERCENT', MULTIPLICATIVE_PRECEDENCE, PERCENT_TOKEN,
-      isOperator: true, isUserDefinableOperator: true);
+      isOperator: true, isBinaryOperator: true, isUserDefinableOperator: true);
 
   static const TokenType PERCENT_EQ = const TokenType(
       '%=', 'PERCENT_EQ', ASSIGNMENT_PRECEDENCE, PERCENT_EQ_TOKEN,
@@ -1447,7 +1447,7 @@
 
   static const TokenType PLUS = const TokenType(
       '+', 'PLUS', ADDITIVE_PRECEDENCE, PLUS_TOKEN,
-      isOperator: true, isUserDefinableOperator: true);
+      isOperator: true, isBinaryOperator: true, isUserDefinableOperator: true);
 
   static const TokenType PLUS_EQ = const TokenType(
       '+=', 'PLUS_EQ', ASSIGNMENT_PRECEDENCE, PLUS_EQ_TOKEN,
@@ -1467,7 +1467,7 @@
 
   static const TokenType QUESTION_QUESTION = const TokenType(
       '??', 'QUESTION_QUESTION', IF_NULL_PRECEDENCE, QUESTION_QUESTION_TOKEN,
-      isOperator: true);
+      isOperator: true, isBinaryOperator: true);
 
   static const TokenType QUESTION_QUESTION_EQ = const TokenType('??=',
       'QUESTION_QUESTION_EQ', ASSIGNMENT_PRECEDENCE, QUESTION_QUESTION_EQ_TOKEN,
@@ -1478,7 +1478,7 @@
 
   static const TokenType SLASH = const TokenType(
       '/', 'SLASH', MULTIPLICATIVE_PRECEDENCE, SLASH_TOKEN,
-      isOperator: true, isUserDefinableOperator: true);
+      isOperator: true, isBinaryOperator: true, isUserDefinableOperator: true);
 
   static const TokenType SLASH_EQ = const TokenType(
       '/=', 'SLASH_EQ', ASSIGNMENT_PRECEDENCE, SLASH_EQ_TOKEN,
@@ -1486,7 +1486,7 @@
 
   static const TokenType STAR = const TokenType(
       '*', 'STAR', MULTIPLICATIVE_PRECEDENCE, STAR_TOKEN,
-      isOperator: true, isUserDefinableOperator: true);
+      isOperator: true, isBinaryOperator: true, isUserDefinableOperator: true);
 
   static const TokenType STAR_EQ = const TokenType(
       '*=', 'STAR_EQ', ASSIGNMENT_PRECEDENCE, STAR_EQ_TOKEN,
@@ -1510,7 +1510,7 @@
 
   static const TokenType TILDE_SLASH = const TokenType(
       '~/', 'TILDE_SLASH', MULTIPLICATIVE_PRECEDENCE, TILDE_SLASH_TOKEN,
-      isOperator: true, isUserDefinableOperator: true);
+      isOperator: true, isBinaryOperator: true, isUserDefinableOperator: true);
 
   static const TokenType TILDE_SLASH_EQ = const TokenType(
       '~/=', 'TILDE_SLASH_EQ', ASSIGNMENT_PRECEDENCE, TILDE_SLASH_EQ_TOKEN,
@@ -1661,6 +1661,11 @@
   final bool isOperator;
 
   /**
+   * `true` if this token type represents a binary operator.
+   */
+  final bool isBinaryOperator;
+
+  /**
    * `true` if this token type represents a keyword starting a top level
    * declaration such as `class`, `enum`, `import`, etc.
    */
@@ -1697,6 +1702,7 @@
   const TokenType(this.lexeme, this.name, this.precedence, this.kind,
       {this.isModifier: false,
       this.isOperator: false,
+      this.isBinaryOperator: false,
       this.isTopLevelKeyword: false,
       this.isUserDefinableOperator: false,
       String? stringValue: 'unspecified'})
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index 084d2b3..c6f96a4 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -338,6 +338,7 @@
   CompileTimeErrorCode.NON_VOID_RETURN_FOR_SETTER,
   CompileTimeErrorCode.NOT_A_TYPE,
   CompileTimeErrorCode.NOT_ASSIGNED_POTENTIALLY_NON_NULLABLE_LOCAL_VARIABLE,
+  CompileTimeErrorCode.NOT_BINARY_OPERATOR,
   CompileTimeErrorCode.NOT_ENOUGH_POSITIONAL_ARGUMENTS,
   CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_INSTANCE_FIELD,
   CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_INSTANCE_FIELD_CONSTRUCTOR,
diff --git a/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
index 4e6836f..abb22ed 100644
--- a/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
@@ -27,7 +27,7 @@
 
   BinaryExpressionResolver({
     required ResolverVisitor resolver,
-  })   : _resolver = resolver,
+  })  : _resolver = resolver,
         _typePropertyResolver = resolver.typePropertyResolver,
         _inferenceHelper = resolver.inferenceHelper;
 
@@ -62,11 +62,17 @@
       return;
     }
 
-    if (operator.isUserDefinableOperator) {
+    if (operator.isUserDefinableOperator && operator.isBinaryOperator) {
       _resolveUserDefinable(node);
       return;
     }
 
+    // Report an error if not already reported by the parser.
+    if (operator != TokenType.BANG_EQ_EQ && operator != TokenType.EQ_EQ_EQ) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.NOT_BINARY_OPERATOR, node, [operator.lexeme]);
+    }
+
     _resolveUnsupportedOperator(node);
   }
 
diff --git a/pkg/analyzer/lib/src/error/codes.dart b/pkg/analyzer/lib/src/error/codes.dart
index 9900a5e..7337eb5 100644
--- a/pkg/analyzer/lib/src/error/codes.dart
+++ b/pkg/analyzer/lib/src/error/codes.dart
@@ -9573,6 +9573,13 @@
 
   /**
    * Parameters:
+   * 0: the name of the operator that is not a binary operator.
+   */
+  static const CompileTimeErrorCode NOT_BINARY_OPERATOR = CompileTimeErrorCode(
+      'NOT_BINARY_OPERATOR', "'{0}' isn't a binary operator.");
+
+  /**
+   * Parameters:
    * 0: the expected number of required arguments
    * 1: the actual number of positional arguments given
    */
diff --git a/pkg/analyzer/test/src/dart/resolution/binary_expression_test.dart b/pkg/analyzer/test/src/dart/resolution/binary_expression_test.dart
index 99c85ca..f3f1dea 100644
--- a/pkg/analyzer/test/src/dart/resolution/binary_expression_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/binary_expression_test.dart
@@ -147,6 +147,25 @@
     );
   }
 
+  test_bangEqEq() async {
+    await assertErrorsInCode(r'''
+f(int a, int b) {
+  a !== b;
+}
+''', [
+      error(ScannerErrorCode.UNSUPPORTED_OPERATOR, 22, 1),
+    ]);
+
+    assertBinaryExpression(
+      findNode.binary('a !== b'),
+      element: null,
+      type: 'dynamic',
+    );
+
+    assertType(findNode.simple('a !=='), 'int');
+    assertType(findNode.simple('b;'), 'int');
+  }
+
   test_eqEq() async {
     await assertNoErrorsInCode(r'''
 f(int a, int b) {
diff --git a/pkg/analyzer/test/src/diagnostics/not_binary_operator_test.dart b/pkg/analyzer/test/src/diagnostics/not_binary_operator_test.dart
new file mode 100644
index 0000000..8d80332
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/not_binary_operator_test.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/src/error/codes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(NonBinaryOperatorTest);
+  });
+}
+
+@reflectiveTest
+class NonBinaryOperatorTest extends PubPackageResolutionTest {
+  test_unaryTilde() async {
+    await assertErrorsInCode('var a = 5 ~ 3;', [
+      error(CompileTimeErrorCode.NOT_BINARY_OPERATOR, 8, 5),
+    ]);
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index 3d1234d..943c049 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -472,6 +472,7 @@
 import 'not_a_type_test.dart' as not_a_type;
 import 'not_assigned_potentially_non_nullable_local_variable_test.dart'
     as not_assigned_potentially_non_nullable_local_variable;
+import 'not_binary_operator_test.dart' as not_binary_operator;
 import 'not_enough_positional_arguments_test.dart'
     as not_enough_positional_arguments;
 import 'not_initialized_non_nullable_instance_field_test.dart'
@@ -970,6 +971,8 @@
     non_bool_expression.main();
     non_bool_negation_expression.main();
     non_bool_operand.main();
+    non_const_call_to_literal_constructor.main();
+    non_const_map_as_expression_statement.main();
     non_constant_annotation_constructor.main();
     non_constant_list_element.main();
     non_constant_case_expression_from_deferred_library.main();
@@ -995,8 +998,7 @@
     non_void_return_for_setter.main();
     not_a_type.main();
     not_assigned_potentially_non_nullable_local_variable.main();
-    non_const_call_to_literal_constructor.main();
-    non_const_map_as_expression_statement.main();
+    not_binary_operator.main();
     not_enough_positional_arguments.main();
     not_initialized_non_nullable_instance_field.main();
     not_initialized_non_nullable_variable.main();
diff --git a/pkg/compiler/lib/src/js_emitter/class_stub_generator.dart b/pkg/compiler/lib/src/js_emitter/class_stub_generator.dart
index 8079a1e..b016b86 100644
--- a/pkg/compiler/lib/src/js_emitter/class_stub_generator.dart
+++ b/pkg/compiler/lib/src/js_emitter/class_stub_generator.dart
@@ -215,85 +215,101 @@
 ///   * `isStatic`.
 ///   * `name`.
 ///   * `isIntercepted.
-List<jsAst.Statement> buildTearOffCode(CompilerOptions options, Emitter emitter,
-    Namer namer, CommonElements commonElements) {
+List<jsAst.Statement> buildTearOffCode(
+    CompilerOptions options, Emitter emitter, CommonElements commonElements) {
   FunctionEntity closureFromTearOff = commonElements.closureFromTearOff;
-  jsAst.Expression tearOffAccessExpression;
-  jsAst.Expression tearOffGlobalObjectString;
-  jsAst.Expression tearOffGlobalObject;
+  jsAst.Expression closureFromTearOffAccessExpression;
   if (closureFromTearOff != null) {
-    tearOffAccessExpression = emitter.staticFunctionAccess(closureFromTearOff);
-    tearOffGlobalObject =
-        js.stringPart(namer.globalObjectForMember(closureFromTearOff));
-    tearOffGlobalObjectString =
-        js.string(namer.globalObjectForMember(closureFromTearOff));
+    closureFromTearOffAccessExpression =
+        emitter.staticFunctionAccess(closureFromTearOff);
   } else {
     // Default values for mocked-up test libraries.
-    tearOffAccessExpression =
+    closureFromTearOffAccessExpression =
         js(r'''function() { throw "Helper 'closureFromTearOff' missing." }''');
-    tearOffGlobalObjectString = js.string('MissingHelperFunction');
-    tearOffGlobalObject = js(
-        r'''(function() { throw "Helper 'closureFromTearOff' missing." })()''');
   }
 
   jsAst.Statement tearOffGetter;
-  if (!options.useContentSecurityPolicy) {
-    jsAst.Expression tearOffAccessText = new jsAst.UnparsedNode(
-        tearOffAccessExpression, options.enableMinification, false);
-    tearOffGetter = js.statement('''
-function tearOffGetter(funcs, applyTrampolineIndex, reflectionInfo, name, isIntercepted) {
-  return isIntercepted
-      ? new Function("funcs", "applyTrampolineIndex", "reflectionInfo", "name",
-                     #tearOffGlobalObjectString, "c",
-          "return function tearOff_" + name + (functionCounter++) + "(receiver) {" +
-            "if (c === null) c = " + #tearOffAccessText + "(" +
-                "this, funcs, applyTrampolineIndex, reflectionInfo, false, true, name);" +
-                "return new c(this, funcs[0], receiver, name);" +
-           "}")(funcs, applyTrampolineIndex, reflectionInfo, name, #tearOffGlobalObject, null)
-      : new Function("funcs", "applyTrampolineIndex", "reflectionInfo", "name",
-                     #tearOffGlobalObjectString, "c",
-          "return function tearOff_" + name + (functionCounter++)+ "() {" +
-            "if (c === null) c = " + #tearOffAccessText + "(" +
-                "this, funcs, applyTrampolineIndex, reflectionInfo, false, false, name);" +
-                "return new c(this, funcs[0], null, name);" +
-             "}")(funcs, applyTrampolineIndex, reflectionInfo, name, #tearOffGlobalObject, null);
-}''', {
-      'tearOffAccessText': tearOffAccessText,
-      'tearOffGlobalObject': tearOffGlobalObject,
-      'tearOffGlobalObjectString': tearOffGlobalObjectString
-    });
-  } else {
-    tearOffGetter = js.statement('''
+  if (options.useContentSecurityPolicy) {
+    tearOffGetter = js.statement(
+      '''
       function tearOffGetter(funcs, applyTrampolineIndex, reflectionInfo, name, isIntercepted) {
         var cache = null;
         return isIntercepted
             ? function(receiver) {
-                if (cache === null) cache = #(
+                if (cache === null) cache = #createTearOffClass(
                     this, funcs, applyTrampolineIndex, reflectionInfo, false, true, name);
                 return new cache(this, funcs[0], receiver, name);
               }
             : function() {
-                if (cache === null) cache = #(
+                if (cache === null) cache = #createTearOffClass(
                     this, funcs, applyTrampolineIndex, reflectionInfo, false, false, name);
                 return new cache(this, funcs[0], null, name);
               };
-      }''', [tearOffAccessExpression, tearOffAccessExpression]);
+      }''',
+      {'createTearOffClass': closureFromTearOffAccessExpression},
+    );
+  } else {
+    // In the CSP version above, the allocation `new cache(...)` is polymorphic
+    // since the same JavaScript anonymous function is used for all instance
+    // tear-offs.
+    //
+    // The following code uses `new Function` to create a fresh instance method
+    // tear-off getter for each method, allowing the allocation to be
+    // monomorphic. This translates into a 2x performance improvement when a
+    // method is torn-off many times.  The cost is that the getter, created via
+    // `new Function`, is more expensive to create, and that cost is at program
+    // startup.
+    //
+    // The a counter in the name ensures that the JavaScript engine does not
+    // attempt to fold all of the almost-identical functions back to the same
+    // instance of Function.
+    //
+    // Functions created by `new Function` are at the JavaScript global scope,
+    // so cannot close-over any values from an 'intermediate' enclosing scope.
+    // We use `new Function` to create a function that is immediately applied to
+    // create a context with the closed-over values. The closed-over values
+    // include parameters, (Dart) top-level definitions, and the local `cache`
+    // variable all in one context (passing `null` to initialize `cache`).
+    tearOffGetter = js.statement(
+      '''
+function tearOffGetter(funcs, applyTrampolineIndex, reflectionInfo, name, isIntercepted) {
+  return isIntercepted
+
+      ? new Function("funcs, applyTrampolineIndex, reflectionInfo, name, createTearOffClass, cache",
+          "return function tearOff_" + name + (functionCounter++) + "(receiver) {" +
+            "if (cache === null) cache = createTearOffClass(" +
+                "this, funcs, applyTrampolineIndex, reflectionInfo, false, true, name);" +
+                "return new cache(this, funcs[0], receiver, name);" +
+           "}")(funcs, applyTrampolineIndex, reflectionInfo, name, #createTearOffClass, null)
+
+      : new Function("funcs, applyTrampolineIndex, reflectionInfo, name, createTearOffClass, cache",
+          "return function tearOff_" + name + (functionCounter++)+ "() {" +
+            "if (cache === null) cache = createTearOffClass(" +
+                "this, funcs, applyTrampolineIndex, reflectionInfo, false, false, name);" +
+                "return new cache(this, funcs[0], null, name);" +
+             "}")(funcs, applyTrampolineIndex, reflectionInfo, name, #createTearOffClass, null);
+}''',
+      {'createTearOffClass': closureFromTearOffAccessExpression},
+    );
   }
 
-  jsAst.Statement tearOff = js.statement('''
-      function tearOff(funcs, applyTrampolineIndex,
+  jsAst.Statement tearOff = js.statement(
+    '''
+    function tearOff(funcs, applyTrampolineIndex,
           reflectionInfo, isStatic, name, isIntercepted) {
       var cache = null;
       return isStatic
           ? function() {
-              if (cache === null) cache = #tearOff(
+              if (cache === null) cache = #createTearOffClass(
                   this, funcs, applyTrampolineIndex,
                   reflectionInfo, true, false, name).prototype;
               return cache;
             }
           : tearOffGetter(funcs, applyTrampolineIndex,
               reflectionInfo, name, isIntercepted);
-    }''', {'tearOff': tearOffAccessExpression});
+    }''',
+    {'createTearOffClass': closureFromTearOffAccessExpression},
+  );
 
-  return <jsAst.Statement>[tearOffGetter, tearOff];
+  return [tearOffGetter, tearOff];
 }
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
index 786b808..3b10c78 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
@@ -768,8 +768,8 @@
       'throwLateFieldADI': _emitter
           .staticFunctionAccess(_closedWorld.commonElements.throwLateFieldADI),
       'operatorIsPrefix': js.string(_namer.fixedNames.operatorIsPrefix),
-      'tearOffCode': new js.Block(buildTearOffCode(
-          _options, _emitter, _namer, _closedWorld.commonElements)),
+      'tearOffCode': new js.Block(
+          buildTearOffCode(_options, _emitter, _closedWorld.commonElements)),
       'embeddedTypes': generateEmbeddedGlobalAccess(TYPES),
       'embeddedInterceptorTags':
           generateEmbeddedGlobalAccess(INTERCEPTORS_BY_TAG),
diff --git a/tools/VERSION b/tools/VERSION
index 1a66e10..1a5a54c 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 37
+PRERELEASE 38
 PRERELEASE_PATCH 0
\ No newline at end of file