Version 2.15.0-2.0.dev

Merge commit '79181abb70700922e20c018288271a48ff702eac' into 'dev'
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index 79abc75..f96a7ee 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -200,6 +200,7 @@
   CompileTimeErrorCode.FOR_IN_WITH_CONST_VARIABLE,
   CompileTimeErrorCode.GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND,
   CompileTimeErrorCode.GENERIC_FUNCTION_TYPE_CANNOT_BE_TYPE_ARGUMENT,
+  CompileTimeErrorCode.GENERIC_METHOD_TYPE_INSTANTIATION_ON_DYNAMIC,
   CompileTimeErrorCode.GETTER_NOT_ASSIGNABLE_SETTER_TYPES,
   CompileTimeErrorCode.GETTER_NOT_SUBTYPE_SETTER_TYPES,
   CompileTimeErrorCode.IF_ELEMENT_CONDITION_FROM_DEFERRED_LIBRARY,
diff --git a/pkg/analyzer/lib/src/dart/analysis/experiments.dart b/pkg/analyzer/lib/src/dart/analysis/experiments.dart
index b8f0909..f03c6aa 100644
--- a/pkg/analyzer/lib/src/dart/analysis/experiments.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/experiments.dart
@@ -46,11 +46,6 @@
   /// The latest known language version.
   static final Version latestSdkLanguageVersion = Version.parse('2.13.0');
 
-  static final FeatureSet latestWithNullSafety = ExperimentStatus.fromStrings2(
-    sdkLanguageVersion: latestSdkLanguageVersion,
-    flags: [],
-  );
-
   /// A map containing information about all known experimental flags.
   static final Map<String, ExperimentalFeature> knownFeatures = _knownFeatures;
 
diff --git a/pkg/analyzer/lib/src/dart/resolver/ast_rewrite.dart b/pkg/analyzer/lib/src/dart/resolver/ast_rewrite.dart
index 090d0ad..b725ead2 100644
--- a/pkg/analyzer/lib/src/dart/resolver/ast_rewrite.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/ast_rewrite.dart
@@ -165,7 +165,8 @@
       // Example:
       //     class C { C.named(); }
       //     C.named
-      return _toConstructorReference(node: node, classElement: element);
+      return _toConstructorReference_prefixed(
+          node: node, classElement: element);
     } else if (element is TypeAliasElement) {
       var aliasedType = element.aliasedType;
       if (aliasedType is InterfaceType) {
@@ -173,13 +174,98 @@
         //     class C { C.named(); }
         //     typedef X = C;
         //     X.named
-        return _toConstructorReference(
+        return _toConstructorReference_prefixed(
             node: node, classElement: aliasedType.element);
       }
     }
     return node;
   }
 
+  AstNode propertyAccess(Scope nameScope, PropertyAccess node) {
+    if (node.isCascaded) {
+      // For example, `List..filled`: this is a property access on an instance
+      // `Type`.
+      return node;
+    }
+    var receiver = node.target!;
+    if (receiver is! FunctionReference) {
+      return node;
+    }
+    var propertyName = node.propertyName;
+    if (propertyName.isSynthetic) {
+      // This isn't a constructor reference.
+      return node;
+    }
+    // A [ConstructorReference] with explicit type arguments is initially parsed
+    // as a [PropertyAccess] with a [FunctionReference] target; for example:
+    // `List<int>.filled` or `core.List<int>.filled`.
+    var receiverIdentifier = receiver.function;
+    if (receiverIdentifier is! Identifier) {
+      // If [receiverIdentifier] is not an Identifier then [node] is not a
+      // ConstructorReference.
+      return node;
+    }
+
+    Element? element;
+    if (receiverIdentifier is SimpleIdentifier) {
+      element = nameScope.lookup(receiverIdentifier.name).getter;
+    } else if (receiverIdentifier is PrefixedIdentifier) {
+      var prefixElement =
+          nameScope.lookup(receiverIdentifier.prefix.name).getter;
+      if (prefixElement is PrefixElement) {
+        element = prefixElement.scope
+            .lookup(receiverIdentifier.identifier.name)
+            .getter;
+      } else {
+        // This expression is something like `foo.List<int>.filled` where `foo`
+        // is not an import prefix.
+        // TODO(srawlins): Tease out a `null` prefixElement from others for
+        // specific errors.
+        return node;
+      }
+    }
+
+    if (element is ClassElement) {
+      // Example:
+      //     class C<T> { C.named(); }
+      //     C<int>.named
+      return _toConstructorReference_propertyAccess(
+        node: node,
+        receiver: receiverIdentifier,
+        typeArguments: receiver.typeArguments!,
+        classElement: element,
+      );
+    } else if (element is TypeAliasElement) {
+      var aliasedType = element.aliasedType;
+      if (aliasedType is InterfaceType) {
+        // Example:
+        //     class C<T> { C.named(); }
+        //     typedef X<T> = C<T>;
+        //     X<int>.named
+        return _toConstructorReference_propertyAccess(
+          node: node,
+          receiver: receiverIdentifier,
+          typeArguments: receiver.typeArguments!,
+          classElement: aliasedType.element,
+        );
+      }
+    }
+
+    // If [receiverIdentifier] is an Identifier, but could not be resolved to
+    // an Element, we cannot assume [node] is a ConstructorReference.
+    //
+    // TODO(srawlins): However, take an example like `Lisst<int>.filled;`
+    // (where 'Lisst' does not resolve to any element). Possibilities include:
+    // the user tried to write a TypeLiteral or a FunctionReference, then access
+    // a property on that (these include: hashCode, runtimeType, tearoff of
+    // toString, and extension methods on Type); or the user tried to write a
+    // ConstructReference. It seems much more likely that the user is trying to
+    // do the latter. Consider doing the work so that the user gets an error in
+    // this case about `Lisst` not being a type, or `Lisst.filled` not being a
+    // known constructor.
+    return node;
+  }
+
   AstNode _instanceCreation_prefix_type_name({
     required MethodInvocation node,
     required PrefixedIdentifier typeNameIdentifier,
@@ -210,7 +296,7 @@
     return instanceCreationExpression;
   }
 
-  AstNode _toConstructorReference(
+  AstNode _toConstructorReference_prefixed(
       {required PrefixedIdentifier node, required ClassElement classElement}) {
     var name = node.identifier.name;
     var constructorElement = name == 'new'
@@ -229,6 +315,31 @@
     return constructorReference;
   }
 
+  AstNode _toConstructorReference_propertyAccess({
+    required PropertyAccess node,
+    required Identifier receiver,
+    required TypeArgumentList typeArguments,
+    required ClassElement classElement,
+  }) {
+    var name = node.propertyName.name;
+    var constructorElement = name == 'new'
+        ? classElement.unnamedConstructor
+        : classElement.getNamedConstructor(name);
+    if (constructorElement == null) {
+      return node;
+    }
+
+    var operator = node.operator;
+
+    var typeName = astFactory.typeName(receiver, typeArguments);
+    var constructorName =
+        astFactory.constructorName(typeName, operator, node.propertyName);
+    var constructorReference =
+        astFactory.constructorReference(constructorName: constructorName);
+    NodeReplacer.replace(node, constructorReference);
+    return constructorReference;
+  }
+
   InstanceCreationExpression _toInstanceCreation_prefix_type({
     required MethodInvocation node,
     required SimpleIdentifier prefixIdentifier,
diff --git a/pkg/analyzer/lib/src/dart/resolver/constructor_reference_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/constructor_reference_resolver.dart
index 0d82de1..ce1fd84 100644
--- a/pkg/analyzer/lib/src/dart/resolver/constructor_reference_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/constructor_reference_resolver.dart
@@ -17,7 +17,10 @@
   ConstructorReferenceResolver(this._resolver);
 
   void resolve(ConstructorReferenceImpl node) {
-    if (!_resolver.isConstructorTearoffsEnabled) {
+    if (!_resolver.isConstructorTearoffsEnabled &&
+        node.constructorName.type.typeArguments == null) {
+      // Only report this if [node] has no explicit type arguments; otherwise
+      // the parser has already reported an error.
       _resolver.errorReporter.reportErrorForNode(
           CompileTimeErrorCode.CONSTRUCTOR_TEAROFFS_NOT_ENABLED, node, []);
     }
diff --git a/pkg/analyzer/lib/src/dart/resolver/for_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/for_resolver.dart
index 1c4f15f..49a31d0 100644
--- a/pkg/analyzer/lib/src/dart/resolver/for_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/for_resolver.dart
@@ -114,9 +114,10 @@
     iterable.accept(_resolver);
     iterable = forEachParts.iterable;
 
-    _resolver.nullableDereferenceVerifier.expression(iterable,
-        errorCode:
-            CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE_AS_ITERATOR);
+    _resolver.nullableDereferenceVerifier.expression(
+      CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE_AS_ITERATOR,
+      iterable,
+    );
 
     loopVariable?.accept(_resolver);
     var elementType = _computeForEachElementType(iterable, isAsync);
diff --git a/pkg/analyzer/lib/src/dart/resolver/function_expression_invocation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/function_expression_invocation_resolver.dart
index 8543e5e..48a6c65 100644
--- a/pkg/analyzer/lib/src/dart/resolver/function_expression_invocation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/function_expression_invocation_resolver.dart
@@ -24,7 +24,7 @@
 
   FunctionExpressionInvocationResolver({
     required ResolverVisitor resolver,
-  })   : _resolver = resolver,
+  })  : _resolver = resolver,
         _typePropertyResolver = resolver.typePropertyResolver,
         _inferenceHelper = resolver.inferenceHelper;
 
@@ -60,8 +60,10 @@
       return;
     }
 
-    _nullableDereferenceVerifier.expression(function,
-        errorCode: CompileTimeErrorCode.UNCHECKED_INVOCATION_OF_NULLABLE_VALUE);
+    _nullableDereferenceVerifier.expression(
+      CompileTimeErrorCode.UNCHECKED_INVOCATION_OF_NULLABLE_VALUE,
+      function,
+    );
 
     if (receiverType is FunctionType) {
       _resolve(node, receiverType, whyNotPromotedList);
diff --git a/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
index 76697c1..0bffbf1 100644
--- a/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
@@ -278,8 +278,11 @@
 
     function.prefix.staticType = prefixType;
     if (prefixType != null && prefixType.isDynamic) {
-      // TODO(srawlins): Report error. See spec text: "We do not allow dynamic
-      // explicit instantiation."
+      _errorReporter.reportErrorForNode(
+        CompileTimeErrorCode.GENERIC_METHOD_TYPE_INSTANTIATION_ON_DYNAMIC,
+        function,
+        [],
+      );
       node.staticType = DynamicTypeImpl.instance;
       return;
     }
diff --git a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
index 44388f6..5021eec 100644
--- a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
@@ -495,9 +495,11 @@
         );
       } else {
         _setDynamicResolution(node, whyNotPromotedList: whyNotPromotedList);
-        _resolver.nullableDereferenceVerifier.report(methodName, receiverType,
-            errorCode: CompileTimeErrorCode
-                .UNCHECKED_METHOD_INVOCATION_OF_NULLABLE_VALUE);
+        _resolver.nullableDereferenceVerifier.report(
+          CompileTimeErrorCode.UNCHECKED_METHOD_INVOCATION_OF_NULLABLE_VALUE,
+          methodName,
+          receiverType,
+        );
       }
       return;
     }
diff --git a/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart b/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
index 357caa6..b367f4a 100644
--- a/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
@@ -870,6 +870,16 @@
   }
 
   @override
+  void visitPropertyAccess(PropertyAccess node) {
+    var newNode = _astRewriter.propertyAccess(_nameScope, node);
+    if (newNode != node) {
+      return newNode.accept(this);
+    }
+
+    super.visitPropertyAccess(node);
+  }
+
+  @override
   void visitSimpleFormalParameter(covariant SimpleFormalParameterImpl node) {
     ParameterElementImpl element;
     if (node.parent is DefaultFormalParameter) {
diff --git a/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart
index 6183525..5fa2a91 100644
--- a/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/type_property_resolver.dart
@@ -143,8 +143,8 @@
         }
       }
       _resolver.nullableDereferenceVerifier.report(
-          propertyErrorEntity, receiverType,
-          errorCode: errorCode, arguments: [name], messages: messages);
+          errorCode, propertyErrorEntity, receiverType,
+          arguments: [name], messages: messages);
       _reportedGetterError = true;
       _reportedSetterError = true;
 
diff --git a/pkg/analyzer/lib/src/dart/resolver/yield_statement_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/yield_statement_resolver.dart
index 4238870..6e47fa5 100644
--- a/pkg/analyzer/lib/src/dart/resolver/yield_statement_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/yield_statement_resolver.dart
@@ -126,9 +126,10 @@
     node.expression.accept(_resolver);
 
     if (node.star != null) {
-      _resolver.nullableDereferenceVerifier.expression(node.expression,
-          errorCode: CompileTimeErrorCode
-              .UNCHECKED_USE_OF_NULLABLE_VALUE_IN_YIELD_EACH);
+      _resolver.nullableDereferenceVerifier.expression(
+        CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE_IN_YIELD_EACH,
+        node.expression,
+      );
     }
 
     bodyContext.addYield(node);
diff --git a/pkg/analyzer/lib/src/error/bool_expression_verifier.dart b/pkg/analyzer/lib/src/error/bool_expression_verifier.dart
index e56e3ad..108cc22 100644
--- a/pkg/analyzer/lib/src/error/bool_expression_verifier.dart
+++ b/pkg/analyzer/lib/src/error/bool_expression_verifier.dart
@@ -25,7 +25,7 @@
     required ResolverVisitor resolver,
     required ErrorReporter errorReporter,
     required NullableDereferenceVerifier nullableDereferenceVerifier,
-  })   : _resolver = resolver,
+  })  : _resolver = resolver,
         _errorReporter = errorReporter,
         _nullableDereferenceVerifier = nullableDereferenceVerifier,
         _boolType = resolver.typeSystem.typeProvider.boolType;
@@ -53,9 +53,10 @@
     if (!_checkForUseOfVoidResult(expression) &&
         !_resolver.typeSystem.isAssignableTo(type, _boolType)) {
       if (type.isDartCoreBool) {
-        _nullableDereferenceVerifier.report(expression, type,
-            errorCode: CompileTimeErrorCode
-                .UNCHECKED_USE_OF_NULLABLE_VALUE_AS_CONDITION,
+        _nullableDereferenceVerifier.report(
+            CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE_AS_CONDITION,
+            expression,
+            type,
             messages: _resolver.computeWhyNotPromotedMessages(
                 expression, whyNotPromoted?.call()));
       } else {
diff --git a/pkg/analyzer/lib/src/error/codes.dart b/pkg/analyzer/lib/src/error/codes.dart
index 7a9de95..fd77bb3 100644
--- a/pkg/analyzer/lib/src/error/codes.dart
+++ b/pkg/analyzer/lib/src/error/codes.dart
@@ -5208,6 +5208,19 @@
               "type, or using 'dynamic' as the type argument here.");
 
   /**
+   * No parameters.
+   */
+  static const CompileTimeErrorCode
+      GENERIC_METHOD_TYPE_INSTANTIATION_ON_DYNAMIC = CompileTimeErrorCode(
+    'GENERIC_METHOD_TYPE_INSTANTIATION_ON_DYNAMIC',
+    "A method tearoff on a target whose type is 'dynamic' can't have type "
+        "arguments.",
+    correction:
+        "Specify the type of the target, or remove the type arguments from the "
+        "method tearoff.",
+  );
+
+  /**
    * 10.3 Setters: It is a compile-time error if a class has a setter named
    * `v=` with argument type `T` and a getter named `v` with return type `S`,
    * and `S` may not be assigned to `T`.
diff --git a/pkg/analyzer/lib/src/error/nullable_dereference_verifier.dart b/pkg/analyzer/lib/src/error/nullable_dereference_verifier.dart
index fb96d1b..3c5caba 100644
--- a/pkg/analyzer/lib/src/error/nullable_dereference_verifier.dart
+++ b/pkg/analyzer/lib/src/error/nullable_dereference_verifier.dart
@@ -27,28 +27,26 @@
     required TypeSystemImpl typeSystem,
     required ErrorReporter errorReporter,
     required ResolverVisitor resolver,
-  })   : _typeSystem = typeSystem,
+  })  : _typeSystem = typeSystem,
         _errorReporter = errorReporter,
         _resolver = resolver;
 
-  bool expression(Expression expression,
-      {DartType? type, ErrorCode? errorCode}) {
+  bool expression(ErrorCode errorCode, Expression expression,
+      {DartType? type}) {
     if (!_typeSystem.isNonNullableByDefault) {
       return false;
     }
 
     type ??= expression.typeOrThrow;
-    return _check(expression, type, errorCode: errorCode);
+    return _check(errorCode, expression, type);
   }
 
-  void report(SyntacticEntity errorEntity, DartType receiverType,
-      {ErrorCode? errorCode,
-      List<String> arguments = const <String>[],
+  void report(
+      ErrorCode errorCode, SyntacticEntity errorEntity, DartType receiverType,
+      {List<String> arguments = const <String>[],
       List<DiagnosticMessage>? messages}) {
     if (receiverType == _typeSystem.typeProvider.nullType) {
       errorCode = CompileTimeErrorCode.INVALID_USE_OF_NULL_VALUE;
-    } else {
-      errorCode ??= CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE;
     }
     if (errorEntity is AstNode) {
       _errorReporter.reportErrorForNode(
@@ -67,8 +65,11 @@
   /// receiver is the implicit `this`, the name of the invocation.
   ///
   /// Returns whether [receiverType] was reported.
-  bool _check(AstNode errorNode, DartType receiverType,
-      {ErrorCode? errorCode}) {
+  bool _check(
+    ErrorCode errorCode,
+    AstNode errorNode,
+    DartType receiverType,
+  ) {
     if (identical(receiverType, DynamicTypeImpl.instance) ||
         !_typeSystem.isPotentiallyNullable(receiverType)) {
       return false;
@@ -79,7 +80,7 @@
       messages = _resolver.computeWhyNotPromotedMessages(
           errorNode, _resolver.flowAnalysis?.flow?.whyNotPromoted(errorNode)());
     }
-    report(errorNode, receiverType, errorCode: errorCode, messages: messages);
+    report(errorCode, errorNode, receiverType, messages: messages);
     return true;
   }
 }
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index bd08b70..a01167d 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -1896,9 +1896,10 @@
     super.visitSpreadElement(node);
 
     if (!node.isNullAware) {
-      nullableDereferenceVerifier.expression(node.expression,
-          errorCode:
-              CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE_IN_SPREAD);
+      nullableDereferenceVerifier.expression(
+        CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE_IN_SPREAD,
+        node.expression,
+      );
     }
   }
 
diff --git a/pkg/analyzer/lib/src/test_utilities/mock_sdk_elements.dart b/pkg/analyzer/lib/src/test_utilities/mock_sdk_elements.dart
index cb56f4d..a3d5f14 100644
--- a/pkg/analyzer/lib/src/test_utilities/mock_sdk_elements.dart
+++ b/pkg/analyzer/lib/src/test_utilities/mock_sdk_elements.dart
@@ -2,10 +2,10 @@
 // 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/dart/analysis/features.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/nullability_suffix.dart';
 import 'package:analyzer/dart/element/type.dart';
-import 'package:analyzer/src/dart/analysis/experiments.dart';
 import 'package:analyzer/src/dart/analysis/session.dart';
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/element/type.dart';
@@ -866,7 +866,7 @@
       'dart.async',
       0,
       0,
-      ExperimentStatus.latestWithNullSafety,
+      FeatureSet.latestLanguageVersion(),
     );
 
     var asyncUnit = CompilationUnitElementImpl();
@@ -959,7 +959,7 @@
       'dart.core',
       0,
       0,
-      ExperimentStatus.latestWithNullSafety,
+      FeatureSet.latestLanguageVersion(),
     );
     coreLibrary.definingCompilationUnit = coreUnit;
 
diff --git a/pkg/analyzer/test/error/error_test.dart b/pkg/analyzer/test/error/error_test.dart
index 6e14b89..b0a7875 100644
--- a/pkg/analyzer/test/error/error_test.dart
+++ b/pkg/analyzer/test/error/error_test.dart
@@ -5,9 +5,9 @@
 import 'dart:core';
 import 'dart:io';
 
+import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/analysis/utilities.dart';
 import 'package:analyzer/dart/ast/ast.dart';
-import 'package:analyzer/src/dart/analysis/experiments.dart';
 import 'package:path/path.dart' as path;
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -78,7 +78,7 @@
     return parseString(
       path: filePath,
       content: File(filePath).readAsStringSync(),
-      featureSet: ExperimentStatus.latestWithNullSafety,
+      featureSet: FeatureSet.latestLanguageVersion(),
     ).unit;
   }
 
diff --git a/pkg/analyzer/test/src/dart/resolution/constructor_reference_test.dart b/pkg/analyzer/test/src/dart/resolution/constructor_reference_test.dart
index b52a502..0c94b3c 100644
--- a/pkg/analyzer/test/src/dart/resolution/constructor_reference_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/constructor_reference_test.dart
@@ -2,6 +2,7 @@
 // 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/dart/ast/ast.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -10,12 +11,383 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(ConstructorReferenceResolutionTest);
+    defineReflectiveTests(ConstructorReferenceResolution_TypeArgsTest);
     defineReflectiveTests(
         ConstructorReferenceResolutionWithoutConstructorTearoffsTest);
   });
 }
 
 @reflectiveTest
+class ConstructorReferenceResolution_TypeArgsTest
+    extends PubPackageResolutionTest {
+  test_alias_generic_named() async {
+    await assertNoErrorsInCode('''
+class A<T, U> {
+  A.foo();
+}
+typedef TA<T, U> = A<U, T>;
+
+void bar() {
+  TA<int, String>.foo;
+}
+''');
+
+    var classElement = findElement.class_('A');
+    assertConstructorReference(
+      findNode.constructorReference('TA<int, String>.foo;'),
+      elementMatcher(classElement.getNamedConstructor('foo')!,
+          substitution: {'T': 'String', 'U': 'int'}),
+      classElement,
+      'A<String, int> Function()',
+      expectedTypeNameType: 'A<String, int>',
+      expectedTypeNameElement: findElement.typeAlias('TA'),
+    );
+  }
+
+  test_alias_generic_unnamed() async {
+    await assertNoErrorsInCode('''
+class A<T> {
+  A();
+}
+typedef TA<T> = A<T>;
+
+void bar() {
+  TA<int>.new;
+}
+''');
+
+    var classElement = findElement.class_('A');
+    assertConstructorReference(
+      findNode.constructorReference('TA<int>.new;'),
+      elementMatcher(classElement.unnamedConstructor,
+          substitution: {'T': 'int'}),
+      classElement,
+      'A<int> Function()',
+      expectedTypeNameType: 'A<int>',
+      expectedTypeNameElement: findElement.typeAlias('TA'),
+    );
+  }
+
+  test_alias_genericWithBound_unnamed() async {
+    await assertNoErrorsInCode('''
+class A<T> {
+  A();
+}
+typedef TA<T extends num> = A<T>;
+
+void bar() {
+  TA<int>.new;
+}
+''');
+
+    var classElement = findElement.class_('A');
+    assertConstructorReference(
+      findNode.constructorReference('TA<int>.new;'),
+      elementMatcher(classElement.unnamedConstructor,
+          substitution: {'T': 'int'}),
+      classElement,
+      'A<int> Function()',
+      expectedTypeNameType: 'A<int>',
+      expectedTypeNameElement: findElement.typeAlias('TA'),
+    );
+  }
+
+  test_alias_genericWithBound_unnamed_badBound() async {
+    await assertErrorsInCode('''
+class A<T> {
+  A();
+}
+typedef TA<T extends num> = A<T>;
+
+void bar() {
+  TA<String>.new;
+}
+''', [
+      error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 75, 6),
+    ]);
+
+    var classElement = findElement.class_('A');
+    assertConstructorReference(
+      findNode.constructorReference('TA<String>.new;'),
+      elementMatcher(classElement.unnamedConstructor,
+          substitution: {'T': 'String'}),
+      classElement,
+      'A<String> Function()',
+      expectedTypeNameType: 'A<String>',
+      expectedTypeNameElement: findElement.typeAlias('TA'),
+    );
+  }
+
+  test_class_generic_named() async {
+    await assertNoErrorsInCode('''
+class A<T> {
+  A.foo();
+}
+
+void bar() {
+  A<int>.foo;
+}
+''');
+
+    var classElement = findElement.class_('A');
+    assertConstructorReference(
+      findNode.constructorReference('A<int>.foo;'),
+      elementMatcher(classElement.getNamedConstructor('foo')!,
+          substitution: {'T': 'int'}),
+      classElement,
+      'A<int> Function()',
+      expectedTypeNameType: 'A<int>',
+    );
+  }
+
+  test_class_generic_named_cascade() async {
+    await assertErrorsInCode('''
+class A<T> {
+  A.foo();
+}
+
+void bar() {
+  A<int>..foo;
+}
+''', [
+      error(CompileTimeErrorCode.UNDEFINED_GETTER, 50, 3),
+    ]);
+
+    // The nodes are not rewritten into a [ConstructorReference].
+    var cascade = findNode.cascade('A<int>..foo;');
+    assertType(cascade, 'Type');
+    var section = cascade.cascadeSections.first as PropertyAccess;
+    assertType(section, 'dynamic');
+    assertType(section.propertyName, 'dynamic');
+  }
+
+  test_class_generic_named_nullAware() async {
+    await assertNoErrorsInCode('''
+class A<T> {
+  A.foo();
+}
+
+void bar() {
+  A<int>?.foo;
+}
+''');
+
+    var classElement = findElement.class_('A');
+    assertConstructorReference(
+      findNode.constructorReference('A<int>?.foo;'),
+      elementMatcher(classElement.getNamedConstructor('foo')!,
+          substitution: {'T': 'int'}),
+      classElement,
+      'A<int> Function()',
+      expectedTypeNameType: 'A<int>',
+    );
+  }
+
+  test_class_generic_named_typeArgs() async {
+    await assertErrorsInCode('''
+class A<T> {
+  A.foo();
+}
+
+void bar() {
+  A<int>.foo<int>;
+}
+''', [
+      error(CompileTimeErrorCode.DISALLOWED_TYPE_INSTANTIATION_EXPRESSION, 42,
+          10),
+      // TODO(srawlins): Stop reporting the error below; the code is not
+      // precise, and it is duplicate with the code above.
+      error(
+          CompileTimeErrorCode
+              .WRONG_NUMBER_OF_TYPE_ARGUMENTS_ANONYMOUS_FUNCTION,
+          52,
+          5),
+    ]);
+
+    var classElement = findElement.class_('A');
+    assertConstructorReference(
+      findNode.constructorReference('A<int>.foo<int>;'),
+      elementMatcher(classElement.getNamedConstructor('foo')!,
+          substitution: {'T': 'int'}),
+      classElement,
+      'A<int> Function()',
+      expectedTypeNameType: 'A<int>',
+    );
+  }
+
+  test_class_generic_unnamed() async {
+    await assertNoErrorsInCode('''
+class A<T> {
+  A();
+}
+
+void bar() {
+  A<int>.new;
+}
+''');
+
+    var classElement = findElement.class_('A');
+    assertConstructorReference(
+      findNode.constructorReference('A<int>.new;'),
+      elementMatcher(classElement.unnamedConstructor,
+          substitution: {'T': 'int'}),
+      classElement,
+      'A<int> Function()',
+      expectedTypeNameType: 'A<int>',
+    );
+  }
+
+  test_class_generic_unnamed_partOfPropertyAccess() async {
+    await assertNoErrorsInCode('''
+class A<T> {
+  A();
+}
+
+void bar() {
+  A<int>.new.runtimeType;
+}
+''');
+
+    var classElement = findElement.class_('A');
+    assertConstructorReference(
+      findNode.constructorReference('A<int>.new'),
+      elementMatcher(classElement.unnamedConstructor,
+          substitution: {'T': 'int'}),
+      classElement,
+      'A<int> Function()',
+      expectedTypeNameType: 'A<int>',
+    );
+  }
+
+  test_class_genericWithBound_unnamed() async {
+    await assertNoErrorsInCode('''
+class A<T extends num> {
+  A();
+}
+
+void bar() {
+  A<int>.new;
+}
+''');
+
+    var classElement = findElement.class_('A');
+    assertConstructorReference(
+      findNode.constructorReference('A<int>.new;'),
+      elementMatcher(classElement.unnamedConstructor,
+          substitution: {'T': 'int'}),
+      classElement,
+      'A<int> Function()',
+      expectedTypeNameType: 'A<int>',
+    );
+  }
+
+  test_class_genericWithBound_unnamed_badBound() async {
+    await assertErrorsInCode('''
+class A<T extends num> {
+  A();
+}
+
+void bar() {
+  A<String>.new;
+}
+''', [
+      error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 52, 6),
+    ]);
+
+    var classElement = findElement.class_('A');
+    assertConstructorReference(
+      findNode.constructorReference('A<String>.new;'),
+      elementMatcher(classElement.unnamedConstructor,
+          substitution: {'T': 'String'}),
+      classElement,
+      'A<String> Function()',
+      expectedTypeNameType: 'A<String>',
+    );
+  }
+
+  test_prefixedAlias_generic_unnamed() async {
+    newFile('$testPackageLibPath/a.dart', content: '''
+class A<T> {
+  A();
+}
+typedef TA<T> = A<T>;
+''');
+    await assertNoErrorsInCode('''
+import 'a.dart' as a;
+void bar() {
+  a.TA<int>.new;
+}
+''');
+
+    var classElement =
+        findElement.importFind('package:test/a.dart').class_('A');
+    assertConstructorReference(
+      findNode.constructorReference('a.TA<int>.new;'),
+      elementMatcher(classElement.unnamedConstructor,
+          substitution: {'T': 'int'}),
+      classElement,
+      'A<int> Function()',
+      expectedTypeNameType: 'A<int>',
+      expectedPrefix: findElement.import('package:test/a.dart').prefix,
+      expectedTypeNameElement:
+          findElement.importFind('package:test/a.dart').typeAlias('TA'),
+    );
+  }
+
+  test_prefixedClass_generic_named() async {
+    newFile('$testPackageLibPath/a.dart', content: '''
+class A<T> {
+  A.foo();
+}
+''');
+    await assertNoErrorsInCode('''
+import 'a.dart' as a;
+void bar() {
+  a.A<int>.foo;
+}
+''');
+
+    var classElement =
+        findElement.importFind('package:test/a.dart').class_('A');
+    assertConstructorReference(
+      findNode.constructorReference('a.A<int>.foo;'),
+      elementMatcher(classElement.getNamedConstructor('foo')!,
+          substitution: {'T': 'int'}),
+      classElement,
+      'A<int> Function()',
+      expectedTypeNameType: 'A<int>',
+      expectedPrefix: findElement.import('package:test/a.dart').prefix,
+    );
+  }
+
+  test_prefixedClass_generic_unnamed() async {
+    newFile('$testPackageLibPath/a.dart', content: '''
+class A<T> {
+  A();
+}
+''');
+    await assertNoErrorsInCode('''
+import 'a.dart' as a;
+void bar() {
+  a.A<int>.new;
+}
+''');
+
+    var classElement =
+        findElement.importFind('package:test/a.dart').class_('A');
+    assertConstructorReference(
+      findNode.constructorReference('a.A<int>.new;'),
+      elementMatcher(classElement.unnamedConstructor,
+          substitution: {'T': 'int'}),
+      classElement,
+      'A<int> Function()',
+      expectedTypeNameType: 'A<int>',
+      expectedPrefix: findElement.import('package:test/a.dart').prefix,
+    );
+  }
+}
+
+@reflectiveTest
 class ConstructorReferenceResolutionTest extends PubPackageResolutionTest {
   test_class_generic_inferFromContext_badTypeArgument() async {
     await assertErrorsInCode('''
@@ -41,6 +413,28 @@
     );
   }
 
+  test_class_generic_named_inferTypeFromContext() async {
+    await assertNoErrorsInCode('''
+class A<T> {
+  A.foo();
+}
+
+A<int> Function() bar() {
+  return A.foo;
+}
+''');
+
+    var classElement = findElement.class_('A');
+    var constructorElement = classElement.getNamedConstructor('foo')!;
+    assertConstructorReference(
+      findNode.constructorReference('A.foo;'),
+      elementMatcher(constructorElement, substitution: {'T': 'int'}),
+      classElement,
+      'A<int> Function()',
+      expectedTypeNameType: 'A<int>',
+    );
+  }
+
   test_class_generic_named_uninstantiated() async {
     await assertNoErrorsInCode('''
 class A<T> {
@@ -127,28 +521,6 @@
     );
   }
 
-  test_classs_generic_named_inferTypeFromContext() async {
-    await assertNoErrorsInCode('''
-class A<T> {
-  A.foo();
-}
-
-A<int> Function() bar() {
-  return A.foo;
-}
-''');
-
-    var classElement = findElement.class_('A');
-    var constructorElement = classElement.getNamedConstructor('foo')!;
-    assertConstructorReference(
-      findNode.constructorReference('A.foo;'),
-      elementMatcher(constructorElement, substitution: {'T': 'int'}),
-      classElement,
-      'A<int> Function()',
-      expectedTypeNameType: 'A<int>',
-    );
-  }
-
   test_typeAlias_generic_named_uninstantiated() async {
     await assertNoErrorsInCode('''
 class A<T, U> {
diff --git a/pkg/analyzer/test/src/dart/resolution/function_reference_test.dart b/pkg/analyzer/test/src/dart/resolution/function_reference_test.dart
index 3675f92..4947dab 100644
--- a/pkg/analyzer/test/src/dart/resolution/function_reference_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/function_reference_test.dart
@@ -582,6 +582,20 @@
     assertType(reference, 'void Function(dynamic)');
   }
 
+  test_receiverIsDynamic() async {
+    await assertErrorsInCode('''
+bar(dynamic a) {
+  a.foo<int>;
+}
+''', [
+      error(CompileTimeErrorCode.GENERIC_METHOD_TYPE_INSTANTIATION_ON_DYNAMIC,
+          19, 5),
+    ]);
+
+    var reference = findNode.functionReference('a.foo<int>;');
+    assertType(reference, 'dynamic');
+  }
+
   test_staticMethod() async {
     await assertNoErrorsInCode('''
 class A {
diff --git a/pkg/analyzer/test/src/dart/resolution/language_version_test.dart b/pkg/analyzer/test/src/dart/resolution/language_version_test.dart
index c94064b..bafbda4 100644
--- a/pkg/analyzer/test/src/dart/resolution/language_version_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/language_version_test.dart
@@ -18,8 +18,7 @@
 }
 
 @reflectiveTest
-class NullSafetyExperimentGlobalTest extends _FeaturesTest
-    with WithNullSafetyMixin {
+class NullSafetyExperimentGlobalTest extends _FeaturesTest {
   test_jsonConfig_legacyContext_nonNullDependency() async {
     _configureTestWithJsonConfig('''
 {
diff --git a/pkg/analyzer/test/src/dart/resolution/type_inference/local_variable_test.dart b/pkg/analyzer/test/src/dart/resolution/type_inference/local_variable_test.dart
index 5d3126f..556c1d0 100644
--- a/pkg/analyzer/test/src/dart/resolution/type_inference/local_variable_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/type_inference/local_variable_test.dart
@@ -9,13 +9,25 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(LocalVariableTest);
-    defineReflectiveTests(LocalVariableWithNullSafetyTest);
+    defineReflectiveTests(LocalVariableWithoutNullSafetyTest);
   });
 }
 
 @reflectiveTest
 class LocalVariableTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
+    with LocalVariableTestCases {
+  test_Never() async {
+    await resolveTestCode('''
+void f(Never a) {
+  var v = a;
+  v;
+}
+''');
+    _assertTypeOfV('Never');
+  }
+}
+
+mixin LocalVariableTestCases on PubPackageResolutionTest {
   test_int() async {
     await resolveTestCode('''
 void f() {
@@ -43,15 +55,5 @@
 }
 
 @reflectiveTest
-class LocalVariableWithNullSafetyTest extends LocalVariableTest
-    with WithNullSafetyMixin {
-  test_Never() async {
-    await resolveTestCode('''
-void f(Never a) {
-  var v = a;
-  v;
-}
-''');
-    _assertTypeOfV('Never');
-  }
-}
+class LocalVariableWithoutNullSafetyTest extends PubPackageResolutionTest
+    with LocalVariableTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/experiment_not_enabled_test.dart b/pkg/analyzer/test/src/diagnostics/experiment_not_enabled_test.dart
index a305fc4..4dbaeab 100644
--- a/pkg/analyzer/test/src/diagnostics/experiment_not_enabled_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/experiment_not_enabled_test.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analyzer/src/dart/error/syntactic_errors.dart';
+import 'package:analyzer/src/error/codes.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
 import '../dart/resolution/context_collection_resolution.dart';
@@ -27,6 +28,7 @@
 }
 ''', [
       error(ParserErrorCode.EXPERIMENT_NOT_ENABLED, 86, 5),
+      error(CompileTimeErrorCode.UNDEFINED_METHOD, 96, 3),
     ]);
   }
 
@@ -42,6 +44,7 @@
 }
 ''', [
       error(ParserErrorCode.EXPERIMENT_NOT_ENABLED, 83, 5),
+      error(CompileTimeErrorCode.UNDEFINED_METHOD, 93, 3),
     ]);
   }
 
diff --git a/pkg/analyzer/test/src/diagnostics/missing_enum_constant_in_switch_test.dart b/pkg/analyzer/test/src/diagnostics/missing_enum_constant_in_switch_test.dart
index e3523e4..7ea8972 100644
--- a/pkg/analyzer/test/src/diagnostics/missing_enum_constant_in_switch_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/missing_enum_constant_in_switch_test.dart
@@ -10,13 +10,63 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(MissingEnumConstantInSwitchTest);
-    defineReflectiveTests(MissingEnumConstantInSwitchWithNullSafetyTest);
+    defineReflectiveTests(MissingEnumConstantInSwitchWithoutNullSafetyTest);
   });
 }
 
 @reflectiveTest
 class MissingEnumConstantInSwitchTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
+    with MissingEnumConstantInSwitchTestCases {
+  test_nullable() async {
+    await assertErrorsInCode('''
+enum E { one, two }
+
+void f(E? e) {
+  switch (e) {
+    case E.one:
+    case E.two:
+      break;
+  }
+}
+''', [
+      error(StaticWarningCode.MISSING_ENUM_CONSTANT_IN_SWITCH, 38, 10),
+    ]);
+  }
+
+  test_nullable_default() async {
+    await assertNoErrorsInCode('''
+enum E { one, two }
+
+void f(E? e) {
+  switch (e) {
+    case E.one:
+      break;
+    default:
+      break;
+  }
+}
+''');
+  }
+
+  test_nullable_null() async {
+    await assertNoErrorsInCode('''
+enum E { one, two }
+
+void f(E? e) {
+  switch (e) {
+    case E.one:
+      break;
+    case E.two:
+      break;
+    case null:
+      break;
+  }
+}
+''');
+  }
+}
+
+mixin MissingEnumConstantInSwitchTestCases on PubPackageResolutionTest {
   test_default() async {
     await assertNoErrorsInCode('''
 enum E { one, two, three }
@@ -99,53 +149,6 @@
 }
 
 @reflectiveTest
-class MissingEnumConstantInSwitchWithNullSafetyTest
-    extends MissingEnumConstantInSwitchTest with WithNullSafetyMixin {
-  test_nullable() async {
-    await assertErrorsInCode('''
-enum E { one, two }
-
-void f(E? e) {
-  switch (e) {
-    case E.one:
-    case E.two:
-      break;
-  }
-}
-''', [
-      error(StaticWarningCode.MISSING_ENUM_CONSTANT_IN_SWITCH, 38, 10),
-    ]);
-  }
-
-  test_nullable_default() async {
-    await assertNoErrorsInCode('''
-enum E { one, two }
-
-void f(E? e) {
-  switch (e) {
-    case E.one:
-      break;
-    default:
-      break;
-  }
-}
-''');
-  }
-
-  test_nullable_null() async {
-    await assertNoErrorsInCode('''
-enum E { one, two }
-
-void f(E? e) {
-  switch (e) {
-    case E.one:
-      break;
-    case E.two:
-      break;
-    case null:
-      break;
-  }
-}
-''');
-  }
-}
+class MissingEnumConstantInSwitchWithoutNullSafetyTest
+    extends PubPackageResolutionTest
+    with MissingEnumConstantInSwitchTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/mixin_of_non_class_test.dart b/pkg/analyzer/test/src/diagnostics/mixin_of_non_class_test.dart
index 53935fa..47c9fd2 100644
--- a/pkg/analyzer/test/src/diagnostics/mixin_of_non_class_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/mixin_of_non_class_test.dart
@@ -10,13 +10,23 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(MixinOfNonClassTest);
-    defineReflectiveTests(MixinOfNonClassWithNullSafetyTest);
+    defineReflectiveTests(MixinOfNonClassWithoutNullSafetyTest);
   });
 }
 
 @reflectiveTest
 class MixinOfNonClassTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
+    with MixinOfNonClassTestCases {
+  test_Never() async {
+    await assertErrorsInCode('''
+class A with Never {}
+''', [
+      error(CompileTimeErrorCode.MIXIN_OF_NON_CLASS, 13, 5),
+    ]);
+  }
+}
+
+mixin MixinOfNonClassTestCases on PubPackageResolutionTest {
   test_class() async {
     await assertErrorsInCode(r'''
 int A = 7;
@@ -165,13 +175,5 @@
 }
 
 @reflectiveTest
-class MixinOfNonClassWithNullSafetyTest extends MixinOfNonClassTest
-    with WithNullSafetyMixin {
-  test_Never() async {
-    await assertErrorsInCode('''
-class A with Never {}
-''', [
-      error(CompileTimeErrorCode.MIXIN_OF_NON_CLASS, 13, 5),
-    ]);
-  }
-}
+class MixinOfNonClassWithoutNullSafetyTest extends PubPackageResolutionTest
+    with MixinOfNonClassTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/mixin_super_class_constraint_non_interface_test.dart b/pkg/analyzer/test/src/diagnostics/mixin_super_class_constraint_non_interface_test.dart
index 204e614..08db55e 100644
--- a/pkg/analyzer/test/src/diagnostics/mixin_super_class_constraint_non_interface_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/mixin_super_class_constraint_non_interface_test.dart
@@ -10,18 +10,12 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(MixinSuperClassConstraintNonInterfaceTest);
-    defineReflectiveTests(
-        MixinSuperClassConstraintNonInterfaceWithNullSafetyTest);
   });
 }
 
 @reflectiveTest
-class MixinSuperClassConstraintNonInterfaceTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {}
-
-@reflectiveTest
-class MixinSuperClassConstraintNonInterfaceWithNullSafetyTest
-    extends MixinSuperClassConstraintNonInterfaceTest with WithNullSafetyMixin {
+class MixinSuperClassConstraintNonInterfaceTest
+    extends PubPackageResolutionTest {
   test_Never() async {
     await assertErrorsInCode('''
 mixin M on Never {}
diff --git a/pkg/analyzer/test/src/diagnostics/non_constant_case_expression_test.dart b/pkg/analyzer/test/src/diagnostics/non_constant_case_expression_test.dart
index d36da75..d77df25 100644
--- a/pkg/analyzer/test/src/diagnostics/non_constant_case_expression_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/non_constant_case_expression_test.dart
@@ -10,13 +10,15 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(NonConstantCaseExpressionTest);
-    defineReflectiveTests(NonConstantCaseExpressionWithNullSafetyTest);
+    defineReflectiveTests(NonConstantCaseExpressionWithoutNullSafetyTest);
   });
 }
 
 @reflectiveTest
 class NonConstantCaseExpressionTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
+    with NonConstantCaseExpressionTestCases {}
+
+mixin NonConstantCaseExpressionTestCases on PubPackageResolutionTest {
   test_constField() async {
     await assertNoErrorsInCode(r'''
 void f(C e) {
@@ -66,5 +68,6 @@
 }
 
 @reflectiveTest
-class NonConstantCaseExpressionWithNullSafetyTest
-    extends NonConstantCaseExpressionTest with WithNullSafetyMixin {}
+class NonConstantCaseExpressionWithoutNullSafetyTest
+    extends PubPackageResolutionTest
+    with NonConstantCaseExpressionTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/not_map_spread_test.dart b/pkg/analyzer/test/src/diagnostics/not_map_spread_test.dart
index c025714..3c008f9 100644
--- a/pkg/analyzer/test/src/diagnostics/not_map_spread_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/not_map_spread_test.dart
@@ -10,13 +10,13 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(NotMapSpreadTest);
-    defineReflectiveTests(NotMapSpreadNullSafetyTest);
+    defineReflectiveTests(NotMapSpreadWithoutNullSafetyTest);
   });
 }
 
 @reflectiveTest
-class NotMapSpreadNullSafetyTest extends NotMapSpreadTest
-    with WithNullSafetyMixin {
+class NotMapSpreadTest extends PubPackageResolutionTest
+    with NotMapSpreadTestCases {
   test_map_typeParameter_bound_mapQuestion() async {
     await assertNoErrorsInCode('''
 void f<T extends Map<int, String>?>(T a) {
@@ -27,9 +27,7 @@
   }
 }
 
-@reflectiveTest
-class NotMapSpreadTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
+mixin NotMapSpreadTestCases on PubPackageResolutionTest {
   test_map() async {
     await assertNoErrorsInCode('''
 var a = {0: 0};
@@ -100,3 +98,7 @@
     ]);
   }
 }
+
+@reflectiveTest
+class NotMapSpreadWithoutNullSafetyTest extends PubPackageResolutionTest
+    with NotMapSpreadTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/return_of_invalid_type_from_catch_error_test.dart b/pkg/analyzer/test/src/diagnostics/return_of_invalid_type_from_catch_error_test.dart
index 1e976ed..9a5d8e7 100644
--- a/pkg/analyzer/test/src/diagnostics/return_of_invalid_type_from_catch_error_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/return_of_invalid_type_from_catch_error_test.dart
@@ -10,13 +10,46 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(ReturnOfInvalidTypeForCatchErrorTest);
-    defineReflectiveTests(ReturnOfInvalidTypeForCatchErrorWithNullSafetyTest);
+    defineReflectiveTests(
+        ReturnOfInvalidTypeForCatchErrorWithoutNullSafetyTest);
   });
 }
 
 @reflectiveTest
 class ReturnOfInvalidTypeForCatchErrorTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
+    with ReturnOfInvalidTypeForCatchErrorTestCases {
+  test_nullableType_emptyBody() async {
+    await assertNoErrorsInCode('''
+void f(Future<int?> future) {
+  future.catchError((e, st) {});
+}
+''');
+  }
+
+  test_nullableType_emptyReturn() async {
+    await assertErrorsInCode('''
+void f(Future<int?> future) {
+  future.catchError((e, st) {
+    return;
+  });
+}
+''', [
+      error(CompileTimeErrorCode.RETURN_WITHOUT_VALUE, 64, 6),
+    ]);
+  }
+
+  test_nullableType_invalidReturnType() async {
+    await assertErrorsInCode('''
+void f(Future<int?> future) {
+  future.catchError((e, st) => '');
+}
+''', [
+      error(HintCode.RETURN_OF_INVALID_TYPE_FROM_CATCH_ERROR, 61, 2),
+    ]);
+  }
+}
+
+mixin ReturnOfInvalidTypeForCatchErrorTestCases on PubPackageResolutionTest {
   test_async_okReturnType() async {
     await assertNoErrorsInCode('''
 void f(Future<int> future) {
@@ -167,35 +200,6 @@
 }
 
 @reflectiveTest
-class ReturnOfInvalidTypeForCatchErrorWithNullSafetyTest
-    extends ReturnOfInvalidTypeForCatchErrorTest with WithNullSafetyMixin {
-  test_nullableType_emptyBody() async {
-    await assertNoErrorsInCode('''
-void f(Future<int?> future) {
-  future.catchError((e, st) {});
-}
-''');
-  }
-
-  test_nullableType_emptyReturn() async {
-    await assertErrorsInCode('''
-void f(Future<int?> future) {
-  future.catchError((e, st) {
-    return;
-  });
-}
-''', [
-      error(CompileTimeErrorCode.RETURN_WITHOUT_VALUE, 64, 6),
-    ]);
-  }
-
-  test_nullableType_invalidReturnType() async {
-    await assertErrorsInCode('''
-void f(Future<int?> future) {
-  future.catchError((e, st) => '');
-}
-''', [
-      error(HintCode.RETURN_OF_INVALID_TYPE_FROM_CATCH_ERROR, 61, 2),
-    ]);
-  }
-}
+class ReturnOfInvalidTypeForCatchErrorWithoutNullSafetyTest
+    extends PubPackageResolutionTest
+    with ReturnOfInvalidTypeForCatchErrorTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/return_of_invalid_type_test.dart b/pkg/analyzer/test/src/diagnostics/return_of_invalid_type_test.dart
index cfe8902..5f7f7cb 100644
--- a/pkg/analyzer/test/src/diagnostics/return_of_invalid_type_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/return_of_invalid_type_test.dart
@@ -11,13 +11,85 @@
   defineReflectiveSuite(() {
     defineReflectiveTests(ReturnOfInvalidTypeTest);
     defineReflectiveTests(ReturnOfInvalidTypeWithNoImplicitCastsTest);
-    defineReflectiveTests(ReturnOfInvalidTypeWithNullSafetyTest);
+    defineReflectiveTests(ReturnOfInvalidTypeWithoutNullSafetyTest);
   });
 }
 
 @reflectiveTest
 class ReturnOfInvalidTypeTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
+    with ReturnOfInvalidTypeTestCases {
+  test_function_async_block_int__to_Future_void() async {
+    await assertErrorsInCode(r'''
+Future<void> f() async {
+  return 0;
+}
+''', [
+      error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_FUNCTION, 34, 1),
+    ]);
+  }
+
+  test_function_async_block_void__to_Future_Null() async {
+    await assertErrorsInCode(r'''
+Future<Null> f(void a) async {
+  return a;
+}
+''', [
+      error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_FUNCTION, 40, 1),
+    ]);
+  }
+
+  test_function_async_block_void__to_FutureOr_ObjectQ() async {
+    await assertErrorsInCode(r'''
+import 'dart:async';
+
+FutureOr<Object?> f(void a) async {
+  return a;
+}
+''', [
+      error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_FUNCTION, 67, 1),
+    ]);
+  }
+
+  test_function_async_expression_dynamic__to_Future_int() async {
+    await assertNoErrorsInCode(r'''
+Future<int> f(dynamic a) async => a;
+''');
+  }
+
+  test_functionExpression_async_futureOr_void__to_Object() async {
+    await assertNoErrorsInCode(r'''
+void a = null;
+
+Object Function() f = () async {
+  return a;
+};
+''');
+  }
+
+  test_functionExpression_async_futureQ_void__to_Object() async {
+    await assertNoErrorsInCode(r'''
+Future<void>? a = (throw 0);
+
+Object Function() f = () async {
+  return a;
+};
+''');
+  }
+
+  test_functionExpression_async_void__to_FutureOr_ObjectQ() async {
+    await assertNoErrorsInCode(r'''
+import 'dart:async';
+
+void a = (throw 0);
+
+FutureOr<Object?> Function() f = () async {
+  return a;
+};
+''');
+  }
+}
+
+mixin ReturnOfInvalidTypeTestCases on PubPackageResolutionTest {
   test_closure() async {
     await assertErrorsInCode('''
 typedef Td = int Function();
@@ -415,75 +487,5 @@
 }
 
 @reflectiveTest
-class ReturnOfInvalidTypeWithNullSafetyTest extends ReturnOfInvalidTypeTest
-    with WithNullSafetyMixin {
-  test_function_async_block_int__to_Future_void() async {
-    await assertErrorsInCode(r'''
-Future<void> f() async {
-  return 0;
-}
-''', [
-      error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_FUNCTION, 34, 1),
-    ]);
-  }
-
-  test_function_async_block_void__to_Future_Null() async {
-    await assertErrorsInCode(r'''
-Future<Null> f(void a) async {
-  return a;
-}
-''', [
-      error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_FUNCTION, 40, 1),
-    ]);
-  }
-
-  test_function_async_block_void__to_FutureOr_ObjectQ() async {
-    await assertErrorsInCode(r'''
-import 'dart:async';
-
-FutureOr<Object?> f(void a) async {
-  return a;
-}
-''', [
-      error(CompileTimeErrorCode.RETURN_OF_INVALID_TYPE_FROM_FUNCTION, 67, 1),
-    ]);
-  }
-
-  test_function_async_expression_dynamic__to_Future_int() async {
-    await assertNoErrorsInCode(r'''
-Future<int> f(dynamic a) async => a;
-''');
-  }
-
-  test_functionExpression_async_futureOr_void__to_Object() async {
-    await assertNoErrorsInCode(r'''
-void a = null;
-
-Object Function() f = () async {
-  return a;
-};
-''');
-  }
-
-  test_functionExpression_async_futureQ_void__to_Object() async {
-    await assertNoErrorsInCode(r'''
-Future<void>? a = (throw 0);
-
-Object Function() f = () async {
-  return a;
-};
-''');
-  }
-
-  test_functionExpression_async_void__to_FutureOr_ObjectQ() async {
-    await assertNoErrorsInCode(r'''
-import 'dart:async';
-
-void a = (throw 0);
-
-FutureOr<Object?> Function() f = () async {
-  return a;
-};
-''');
-  }
-}
+class ReturnOfInvalidTypeWithoutNullSafetyTest extends PubPackageResolutionTest
+    with ReturnOfInvalidTypeTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/return_type_invalid_for_catch_error_test.dart b/pkg/analyzer/test/src/diagnostics/return_type_invalid_for_catch_error_test.dart
index fd534d0..b5a19e8 100644
--- a/pkg/analyzer/test/src/diagnostics/return_type_invalid_for_catch_error_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/return_type_invalid_for_catch_error_test.dart
@@ -10,13 +10,25 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(ReturnTypeInvalidForCatchErrorTest);
-    defineReflectiveTests(ReturnTypeInvalidForCatchErrorWithNullSafetyTest);
+    defineReflectiveTests(ReturnTypeInvalidForCatchErrorWithoutNullSafetyTest);
   });
 }
 
 @reflectiveTest
 class ReturnTypeInvalidForCatchErrorTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
+    with ReturnTypeInvalidForCatchErrorTestCases {
+  test_nullableReturnType() async {
+    await assertErrorsInCode('''
+void f(Future<int> future, String? Function(dynamic, StackTrace) cb) {
+  future.catchError(cb);
+}
+''', [
+      error(HintCode.RETURN_TYPE_INVALID_FOR_CATCH_ERROR, 91, 2),
+    ]);
+  }
+}
+
+mixin ReturnTypeInvalidForCatchErrorTestCases on PubPackageResolutionTest {
   test_dynamic_returnTypeIsUnrelatedFuture() async {
     await assertNoErrorsInCode('''
 void f(
@@ -87,15 +99,6 @@
 }
 
 @reflectiveTest
-class ReturnTypeInvalidForCatchErrorWithNullSafetyTest
-    extends ReturnTypeInvalidForCatchErrorTest with WithNullSafetyMixin {
-  test_nullableReturnType() async {
-    await assertErrorsInCode('''
-void f(Future<int> future, String? Function(dynamic, StackTrace) cb) {
-  future.catchError(cb);
-}
-''', [
-      error(HintCode.RETURN_TYPE_INVALID_FOR_CATCH_ERROR, 91, 2),
-    ]);
-  }
-}
+class ReturnTypeInvalidForCatchErrorWithoutNullSafetyTest
+    extends PubPackageResolutionTest
+    with ReturnTypeInvalidForCatchErrorTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/return_without_value_test.dart b/pkg/analyzer/test/src/diagnostics/return_without_value_test.dart
index f90e7e6..b6149bb 100644
--- a/pkg/analyzer/test/src/diagnostics/return_without_value_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/return_without_value_test.dart
@@ -10,13 +10,15 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(ReturnWithoutValueTest);
-    defineReflectiveTests(ReturnWithoutValueWithNullSafetyTest);
+    defineReflectiveTests(ReturnWithoutValueWithoutNullSafetyTest);
   });
 }
 
 @reflectiveTest
 class ReturnWithoutValueTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
+    with ReturnWithoutValueTestCases {}
+
+mixin ReturnWithoutValueTestCases on PubPackageResolutionTest {
   test_async_futureInt() async {
     await assertErrorsInCode('''
 Future<int> f() async {
@@ -163,5 +165,5 @@
 }
 
 @reflectiveTest
-class ReturnWithoutValueWithNullSafetyTest extends ReturnWithoutValueTest
-    with WithNullSafetyMixin {}
+class ReturnWithoutValueWithoutNullSafetyTest extends PubPackageResolutionTest
+    with ReturnWithoutValueTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/sdk_version_never_test.dart b/pkg/analyzer/test/src/diagnostics/sdk_version_never_test.dart
index 4773eae..ad2aa2d 100644
--- a/pkg/analyzer/test/src/diagnostics/sdk_version_never_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/sdk_version_never_test.dart
@@ -11,25 +11,12 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(SdkVersionNeverTest);
-    defineReflectiveTests(SdkVersionNeverWithNullSafetyTest);
+    defineReflectiveTests(SdkVersionNeverWithoutNullSafetyTest);
   });
 }
 
 @reflectiveTest
-class SdkVersionNeverTest extends SdkConstraintVerifierTest
-    with WithoutNullSafetyMixin {
-  test_languageVersionBeforeNullSafety() async {
-    await verifyVersion('2.7.0', r'''
-Never foo;
-''', expectedErrors: [
-      error(HintCode.SDK_VERSION_NEVER, 0, 5),
-    ]);
-  }
-}
-
-@reflectiveTest
-class SdkVersionNeverWithNullSafetyTest extends SdkConstraintVerifierTest
-    with WithNullSafetyMixin {
+class SdkVersionNeverTest extends SdkConstraintVerifierTest {
   test_experimentEnabled() async {
     await verifyVersion('2.7.0', r'''
 Never foo = (throw 42);
@@ -45,3 +32,15 @@
     ]);
   }
 }
+
+@reflectiveTest
+class SdkVersionNeverWithoutNullSafetyTest extends SdkConstraintVerifierTest
+    with WithoutNullSafetyMixin {
+  test_languageVersionBeforeNullSafety() async {
+    await verifyVersion('2.7.0', r'''
+Never foo;
+''', expectedErrors: [
+      error(HintCode.SDK_VERSION_NEVER, 0, 5),
+    ]);
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/type_parameter_supertype_of_its_bound_test.dart b/pkg/analyzer/test/src/diagnostics/type_parameter_supertype_of_its_bound_test.dart
index 001d947..fdfa182 100644
--- a/pkg/analyzer/test/src/diagnostics/type_parameter_supertype_of_its_bound_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/type_parameter_supertype_of_its_bound_test.dart
@@ -10,13 +10,16 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(TypeParameterSupertypeOfItsBoundTest);
-    defineReflectiveTests(TypeParameterSupertypeOfItsBoundWithNullSafetyTest);
+    defineReflectiveTests(
+        TypeParameterSupertypeOfItsBoundWithoutNullSafetyTest);
   });
 }
 
 @reflectiveTest
 class TypeParameterSupertypeOfItsBoundTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
+    with TypeParameterSupertypeOfItsBoundTestCases {}
+
+mixin TypeParameterSupertypeOfItsBoundTestCases on PubPackageResolutionTest {
   test_1of1() async {
     await assertErrorsInCode(r'''
 class A<T extends T> {
@@ -50,5 +53,6 @@
 }
 
 @reflectiveTest
-class TypeParameterSupertypeOfItsBoundWithNullSafetyTest
-    extends TypeParameterSupertypeOfItsBoundTest with WithNullSafetyMixin {}
+class TypeParameterSupertypeOfItsBoundWithoutNullSafetyTest
+    extends PubPackageResolutionTest
+    with TypeParameterSupertypeOfItsBoundTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/undefined_hidden_name_test.dart b/pkg/analyzer/test/src/diagnostics/undefined_hidden_name_test.dart
index 6fcfad3..cf1eba2 100644
--- a/pkg/analyzer/test/src/diagnostics/undefined_hidden_name_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/undefined_hidden_name_test.dart
@@ -10,13 +10,15 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(UndefinedHiddenNameTest);
-    defineReflectiveTests(UndefinedHiddenNameWithNullSafetyTest);
+    defineReflectiveTests(UndefinedHiddenNameWithoutNullSafetyTest);
   });
 }
 
 @reflectiveTest
 class UndefinedHiddenNameTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
+    with UndefinedHiddenNameTestCases {}
+
+mixin UndefinedHiddenNameTestCases on PubPackageResolutionTest {
   test_export() async {
     newFile('$testPackageLibPath/lib1.dart');
     await assertErrorsInCode(r'''
@@ -38,5 +40,5 @@
 }
 
 @reflectiveTest
-class UndefinedHiddenNameWithNullSafetyTest extends UndefinedHiddenNameTest
-    with WithNullSafetyMixin {}
+class UndefinedHiddenNameWithoutNullSafetyTest extends PubPackageResolutionTest
+    with UndefinedHiddenNameTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/undefined_identifier_test.dart b/pkg/analyzer/test/src/diagnostics/undefined_identifier_test.dart
index 2b0b5a5..c339039 100644
--- a/pkg/analyzer/test/src/diagnostics/undefined_identifier_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/undefined_identifier_test.dart
@@ -11,13 +11,38 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(UndefinedIdentifierTest);
-    defineReflectiveTests(UndefinedIdentifierWithNullSafetyTest);
+    defineReflectiveTests(UndefinedIdentifierWithoutNullSafetyTest);
   });
 }
 
 @reflectiveTest
 class UndefinedIdentifierTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
+    with UndefinedIdentifierTestCases {
+  test_get_from_external_variable_final_valid() async {
+    await assertNoErrorsInCode('''
+external final int x;
+int f() => x;
+''');
+  }
+
+  test_get_from_external_variable_valid() async {
+    await assertNoErrorsInCode('''
+external int x;
+int f() => x;
+''');
+  }
+
+  test_set_external_variable_valid() async {
+    await assertNoErrorsInCode('''
+external int x;
+void f(int value) {
+  x = value;
+}
+''');
+  }
+}
+
+mixin UndefinedIdentifierTestCases on PubPackageResolutionTest {
   test_annotation_favors_scope_resolution_over_this_resolution_class() async {
     // If an annotation on a class type parameter cannot be resolved using the
     // normal scope resolution mechanism, it is resolved via implicit `this`.
@@ -397,28 +422,5 @@
 }
 
 @reflectiveTest
-class UndefinedIdentifierWithNullSafetyTest extends UndefinedIdentifierTest
-    with WithNullSafetyMixin {
-  test_get_from_external_variable_final_valid() async {
-    await assertNoErrorsInCode('''
-external final int x;
-int f() => x;
-''');
-  }
-
-  test_get_from_external_variable_valid() async {
-    await assertNoErrorsInCode('''
-external int x;
-int f() => x;
-''');
-  }
-
-  test_set_external_variable_valid() async {
-    await assertNoErrorsInCode('''
-external int x;
-void f(int value) {
-  x = value;
-}
-''');
-  }
-}
+class UndefinedIdentifierWithoutNullSafetyTest extends PubPackageResolutionTest
+    with UndefinedIdentifierTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/undefined_shown_name_test.dart b/pkg/analyzer/test/src/diagnostics/undefined_shown_name_test.dart
index ea89f9a..f486367 100644
--- a/pkg/analyzer/test/src/diagnostics/undefined_shown_name_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/undefined_shown_name_test.dart
@@ -10,13 +10,15 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(UndefinedShownNameTest);
-    defineReflectiveTests(UndefinedShownNameWithNullSafetyTest);
+    defineReflectiveTests(UndefinedShownNameWithoutNullSafetyTest);
   });
 }
 
 @reflectiveTest
 class UndefinedShownNameTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
+    with UndefinedShownNameTestCases {}
+
+mixin UndefinedShownNameTestCases on PubPackageResolutionTest {
   test_export() async {
     newFile('$testPackageLibPath/lib1.dart');
     await assertErrorsInCode(r'''
@@ -38,5 +40,5 @@
 }
 
 @reflectiveTest
-class UndefinedShownNameWithNullSafetyTest extends UndefinedShownNameTest
-    with WithNullSafetyMixin {}
+class UndefinedShownNameWithoutNullSafetyTest extends PubPackageResolutionTest
+    with UndefinedShownNameTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/unnecessary_type_check_test.dart b/pkg/analyzer/test/src/diagnostics/unnecessary_type_check_test.dart
index 65e5479..fef92e8 100644
--- a/pkg/analyzer/test/src/diagnostics/unnecessary_type_check_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/unnecessary_type_check_test.dart
@@ -10,15 +10,36 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(UnnecessaryTypeCheckFalseTest);
-    defineReflectiveTests(UnnecessaryTypeCheckFalseWithNullSafetyTest);
+    defineReflectiveTests(UnnecessaryTypeCheckFalseWithoutNullSafetyTest);
     defineReflectiveTests(UnnecessaryTypeCheckTrueTest);
-    defineReflectiveTests(UnnecessaryTypeCheckTrueWithNullSafetyTest);
+    defineReflectiveTests(UnnecessaryTypeCheckTrueWithoutNullSafetyTest);
   });
 }
 
 @reflectiveTest
 class UnnecessaryTypeCheckFalseTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
+    with UnnecessaryTypeCheckFalseTestCases {
+  @override
+  test_type_not_object() async {
+    await assertNoErrorsInCode(r'''
+void f<T>(T a) {
+  a is! Object;
+}
+''');
+  }
+
+  test_type_not_objectQuestion() async {
+    await assertErrorsInCode(r'''
+void f<T>(T a) {
+  a is! Object?;
+}
+''', [
+      error(HintCode.UNNECESSARY_TYPE_CHECK_FALSE, 19, 13),
+    ]);
+  }
+}
+
+mixin UnnecessaryTypeCheckFalseTestCases on PubPackageResolutionTest {
   test_null_not_Null() async {
     await assertErrorsInCode(r'''
 var b = null is! Null;
@@ -49,31 +70,34 @@
 }
 
 @reflectiveTest
-class UnnecessaryTypeCheckFalseWithNullSafetyTest
-    extends UnnecessaryTypeCheckFalseTest with WithNullSafetyMixin {
+class UnnecessaryTypeCheckFalseWithoutNullSafetyTest
+    extends PubPackageResolutionTest
+    with UnnecessaryTypeCheckFalseTestCases, WithoutNullSafetyMixin {}
+
+@reflectiveTest
+class UnnecessaryTypeCheckTrueTest extends PubPackageResolutionTest
+    with UnnecessaryTypeCheckTrueTestCases {
   @override
-  test_type_not_object() async {
+  test_type_is_object() async {
     await assertNoErrorsInCode(r'''
 void f<T>(T a) {
-  a is! Object;
+  a is Object;
 }
 ''');
   }
 
-  test_type_not_objectQuestion() async {
+  test_type_is_objectQuestion() async {
     await assertErrorsInCode(r'''
 void f<T>(T a) {
-  a is! Object?;
+  a is Object?;
 }
 ''', [
-      error(HintCode.UNNECESSARY_TYPE_CHECK_FALSE, 19, 13),
+      error(HintCode.UNNECESSARY_TYPE_CHECK_TRUE, 19, 12),
     ]);
   }
 }
 
-@reflectiveTest
-class UnnecessaryTypeCheckTrueTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
+mixin UnnecessaryTypeCheckTrueTestCases on PubPackageResolutionTest {
   test_null_is_Null() async {
     await assertErrorsInCode(r'''
 var b = null is Null;
@@ -104,24 +128,6 @@
 }
 
 @reflectiveTest
-class UnnecessaryTypeCheckTrueWithNullSafetyTest
-    extends UnnecessaryTypeCheckTrueTest with WithNullSafetyMixin {
-  @override
-  test_type_is_object() async {
-    await assertNoErrorsInCode(r'''
-void f<T>(T a) {
-  a is Object;
-}
-''');
-  }
-
-  test_type_is_objectQuestion() async {
-    await assertErrorsInCode(r'''
-void f<T>(T a) {
-  a is Object?;
-}
-''', [
-      error(HintCode.UNNECESSARY_TYPE_CHECK_TRUE, 19, 12),
-    ]);
-  }
-}
+class UnnecessaryTypeCheckTrueWithoutNullSafetyTest
+    extends PubPackageResolutionTest
+    with UnnecessaryTypeCheckTrueTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/analyzer/test/src/diagnostics/yield_of_invalid_type_test.dart b/pkg/analyzer/test/src/diagnostics/yield_of_invalid_type_test.dart
index 072b733..ddb8c9c 100644
--- a/pkg/analyzer/test/src/diagnostics/yield_of_invalid_type_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/yield_of_invalid_type_test.dart
@@ -10,13 +10,15 @@
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(YieldOfInvalidTypeTest);
-    defineReflectiveTests(YieldOfInvalidTypeWithNullSafetyTest);
+    defineReflectiveTests(YieldOfInvalidTypeWithoutNullSafetyTest);
   });
 }
 
 @reflectiveTest
 class YieldOfInvalidTypeTest extends PubPackageResolutionTest
-    with WithoutNullSafetyMixin {
+    with YieldOfInvalidTypeTestCases {}
+
+mixin YieldOfInvalidTypeTestCases on PubPackageResolutionTest {
   test_none_asyncStar_dynamic_to_streamInt() async {
     await assertErrorsInCode(
         '''
@@ -448,5 +450,5 @@
 }
 
 @reflectiveTest
-class YieldOfInvalidTypeWithNullSafetyTest extends YieldOfInvalidTypeTest
-    with WithNullSafetyMixin {}
+class YieldOfInvalidTypeWithoutNullSafetyTest extends PubPackageResolutionTest
+    with YieldOfInvalidTypeTestCases, WithoutNullSafetyMixin {}
diff --git a/pkg/compiler/lib/src/deferred_load/deferred_load.dart b/pkg/compiler/lib/src/deferred_load/deferred_load.dart
index 22d19c6..c531ba2 100644
--- a/pkg/compiler/lib/src/deferred_load/deferred_load.dart
+++ b/pkg/compiler/lib/src/deferred_load/deferred_load.dart
@@ -2,6 +2,270 @@
 // 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.
 
+/// *Overview of deferred loading*
+///
+/// Deferred loading allows developers to specify deferred imports. These
+/// imports represent explicit asynchronous splits of the application that
+/// allows code to be delivered in pieces.
+///
+/// The initial download of an application will exclude code used only by
+/// deferred imports. As the application reaches a
+/// `deferred_import.loadLibrary()` instruction, it will download and initialize
+/// any code needed by that deferred import.
+///
+/// Very often separate deferred imports access common code.  When that happens,
+/// the compiler places the shared code in separate files. At runtime, the
+/// application will only download shared code once when the first deferred
+/// import that needs that code gets loaded. To achieve this, the compiler
+/// generates _load lists_: a list of JavaScript files that need to be
+/// downloaded for every deferred import in the program.
+///
+/// Each generated JavaScript file has an initialzation within it. The files can
+/// be concatenated together in a bundle without affecting the initialization
+/// logic. This is used by customers to reduce the download latency when they
+/// know that multiple files will be loaded at once.
+///
+/// *The code splitting algorithm*
+///
+/// The goal of this library and the [DeferredLoadingTask] is to determine how
+/// to best split code in multiple files according to the principles described
+/// above.
+///
+/// We do so by partitioning code into output units ([OutputUnit]s in our
+/// implementation). The partitioning reflects how code is shared between
+/// different deferred imports. Each output unit is associated a set of deferred
+/// imports (an [ImportSet] in our implementation). These are the deferred
+/// imports that need the code that is stored in that output unit. Code that is
+/// needed by a single deferred import, will be associated with a set containing
+/// that deferred import only (a singleton set), but code that is shared by 10
+/// deferred imports will be associated with a set containing all of those
+/// imports instead.  We determine whether code is shared based on how code is
+/// accessed in the program. An element is considered to be accessed by a
+/// deferred import if it is either loaded and invoked from that import or
+/// transitively accessed by an element that was invoked by that import.
+///
+/// In theory, there could be an exponential number of output units: one per
+/// subset of deferred imports in the program. In practice, large apps do have a
+/// large number of output units, but the result is not exponential. This is
+/// both because not all deferred imports have code in common and because many
+/// deferred imports end up having the same code in common.
+///
+/// *Main output unit*:
+///
+/// The main output unit contains any code accessed directly from main. Such
+/// code may be accessed by deferred imports too, but because it is accessed
+/// from the main entrypoint of the program, possibly synchronously, we do not
+/// split out the code or defer it. Our current implementation uses an empty
+/// import-set as a sentinel value to represent this output unit.
+///
+/// *Dependency graph*:
+///
+/// We use the element model to discover dependencies between elements.
+/// We distinguish two kinds of dependencies: deferred or direct (aka.
+/// non-deferred):
+///
+///   * Deferred dependencies are only used to discover root elements. Roots
+///   are elements immediately loaded and used from deferred import prefixes in
+///   a program.
+///
+///   * Direct dependencies are used to recursively update which output unit
+///   should be associated with an element.
+///
+/// *Algorithm Principle*:
+///
+/// Conceptually the algorithm consists of associating an element with an
+/// import-set. When we discover a root, we mark it and everything it can reach
+/// as being used by that import. Marking elements as used by that import
+/// consists of adding the import to the import-set of all those reachable
+/// elements.
+///
+/// An earlier version of this algorithm was implemented with this simple
+/// approach: we kept a map from entities to a [Set] of imports and updated the
+/// sets iteratively. However, as customer applications grew, we needed a more
+/// specialized and efficient implementation.
+///
+/// *ImportSet representation and related optimizations*:
+///
+/// The most important change to scale the algorithm was to use an efficient
+/// representation of entity to import-set associations. For large apps there
+/// are a lot of entities, and the simple representation of having a [Set] per
+/// entity was too expensive. We observed that many of such sets had the same
+/// imports (which makes sense given that many elements ended up together in the
+/// same output units). This led us to design the [ImportSet] abstraction: a
+/// representation of import-sets that guarantees that each import-set has a
+/// canonical representation. Memory-wise this was a big win: we now bounded
+/// the heap utilization to one [ImportSet] instance per unique import-set.
+///
+/// This representation is not perfect. Simple operations, like adding an import
+/// to an import-set, are now worse-case linear. So it was important to add a
+/// few optimizations in the algorithm in order to adapt to the new
+/// representation.
+///
+/// The principle of our optimizations is to make bulk updates. Rather than
+/// adding an import at a time for all reachable elements, we changed the
+/// algorithm to make updates in bulk in two ways:
+///
+///   * Batch unions: when possible add more than one import at once, and
+///
+///   * Update elements in segments: when an element and its reachable
+///   dependencies would change in the same way, update them all together.
+///
+/// To achieve these bulk updates, the algorithm uses a two tier algorithm:
+///
+///   * The top tier uses a worklist to track the start of a bulk update, either
+///   from a root (entities that dominate code used by a single deferred import)
+///   or from a merge point in the dependency graph (entities that dominate
+///   shared code between multiple imports).
+///
+///   * The second tier is where bulk updates are made, these don't use a
+///   worklist, but simply a DFS recursive traversal of the dependency graph.
+///   The DFS traversal stops at merge points and makes note of them by
+///   updating the top tier worklist.
+///
+///
+/// *Example*:
+///
+/// Consider this dependency graph (ignoring elements in the main output unit):
+///
+///   deferred import A: a1 ---> s1 ---> s2  -> s3
+///                              ^       ^
+///                              |       |
+///   deferred import B: b1 -----+       |
+///                                      |
+///   deferred import C: c1 ---> c2 ---> c3
+///
+/// Here a1, b1, and c1 are roots, while s1 and s2 are merge points. The
+/// algorithm will compute a result with 5 deferred output units:
+//
+///   * unit {A}:        contains a1
+///   * unit {B}:        contains b1
+///   * unit {C}:        contains c1, c2, and c3
+///   * unit {A, B}:     contains s1
+///   * unit {A, B, C}:  contains s2, and s3
+///
+/// After marking everything reachable from main as part of the main output
+/// unit, our algorithm will work as follows:
+///
+///   * Initially all deferred elements have no mapping.
+///   * We make note of work to do, initially to mark the root of each
+///     deferred import:
+///        * a1 with A, and recurse from there.
+///        * b1 with B, and recurse from there.
+///        * c1 with C, and recurse from there.
+///   * We update a1, s1, s2, s3 in bulk, from no mapping to {A}.
+///   * We update b1 from no mapping to {B}, and when we find s1 we notice
+///     that s1 is already associated with another import set {A}. This is a
+///     merge point that can't be updated in bulk, so we make
+///     note of additional work for later to mark s1 with {A, B}
+///   * We update in bulk c1, c2, c3 to {C}, and make a note to update s2 with
+///     {A, C} (another merge point).
+///   * We update s1 to {A, B}, and update the existing note to update s2, now
+///     with {A, B, C}
+///   * Finally we update s2 and s3 with {A, B, C} in bulk, without ever
+///     updating them to the intermediate state {A, C}.
+///
+/// *How bulk segment updates work?*
+///
+/// The principle of the bulk segment update is similar to memoizing the result
+/// of a union operation. We replace a union operation with a cached result if
+/// we can tell that the inputs to the operation are the same.
+///
+/// Our implementation doesn't use a cache table to memoize arbitrary unions.
+/// Instead it only memoizes one union at a time: it tries to reuse the result
+/// of a union applied to one entity, when updating the import-sets of its
+/// transitive dependencies.
+///
+/// Consider a modification of the example above where we add s4 and s5 as
+/// additional dependencies of s3. Conceptually, we are applying this sequence
+/// of union operations:
+///
+///    importSet[s2] = importSet[s2] UNION {B, C}
+///    importSet[s3] = importSet[s3] UNION {B, C}
+///    importSet[s4] = importSet[s4] UNION {B, C}
+///    importSet[s5] = importSet[s5] UNION {B, C}
+///
+/// When the algorithm is updating s2, it checks whether any of the entities
+/// reachable from s2 also have the same import-set as s2, and if so, we know
+/// that the union result is the same.
+///
+/// Our implementation uses the term `oldSet` to represent the first input of
+/// the memoized union operation, and `newSet` to represent the result:
+///
+///    oldSet = importSet[s2]        // = A
+///    newSet = oldSet UNION {B, C}  // = {A, B, C}
+///
+/// Then the updates are encoded as:
+///
+///    update(s2, oldSet, newSet);
+///    update(s3, oldSet, newSet);
+///    update(s4, oldSet, newSet);
+///    update(s5, oldSet, newSet);
+///
+/// where:
+///
+///    update(s5, oldSet, newSet) {
+///      var currentSet = importSet[s];
+///      if (currentSet == oldSet) {
+///        // Use the memoized result, whohoo!
+///        importSet[s] = newSet;
+///      } else {
+///        // Don't use the memoized result, instead use the worklist to later
+///        // update `s` with the appropriate union operation.
+///      }
+///    }
+///
+/// As a result of this, the update to the import set for s2, s3, s4 and s5
+/// becomes a single if-check and an assignment, but the union operation was
+/// only executed once.
+///
+/// *Constraints*:
+///
+/// By default our algorithm considers all deferred imports equally and
+/// potentially occurring at any time in the application lifetime. In practice,
+/// apps use deferred imports to layer the load of their application and, often,
+/// developers know how imports will be loaded over time.
+///
+/// Dart2js accepts a configuration file to specify constraints about deferred
+/// imports. There are many kinds of constraints that help developers encode how
+/// their applications work.
+///
+/// To model constraints, the deferred loading algorithm was changed to include
+/// _set transitions_: these are changes made to import-sets to effectively
+/// encode the constraints.
+///
+/// Consider, for example, a program with two deferred imports `A` and `B`. Our
+/// unconstrained algorithm will split the code in 3 files:
+///
+///   * code unique to `A` (represented by the import set `{A}`)
+///
+///   * code unique to `B` (represented by the import set `{B}`)
+///
+///   * code shared between `A and `B (represented by the import set `{A, B}`)
+///
+/// When an end-user loads the user journey corresponding to `A`, the code for
+/// `{A}` and `{A,B}` gets loaded. When they load the user journey corresponding
+/// to `B`, `{B}` and `{A, B}` gets loaded.
+///
+/// An ordering constraint saying that `B` always loads after `A` tells our
+/// algorithm that, even though there exists code that is unique to `A`, we
+/// could merge it together with the shared code between `A` and `B`, since the
+/// user never intends to load `B` first. The result would be to have two files
+/// instead:
+///
+///   * code unique to `B` (represented by the import set `{B}`)
+///
+///   * code unique to A and code shared between A and B (represented by the
+///   import set `{A, B}`)
+///
+///
+/// In this example, the set transition is to convert any set containing `{A}`
+/// into a set containing `{A, B}`.
+///
+// TODO(joshualitt): update doc above when main is represented by a set
+// containing an implict import corresponding to `main`.
+// TODO(sigmund): investigate different heuristics for how to select the next
+// work item (e.g. we might converge faster if we pick first the update that
+// contains a bigger delta.)
 library deferred_load;
 
 import 'dart:collection' show Queue;
@@ -662,81 +926,7 @@
 
   /// Performs the deferred loading algorithm.
   ///
-  /// The deferred loading algorithm maps elements and constants to an output
-  /// unit. Each output unit is identified by a subset of deferred imports (an
-  /// [ImportSet]), and they will contain the elements that are inherently used
-  /// by all those deferred imports. An element is used by a deferred import if
-  /// it is either loaded by that import or transitively accessed by an element
-  /// that the import loads.  An empty set represents the main output unit,
-  /// which contains any elements that are accessed directly and are not
-  /// deferred.
-  ///
-  /// The algorithm traverses the element model recursively looking for
-  /// dependencies between elements. These dependencies may be deferred or
-  /// non-deferred. Deferred dependencies are mainly used to discover the root
-  /// elements that are loaded from deferred imports, while non-deferred
-  /// dependencies are used to recursively associate more elements to output
-  /// units.
-  ///
-  /// Naively, the algorithm traverses each root of a deferred import and marks
-  /// everything it can reach as being used by that import. To reduce how many
-  /// times we visit an element, we use an algorithm that works in segments: it
-  /// marks elements with a subset of deferred imports at a time, until it
-  /// detects a merge point where more deferred imports could be considered at
-  /// once.
-  ///
-  /// For example, consider this dependency graph (ignoring elements in the main
-  /// output unit):
-  ///
-  ///   deferred import A: a1 ---> s1 ---> s2  -> s3
-  ///                              ^       ^
-  ///                              |       |
-  ///   deferred import B: b1 -----+       |
-  ///                                      |
-  ///   deferred import C: c1 ---> c2 ---> c3
-  ///
-  /// The algorithm will compute a result with 5 deferred output units:
-  //
-  ///   * unit {A}:        contains a1
-  ///   * unit {B}:        contains b1
-  ///   * unit {C}:        contains c1, c2, and c3
-  ///   * unit {A, B}:     contains s1
-  ///   * unit {A, B, C}:  contains s2, and s3
-  ///
-  /// After marking everything reachable from main as part of the main output
-  /// unit, our algorithm will work as follows:
-  ///
-  ///   * Initially all deferred elements have no mapping.
-  ///   * We make note of work to do, initially to mark the root of each
-  ///     deferred import:
-  ///        * a1 with A, and recurse from there.
-  ///        * b1 with B, and recurse from there.
-  ///        * c1 with C, and recurse from there.
-  ///   * we update a1, s1, s2, s3 from no mapping to {A}
-  ///   * we update b1 from no mapping to {B}, and when we find s1 we notice
-  ///     that s1 is already associated with another import set {A}, so we make
-  ///     note of additional work for later to mark s1 with {A, B}
-  ///   * we update c1, c2, c3 to {C}, and make a note to update s2 with {A, C}
-  ///   * we update s1 to {A, B}, and update the existing note to update s2, now
-  ///     with {A, B, C}
-  ///   * finally we update s2 and s3 with {A, B, C} in one go, without ever
-  ///     updating them to the intermediate state {A, C}.
-  ///
-  /// The implementation below does atomic updates from one import-set to
-  /// another.  At first we add one deferred import at a time, but as the
-  /// algorithm progesses it may update a small import-set with a larger
-  /// import-set in one go. The key of this algorithm is to detect when sharing
-  /// begins, so we can update those elements more efficently.
-  ///
-  /// To detect these merge points where sharing begins, the implementation
-  /// below uses `a swap operation`: we first compare what the old import-set
-  /// is, and if it matches our expectation, the swap is done and we recurse,
-  /// otherwise a merge root was detected and we enqueue a new segment of
-  /// updates for later.
-  ///
-  /// TODO(sigmund): investigate different heuristics for how to select the next
-  /// work item (e.g. we might converge faster if we pick first the update that
-  /// contains a bigger delta.)
+  /// See the top-level library comment for details.
   OutputUnitData run(FunctionEntity main, KClosedWorld closedWorld) {
     return metrics.time.measure(() => _run(main, closedWorld));
   }
diff --git a/pkg/dds/lib/src/dap/adapters/dart.dart b/pkg/dds/lib/src/dap/adapters/dart.dart
index b8c01a5..2d8e462 100644
--- a/pkg/dds/lib/src/dap/adapters/dart.dart
+++ b/pkg/dds/lib/src/dap/adapters/dart.dart
@@ -367,7 +367,7 @@
   ) async {
     switch (request.command) {
 
-      /// Used by tests to validate available protocols (eg. DDS). There may be
+      /// Used by tests to validate available protocols (e.g. DDS). There may be
       /// value in making this available to clients in future, but for now it's
       /// internal.
       case '_getSupportedProtocols':
@@ -546,11 +546,11 @@
         ),
       ],
       supportsClipboardContext: true,
-      // TODO(dantup): All of these...
-      // supportsConditionalBreakpoints: true,
+      supportsConditionalBreakpoints: true,
       supportsConfigurationDoneRequest: true,
       supportsDelayedStackTraceLoading: true,
       supportsEvaluateForHovers: true,
+      // TODO(dantup): All of these...
       // supportsLogPoints: true,
       // supportsRestartFrame: true,
       supportsTerminateRequest: true,
@@ -560,6 +560,36 @@
     sendEvent(InitializedEventBody());
   }
 
+  /// Checks whether this library is from an external package.
+  ///
+  /// This is used to support debugging "Just My Code" so Pub packages can be
+  /// marked as not-debuggable.
+  ///
+  /// A library is considered local if the path is within the 'cwd' or
+  /// 'additionalProjectPaths' in the launch arguments. An editor should include
+  /// the paths of all open workspace folders in 'additionalProjectPaths' to
+  /// support this feature correctly.
+  bool isExternalPackageLibrary(Uri uri) {
+    if (!uri.isScheme('package')) {
+      return false;
+    }
+    final libraryPath = resolvePackageUri(uri);
+    if (libraryPath == null) {
+      return false;
+    }
+
+    // Always compare paths case-insensitively to avoid any issues where APIs
+    // may have returned different casing (e.g. Windows drive letters). It's
+    // almost certain a user wouldn't have a "local" package and an "external"
+    // package with paths differing only be case.
+    final libraryPathLower = libraryPath.toLowerCase();
+    return !projectPaths.any((projectPath) =>
+        path.isWithin(projectPath.toLowerCase(), libraryPathLower));
+  }
+
+  /// Checks whether this library is from the SDK.
+  bool isSdkLibrary(Uri uri) => uri.isScheme('dart');
+
   /// Overridden by sub-classes to handle when the client sends a
   /// `launchRequest` (a request to start running/debugging an app).
   ///
@@ -588,6 +618,20 @@
     sendResponse();
   }
 
+  /// Checks whether a library URI should be considered debuggable.
+  ///
+  /// Initial values are provided in the launch arguments, but may be updated
+  /// by the `updateDebugOptions` custom request.
+  bool libaryIsDebuggable(Uri uri) {
+    if (isSdkLibrary(uri)) {
+      return _isolateManager.debugSdkLibraries;
+    } else if (isExternalPackageLibrary(uri)) {
+      return _isolateManager.debugExternalPackageLibraries;
+    } else {
+      return true;
+    }
+  }
+
   /// Handles the clients "next" ("step over") request for the thread in
   /// [args.threadId].
   @override
@@ -831,7 +875,7 @@
       // should return all.
       final limit = numFrames == 0 ? null : startFrame + numFrames;
       final stack = await vmService?.getStack(thread.isolate.id!, limit: limit);
-      final frames = stack?.frames;
+      final frames = stack?.asyncCausalFrames ?? stack?.frames;
 
       if (stack != null && frames != null) {
         // When the call stack is truncated, we always add [stackFrameBatchSize]
@@ -847,10 +891,19 @@
             ? frames.length + stackFrameBatchSize
             : frames.length;
 
+        // Find the first async marker, because some functionality only works
+        // up until the first async bounday (e.g. rewind) since we're showing
+        // the user async frames which are out-of-sync with the real frames
+        // past that point.
+        final firstAsyncMarkerIndex = frames.indexWhere(
+          (frame) => frame.kind == vm.FrameKind.kAsyncSuspensionMarker,
+        );
+
         Future<StackFrame> convert(int index, vm.Frame frame) async {
           return _converter.convertVmToDapStackFrame(
             thread,
             frame,
+            firstAsyncMarkerIndex: firstAsyncMarkerIndex,
             isTopFrame: startFrame == 0 && index == 0,
           );
         }
diff --git a/pkg/dds/lib/src/dap/adapters/dart_cli.dart b/pkg/dds/lib/src/dap/adapters/dart_cli.dart
index 9ad8e80..a44f520 100644
--- a/pkg/dds/lib/src/dap/adapters/dart_cli.dart
+++ b/pkg/dds/lib/src/dap/adapters/dart_cli.dart
@@ -21,7 +21,7 @@
 
   /// The location of the vm-service-info file (if debugging).
   ///
-  /// This may be provided by the user (eg. if attaching) or generated by the DA.
+  /// This may be provided by the user (e.g. if attaching) or generated by the DA.
   File? _vmServiceInfoFile;
 
   /// A watcher for [_vmServiceInfoFile] to detect when the VM writes the service
@@ -68,7 +68,7 @@
     if (!isAttach) {
       // Capture the PID from the VM Service so that we can terminate it when
       // cleaning up. Terminating the process might not be enough as it could be
-      // just a shell script (eg. pub on Windows) and may not pass the
+      // just a shell script (e.g. pub on Windows) and may not pass the
       // signal on correctly.
       // See: https://github.com/Dart-Code/Dart-Code/issues/907
       final pid = vmInfo.pid;
diff --git a/pkg/dds/lib/src/dap/isolate_manager.dart b/pkg/dds/lib/src/dap/isolate_manager.dart
index 6861be1..e8d06fe 100644
--- a/pkg/dds/lib/src/dap/isolate_manager.dart
+++ b/pkg/dds/lib/src/dap/isolate_manager.dart
@@ -4,7 +4,6 @@
 
 import 'dart:async';
 
-import 'package:path/path.dart' as path;
 import 'package:vm_service/vm_service.dart' as vm;
 
 import 'adapters/dart.dart';
@@ -56,6 +55,10 @@
   /// isolates that appear after initial breakpoints were sent.
   final Map<String, List<SourceBreakpoint>> _clientBreakpointsByUri = {};
 
+  /// Tracks client breakpoints by the ID assigned by the VM so we can look up
+  /// conditions/logpoints when hitting breakpoints.
+  final Map<String, SourceBreakpoint> _clientBreakpointsByVmId = {};
+
   /// Tracks breakpoints created in the VM so they can be removed when the
   /// editor sends new breakpoints (currently the editor just sends a new list
   /// and not requests to add/remove).
@@ -272,6 +275,27 @@
   ThreadInfo? threadForIsolate(vm.IsolateRef? isolate) =>
       isolate?.id != null ? _threadsByIsolateId[isolate!.id!] : null;
 
+  /// Evaluates breakpoint condition [condition] and returns whether the result
+  /// is true (or non-zero for a numeric), sending any evaluation error to the
+  /// client.
+  Future<bool> _breakpointConditionEvaluatesTrue(
+    ThreadInfo thread,
+    String condition,
+  ) async {
+    final result =
+        await _evaluateAndPrintErrors(thread, condition, 'condition');
+    if (result == null) {
+      return false;
+    }
+
+    // Values we consider true for breakpoint conditions are boolean true,
+    // or non-zero numerics.
+    return (result.kind == vm.InstanceKind.kBool &&
+            result.valueAsString == 'true') ||
+        (result.kind == vm.InstanceKind.kInt && result.valueAsString != '0') ||
+        (result.kind == vm.InstanceKind.kDouble && result.valueAsString != '0');
+  }
+
   /// Configures a new isolate, setting it's exception-pause mode, which
   /// libraries are debuggable, and sending all breakpoints.
   Future<void> _configureIsolate(vm.IsolateRef isolate) async {
@@ -282,6 +306,44 @@
     ], eagerError: true);
   }
 
+  /// Evaluates an expression, returning the result if it is a [vm.InstanceRef]
+  /// and sending any error as an [OutputEvent].
+  Future<vm.InstanceRef?> _evaluateAndPrintErrors(
+    ThreadInfo thread,
+    String expression,
+    String type,
+  ) async {
+    try {
+      final result = await _adapter.vmService?.evaluateInFrame(
+        thread.isolate.id!,
+        0,
+        expression,
+        disableBreakpoints: true,
+      );
+
+      if (result is vm.InstanceRef) {
+        return result;
+      } else if (result is vm.ErrorRef) {
+        final message = result.message ?? '<error ref>';
+        _adapter.sendOutput(
+          'console',
+          'Debugger failed to evaluate breakpoint $type "$expression": $message',
+        );
+      } else if (result is vm.Sentinel) {
+        final message = result.valueAsString ?? '<collected>';
+        _adapter.sendOutput(
+          'console',
+          'Debugger failed to evaluate breakpoint $type "$expression": $message',
+        );
+      }
+    } catch (e) {
+      _adapter.sendOutput(
+        'console',
+        'Debugger failed to evaluate breakpoint $type "$expression": $e',
+      );
+    }
+  }
+
   void _handleExit(vm.Event event) {
     final isolate = event.isolate!;
     final isolateId = isolate.id!;
@@ -341,6 +403,20 @@
       if (eventKind == vm.EventKind.kPauseBreakpoint &&
           (event.pauseBreakpoints?.isNotEmpty ?? false)) {
         reason = 'breakpoint';
+        // Look up the client breakpoints that correspond to the VM breakpoint(s)
+        // we hit. It's possible some of these may be missing because we could
+        // hit a breakpoint that was set before we were attached.
+        final breakpoints = event.pauseBreakpoints!
+            .map((bp) => _clientBreakpointsByVmId[bp.id!])
+            .toSet();
+
+        // Resume if there are no (non-logpoint) breakpoints, of any of the
+        // breakpoints don't have false conditions.
+        if (breakpoints.isEmpty ||
+            !await _shouldHitBreakpoint(thread, breakpoints)) {
+          await resumeThread(thread.threadId);
+          return;
+        }
       } else if (eventKind == vm.EventKind.kPauseBreakpoint) {
         reason = 'step';
       } else if (eventKind == vm.EventKind.kPauseException) {
@@ -372,55 +448,6 @@
     }
   }
 
-  /// Checks whether this library is from an external package.
-  ///
-  /// This is used to support debugging "Just My Code" so Pub packages can be
-  /// marked as not-debuggable.
-  ///
-  /// A library is considered local if the path is within the 'cwd' or
-  /// 'additionalProjectPaths' in the launch arguments. An editor should include
-  /// the paths of all open workspace folders in 'additionalProjectPaths' to
-  /// support this feature correctly.
-  bool _isExternalPackageLibrary(vm.LibraryRef library) {
-    final libraryUri = library.uri;
-    if (libraryUri == null) {
-      return false;
-    }
-    final uri = Uri.parse(libraryUri);
-    if (!uri.isScheme('package')) {
-      return false;
-    }
-    final libraryPath = _adapter.resolvePackageUri(uri);
-    if (libraryPath == null) {
-      return false;
-    }
-
-    // Always compare paths case-insensitively to avoid any issues where APIs
-    // may have returned different casing (eg. Windows drive letters). It's
-    // almost certain a user wouldn't have a "local" package and an "external"
-    // package with paths differing only be case.
-    final libraryPathLower = libraryPath.toLowerCase();
-    return !_adapter.projectPaths.any((projectPath) =>
-        path.isWithin(projectPath.toLowerCase(), libraryPathLower));
-  }
-
-  bool _isSdkLibrary(vm.LibraryRef library) =>
-      library.uri?.startsWith('dart:') ?? false;
-
-  /// Checks whether a library should be considered debuggable.
-  ///
-  /// Initial values are provided in the launch arguments, but may be updated
-  /// by the `updateDebugOptions` custom request.
-  bool _libaryIsDebuggable(vm.LibraryRef library) {
-    if (_isSdkLibrary(library)) {
-      return debugSdkLibraries;
-    } else if (_isExternalPackageLibrary(library)) {
-      return debugExternalPackageLibraries;
-    } else {
-      return true;
-    }
-  }
-
   /// Sets breakpoints for an individual isolate.
   ///
   /// If [uri] is provided, only breakpoints for that URI will be sent (used
@@ -456,6 +483,7 @@
             isolateId, uri, bp.line,
             column: bp.column);
         existingBreakpointsForIsolateAndUri.add(vmBp);
+        _clientBreakpointsByVmId[vmBp.id!] = bp;
       });
     }
   }
@@ -489,10 +517,40 @@
       return;
     }
     await Future.wait(libraries.map((library) async {
-      final isDebuggable = _libaryIsDebuggable(library);
+      final libraryUri = library.uri;
+      final isDebuggable = libraryUri != null
+          ? _adapter.libaryIsDebuggable(Uri.parse(libraryUri))
+          : false;
       await service.setLibraryDebuggable(isolateId, library.id!, isDebuggable);
     }));
   }
+
+  /// Checks whether a breakpoint the VM paused at is one we should actually
+  /// remain at. That is, it either has no condition, or its condition evaluates
+  /// to something truthy.
+  Future<bool> _shouldHitBreakpoint(
+    ThreadInfo thread,
+    Set<SourceBreakpoint?> breakpoints,
+  ) async {
+    // If any were missing (they're null) or do not have a condition, we should
+    // hit the breakpoint.
+    final clientBreakpointsWithConditions =
+        breakpoints.where((bp) => bp?.condition?.isNotEmpty ?? false).toList();
+    if (breakpoints.length != clientBreakpointsWithConditions.length) {
+      return true;
+    }
+
+    // Otherwise, we need to evaluate all of the conditions and see if any are
+    // true, in which case we will also hit.
+    final conditions =
+        clientBreakpointsWithConditions.map((bp) => bp!.condition!).toSet();
+
+    final results = await Future.wait(conditions.map(
+      (condition) => _breakpointConditionEvaluatesTrue(thread, condition),
+    ));
+
+    return results.any((result) => result);
+  }
 }
 
 /// Holds state for a single Isolate/Thread.
diff --git a/pkg/dds/lib/src/dap/protocol_converter.dart b/pkg/dds/lib/src/dap/protocol_converter.dart
index 1243e5a..c22b2a4 100644
--- a/pkg/dds/lib/src/dap/protocol_converter.dart
+++ b/pkg/dds/lib/src/dap/protocol_converter.dart
@@ -357,6 +357,18 @@
       }
     }
 
+    // If a source would be considered not-debuggable (for example it's in the
+    // SDK and debugSdkLibraries=false) then we should also mark it as
+    // deemphasized so that the editor can jump up the stack to the first frame
+    // of debuggable code.
+    final isDebuggable = uri != null && _adapter.libaryIsDebuggable(uri);
+    final presentationHint = isDebuggable ? null : 'deemphasize';
+    final origin = uri != null && _adapter.isSdkLibrary(uri)
+        ? 'from the SDK'
+        : uri != null && _adapter.isExternalPackageLibrary(uri)
+            ? 'from external packages'
+            : null;
+
     final source = canShowSource
         ? dap.Source(
             name: uriIsPackage
@@ -366,8 +378,10 @@
                     : uri?.toString() ?? '<unknown source>',
             path: sourcePath,
             sourceReference: sourceReference,
-            origin: null,
-            adapterData: location.script)
+            origin: origin,
+            adapterData: location.script,
+            presentationHint: presentationHint,
+          )
         : null;
 
     // The VM only allows us to restart from frames that are not the top frame,
diff --git a/pkg/dds/test/dap/integration/debug_breakpoints_test.dart b/pkg/dds/test/dap/integration/debug_breakpoints_test.dart
index eadf81f..6ada4bc 100644
--- a/pkg/dds/test/dap/integration/debug_breakpoints_test.dart
+++ b/pkg/dds/test/dap/integration/debug_breakpoints_test.dart
@@ -331,6 +331,84 @@
         client.stepIn(stop.threadId!),
       ], eagerError: true);
     });
+  }, timeout: Timeout.none);
+
+  group('debug mode conditional breakpoints', () {
+    test('stops with condition evaluating to true', () async {
+      final client = dap.client;
+      final testFile = dap.createTestFile(simpleBreakpointProgram);
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+
+      await client.hitBreakpoint(
+        testFile,
+        breakpointLine,
+        condition: '1 == 1',
+      );
+    });
+
+    test('does not stop with condition evaluating to false', () async {
+      final client = dap.client;
+      final testFile = dap.createTestFile(simpleBreakpointProgram);
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+
+      await client.doNotHitBreakpoint(
+        testFile,
+        breakpointLine,
+        condition: '1 == 2',
+      );
+    });
+
+    test('stops with condition evaluating to non-zero', () async {
+      final client = dap.client;
+      final testFile = dap.createTestFile(simpleBreakpointProgram);
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+
+      await client.hitBreakpoint(
+        testFile,
+        breakpointLine,
+        condition: '1 + 1',
+      );
+    });
+
+    test('does not stop with condition evaluating to zero', () async {
+      final client = dap.client;
+      final testFile = dap.createTestFile(simpleBreakpointProgram);
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+
+      await client.doNotHitBreakpoint(
+        testFile,
+        breakpointLine,
+        condition: '1 - 1',
+      );
+    });
+
+    test('reports evaluation errors for conditions', () async {
+      final client = dap.client;
+      final testFile = dap.createTestFile(simpleBreakpointProgram);
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+
+      final outputEventsFuture = client.outputEvents.toList();
+
+      await client.doNotHitBreakpoint(
+        testFile,
+        breakpointLine,
+        condition: "1 + 'a'",
+      );
+
+      final outputEvents = await outputEventsFuture;
+      final outputMessages = outputEvents.map((e) => e.output);
+
+      final hasPrefix = startsWith(
+          'Debugger failed to evaluate breakpoint condition "1 + \'a\'": '
+          'evaluateInFrame: (113) Expression compilation error');
+      final hasDescriptiveMessage = contains(
+          "A value of type 'String' can't be assigned to a variable of type 'num'");
+
+      expect(
+        outputMessages,
+        containsAll([allOf(hasPrefix, hasDescriptiveMessage)]),
+      );
+    });
     // These tests can be slow due to starting up the external server process.
   }, timeout: Timeout.none);
 }
diff --git a/pkg/dds/test/dap/integration/debug_stack_test.dart b/pkg/dds/test/dap/integration/debug_stack_test.dart
new file mode 100644
index 0000000..2a348e8
--- /dev/null
+++ b/pkg/dds/test/dap/integration/debug_stack_test.dart
@@ -0,0 +1,162 @@
+// 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:test/test.dart';
+
+import 'test_client.dart';
+import 'test_scripts.dart';
+import 'test_support.dart';
+
+main() {
+  late DapTestSession dap;
+  setUp(() async {
+    dap = await DapTestSession.setUp();
+  });
+  tearDown(() => dap.tearDown());
+
+  group('debug mode stack trace', () {
+    test('includes expected names and async boundaries', () async {
+      final client = dap.client;
+      final testFile = await dap.createTestFile(simpleAsyncProgram);
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+
+      final stop = await client.hitBreakpoint(testFile, breakpointLine);
+      final stack = await client.getValidStack(
+        stop.threadId!,
+        startFrame: 0,
+        numFrames: 8,
+      );
+
+      expect(
+        stack.stackFrames.map((f) => f.name),
+        equals([
+          'four',
+          'three',
+          '<asynchronous gap>',
+          'two',
+          '<asynchronous gap>',
+          'one',
+          '<asynchronous gap>',
+          'main',
+        ]),
+      );
+
+      // Ensure async gaps have their presentationHint set to 'label'.
+      expect(
+        stack.stackFrames.map((f) => f.presentationHint),
+        equals([
+          null,
+          null,
+          'label',
+          null,
+          'label',
+          null,
+          'label',
+          null,
+        ]),
+      );
+    });
+
+    test('only sets canRestart where VM can rewind', () async {
+      final client = dap.client;
+      final testFile = await dap.createTestFile(simpleAsyncProgram);
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+
+      final stop = await client.hitBreakpoint(testFile, breakpointLine);
+      final stack = await client.getValidStack(
+        stop.threadId!,
+        startFrame: 0,
+        numFrames: 8,
+      );
+
+      expect(
+        stack.stackFrames.map((f) => f.canRestart ?? false),
+        equals([
+          // Top frame cannot be rewound to:
+          // https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#resume
+          isFalse,
+          // Other frames can
+          isTrue,
+          // Until after an async boundary
+          isFalse,
+          isFalse,
+          isFalse,
+          isFalse,
+          isFalse,
+          isFalse,
+        ]),
+      );
+    });
+
+    test('deemphasizes SDK frames when debugSdk=false', () async {
+      final client = dap.client;
+      final testFile = await dap.createTestFile(sdkStackFrameProgram);
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+
+      final stop = await client.hitBreakpoint(
+        testFile,
+        breakpointLine,
+        launch: () => client.launch(testFile.path, debugSdkLibraries: false),
+      );
+      final stack = await client.getValidStack(
+        stop.threadId!,
+        startFrame: 0,
+        numFrames: 100,
+      );
+
+      // Get all frames that are SDK Frames
+      final sdkFrames = stack.stackFrames
+          .where((frame) => frame.source?.name?.startsWith('dart:') ?? false)
+          .toList();
+      // Ensure we got some frames for the test to be valid.
+      expect(sdkFrames, isNotEmpty);
+
+      for (final sdkFrame in sdkFrames) {
+        expect(
+          sdkFrame.source?.presentationHint,
+          equals('deemphasize'),
+          reason: '${sdkFrame.source!.name} should be deemphasized',
+        );
+      }
+    });
+
+    test('does not deemphasize SDK frames when debugSdk=true', () async {
+      final client = dap.client;
+      final testFile = await dap.createTestFile(sdkStackFrameProgram);
+      final breakpointLine = lineWith(testFile, '// BREAKPOINT');
+
+      final stop = await client.hitBreakpoint(
+        testFile,
+        breakpointLine,
+        launch: () => client.launch(testFile.path, debugSdkLibraries: true),
+      );
+      final stack = await client.getValidStack(
+        stop.threadId!,
+        startFrame: 0,
+        numFrames: 100,
+      );
+
+      // Get all frames that are SDK Frames
+      final sdkFrames = stack.stackFrames
+          .where((frame) => frame.source?.name?.startsWith('dart:') ?? false)
+          .toList();
+      // Ensure we got some frames for the test to be valid.
+      expect(sdkFrames, isNotEmpty);
+
+      for (final sdkFrame in sdkFrames) {
+        expect(
+          sdkFrame.source?.presentationHint,
+          isNot(equals('deemphasize')),
+          reason: '${sdkFrame.source!.name} should not be deemphasized',
+        );
+        expect(
+          sdkFrame.source?.origin,
+          equals('from the SDK'),
+          reason: '${sdkFrame.source!.name} should be labelled as SDK code',
+        );
+      }
+    });
+    // These tests can be slow due to starting up the external server process.
+  }, timeout: Timeout.none);
+}
diff --git a/pkg/dds/test/dap/integration/debug_test.dart b/pkg/dds/test/dap/integration/debug_test.dart
index 1986253..18b544c 100644
--- a/pkg/dds/test/dap/integration/debug_test.dart
+++ b/pkg/dds/test/dap/integration/debug_test.dart
@@ -5,7 +5,6 @@
 import 'dart:io';
 
 import 'package:dds/src/dap/protocol_generated.dart';
-import 'package:pedantic/pedantic.dart';
 import 'package:test/test.dart';
 
 import 'test_client.dart';
@@ -61,7 +60,6 @@
       // request and capture the args.
       RunInTerminalRequestArguments? runInTerminalArgs;
       Process? proc;
-      var processExited = false;
       dap.client.handleRequest(
         'runInTerminal',
         (args) async {
@@ -78,7 +76,6 @@
             runArgs.args.skip(1).toList(),
             workingDirectory: runArgs.cwd,
           );
-          unawaited(proc!.exitCode.then((_) => processExited = true));
 
           return RunInTerminalResponseBody(processId: proc!.pid);
         },
@@ -98,7 +95,7 @@
         containsAllInOrder([Platform.resolvedExecutable, testFile.path]),
       );
       expect(proc!.pid, isPositive);
-      expect(processExited, isTrue);
+      expect(proc!.exitCode, completes);
     });
 
     test('provides a list of threads', () async {
diff --git a/pkg/dds/test/dap/integration/test_client.dart b/pkg/dds/test/dap/integration/test_client.dart
index b7c9ecd..1299701 100644
--- a/pkg/dds/test/dap/integration/test_client.dart
+++ b/pkg/dds/test/dap/integration/test_client.dart
@@ -391,6 +391,7 @@
   Future<StoppedEventBody> hitBreakpoint(
     File file,
     int line, {
+    String? condition,
     Future<Response> Function()? launch,
   }) async {
     final stop = expectStop('breakpoint', file: file, line: line);
@@ -400,7 +401,7 @@
       sendRequest(
         SetBreakpointsArguments(
           source: Source(path: file.path),
-          breakpoints: [SourceBreakpoint(line: line)],
+          breakpoints: [SourceBreakpoint(line: line, condition: condition)],
         ),
       ),
       launch?.call() ?? this.launch(file.path),
@@ -434,6 +435,30 @@
     return stop;
   }
 
+  /// Sets a breakpoint at [line] in [file] and expects _not_ to hit it after
+  /// running the script (instead the script is expected to terminate).
+  ///
+  /// Launch options can be customised by passing a custom [launch] function that
+  /// will be used instead of calling `launch(file.path)`.
+  Future<void> doNotHitBreakpoint(
+    File file,
+    int line, {
+    String? condition,
+    Future<Response> Function()? launch,
+  }) async {
+    await Future.wait([
+      event('terminated'),
+      initialize(),
+      sendRequest(
+        SetBreakpointsArguments(
+          source: Source(path: file.path),
+          breakpoints: [SourceBreakpoint(line: line, condition: condition)],
+        ),
+      ),
+      launch?.call() ?? this.launch(file.path),
+    ], eagerError: true);
+  }
+
   /// Returns whether DDS is available for the VM Service the debug adapter
   /// is connected to.
   Future<bool> get ddsAvailable async {
diff --git a/pkg/dds/test/dap/integration/test_scripts.dart b/pkg/dds/test/dap/integration/test_scripts.dart
index 0a5a492..5582f34 100644
--- a/pkg/dds/test/dap/integration/test_scripts.dart
+++ b/pkg/dds/test/dap/integration/test_scripts.dart
@@ -7,6 +7,43 @@
   void main(List<String> args) {}
 ''';
 
+/// A simple async Dart script that when stopped at the line of '// BREAKPOINT'
+/// will contain SDK frames in the call stack.
+const sdkStackFrameProgram = r'''
+  void main() {
+    [0].where((i) {
+      return i == 0; // BREAKPOINT
+    }).toList();
+  }
+''';
+
+/// A simple async Dart script that when stopped at the line of '// BREAKPOINT'
+/// will contain multiple stack frames across some async boundaries.
+const simpleAsyncProgram = r'''
+  import 'dart:async';
+
+  Future<void> main() async {
+    await one();
+  }
+
+  Future<void> one() async {
+    await two();
+  }
+
+  Future<void> two() async {
+    await three();
+  }
+
+  Future<void> three() async {
+    await Future.delayed(const Duration(microseconds: 1));
+    four();
+  }
+
+  void four() {
+    print('!'); // BREAKPOINT
+  }
+''';
+
 /// A simple Dart script that should run with no errors and contains a comment
 /// marker '// BREAKPOINT' for use in tests that require stopping at a breakpoint
 /// but require no other context.
diff --git a/pkg/dds/tool/dap/codegen.dart b/pkg/dds/tool/dap/codegen.dart
index edd064b..7dee5b3 100644
--- a/pkg/dds/tool/dap/codegen.dart
+++ b/pkg/dds/tool/dap/codegen.dart
@@ -482,7 +482,7 @@
     } else if (type.isUnion) {
       final types = type.unionTypes;
 
-      // Write a check against each type, eg.:
+      // Write a check against each type, e.g.:
       // x is y ? new Either.tx(x) : (...)
       for (var i = 0; i < types.length; i++) {
         final isLast = i == types.length - 1;
diff --git a/pkg/nnbd_migration/lib/instrumentation.dart b/pkg/nnbd_migration/lib/instrumentation.dart
index fb1afee..c2700c1 100644
--- a/pkg/nnbd_migration/lib/instrumentation.dart
+++ b/pkg/nnbd_migration/lib/instrumentation.dart
@@ -240,6 +240,7 @@
 enum EdgeOriginKind {
   alreadyMigratedType,
   alwaysNullableType,
+  angularAnnotation,
   argumentErrorCheckNotNull,
   callTearOff,
   compoundAssignment,
diff --git a/pkg/nnbd_migration/lib/src/edge_origin.dart b/pkg/nnbd_migration/lib/src/edge_origin.dart
index 2c2643f..ea9e4b1 100644
--- a/pkg/nnbd_migration/lib/src/edge_origin.dart
+++ b/pkg/nnbd_migration/lib/src/edge_origin.dart
@@ -64,6 +64,19 @@
   EdgeOriginKind get kind => EdgeOriginKind.alwaysNullableType;
 }
 
+/// Edge origin resulting from the presence of an Angular annotation such as
+/// `@Optional()`, `@ViewChild(...)`, or `@ContentChild(...)`.
+class AngularAnnotationOrigin extends EdgeOrigin {
+  AngularAnnotationOrigin(Source? source, AstNode node) : super(source, node);
+
+  @override
+  String get description =>
+      "annotated with an Angular annotation indicating nullability";
+
+  @override
+  EdgeOriginKind get kind => EdgeOriginKind.angularAnnotation;
+}
+
 /// Edge origin resulting from the presence of a call to
 /// `ArgumentError.checkNotNull`.
 ///
diff --git a/pkg/nnbd_migration/lib/src/node_builder.dart b/pkg/nnbd_migration/lib/src/node_builder.dart
index b50bed1..05cb79d 100644
--- a/pkg/nnbd_migration/lib/src/node_builder.dart
+++ b/pkg/nnbd_migration/lib/src/node_builder.dart
@@ -465,9 +465,13 @@
         _variables!.recordDecoratedElementType(
             declaredElement.variable, decoratedType.returnType);
       } else {
-        _variables!.recordDecoratedElementType(
-            declaredElement.variable, decoratedType.positionalParameters![0],
+        var type = decoratedType.positionalParameters![0];
+        _variables!.recordDecoratedElementType(declaredElement.variable, type,
             soft: true);
+        if (_hasAngularChildAnnotation(node.metadata)) {
+          _graph.makeNullable(
+              type!.node!, AngularAnnotationOrigin(source, node));
+        }
       }
     }
     return null;
@@ -668,6 +672,12 @@
       _variables!.recordDecoratedElementType(declaredElement, type);
       variable.initializer?.accept(this);
     }
+    var parent = node.parent;
+    if (parent is FieldDeclaration) {
+      if (_hasAngularChildAnnotation(parent.metadata)) {
+        _graph.makeNullable(type!.node!, AngularAnnotationOrigin(source, node));
+      }
+    }
     return null;
   }
 
@@ -794,6 +804,15 @@
       _handleNullabilityHint(node, decoratedType);
     }
     _variables!.recordDecoratedElementType(declaredElement, decoratedType);
+    for (var annotation in node.metadata) {
+      var element = annotation.element;
+      if (element is ConstructorElement &&
+          element.enclosingElement.name == 'Optional' &&
+          _isAngularUri(element.librarySource.uri)) {
+        _graph.makeNullable(
+            decoratedType!.node!, AngularAnnotationOrigin(source, node));
+      }
+    }
     if (declaredElement.isNamed) {
       _namedParameters![declaredElement.name] = decoratedType;
     } else {
@@ -884,6 +903,35 @@
         .recordDecoratedDirectSupertypes(declaredElement, decoratedSupertypes);
   }
 
+  /// Determines if the given [metadata] contains a reference to one of the
+  /// Angular annotations `ViewChild` or `ContentChild`, either of which implies
+  /// nullability of the underlying property.
+  bool _hasAngularChildAnnotation(NodeList<Annotation> metadata) {
+    for (var annotation in metadata) {
+      var element = annotation.element;
+      if (element is ConstructorElement) {
+        var name = element.enclosingElement.name;
+        if ((name == 'ViewChild' || name == 'ContentChild') &&
+            _isAngularUri(element.librarySource.uri)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  /// Determines whether the given [uri] comes from the Angular package.
+  bool _isAngularUri(Uri uri) {
+    if (uri.scheme != 'package') return false;
+    var packageName = uri.pathSegments[0];
+    if (packageName == 'angular') return true;
+    if (packageName == 'third_party.dart_src.angular.angular') {
+      // This name is used for angular development internally at Google.
+      return true;
+    }
+    return false;
+  }
+
   T _pushNullabilityNodeTarget<T>(
       NullabilityNodeTarget target, T Function() fn) {
     NullabilityNodeTarget? previousTarget = _target;
diff --git a/pkg/nnbd_migration/test/abstract_context.dart b/pkg/nnbd_migration/test/abstract_context.dart
index 4e7b83e..88ee434 100644
--- a/pkg/nnbd_migration/test/abstract_context.dart
+++ b/pkg/nnbd_migration/test/abstract_context.dart
@@ -45,6 +45,33 @@
 
   String get testsPath => '$homePath/tests';
 
+  /// Makes a mock version of the Angular package available for unit testing.
+  ///
+  /// If optional argument [internalUris] is `true`, the mock Angular package
+  /// will be located in a package called `third_party.dart_src.angular.angular`
+  /// (as it is in Google3), and `package:angular` will simply re-export it;
+  /// this allows the test to reflect usage in internal sources.
+  void addAngularPackage({bool internalUris = false}) {
+    addPackageFile(
+        internalUris ? 'third_party.dart_src.angular.angular' : 'angular',
+        'angular.dart', '''
+class ContentChild {
+  const ContentChild(Object selector, {Object? read});
+}
+class Optional {
+  const Optional();
+}
+class ViewChild {
+  const ViewChild(Object selector, {Object? read});
+}
+''');
+    if (internalUris) {
+      addPackageFile('angular', 'angular.dart', '''
+export 'package:third_party.dart_src.angular.angular/angular.dart';
+''');
+    }
+  }
+
   void addBuiltValuePackage() {
     addPackageFile('built_value', 'built_value.dart', '''
 abstract class Built<V extends Built<V, B>, B extends Builder<V, B>> {}
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart
index cc1bc01..5307de5 100644
--- a/pkg/nnbd_migration/test/api_test.dart
+++ b/pkg/nnbd_migration/test/api_test.dart
@@ -356,6 +356,181 @@
     await _checkSingleFileChanges(content, expected);
   }
 
+  Future<void> test_angular_contentChild_field() async {
+    addAngularPackage();
+    var content = '''
+import 'dart:html';
+import 'package:angular/angular.dart';
+
+class MyComponent {
+  // Initialize this.bar in the constructor just so the migration tool doesn't
+  // decide to make it nullable due to the lack of initializer.
+  MyComponent(this.bar);
+
+  @ContentChild('foo')
+  Element bar;
+}
+''';
+    var expected = '''
+import 'dart:html';
+import 'package:angular/angular.dart';
+
+class MyComponent {
+  // Initialize this.bar in the constructor just so the migration tool doesn't
+  // decide to make it nullable due to the lack of initializer.
+  MyComponent(this.bar);
+
+  @ContentChild('foo')
+  Element? bar;
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  Future<void> test_angular_optional_constructor_param() async {
+    addAngularPackage();
+    var content = '''
+import 'package:angular/angular.dart';
+
+class MyComponent {
+  MyComponent(@Optional() String foo);
+}
+''';
+    var expected = '''
+import 'package:angular/angular.dart';
+
+class MyComponent {
+  MyComponent(@Optional() String? foo);
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  Future<void> test_angular_optional_constructor_param_field_formal() async {
+    addAngularPackage();
+    var content = '''
+import 'package:angular/angular.dart';
+
+class MyComponent {
+  String foo;
+  MyComponent(@Optional() this.foo);
+}
+''';
+    var expected = '''
+import 'package:angular/angular.dart';
+
+class MyComponent {
+  String? foo;
+  MyComponent(@Optional() this.foo);
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  Future<void> test_angular_optional_constructor_param_internal() async {
+    addAngularPackage(internalUris: true);
+    var content = '''
+import 'package:angular/angular.dart';
+
+class MyComponent {
+  MyComponent(@Optional() String foo);
+}
+''';
+    var expected = '''
+import 'package:angular/angular.dart';
+
+class MyComponent {
+  MyComponent(@Optional() String? foo);
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  Future<void> test_angular_viewChild_field() async {
+    addAngularPackage();
+    var content = '''
+import 'dart:html';
+import 'package:angular/angular.dart';
+
+class MyComponent {
+  // Initialize this.bar in the constructor just so the migration tool doesn't
+  // decide to make it nullable due to the lack of initializer.
+  MyComponent(this.bar);
+
+  @ViewChild('foo')
+  Element bar;
+}
+''';
+    var expected = '''
+import 'dart:html';
+import 'package:angular/angular.dart';
+
+class MyComponent {
+  // Initialize this.bar in the constructor just so the migration tool doesn't
+  // decide to make it nullable due to the lack of initializer.
+  MyComponent(this.bar);
+
+  @ViewChild('foo')
+  Element? bar;
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  Future<void> test_angular_viewChild_field_internal() async {
+    addAngularPackage(internalUris: true);
+    var content = '''
+import 'dart:html';
+import 'package:angular/angular.dart';
+
+class MyComponent {
+  // Initialize this.bar in the constructor just so the migration tool doesn't
+  // decide to make it nullable due to the lack of initializer.
+  MyComponent(this.bar);
+
+  @ViewChild('foo')
+  Element bar;
+}
+''';
+    var expected = '''
+import 'dart:html';
+import 'package:angular/angular.dart';
+
+class MyComponent {
+  // Initialize this.bar in the constructor just so the migration tool doesn't
+  // decide to make it nullable due to the lack of initializer.
+  MyComponent(this.bar);
+
+  @ViewChild('foo')
+  Element? bar;
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  Future<void> test_angular_viewChild_setter() async {
+    addAngularPackage();
+    var content = '''
+import 'dart:html';
+import 'package:angular/angular.dart';
+
+class MyComponent {
+  @ViewChild('foo')
+  set bar(Element element) {}
+}
+''';
+    var expected = '''
+import 'dart:html';
+import 'package:angular/angular.dart';
+
+class MyComponent {
+  @ViewChild('foo')
+  set bar(Element? element) {}
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
   Future<void> test_argumentError_checkNotNull_implies_non_null_intent() async {
     var content = '''
 void f(int i) {
diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn
index 3016678..dd25e81 100644
--- a/runtime/BUILD.gn
+++ b/runtime/BUILD.gn
@@ -212,7 +212,10 @@
 
     if (is_fuchsia) {
       # safe-stack does not work with the interpreter.
-      cflags += [ "-fno-sanitize=safe-stack" ]
+      cflags += [
+        "-fno-sanitize=safe-stack",
+        "-Wno-deprecated-copy",
+      ]
     }
   }
 }
diff --git a/runtime/lib/double.cc b/runtime/lib/double.cc
index bc7c33b..53e38dc 100644
--- a/runtime/lib/double.cc
+++ b/runtime/lib/double.cc
@@ -101,17 +101,6 @@
   return Smi::New(((uval >> 32) ^ (uval)) & kSmiMax);
 }
 
-DEFINE_NATIVE_ENTRY(Double_trunc_div, 0, 2) {
-  double left = Double::CheckedHandle(zone, arguments->NativeArgAt(0)).value();
-  GET_NON_NULL_NATIVE_ARGUMENT(Double, right_object, arguments->NativeArgAt(1));
-  double right = right_object.value();
-  if (FLAG_trace_intrinsified_natives) {
-    OS::PrintErr("Double_trunc_div %f ~/ %f\n", left, right);
-  }
-  return DoubleToInteger(trunc(left / right),
-                         "Result of truncating division is Infinity or NaN");
-}
-
 DEFINE_NATIVE_ENTRY(Double_modulo, 0, 2) {
   double left = Double::CheckedHandle(zone, arguments->NativeArgAt(0)).value();
   GET_NON_NULL_NATIVE_ARGUMENT(Double, right_object, arguments->NativeArgAt(1));
diff --git a/runtime/vm/allocation.h b/runtime/vm/allocation.h
index fc4be72..fd1bdfa 100644
--- a/runtime/vm/allocation.h
+++ b/runtime/vm/allocation.h
@@ -75,4 +75,8 @@
 
 }  // namespace dart
 
+// Prevent use of `new (zone) DoesNotExtendZoneAllocated()`, which places the
+// DoesNotExtendZoneAllocated on top of the Zone.
+void* operator new(size_t size, dart::Zone* zone) = delete;
+
 #endif  // RUNTIME_VM_ALLOCATION_H_
diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h
index e9710df6..c8f50e2 100644
--- a/runtime/vm/bootstrap_natives.h
+++ b/runtime/vm/bootstrap_natives.h
@@ -89,7 +89,6 @@
   V(Double_sub, 2)                                                             \
   V(Double_mul, 2)                                                             \
   V(Double_div, 2)                                                             \
-  V(Double_trunc_div, 2)                                                       \
   V(Double_remainder, 2)                                                       \
   V(Double_modulo, 2)                                                          \
   V(Double_greaterThanFromInteger, 2)                                          \
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 527fa42..d2bf1c4 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -357,7 +357,7 @@
 bool HierarchyInfo::CanUseSubtypeRangeCheckFor(const AbstractType& type) {
   ASSERT(type.IsFinalized());
 
-  if (!type.IsInstantiated() || !type.IsType() || type.IsDartFunctionType()) {
+  if (!type.IsInstantiated() || !type.IsType()) {
     return false;
   }
 
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index c239df3..160abbe 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -15774,12 +15774,15 @@
   return count;
 }
 
-void ICData::WriteSentinel(const Array& data, intptr_t test_entry_length) {
+void ICData::WriteSentinel(const Array& data,
+                           intptr_t test_entry_length,
+                           const Object& back_ref) {
   ASSERT(!data.IsNull());
   RELEASE_ASSERT(smi_illegal_cid().Value() == kIllegalCid);
-  for (intptr_t i = 1; i <= test_entry_length; i++) {
+  for (intptr_t i = 2; i <= test_entry_length; i++) {
     data.SetAt(data.Length() - i, smi_illegal_cid());
   }
+  data.SetAt(data.Length() - 1, back_ref);
 }
 
 #if defined(DEBUG)
@@ -15825,6 +15828,11 @@
   for (intptr_t i = start; i < end; i++) {
     data.SetAt(i, smi_illegal_cid());
   }
+  // The last slot in the last entry of the [ICData::entries_] is a back-ref to
+  // the [ICData] itself.
+  if (index == (len - 1)) {
+    data.SetAt(end - 1, *this);
+  }
 }
 
 void ICData::ClearCountAt(intptr_t index,
@@ -15985,7 +15993,7 @@
   // Grow the array and write the new final sentinel into place.
   const intptr_t new_len = data.Length() + TestEntryLength();
   data = Array::Grow(data, new_len, Heap::kOld);
-  WriteSentinel(data, TestEntryLength());
+  WriteSentinel(data, TestEntryLength(), *this);
   return data.ptr();
 }
 
@@ -16113,7 +16121,8 @@
   data = entries();
   const intptr_t entry_length = TestEntryLength();
   intptr_t data_pos = index * TestEntryLength();
-  for (intptr_t i = 0; i < entry_length; i++) {
+  const intptr_t kBackRefLen = (index == (Length() - 1)) ? 1 : 0;
+  for (intptr_t i = 0; i < entry_length - kBackRefLen; i++) {
     if (data.At(data_pos++) != smi_illegal_cid().ptr()) {
       return false;
     }
@@ -16349,7 +16358,7 @@
 
     pos += result.TestEntryLength();
   }
-  WriteSentinel(data, result.TestEntryLength());
+  WriteSentinel(data, result.TestEntryLength(), result);
   result.set_entries(data);
   ASSERT(result.NumberOfChecksIs(aggregate.length()));
   return result.ptr();
@@ -16419,7 +16428,8 @@
   // IC data array must be null terminated (sentinel entry).
   const intptr_t len = TestEntryLengthFor(num_args_tested, tracking_exactness);
   const Array& array = Array::Handle(Array::New(len, Heap::kOld));
-  WriteSentinel(array, len);
+  // Only empty [ICData]s are allowed to have a non-ICData backref.
+  WriteSentinel(array, len, /*back_ref=*/smi_illegal_cid());
   array.MakeImmutable();
   return array.ptr();
 }
@@ -16548,7 +16558,7 @@
 #if !defined(DART_PRECOMPILED_RUNTIME)
   array.SetAt(CountIndexFor(num_args_tested), Object::smi_zero());
 #endif
-  WriteSentinel(array, entry_len);
+  WriteSentinel(array, entry_len, result);
 
   result.set_entries(array);
 
@@ -16632,6 +16642,26 @@
 }
 #endif
 
+ICDataPtr ICData::ICDataOfEntriesArray(const Array& array) {
+  const auto& back_ref = Object::Handle(array.At(array.Length() - 1));
+  if (back_ref.ptr() == smi_illegal_cid().ptr()) {
+    // The ICData must be empty.
+#if defined(DEBUG)
+    const int kMaxTestEntryLen = TestEntryLengthFor(2, true);
+    ASSERT(array.Length() <= kMaxTestEntryLen);
+    for (intptr_t i = 0; i < array.Length(); ++i) {
+      ASSERT(array.At(i) == Object::sentinel().ptr());
+    }
+#endif
+    return ICData::null();
+  }
+  const auto& ic_data = ICData::Cast(back_ref);
+#if defined(DEBUG)
+  ic_data.IsSentinelAt(ic_data.Length() - 1);
+#endif
+  return ic_data.ptr();
+}
+
 const char* WeakSerializationReference::ToCString() const {
   return Object::Handle(target()).ToCString();
 }
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 0778e23..112efdd 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -2314,6 +2314,12 @@
   // Generates a new ICData with descriptor and data array copied (deep clone).
   static ICDataPtr Clone(const ICData& from);
 
+  // Gets the [ICData] from the [ICData::entries_] array (which stores a back
+  // ref).
+  //
+  // May return `null` if the [ICData] is empty.
+  static ICDataPtr ICDataOfEntriesArray(const Array& array);
+
   static intptr_t TestEntryLengthFor(intptr_t num_args,
                                      bool tracking_exactness);
 
@@ -2473,7 +2479,9 @@
                                  RebindRule rebind_rule,
                                  const AbstractType& receiver_type);
 
-  static void WriteSentinel(const Array& data, intptr_t test_entry_length);
+  static void WriteSentinel(const Array& data,
+                            intptr_t test_entry_length,
+                            const Object& back_ref);
 
   // A cache of VM heap allocated preinitialized empty ic data entry arrays.
   static ArrayPtr cached_icdata_arrays_[kCachedICDataArrayCount];
diff --git a/runtime/vm/os_thread.h b/runtime/vm/os_thread.h
index dca5003..7601cdc 100644
--- a/runtime/vm/os_thread.h
+++ b/runtime/vm/os_thread.h
@@ -62,7 +62,6 @@
   friend class MutexLocker;
   friend class SafepointMutexLocker;
   friend class OSThreadIterator;
-  friend class TimelineEventBlockIterator;
   friend class TimelineEventRecorder;
   friend class PageSpace;
   friend void Dart_TestMutex();
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index d2d7f4d..a545c0e 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -1832,26 +1832,6 @@
 }
 #endif  // defined(DART_PRECOMPILED_RUNTIME)
 
-#if !defined(DART_PRECOMPILED_RUNTIME)
-static ICDataPtr FindICDataForInstanceCall(Zone* zone,
-                                           const Code& code,
-                                           uword pc) {
-  uword pc_offset = pc - code.PayloadStart();
-  const PcDescriptors& descriptors =
-      PcDescriptors::Handle(zone, code.pc_descriptors());
-  PcDescriptors::Iterator iter(descriptors, UntaggedPcDescriptors::kIcCall);
-  intptr_t deopt_id = -1;
-  while (iter.MoveNext()) {
-    if (iter.PcOffset() == pc_offset) {
-      deopt_id = iter.DeoptId();
-      break;
-    }
-  }
-  ASSERT(deopt_id != -1);
-  return Function::Handle(zone, code.function()).FindICData(deopt_id);
-}
-#endif  // !defined(DART_PRECOMPILED_RUNTIME)
-
 #if defined(DART_PRECOMPILED_RUNTIME)
 void PatchableCallHandler::DoMonomorphicMissAOT(
     const Object& data,
@@ -1917,22 +1897,53 @@
 void PatchableCallHandler::DoMonomorphicMissJIT(
     const Object& data,
     const Function& target_function) {
-  const ICData& ic_data = ICData::Handle(
-      zone_,
-      FindICDataForInstanceCall(zone_, caller_code_, caller_frame_->pc()));
-  RELEASE_ASSERT(!ic_data.IsNull());
+  // Monomorphic calls use the ICData::entries() as their data.
+  const auto& ic_data_entries = Array::Cast(data);
+  // Any non-empty ICData::entries() has a backref to it's ICData.
+  const auto& ic_data =
+      ICData::Handle(zone_, ICData::ICDataOfEntriesArray(ic_data_entries));
+
+  const classid_t current_cid = receiver().GetClassId();
+  const classid_t old_cid = ic_data.GetReceiverClassIdAt(0);
+  const bool same_receiver = current_cid == old_cid;
+
+  // The target didn't change, so we can stay inside monomorphic state.
+  if (same_receiver) {
+    // We got a miss because the old target code got disabled.
+    // Notice the reverse is not true: If the old code got disabled, the call
+    // might still have a different receiver then last time and possibly a
+    // different target.
+    ASSERT(miss_handler_ == MissHandler::kFixCallersTargetMonomorphic ||
+           !IsolateGroup::Current()->ContainsOnlyOneIsolate());
+
+    // No need to update ICData - it's already up-to-date.
+
+    if (FLAG_trace_ic) {
+      OS::PrintErr("Instance call at %" Px
+                   " updating code (old code was disabled)\n",
+                   caller_frame_->pc());
+    }
+
+    // We stay in monomorphic state, patch the code object and keep the same
+    // data (old ICData entries array).
+    const auto& code = Code::Handle(zone_, target_function.EnsureHasCode());
+    CodePatcher::PatchInstanceCallAt(caller_frame_->pc(), caller_code_, data,
+                                     code);
+    ReturnJIT(code, data, target_function);
+    return;
+  }
 
   ASSERT(ic_data.NumArgsTested() == 1);
   const Code& stub = ic_data.is_tracking_exactness()
                          ? StubCode::OneArgCheckInlineCacheWithExactnessCheck()
                          : StubCode::OneArgCheckInlineCache();
-  CodePatcher::PatchInstanceCallAt(caller_frame_->pc(), caller_code_, ic_data,
-                                   stub);
   if (FLAG_trace_ic) {
     OS::PrintErr("Instance call at %" Px
-                 " switching to polymorphic dispatch, %s\n",
+                 " switching monomorphic to polymorphic dispatch, %s\n",
                  caller_frame_->pc(), ic_data.ToCString());
   }
+  CodePatcher::PatchInstanceCallAt(caller_frame_->pc(), caller_code_, ic_data,
+                                   stub);
 
   ASSERT(caller_arguments_.length() == 1);
   UpdateICDataWithTarget(ic_data, target_function);
@@ -2231,14 +2242,13 @@
     }
 #else
     case kArrayCid: {
-      // ICData three-element array: Smi(receiver CID), Smi(count),
-      // Function(target). It is the Array from ICData::entries_.
-      const auto& ic_data = ICData::Handle(
-          zone_,
-          FindICDataForInstanceCall(zone_, caller_code_, caller_frame_->pc()));
-      RELEASE_ASSERT(!ic_data.IsNull());
-      name_ = ic_data.target_name();
+      // Monomorphic calls use the ICData::entries() as their data.
+      const auto& ic_data_entries = Array::Cast(data);
+      // Any non-empty ICData::entries() has a backref to it's ICData.
+      const auto& ic_data =
+          ICData::Handle(zone_, ICData::ICDataOfEntriesArray(ic_data_entries));
       args_descriptor_ = ic_data.arguments_descriptor();
+      name_ = ic_data.target_name();
       break;
     }
 #endif  // defined(DART_PRECOMPILED_RUNTIME)
diff --git a/runtime/vm/thread_interrupter.h b/runtime/vm/thread_interrupter.h
index 954a174..c5d770e 100644
--- a/runtime/vm/thread_interrupter.h
+++ b/runtime/vm/thread_interrupter.h
@@ -120,9 +120,9 @@
     intptr_t old_value = sample_buffer_lock_.load(std::memory_order_relaxed);
     intptr_t new_value;
     do {
-      if (old_value > 0) {
+      while (old_value > 0) {
+        // Spin waiting for outstanding SIGPROFs to complete.
         old_value = sample_buffer_lock_.load(std::memory_order_relaxed);
-        continue;  // Spin waiting for outstanding SIGPROFs to complete.
       }
       new_value = old_value - 1;
     } while (!sample_buffer_lock_.compare_exchange_weak(
diff --git a/runtime/vm/timeline.cc b/runtime/vm/timeline.cc
index e57fa3b..de13805 100644
--- a/runtime/vm/timeline.cc
+++ b/runtime/vm/timeline.cc
@@ -1516,44 +1516,6 @@
 #endif
 }
 
-TimelineEventBlockIterator::TimelineEventBlockIterator(
-    TimelineEventRecorder* recorder)
-    : current_(NULL), recorder_(NULL) {
-  Reset(recorder);
-}
-
-TimelineEventBlockIterator::~TimelineEventBlockIterator() {
-  Reset(NULL);
-}
-
-void TimelineEventBlockIterator::Reset(TimelineEventRecorder* recorder) {
-  // Clear current.
-  current_ = NULL;
-  if (recorder_ != NULL) {
-    // Unlock old recorder.
-    recorder_->lock_.Unlock();
-  }
-  recorder_ = recorder;
-  if (recorder_ == NULL) {
-    return;
-  }
-  // Lock new recorder.
-  recorder_->lock_.Lock();
-  // Queue up first block.
-  current_ = recorder_->GetHeadBlockLocked();
-}
-
-bool TimelineEventBlockIterator::HasNext() const {
-  return current_ != NULL;
-}
-
-TimelineEventBlock* TimelineEventBlockIterator::Next() {
-  ASSERT(current_ != NULL);
-  TimelineEventBlock* r = current_;
-  current_ = current_->next();
-  return r;
-}
-
 void DartTimelineEventHelpers::ReportTaskEvent(Thread* thread,
                                                TimelineEvent* event,
                                                int64_t id,
diff --git a/runtime/vm/timeline.h b/runtime/vm/timeline.h
index 69302aa..b6d1908 100644
--- a/runtime/vm/timeline.h
+++ b/runtime/vm/timeline.h
@@ -757,7 +757,6 @@
   int64_t time_high_micros_;
 
   friend class TimelineEvent;
-  friend class TimelineEventBlockIterator;
   friend class TimelineStream;
   friend class TimelineTestHelper;
   friend class Timeline;
@@ -887,25 +886,6 @@
   friend class TimelineTestHelper;
 };
 
-// An iterator for blocks.
-class TimelineEventBlockIterator {
- public:
-  explicit TimelineEventBlockIterator(TimelineEventRecorder* recorder);
-  ~TimelineEventBlockIterator();
-
-  void Reset(TimelineEventRecorder* recorder);
-
-  // Returns false when there are no more blocks.
-  bool HasNext() const;
-
-  // Returns the next block and moves forward.
-  TimelineEventBlock* Next();
-
- private:
-  TimelineEventBlock* current_;
-  TimelineEventRecorder* recorder_;
-};
-
 // The TimelineEventPlatformRecorder records timeline events to a platform
 // specific destination. It's implementation is in the timeline_{linux,...}.cc
 // files.
diff --git a/runtime/vm/type_testing_stubs.cc b/runtime/vm/type_testing_stubs.cc
index 81f4d21..48c073e 100644
--- a/runtime/vm/type_testing_stubs.cc
+++ b/runtime/vm/type_testing_stubs.cc
@@ -322,13 +322,6 @@
     __ BranchIfNotSmi(TypeTestABI::kInstanceReg, &non_smi_value);
     __ Ret();
     __ Bind(&non_smi_value);
-  } else if (type.IsDartFunctionType()) {
-    compiler::Label continue_checking;
-    __ CompareImmediate(TTSInternalRegs::kScratchReg, kClosureCid);
-    __ BranchIf(NOT_EQUAL, &continue_checking);
-    __ Ret();
-    __ Bind(&continue_checking);
-
   } else if (type.IsObjectType()) {
     ASSERT(type.IsNonNullable() &&
            IsolateGroup::Current()->use_strict_null_safety_checks());
diff --git a/runtime/vm/type_testing_stubs_test.cc b/runtime/vm/type_testing_stubs_test.cc
index cf4fe9f..e036dfa 100644
--- a/runtime/vm/type_testing_stubs_test.cc
+++ b/runtime/vm/type_testing_stubs_test.cc
@@ -1133,6 +1133,54 @@
   RunTTSTest(dst_type, {Smi::Handle(Smi::New(0)), tav_null, tav_null});
 }
 
+// Check that we generate correct TTS for type Function (the non-FunctionType
+// version).
+ISOLATE_UNIT_TEST_CASE(TTS_Function) {
+  const char* kScript =
+      R"(
+          class A<T> {}
+
+          createF() => (){};
+          createG() => () => 3;
+          createH() => (int x, String y, {int z : 0}) =>  x + z;
+
+          createAInt() => A<int>();
+          createAFunction() => A<Function>();
+  )";
+
+  const auto& root_library = Library::Handle(LoadTestScript(kScript));
+  const auto& obj_f = Object::Handle(Invoke(root_library, "createF"));
+  const auto& obj_g = Object::Handle(Invoke(root_library, "createG"));
+  const auto& obj_h = Object::Handle(Invoke(root_library, "createH"));
+  const auto& obj_null = Instance::Handle();
+
+  const auto& tav_null = TypeArguments::Handle(TypeArguments::null());
+  const auto& type_function = Type::Handle(Type::DartFunctionType());
+
+  RunTTSTest(type_function, {obj_f, tav_null, tav_null});
+  RunTTSTest(type_function, {obj_g, tav_null, tav_null});
+  RunTTSTest(type_function, {obj_h, tav_null, tav_null});
+  if (!thread->isolate_group()->use_strict_null_safety_checks()) {
+    RunTTSTest(type_function, {obj_null, tav_null, tav_null});
+  } else {
+    RunTTSTest(type_function, Failure({obj_null, tav_null, tav_null}));
+  }
+
+  const auto& class_a = Class::Handle(GetClass(root_library, "A"));
+  const auto& obj_a_int = Object::Handle(Invoke(root_library, "createAInt"));
+  const auto& obj_a_function =
+      Object::Handle(Invoke(root_library, "createAFunction"));
+
+  auto& tav_function = TypeArguments::Handle(TypeArguments::New(1));
+  tav_function.SetTypeAt(0, type_function);
+  CanonicalizeTAV(&tav_function);
+  auto& type_a_function = Type::Handle(Type::New(class_a, tav_function));
+  FinalizeAndCanonicalize(&type_a_function);
+
+  RunTTSTest(type_a_function, {obj_a_function, tav_null, tav_null});
+  RunTTSTest(type_a_function, Failure({obj_a_int, tav_null, tav_null}));
+}
+
 ISOLATE_UNIT_TEST_CASE(TTS_Partial) {
   const char* kScript =
       R"(
diff --git a/runtime/vm/zone.cc b/runtime/vm/zone.cc
index 744c8a5..5fa1efc 100644
--- a/runtime/vm/zone.cc
+++ b/runtime/vm/zone.cc
@@ -163,8 +163,7 @@
 // is created within a new thread or ApiNativeScope when calculating high
 // watermarks or memory consumption.
 Zone::Zone()
-    : canary_(kCanary),
-      position_(reinterpret_cast<uword>(&buffer_)),
+    : position_(reinterpret_cast<uword>(&buffer_)),
       limit_(position_ + kInitialChunkSize),
       head_(NULL),
       large_segments_(NULL),
@@ -179,7 +178,6 @@
 }
 
 Zone::~Zone() {
-  ASSERT(canary_ == kCanary);
   if (FLAG_trace_zones) {
     DumpZoneSizes();
   }
diff --git a/runtime/vm/zone.h b/runtime/vm/zone.h
index e4a0030..b30f446 100644
--- a/runtime/vm/zone.h
+++ b/runtime/vm/zone.h
@@ -141,10 +141,6 @@
   template <class ElementType>
   static inline void CheckLength(intptr_t len);
 
-  // Guard against `new (zone) DoesNotExtendZoneAllocated()`.
-  static constexpr uint64_t kCanary = 0x656e6f7a74726164ull;  // "dartzone"
-  uint64_t canary_;
-
   // The free region in the current (head) segment or the initial buffer is
   // represented as the half-open interval [position, limit). The 'position'
   // variable is guaranteed to be aligned as dictated by kAlignment.
diff --git a/sdk/lib/_internal/vm/lib/double.dart b/sdk/lib/_internal/vm/lib/double.dart
index cc518e5..ab3b88d 100644
--- a/sdk/lib/_internal/vm/lib/double.dart
+++ b/sdk/lib/_internal/vm/lib/double.dart
@@ -49,12 +49,9 @@
   double _mul(double other) native "Double_mul";
 
   int operator ~/(num other) {
-    return _trunc_div(other.toDouble());
+    return (this / other.toDouble()).truncate();
   }
 
-  @pragma("vm:non-nullable-result-type")
-  int _trunc_div(double other) native "Double_trunc_div";
-
   @pragma("vm:recognized", "asm-intrinsic")
   @pragma("vm:exact-result-type", _Double)
   @pragma("vm:never-inline")
@@ -142,7 +139,7 @@
   }
 
   int _truncDivFromInteger(int other) {
-    return new _Double.fromInteger(other)._trunc_div(this);
+    return (new _Double.fromInteger(other) / this).truncate();
   }
 
   double _moduloFromInteger(int other) {
diff --git a/tests/language/constructor/reference_test.dart b/tests/language/constructor/reference_test.dart
index 0630a86..36ca27d 100644
--- a/tests/language/constructor/reference_test.dart
+++ b/tests/language/constructor/reference_test.dart
@@ -98,6 +98,8 @@
   // ^^^^^
   // [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
   // [cfe] This requires the 'constructor-tearoffs' language feature to be enabled.
+  //           ^^^
+  // [analyzer] COMPILE_TIME_ERROR.UNDEFINED_METHOD
   //       ^
   // [cfe] Getter not found: 'bar'.
   Foo.bar<int>();
diff --git a/tests/language/type_object/explicit_instantiated_type_literal_test.dart b/tests/language/type_object/explicit_instantiated_type_literal_test.dart
index 6b3b07f..5d036e6 100644
--- a/tests/language/type_object/explicit_instantiated_type_literal_test.dart
+++ b/tests/language/type_object/explicit_instantiated_type_literal_test.dart
@@ -45,9 +45,7 @@
   Expect.identical(C<int>, prefix.C<int>);
 
   (<T extends num>() {
-    Expect.notIdentical(C<T>, C<T>);
     Expect.equals(C<T>, C<T>);
-    Expect.notIdentical(prefix.C<T>, prefix.C<T>);
     Expect.equals(prefix.C<T>, prefix.C<T>);
   }<int>());
 }
diff --git a/tests/language_2/constructor/reference_test.dart b/tests/language_2/constructor/reference_test.dart
index 6e3dac9..6dff1ad 100644
--- a/tests/language_2/constructor/reference_test.dart
+++ b/tests/language_2/constructor/reference_test.dart
@@ -100,6 +100,8 @@
   // ^^^^^
   // [analyzer] SYNTACTIC_ERROR.EXPERIMENT_NOT_ENABLED
   // [cfe] This requires the 'constructor-tearoffs' language feature to be enabled.
+  //           ^^^
+  // [analyzer] COMPILE_TIME_ERROR.UNDEFINED_METHOD
   //       ^
   // [cfe] Getter not found: 'bar'.
   Foo.bar<int>();
diff --git a/tools/VERSION b/tools/VERSION
index 369f442..39cf8998 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 1
+PRERELEASE 2
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/dom/scripts/dartdomgenerator.py b/tools/dom/scripts/dartdomgenerator.py
index 944d36a..a392020 100755
--- a/tools/dom/scripts/dartdomgenerator.py
+++ b/tools/dom/scripts/dartdomgenerator.py
@@ -95,7 +95,8 @@
                          dart2js_output_dir,
                          update_dom_metadata=False,
                          logging_level=logging.WARNING,
-                         dart_js_interop=False):
+                         dart_js_interop=False,
+                         generate_static_extensions=False):
     print('\n ----- Accessing DOM using %s -----\n' %
           ('dart:js' if dart_js_interop else 'C++'))
 
@@ -167,8 +168,9 @@
         backend_options = GeneratorOptions(template_loader, webkit_database,
                                            type_registry, renamer, metadata,
                                            dart_js_interop)
+
         backend_factory = lambda interface:\
-            Dart2JSBackend(interface, backend_options, logging_level)
+            Dart2JSBackend(interface, backend_options, logging_level, generate_static_extensions)
 
         dart_output_dir = os.path.join(dart2js_output_dir, 'dart')
         dart_libraries = DartLibraries(HTML_LIBRARY_NAMES, template_loader,
@@ -306,6 +308,12 @@
         action='store_true',
         default=False,
         help='Do not generate the sdk/lib/js/cached_patches.dart file')
+    parser.add_option(
+        '--generate-static-extensions',
+        dest='generate_static_extensions',
+        action='store_true',
+        default=False,
+        help='Generate static extension members for dart:html classes')
 
     (options, args) = parser.parse_args()
 
@@ -337,7 +345,8 @@
 
     GenerateFromDatabase(database, dart2js_output_dir,
                          options.update_dom_metadata, logging_level,
-                         options.dart_js_interop)
+                         options.dart_js_interop,
+                         options.generate_static_extensions)
 
     file_generation_start_time = time.time()
 
diff --git a/tools/dom/scripts/htmldartgenerator.py b/tools/dom/scripts/htmldartgenerator.py
index 7891aa3..22890c9 100644
--- a/tools/dom/scripts/htmldartgenerator.py
+++ b/tools/dom/scripts/htmldartgenerator.py
@@ -535,7 +535,7 @@
         return mixins
 
     def AddConstructors(self, constructors, factory_name,
-                        factory_constructor_name):
+                        factory_constructor_name, emmiter):
         """ Adds all of the constructors.
     Arguments:
       constructors - List of the constructors to be added.
@@ -543,13 +543,15 @@
       factory_constructor_name - The name of the constructor on the
           factory_name to call (calls an autogenerated FactoryProvider
           if unspecified)
+      emmiter - Emmiter used to emit constructors when generating classes
+          using the static extension pattern.
     """
         for constructor_info in constructors:
             self._AddConstructor(constructor_info, factory_name,
-                                 factory_constructor_name)
+                                 factory_constructor_name, emmiter)
 
     def _AddConstructor(self, constructor_info, factory_name,
-                        factory_constructor_name):
+                        factory_constructor_name, emmiter):
         # Hack to ignore the constructor used by JavaScript.
         if ((self._interface.id == 'HTMLImageElement' or
              self._interface.id == 'Blob' or
@@ -563,6 +565,8 @@
         metadata = self._metadata.GetFormattedMetadata(
             self._library_name, self._interface, self._interface.id, '  ')
 
+        target_emitter = emmiter if emmiter else self._members_emitter
+
         if not factory_constructor_name:
             factory_constructor_name = '_create'
             factory_parameters = constructor_info.ParametersAsArgumentList()
@@ -586,7 +590,7 @@
                 factory_name, factory_constructor_name, factory_parameters,
                 constructor_info)
             if not has_optional:
-                self._members_emitter.Emit(
+                target_emitter.Emit(
                     '\n  $(METADATA)'
                     'factory $CTOR($PARAMS) => '
                     '$FACTORY_CALL;\n',
@@ -595,7 +599,7 @@
                     FACTORY_CALL=factory_call,
                     METADATA=metadata)
             else:
-                inits = self._members_emitter.Emit(
+                inits = target_emitter.Emit(
                     '\n  $(METADATA)'
                     'factory $CONSTRUCTOR($PARAMS) {\n'
                     '    $CONSTRUCTOR e = $FACTORY_CALL;\n'
@@ -664,7 +668,7 @@
                     FACTORY_NAME=qualified_name,
                     FACTORY_PARAMS=args)
                 self.EmitStaticFactoryOverload(constructor_info, name,
-                                               arguments)
+                                               arguments, emmiter)
 
             def IsOptional(signature_index, argument):
                 return self.IsConstructorArgumentOptional(argument)
@@ -678,13 +682,12 @@
                 METADATA=metadata,
                 PARAMS=constructor_info.ParametersAsDeclaration(InputType))
 
-            overload_emitter = self._members_emitter
             overload_declaration = entry_declaration
 
             self._GenerateOverloadDispatcher(constructor_info,
                                              constructor_info.idl_args, False,
                                              overload_declaration, GenerateCall,
-                                             IsOptional, overload_emitter)
+                                             IsOptional, target_emitter)
 
     def _AddFutureifiedOperation(self, info, html_name):
         """Given a API function that uses callbacks, convert it to using Futures.
@@ -813,12 +816,13 @@
             FUTURE_GENERIC=future_generic,
             ORIGINAL_FUNCTION=html_name)
 
-    def EmitHelpers(self, base_class):
-        if not self._members_emitter:
+    def EmitHelpers(self, base_class, emmiter):
+        if (not self._members_emitter) and (not emmiter):
             return
 
         if self._interface.id not in custom_html_constructors:
-            self._members_emitter.Emit(
+            target_emmiter = emmiter if emmiter else self._members_emitter
+            target_emmiter.Emit(
                 '  // To suppress missing implicit constructor warnings.\n'
                 '  factory $CLASSNAME._() { '
                 'throw new UnsupportedError("Not supported"); }\n',
diff --git a/tools/dom/scripts/systemhtml.py b/tools/dom/scripts/systemhtml.py
index 5749cd9..c8ff6b3 100644
--- a/tools/dom/scripts/systemhtml.py
+++ b/tools/dom/scripts/systemhtml.py
@@ -16,6 +16,14 @@
 
 _logger = logging.getLogger('systemhtml')
 
+
+def CanUseStaticExtensions(interface, should):
+    if not should:
+        return False
+    static_extension_interfaces = []  # Classes to be migrated
+    return interface in static_extension_interfaces
+
+
 HTML_LIBRARY_NAMES = [
     'html', 'indexed_db', 'svg', 'web_audio', 'web_gl', 'web_sql'
 ]
@@ -809,24 +817,34 @@
             NULLASSERT='!')
         stream_getter_signatures_emitter = None
         element_stream_getters_emitter = None
+        class_members_emitter = None
         if type(implementation_members_emitter) == tuple:
             # We add event stream getters for both Element and ElementList, so in
             # impl_Element.darttemplate, we have two additional "holes" for emitters
             # to fill in, with small variations. These store these specialized
             # emitters.
-            assert len(implementation_members_emitter) == 3
-            stream_getter_signatures_emitter = \
-                implementation_members_emitter[0]
-            element_stream_getters_emitter = implementation_members_emitter[1]
-            implementation_members_emitter = \
-                implementation_members_emitter[2]
+            if (len(implementation_members_emitter) == 3):
+                stream_getter_signatures_emitter = \
+                    implementation_members_emitter[0]
+                element_stream_getters_emitter = implementation_members_emitter[
+                    1]
+                implementation_members_emitter = \
+                    implementation_members_emitter[2]
+
+            # We add special emmiters for classes migrated to static type extensions
+            elif (len(implementation_members_emitter) == 2):
+                class_members_emitter = \
+                    implementation_members_emitter[0]
+                implementation_members_emitter = \
+                    implementation_members_emitter[1]
         self._backend.StartInterface(implementation_members_emitter)
-        self._backend.EmitHelpers(base_class)
+        self._backend.EmitHelpers(base_class, class_members_emitter)
         self._event_generator.EmitStreamProviders(
             self._interface, self._backend.CustomJSMembers(),
             implementation_members_emitter, self._library_name)
         self._backend.AddConstructors(constructors, factory_provider,
-                                      factory_constructor_name)
+                                      factory_constructor_name,
+                                      class_members_emitter)
 
         isElement = False
         for parent in self._database.Hierarchy(self._interface):
@@ -1243,9 +1261,14 @@
   interface.
   """
 
-    def __init__(self, interface, options, logging_level=logging.WARNING):
+    def __init__(self,
+                 interface,
+                 options,
+                 logging_level=logging.WARNING,
+                 generate_static_extensions=False):
         super(Dart2JSBackend, self).__init__(interface, options, False, _logger)
 
+        self._generate_static_extensions = generate_static_extensions
         self._database = options.database
         self._template_loader = options.templates
         self._type_registry = options.type_registry
@@ -1253,6 +1276,7 @@
         self._metadata = options.metadata
         self._interface_type_info = self._type_registry.TypeInfo(
             self._interface.id)
+        self._interface_name = self._interface_type_info.interface_name()
         self._current_secondary_parent = None
         self._library_name = self._renamer.GetLibraryName(self._interface)
         # Global constants for all WebGLRenderingContextBase, WebGL2RenderingContextBase, WebGLDrawBuffers
@@ -1289,9 +1313,15 @@
                 # TODO(terry): There are no mutable maplikes yet.
                 template_file_content = self._template_loader.Load(
                     'dart2js_maplike_impl.darttemplate')
+
             else:
-                template_file_content = self._template_loader.Load(
-                    'dart2js_impl.darttemplate')
+                if CanUseStaticExtensions(self._interface_name,
+                                          self._generate_static_extensions):
+                    template_file_content = self._template_loader.Load(
+                        'dart2js_static_extension_impl.darttemplate')
+                else:
+                    template_file_content = self._template_loader.Load(
+                        'dart2js_impl.darttemplate')
         return template_file_content
 
     def StartInterface(self, members_emitter):
@@ -1349,7 +1379,8 @@
     def IsConstructorArgumentOptional(self, argument):
         return argument.optional
 
-    def EmitStaticFactoryOverload(self, constructor_info, name, arguments):
+    def EmitStaticFactoryOverload(self, constructor_info, name, arguments,
+                                  emmiter):
         if self._interface_type_info.has_generated_interface():
             # Use dart_type name, we're generating.
             interface_name = self._interface_type_info.interface_name()
@@ -1361,7 +1392,7 @@
         arguments = constructor_info.ParametersAsArgumentList(index)
         if arguments:
             arguments = ', ' + arguments
-        self._members_emitter.Emit(
+        (emmiter if (emmiter != None) else self._members_emitter).Emit(
             "  static $INTERFACE_NAME $NAME($PARAMETERS) => "
             "JS('$INTERFACE_NAME', 'new $CTOR_NAME($PLACEHOLDERS)'$ARGUMENTS);\n",
             INTERFACE_NAME=interface_name,
@@ -1598,7 +1629,8 @@
                 if attribute.type.nullable:
                     promiseType += '?'
 
-                template = '\n  $RENAME$(ANNOTATIONS)$TYPE get $NAME => $PROMISE_CALL(JS("$TYPE_DESC", "#.$NAME", this));\n'
+                template = '\n  $RENAME$(ANNOTATIONS)$TYPE get $NAME => '\
+                    '$PROMISE_CALL(JS("$TYPE_DESC", "#.$NAME", this));\n'
 
                 self._members_emitter.Emit(
                     template,
@@ -1676,10 +1708,17 @@
                 \n
                 \n  $STATIC $NONNULLTYPE get $HTML_NAME => _$HTML_NAME$NULLASSERT;"""
         else:
-            template = """\n  $RENAME
-                \n  $METADATA
-                \n  $STATIC $TYPE get $HTML_NAME native;
-                \n"""
+            if CanUseStaticExtensions(self._interface_name,
+                                      self._generate_static_extensions):
+                template = """\n $RENAME
+                    \n $METADATA
+                    \n $STATIC $TYPE get $HTML_NAME => js_util.getProperty(this, '$JSNAME');
+                    \n"""
+            else:
+                template = """\n  $RENAME
+                    \n  $METADATA
+                    \n  $STATIC $TYPE get $HTML_NAME native;
+                    \n"""
         self._members_emitter.Emit(template,
                                    RENAME=rename if rename else '',
                                    METADATA=metadata if metadata else '',
@@ -1687,7 +1726,8 @@
                                    STATIC='static' if attr.is_static else '',
                                    TYPE=return_type,
                                    NULLASSERT='!',
-                                   NONNULLTYPE=non_null_return_type)
+                                   NONNULLTYPE=non_null_return_type,
+                                   JSNAME=rename if rename else html_name)
 
     def _AddRenamingSetter(self, attr, html_name, rename):
         conversion = self._InputConversion(attr.type.id, attr.id)
@@ -1705,14 +1745,23 @@
         if self._IsACompatibilityConflict(self._interface.id, attr):
             # Force non-nullable if it's a manual conflict.
             nullable_type = False
-        self._members_emitter.Emit(
-            '\n  $RENAME'
-            '\n  $STATIC set $HTML_NAME($TYPE value) native;'
-            '\n',
-            RENAME=rename if rename else '',
-            HTML_NAME=html_name,
-            STATIC='static ' if attr.is_static else '',
-            TYPE=self.SecureOutputType(attr.type.id, nullable=nullable_type))
+        if CanUseStaticExtensions(self._interface_name,
+                                  self._generate_static_extensions):
+            template = """\n $RENAME
+                \n $STATIC set $HTML_NAME($TYPE value)
+                 => js_util.setProperty(this, '$JSNAME', value);
+                \n"""
+        else:
+            template = """\n  $RENAME
+                \n $STATIC set $HTML_NAME($TYPE value) native;
+                \n"""
+        self._members_emitter.Emit(template,
+                                   RENAME=rename if rename else '',
+                                   HTML_NAME=html_name,
+                                   STATIC='static ' if attr.is_static else '',
+                                   TYPE=self.SecureOutputType(
+                                       attr.type.id, nullable=nullable_type),
+                                   JSNAME=rename if rename else html_name)
 
     def _AddConvertingGetter(self, attr, html_name, conversion):
         # dynamic should not be marked with ?
@@ -1950,19 +1999,25 @@
         else:
             self._members_emitter.Emit(
                 '\n'
+                '  $RENAME$METADATA$MODIFIERS$TYPE $NAME($PARAMS) => '\
+                'js_util.callMethod(this, \'$JSNAME\', [$ARGS]);\n'
+                if CanUseStaticExtensions(self._interface_name, self._generate_static_extensions) else
                 '  $RENAME$METADATA$MODIFIERS$TYPE $NAME($PARAMS) native;\n',
                 RENAME=self._RenamingAnnotation(info.declared_name, html_name),
-                METADATA=self._Metadata(info.type_name, info.declared_name,
-                                        self.SecureOutputType(info.type_name,
-                                            nullable=info.type_nullable),
-                                        info.type_nullable),
+                METADATA=self._Metadata(
+                    info.type_name, info.declared_name,
+                    self.SecureOutputType(info.type_name,
+                                          nullable=info.type_nullable),
+                    info.type_nullable),
                 MODIFIERS='static ' if info.IsStatic() else '',
                 TYPE=self.SecureOutputType(resultType,
                                            can_narrow_type=True,
                                            nullable=info.type_nullable),
                 NAME=html_name,
                 PARAMS=info.ParametersAsDeclaration(self._NarrowInputType,
-                                                    force_optional))
+                                                    force_optional),
+                ARGS=info.ParametersAsArgumentList(),
+                JSNAME=info.declared_name if info.declared_name != html_name else html_name)
 
     def _AddOperationWithConversions(self, info, html_name):
         # Assert all operations have same return type.
@@ -2029,7 +2084,8 @@
                         )
                 self._members_emitter.Emit(
                     '  $RENAME$METADATA$MODIFIERS$TYPE$TARGET($PARAMS) =>\n'
-                    '      promiseToFuture(JS("", "#.$JSNAME($HASH_STR)", this$CALLING_PARAMS));\n',
+                    '      promiseToFuture(JS("", "#.$JSNAME($HASH_STR)"'\
+                    ', this$CALLING_PARAMS));\n',
                     RENAME=self._RenamingAnnotation(info.declared_name, target),
                     METADATA=self._Metadata(info.type_name, info.declared_name,
                                             None, info.type_nullable),
diff --git a/tools/dom/templates/dart2js_static_extension_impl.darttemplate b/tools/dom/templates/dart2js_static_extension_impl.darttemplate
new file mode 100644
index 0000000..e2b3c61
--- /dev/null
+++ b/tools/dom/templates/dart2js_static_extension_impl.darttemplate
@@ -0,0 +1,11 @@
+// Copyright (c) 2012, 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.
+
+part of $LIBRARYNAME;
+
+$(ANNOTATIONS)@sealed$(NATIVESPEC)$(CLASS_MODIFIERS)class $CLASSNAME$EXTENDS$MIXINS$IMPLEMENTS { $!CLASSMEMBERS }
+
+extension $(CLASSNAME)Extension on $CLASSNAME {
+$!EXTENSIONMEMBERS
+}