Version 2.17.0-233.0.dev

Merge commit '727a35c588adaf45817226225f77debf903e8f73' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index 4d8dd70..fa954ae 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -580,7 +580,7 @@
       "name": "shelf",
       "rootUri": "../third_party/pkg/shelf",
       "packageUri": "lib/",
-      "languageVersion": "2.12"
+      "languageVersion": "2.16"
     },
     {
       "name": "shelf_packages_handler",
diff --git a/DEPS b/DEPS
index 8153c4c..98aeeab 100644
--- a/DEPS
+++ b/DEPS
@@ -148,7 +148,7 @@
   "shelf_static_rev": "202ec1a53c9a830c17cf3b718d089cf7eba568ad",
   "shelf_packages_handler_rev": "78302e67c035047e6348e692b0c1182131f0fe35",
   "shelf_proxy_tag": "ccd311f64d8689e7a145d703ba267975d6df9e28", # 1.0.0
-  "shelf_rev": "46483f896cc4308ee3d8e997030ae799b72aa16a",
+  "shelf_rev": "78ac724a7944700340a3cef28c84bccbe62e9367",
   "shelf_web_socket_rev": "24fb8a04befa75a94ac63a27047b231d1a22aab4",
   "source_map_stack_trace_rev": "80709f2d2fe5086ab50d744a28a2d26ea4384a1b",
   "source_maps-0.9.4_rev": "38524",
diff --git a/pkg/_fe_analyzer_shared/pubspec.yaml b/pkg/_fe_analyzer_shared/pubspec.yaml
index e4718a0..e5e361b 100644
--- a/pkg/_fe_analyzer_shared/pubspec.yaml
+++ b/pkg/_fe_analyzer_shared/pubspec.yaml
@@ -1,5 +1,5 @@
 name: _fe_analyzer_shared
-version: 36.0.0
+version: 37.0.0
 description: Logic that is shared between the front_end and analyzer packages.
 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/_fe_analyzer_shared
 
diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
index 41043e5..1ea1e2e 100644
--- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
+++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
@@ -1,28 +1,42 @@
 # This file contains a map. The key of each top-level entry is the unique name
-# of either an error code or a lint name. The value of each top-level entry is
-# a map describing the status of fixes related to that diagnostic.
+# of an error code. The value of each top-level entry is a map describing the
+# status of fixes related to that diagnostic.
 #
 # In every second-level map, the first key is `status` and the corresponding
 # value is one of the following:
-# - needsEvaluation, if the diagnostic has not been evaluated in terms of fixes.
-# - hasFix, if the diagnostic has one or more fixes.
-# - noFix, if no fix seems appropriate for the diagnostic. There should be a
+# - 'needsEvaluation', if the diagnostic has not been evaluated in terms of
+#   fixes.
+# - 'hasFix', if the diagnostic has one or more fixes.
+# - 'noFix', if no fix seems appropriate for the diagnostic. There should be a
 #   second key named `reason` whose value is text explaining why there is no
 #   appropriate fix.
-# - needsFix, if the diagnostic needs a fix, with a possible issue link. If an
+# - 'needsFix', if the diagnostic needs a fix, with a possible issue link. If an
 #   issue has been opened, there should be a second key named `issue` whose
 #   value is the URL of the issue.
 #
+# The other keys in the second-level map are all optional, and include
+# - 'since', whose value is the version number of the SDK in which the
+#   diagnostic was added.
+# - 'notes', whose value is text, typically describing why we think a fix for
+#   the diagnostic is not appropriate.
+# - 'issue', whose value is a list of the URLs of GitHub issues for the fixes
+#   that should be added. Ideally every issue marked as 'needFix' would have an
+#   issue created for it.
+#
 # Stats:
-# - 809 "needsEvaluation"
-# -  19 "needsFix"
-# - 253 "hasFix"
-# -   3 "noFix"
+# - 759 "needsEvaluation"
+# -  32 "needsFix"
+# - 265 "hasFix"
+# -  41 "noFix"
 
 AnalysisOptionsErrorCode.INCLUDED_FILE_PARSE_ERROR:
   status: noFix
+  notes: |-
+    The fix needs to be made in the included file.
 AnalysisOptionsErrorCode.PARSE_ERROR:
   status: noFix
+  notes: |-
+    There isn't enough information to be able to provide a fix.
 AnalysisOptionsHintCode.PREVIEW_DART_2_SETTING_DEPRECATED:
   status: needsEvaluation
 AnalysisOptionsHintCode.STRONG_MODE_SETTING_DEPRECATED:
@@ -32,11 +46,22 @@
 AnalysisOptionsWarningCode.ANALYSIS_OPTION_DEPRECATED:
   status: needsEvaluation
 AnalysisOptionsWarningCode.INCLUDE_FILE_NOT_FOUND:
-  status: needsEvaluation
+  status: noFix
+  notes: |-
+    It would not be performant to search the disk for analysis options files
+    that could be included.
+    
+    We could potentially have a fix to create the referenced file that currently
+    doesn't exist.
 AnalysisOptionsWarningCode.INCLUDED_FILE_WARNING:
-  status: needsEvaluation
+  status: noFix
+  notes: |-
+    The fix needs to be made in the included file.
 AnalysisOptionsWarningCode.INVALID_OPTION:
-  status: needsEvaluation
+  status: needsFix
+  notes: |-
+    We could look for valid options that are similar to the invalid option and
+    replace the invalid option with the selected replacement.
 AnalysisOptionsWarningCode.INVALID_SECTION_FORMAT:
   status: needsEvaluation
 AnalysisOptionsWarningCode.SPEC_MODE_REMOVED:
@@ -276,6 +301,7 @@
   status: needsEvaluation
 CompileTimeErrorCode.DEFAULT_LIST_CONSTRUCTOR:
   status: hasFix
+  issue: https://github.com/dart-lang/sdk/issues/48644
 CompileTimeErrorCode.DEFAULT_VALUE_IN_REDIRECTING_FACTORY_CONSTRUCTOR:
   status: needsEvaluation
 CompileTimeErrorCode.DEFAULT_VALUE_ON_REQUIRED_PARAMETER:
@@ -560,7 +586,8 @@
   status: needsEvaluation
 CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_SET:
   status: needsEvaluation
-CompileTimeErrorCode.INVALID_URI: needsEvaluation
+CompileTimeErrorCode.INVALID_URI:
+  status: needsEvaluation
 CompileTimeErrorCode.INVALID_USE_OF_COVARIANT:
   status: needsEvaluation
 CompileTimeErrorCode.INVALID_USE_OF_NULL_VALUE:
@@ -1141,9 +1168,13 @@
 FfiCode.SUBTYPE_OF_STRUCT_CLASS_IN_WITH:
   status: needsEvaluation
 HintCode.ARGUMENT_TYPE_NOT_ASSIGNABLE_TO_ERROR_HANDLER:
-  status: needsEvaluation
+  status: noFix
+  notes: |-
+    Fix depends on user's intent, which can't be known.
 HintCode.ASSIGNMENT_OF_DO_NOT_STORE:
-  status: needsEvaluation
+  status: noFix
+  notes: |-
+    Fix depends on user's intent, which can't be known.
 HintCode.BODY_MIGHT_COMPLETE_NORMALLY_NULLABLE:
   status: hasFix
 HintCode.CAN_BE_NULL_AFTER_NULL_AWARE:
@@ -1155,21 +1186,33 @@
 HintCode.DEAD_CODE_ON_CATCH_SUBTYPE:
   status: hasFix
 HintCode.DEPRECATED_EXTENDS_FUNCTION:
-  status: needsEvaluation
+  status: needsFix
+  notes: |-
+    The fix is to remove `Function` from where it's referenced.
 HintCode.DEPRECATED_FUNCTION_CLASS_DECLARATION:
-  status: needsEvaluation
+  status: noFix
+  notes: |-
+    The fix is to rename the class, which can't be done as a fix.
 HintCode.DEPRECATED_IMPLEMENTS_FUNCTION:
-  status: needsEvaluation
+  status: needsFix
+  notes: |-
+    The fix is to remove `Function` from where it's referenced.
 HintCode.DEPRECATED_MEMBER_USE:
   status: hasFix
 HintCode.DEPRECATED_MEMBER_USE_FROM_SAME_PACKAGE:
-  status: needsEvaluation
+  status: needsFix
+  notes: |-
+    Should probably be able to use `DataDriven`.
 HintCode.DEPRECATED_MEMBER_USE_FROM_SAME_PACKAGE_WITH_MESSAGE:
-  status: needsEvaluation
+  status: needsFix
+  notes: |-
+    Should probably be able to use `DataDriven`.
 HintCode.DEPRECATED_MEMBER_USE_WITH_MESSAGE:
   status: hasFix
 HintCode.DEPRECATED_MIXIN_FUNCTION:
-  status: needsEvaluation
+  status: needsFix
+  notes: |-
+    The fix is to remove `Function` from where it's referenced.
 HintCode.DEPRECATED_NEW_IN_COMMENT_REFERENCE:
   status: hasFix
 HintCode.DIVISION_OPTIMIZATION:
@@ -1177,19 +1220,29 @@
 HintCode.DUPLICATE_HIDDEN_NAME:
   status: hasFix
 HintCode.DUPLICATE_IGNORE:
-  status: needsEvaluation
+  status: needsFix
+  notes: |-
+    One fix is to remove the duplicated error code.
 HintCode.DUPLICATE_IMPORT:
   status: hasFix
 HintCode.DUPLICATE_SHOWN_NAME:
   status: hasFix
 HintCode.EQUAL_ELEMENTS_IN_SET:
-  status: needsEvaluation
+  status: noFix
+  notes: |-
+    Fix depends on user's intent, which can't be known.
 HintCode.EQUAL_KEYS_IN_MAP:
-  status: needsEvaluation
+  status: noFix
+  notes: |-
+    Fix depends on user's intent, which can't be known.
 HintCode.FILE_IMPORT_INSIDE_LIB_REFERENCES_FILE_OUTSIDE:
-  status: needsEvaluation
+  status: noFix
+  notes: |-
+    Fix depends on user's intent, which can't be known.
 HintCode.FILE_IMPORT_OUTSIDE_LIB_REFERENCES_FILE_INSIDE:
-  status: needsEvaluation
+  status: needsFix
+  notes: |-
+    One fix is to convert the reference to a 'package:' URI.
 HintCode.IMPORT_DEFERRED_LIBRARY_WITH_LOAD_FUNCTION:
   status: needsEvaluation
 HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE:
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index 10fc9e5..9fb3607 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -874,6 +874,7 @@
       ChangeTo.classOrMixin,
     ],
     CompileTimeErrorCode.DEFAULT_LIST_CONSTRUCTOR: [
+      ConvertToListLiteral.newInstance,
       ReplaceWithFilled.newInstance,
     ],
     CompileTimeErrorCode.EXTENDS_NON_CLASS: [
diff --git a/pkg/analysis_server/test/src/services/correction/fix/convert_to_list_literal_test.dart b/pkg/analysis_server/test/src/services/correction/fix/convert_to_list_literal_test.dart
index a74a556..31b6596 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/convert_to_list_literal_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/convert_to_list_literal_test.dart
@@ -13,6 +13,7 @@
   defineReflectiveSuite(() {
     defineReflectiveTests(ConvertToListLiteralBulkTest);
     defineReflectiveTests(ConvertToListLiteralTest);
+    defineReflectiveTests(ConvertToListLiteralWithNullSafetyTest);
   });
 }
 
@@ -80,3 +81,18 @@
 ''');
   }
 }
+
+@reflectiveTest
+class ConvertToListLiteralWithNullSafetyTest extends FixProcessorTest {
+  @override
+  FixKind get kind => DartFixKind.CONVERT_TO_LIST_LITERAL;
+
+  Future<void> test_default() async {
+    await resolveTestCode('''
+final l = List();
+''');
+    await assertHasFix('''
+final l = [];
+''');
+  }
+}
diff --git a/pkg/analyzer/CHANGELOG.md b/pkg/analyzer/CHANGELOG.md
index 5c1cf47..0c4a820 100644
--- a/pkg/analyzer/CHANGELOG.md
+++ b/pkg/analyzer/CHANGELOG.md
@@ -1,4 +1,4 @@
-## 3.4.0-dev
+## 3.4.0
 * Deprecated `Resource.parent2`, use `parent` instead.
 * Deprecated `astFactory`, clients should not create AST nodes manually.
 * Changed `CompilationUnit.lineInfo` to be non-nullable.
diff --git a/pkg/analyzer/lib/dart/element/element.dart b/pkg/analyzer/lib/dart/element/element.dart
index 0a0a1e6..fcbceef 100644
--- a/pkg/analyzer/lib/dart/element/element.dart
+++ b/pkg/analyzer/lib/dart/element/element.dart
@@ -1237,6 +1237,10 @@
   /// invoke an undefined method on an object.
   static final String NO_SUCH_METHOD_METHOD_NAME = "noSuchMethod";
 
+  /// Return `true` if this function represents `identical` from the
+  /// `dart:core` library.
+  bool get isDartCoreIdentical;
+
   /// Return `true` if the function is an entry point, i.e. a top-level function
   /// and has the name `main`.
   bool get isEntryPoint;
diff --git a/pkg/analyzer/lib/src/dart/constant/potentially_constant.dart b/pkg/analyzer/lib/src/dart/constant/potentially_constant.dart
index 66503bc..6c13da9 100644
--- a/pkg/analyzer/lib/src/dart/constant/potentially_constant.dart
+++ b/pkg/analyzer/lib/src/dart/constant/potentially_constant.dart
@@ -269,9 +269,9 @@
 
   void _methodInvocation(MethodInvocation node) {
     var arguments = node.argumentList.arguments;
-    if (arguments.length == 2 && node.methodName.name == 'identical') {
-      var library = node.methodName.staticElement?.library;
-      if (library?.isDartCore == true) {
+    if (arguments.length == 2) {
+      var element = node.methodName.staticElement;
+      if (element is FunctionElement && element.isDartCoreIdentical) {
         collect(arguments[0]);
         collect(arguments[1]);
         return;
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 8a9ab33..d33f063 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -3349,6 +3349,11 @@
   }
 
   @override
+  bool get isDartCoreIdentical {
+    return isStatic && name == 'identical' && library.isDartCore;
+  }
+
+  @override
   bool get isEntryPoint {
     return isStatic && displayName == FunctionElement.MAIN_FUNCTION_NAME;
   }
diff --git a/pkg/analyzer/lib/src/dart/element/member.dart b/pkg/analyzer/lib/src/dart/element/member.dart
index f6195f1..8d8f03d 100644
--- a/pkg/analyzer/lib/src/dart/element/member.dart
+++ b/pkg/analyzer/lib/src/dart/element/member.dart
@@ -419,6 +419,9 @@
   Element get enclosingElement => declaration.enclosingElement;
 
   @override
+  bool get isDartCoreIdentical => declaration.isDartCoreIdentical;
+
+  @override
   bool get isEntryPoint => declaration.isEntryPoint;
 
   @override
diff --git a/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart
index 46980c7..2c9c197 100644
--- a/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart
@@ -31,7 +31,7 @@
     AnnotationImpl node,
     ClassElement classElement,
     SimpleIdentifierImpl? constructorName,
-    ArgumentList argumentList,
+    ArgumentListImpl argumentList,
     List<WhyNotPromotedGetter> whyNotPromotedList,
   ) {
     ConstructorElement? constructorElement;
@@ -98,7 +98,7 @@
     SimpleIdentifierImpl? constructorName,
     List<TypeParameterElement> typeParameters,
     ConstructorElement? constructorElement,
-    ArgumentList argumentList,
+    ArgumentListImpl argumentList,
     InterfaceType Function(List<DartType> typeArguments) instantiateElement,
     List<WhyNotPromotedGetter> whyNotPromotedList,
   ) {
@@ -116,6 +116,7 @@
       annotationInferrer.resolveInvocation(
           resolver: _resolver,
           node: node,
+          argumentList: argumentList,
           rawType: null,
           contextType: null,
           whyNotPromotedList: whyNotPromotedList);
@@ -131,6 +132,7 @@
     annotationInferrer.resolveInvocation(
         resolver: _resolver,
         node: node,
+        argumentList: argumentList,
         rawType: constructorRawType,
         contextType: null,
         whyNotPromotedList: whyNotPromotedList);
@@ -338,7 +340,7 @@
     TypeAliasElement typeAliasElement,
     SimpleIdentifierImpl? constructorName,
     InterfaceType aliasedType,
-    ArgumentList argumentList,
+    ArgumentListImpl argumentList,
     List<WhyNotPromotedGetter> whyNotPromotedList,
   ) {
     var constructorElement = aliasedType.lookUpConstructor(
@@ -402,6 +404,7 @@
       AnnotationInferrer(constructorName: null).resolveInvocation(
           resolver: _resolver,
           node: node,
+          argumentList: arguments,
           rawType: null,
           contextType: null,
           whyNotPromotedList: whyNotPromotedList);
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 ddbdfdb..d345b15 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
@@ -119,6 +119,7 @@
         const FunctionExpressionInvocationInferrer().resolveInvocation(
       resolver: _resolver,
       node: node,
+      argumentList: node.argumentList,
       rawType: rawType,
       whyNotPromotedList: whyNotPromotedList,
       contextType: contextType,
@@ -208,6 +209,7 @@
     const FunctionExpressionInvocationInferrer().resolveInvocation(
         resolver: _resolver,
         node: node,
+        argumentList: node.argumentList,
         rawType: null,
         contextType: contextType,
         whyNotPromotedList: whyNotPromotedList);
diff --git a/pkg/analyzer/lib/src/dart/resolver/instance_creation_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/instance_creation_expression_resolver.dart
index bfdf73f..74c6873 100644
--- a/pkg/analyzer/lib/src/dart/resolver/instance_creation_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/instance_creation_expression_resolver.dart
@@ -65,6 +65,7 @@
     const InstanceCreationInferrer().resolveInvocation(
         resolver: _resolver,
         node: node,
+        argumentList: node.argumentList,
         rawType: elementToInfer?.asType,
         contextType: contextType,
         whyNotPromotedList: whyNotPromotedList);
diff --git a/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart b/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart
index 038cc4f..4112fcde 100644
--- a/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart
@@ -173,6 +173,7 @@
     var returnType = const MethodInvocationInferrer().resolveInvocation(
       resolver: _resolver,
       node: node,
+      argumentList: node.argumentList,
       rawType: rawType,
       contextType: contextType,
       whyNotPromotedList: whyNotPromotedList,
diff --git a/pkg/analyzer/lib/src/dart/resolver/invocation_inferrer.dart b/pkg/analyzer/lib/src/dart/resolver/invocation_inferrer.dart
index c8117fd..a2c3b09 100644
--- a/pkg/analyzer/lib/src/dart/resolver/invocation_inferrer.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/invocation_inferrer.dart
@@ -36,9 +36,6 @@
       CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS;
 
   @override
-  ArgumentListImpl _getArgumentList(AnnotationImpl node) => node.arguments!;
-
-  @override
   bool _getIsConst(AnnotationImpl node) => true;
 
   @override
@@ -66,21 +63,10 @@
 }
 
 /// Specialization of [InvocationInferrer] for performing type inference on AST
-/// nodes of type [ExtensionOverride].
-class ExtensionOverrideInferrer
-    extends InvocationInferrer<ExtensionOverrideImpl> {
-  const ExtensionOverrideInferrer() : super._();
-
-  @override
-  ArgumentListImpl _getArgumentList(ExtensionOverrideImpl node) =>
-      node.argumentList;
-}
-
-/// Specialization of [InvocationInferrer] for performing type inference on AST
 /// nodes that require full downward and upward inference.
 abstract class FullInvocationInferrer<Node extends AstNodeImpl>
     extends InvocationInferrer<Node> {
-  const FullInvocationInferrer._() : super._();
+  const FullInvocationInferrer._();
 
   bool get _needsTypeArgumentBoundsCheck => false;
 
@@ -91,6 +77,7 @@
   DartType resolveInvocation({
     required ResolverVisitor resolver,
     required Node node,
+    required ArgumentListImpl argumentList,
     required FunctionType? rawType,
     required DartType? contextType,
     required List<WhyNotPromotedGetter> whyNotPromotedList,
@@ -175,37 +162,31 @@
     super.resolveInvocation(
         resolver: resolver,
         node: node,
+        argumentList: argumentList,
         rawType: invokeType,
         contextType: contextType,
         whyNotPromotedList: whyNotPromotedList);
 
-    var argumentList = _getArgumentList(node);
-
     if (inferrer != null) {
-      if (rawType != null) {
-        // Get the parameters that correspond to the uninstantiated generic.
-        List<ParameterElement?> rawParameters =
-            ResolverVisitor.resolveArgumentsToParameters(
-          argumentList: argumentList,
-          parameters: rawType.parameters,
-        );
+      // Get the parameters that correspond to the uninstantiated generic.
+      List<ParameterElement?> rawParameters =
+          ResolverVisitor.resolveArgumentsToParameters(
+        argumentList: argumentList,
+        parameters: rawType!.parameters,
+      );
 
-        List<ParameterElement> params = <ParameterElement>[];
-        List<DartType> argTypes = <DartType>[];
-        for (int i = 0, length = rawParameters.length; i < length; i++) {
-          ParameterElement? parameter = rawParameters[i];
-          if (parameter != null) {
-            params.add(parameter);
-            argTypes.add(argumentList.arguments[i].typeOrThrow);
-          }
+      List<ParameterElement> params = <ParameterElement>[];
+      List<DartType> argTypes = <DartType>[];
+      for (int i = 0, length = rawParameters.length; i < length; i++) {
+        ParameterElement? parameter = rawParameters[i];
+        if (parameter != null) {
+          params.add(parameter);
+          argTypes.add(argumentList.arguments[i].typeOrThrow);
         }
-        inferrer.constrainArguments(
-            parameters: params, argumentTypes: argTypes);
-        typeArgumentTypes = inferrer.upwardsInfer();
-        invokeType = rawType.instantiate(typeArgumentTypes);
-      } else {
-        typeArgumentTypes = const [];
       }
+      inferrer.constrainArguments(parameters: params, argumentTypes: argTypes);
+      typeArgumentTypes = inferrer.upwardsInfer();
+      invokeType = rawType.instantiate(typeArgumentTypes);
     }
 
     var parameters = _storeResult(node, typeArgumentTypes, invokeType);
@@ -276,10 +257,6 @@
   bool get _needsTypeArgumentBoundsCheck => true;
 
   @override
-  ArgumentListImpl _getArgumentList(InstanceCreationExpressionImpl node) =>
-      node.argumentList;
-
-  @override
   ConstructorNameImpl _getErrorNode(InstanceCreationExpressionImpl node) =>
       node.constructorName;
 
@@ -327,9 +304,6 @@
   const InvocationExpressionInferrer._() : super._();
 
   @override
-  ArgumentListImpl _getArgumentList(Node node) => node.argumentList;
-
-  @override
   Expression _getErrorNode(Node node) => node.function;
 
   @override
@@ -346,8 +320,11 @@
 
 /// Base class containing functionality for performing type inference on AST
 /// nodes that invoke a method, function, or constructor.
-abstract class InvocationInferrer<Node extends AstNodeImpl> {
-  const InvocationInferrer._();
+///
+/// This class may be used directly for inference of [ExtensionOverride],
+/// [RedirectingConstructorInvocation], or [SuperConstructorInvocation].
+class InvocationInferrer<Node extends AstNodeImpl> {
+  const InvocationInferrer();
 
   /// Performs type inference on an invocation expression of type [Node].
   /// [rawType] should be the type of the function the invocation is resolved to
@@ -355,6 +332,7 @@
   void resolveInvocation({
     required ResolverVisitor resolver,
     required Node node,
+    required ArgumentListImpl argumentList,
     required FunctionType? rawType,
     required DartType? contextType,
     required List<WhyNotPromotedGetter> whyNotPromotedList,
@@ -369,7 +347,6 @@
         }
       }
     }
-    var argumentList = _getArgumentList(node);
     resolver.checkUnreachableNode(argumentList);
     var flow = resolver.flowAnalysis.flow;
     var positionalParameterIndex = 0;
@@ -419,9 +396,6 @@
           DartType parameterType, DartType? methodInvocationContext) =>
       parameterType;
 
-  /// Gets the argument list for the invocation.  TODO(paulberry): remove?
-  ArgumentListImpl _getArgumentList(Node node);
-
   /// Determines whether [node] is an invocation of the core function
   /// `identical` (which needs special flow analysis treatment).
   bool _isIdentical(Node node) => false;
@@ -466,8 +440,7 @@
   bool _isIdentical(MethodInvocationImpl node) {
     var invokedMethod = node.methodName.staticElement;
     return invokedMethod is FunctionElement &&
-        invokedMethod.name == 'identical' &&
-        invokedMethod.library.isDartCore &&
+        invokedMethod.isDartCoreIdentical &&
         node.argumentList.arguments.length == 2;
   }
 
@@ -488,26 +461,3 @@
     return returnType;
   }
 }
-
-/// Specialization of [InvocationInferrer] for performing type inference on AST
-/// nodes of type [RedirectingConstructorInvocation].
-class RedirectingConstructorInvocationInferrer
-    extends InvocationInferrer<RedirectingConstructorInvocationImpl> {
-  const RedirectingConstructorInvocationInferrer() : super._();
-
-  @override
-  ArgumentListImpl _getArgumentList(
-          RedirectingConstructorInvocationImpl node) =>
-      node.argumentList;
-}
-
-/// Specialization of [InvocationInferrer] for performing type inference on AST
-/// nodes of type [SuperConstructorInvocation].
-class SuperConstructorInvocationInferrer
-    extends InvocationInferrer<SuperConstructorInvocationImpl> {
-  const SuperConstructorInvocationInferrer() : super._();
-
-  @override
-  ArgumentListImpl _getArgumentList(SuperConstructorInvocationImpl node) =>
-      node.argumentList;
-}
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 776d654..ebfd7d4 100644
--- a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
@@ -332,6 +332,7 @@
         .resolveInvocation(
             resolver: _resolver,
             node: node,
+            argumentList: node.argumentList,
             rawType: rawType is FunctionType ? rawType : null,
             contextType: contextType,
             whyNotPromotedList: whyNotPromotedList);
@@ -476,6 +477,7 @@
     const MethodInvocationInferrer().resolveInvocation(
         resolver: _resolver,
         node: node,
+        argumentList: node.argumentList,
         rawType: rawType,
         whyNotPromotedList: whyNotPromotedList,
         contextType: contextType);
@@ -549,6 +551,7 @@
       const MethodInvocationInferrer().resolveInvocation(
           resolver: _resolver,
           node: node,
+          argumentList: node.argumentList,
           rawType: null,
           contextType: contextType,
           whyNotPromotedList: whyNotPromotedList);
@@ -568,6 +571,7 @@
       const MethodInvocationInferrer().resolveInvocation(
           resolver: _resolver,
           node: node,
+          argumentList: node.argumentList,
           rawType: null,
           contextType: contextType,
           whyNotPromotedList: whyNotPromotedList);
diff --git a/pkg/analyzer/lib/src/error/use_result_verifier.dart b/pkg/analyzer/lib/src/error/use_result_verifier.dart
index f0a60a8..a54515c 100644
--- a/pkg/analyzer/lib/src/error/use_result_verifier.dart
+++ b/pkg/analyzer/lib/src/error/use_result_verifier.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/error/listener.dart';
 import 'package:analyzer/src/dart/ast/ast.dart';
@@ -157,23 +158,56 @@
       }
     }
 
-    if (parent is ParenthesizedExpression ||
+    if (parent is PostfixExpression) {
+      if (parent.operator.type == TokenType.BANG) {
+        // Null-checking a result is not a "use."
+        return _isUsed(parent);
+      } else {
+        // Other uses, like `++`, count as a "use."
+        return true;
+      }
+    }
+
+    if (parent is AwaitExpression ||
         parent is ConditionalExpression ||
-        parent is AwaitExpression) {
+        parent is ForElement ||
+        parent is IfElement ||
+        parent is ParenthesizedExpression ||
+        parent is SpreadElement) {
       return _isUsed(parent);
     }
 
+    if (parent is ForParts) {
+      // If [node] is the condition of a for-loop, it is used; if it is one of
+      // the updaters, it is not.
+      return parent.condition == node;
+    }
+
     return parent is ArgumentList ||
-        // Node should always be RHS so no need to check for a property assignment.
+        parent is AssertInitializer ||
+        parent is AssertStatement ||
+        // Node should always be RHS so no need to check for a property
+        // assignment.
         parent is AssignmentExpression ||
-        parent is VariableDeclaration ||
+        parent is BinaryExpression ||
+        parent is ConstructorFieldInitializer ||
+        parent is DoStatement ||
+        parent is ExpressionFunctionBody ||
+        parent is ForEachParts ||
+        parent is ForLoopParts ||
+        parent is FunctionExpressionInvocation ||
+        parent is IfStatement ||
+        parent is IndexExpression ||
+        parent is ListLiteral ||
+        parent is MapLiteralEntry ||
         parent is MethodInvocation ||
         parent is PropertyAccess ||
-        parent is ExpressionFunctionBody ||
         parent is ReturnStatement ||
-        parent is FunctionExpressionInvocation ||
-        parent is ListLiteral ||
         parent is SetOrMapLiteral ||
-        parent is MapLiteralEntry;
+        parent is SwitchStatement ||
+        parent is ThrowExpression ||
+        parent is VariableDeclaration ||
+        parent is WhileStatement ||
+        parent is YieldStatement;
   }
 }
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 1c07741..6b2ba66 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -1540,9 +1540,10 @@
 
     var receiverContextType =
         ExtensionMemberResolver(this).computeOverrideReceiverContextType(node);
-    const ExtensionOverrideInferrer().resolveInvocation(
+    const InvocationInferrer<ExtensionOverrideImpl>().resolveInvocation(
         resolver: this,
         node: node,
+        argumentList: node.argumentList,
         rawType: receiverContextType == null
             ? null
             : FunctionTypeImpl(
@@ -2163,12 +2164,14 @@
     var whyNotPromotedList = <Map<DartType, NonPromotionReason> Function()>[];
     elementResolver.visitRedirectingConstructorInvocation(
         node as RedirectingConstructorInvocationImpl);
-    const RedirectingConstructorInvocationInferrer().resolveInvocation(
-        resolver: this,
-        node: node,
-        rawType: node.staticElement?.type,
-        contextType: null,
-        whyNotPromotedList: whyNotPromotedList);
+    const InvocationInferrer<RedirectingConstructorInvocationImpl>()
+        .resolveInvocation(
+            resolver: this,
+            node: node,
+            argumentList: node.argumentList,
+            rawType: node.staticElement?.type,
+            contextType: null,
+            whyNotPromotedList: whyNotPromotedList);
     checkForArgumentTypesNotAssignableInList(
         node.argumentList, whyNotPromotedList);
   }
@@ -2271,12 +2274,14 @@
     var whyNotPromotedList = <Map<DartType, NonPromotionReason> Function()>[];
     elementResolver.visitSuperConstructorInvocation(
         node as SuperConstructorInvocationImpl);
-    const SuperConstructorInvocationInferrer().resolveInvocation(
-        resolver: this,
-        node: node,
-        rawType: node.staticElement?.type,
-        contextType: null,
-        whyNotPromotedList: whyNotPromotedList);
+    const InvocationInferrer<SuperConstructorInvocationImpl>()
+        .resolveInvocation(
+            resolver: this,
+            node: node,
+            argumentList: node.argumentList,
+            rawType: node.staticElement?.type,
+            contextType: null,
+            whyNotPromotedList: whyNotPromotedList);
     checkForArgumentTypesNotAssignableInList(
         node.argumentList, whyNotPromotedList);
   }
diff --git a/pkg/analyzer/pubspec.yaml b/pkg/analyzer/pubspec.yaml
index 24b3c13..c740b60 100644
--- a/pkg/analyzer/pubspec.yaml
+++ b/pkg/analyzer/pubspec.yaml
@@ -1,5 +1,5 @@
 name: analyzer
-version: 3.4.0-dev
+version: 3.4.0
 description: This package provides a library that performs static analysis of Dart code.
 homepage: https://github.com/dart-lang/sdk/tree/main/pkg/analyzer
 
@@ -7,7 +7,7 @@
   sdk: '>=2.14.0 <3.0.0'
 
 dependencies:
-  _fe_analyzer_shared: ^36.0.0
+  _fe_analyzer_shared: ^37.0.0
   collection: ^1.15.0
   convert: ^3.0.0
   crypto: ^3.0.0
diff --git a/pkg/analyzer/test/src/diagnostics/unused_result_test.dart b/pkg/analyzer/test/src/diagnostics/unused_result_test.dart
index 812bf13..1954c47 100644
--- a/pkg/analyzer/test/src/diagnostics/unused_result_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/unused_result_test.dart
@@ -497,6 +497,37 @@
     ]);
   }
 
+  test_method_result_assertInitializer() async {
+    await assertNoErrorsInCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @useResult
+  int foo() => 1;
+}
+
+class B {
+  B(A a) :
+    assert(a.foo() != 7);
+}
+''');
+  }
+
+  test_method_result_assertStatement() async {
+    await assertNoErrorsInCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @useResult
+  int foo() => 1;
+}
+
+void f(A a) {
+  assert(a.foo() != 7);
+}
+''');
+  }
+
   test_method_result_assigned() async {
     await assertNoErrorsInCode(r'''
 import 'package:meta/meta.dart';
@@ -513,6 +544,234 @@
 ''');
   }
 
+  test_method_result_binaryExpression() async {
+    await assertNoErrorsInCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @useResult
+  int foo() => 1;
+}
+
+void f(A a) {
+  1 + a.foo();
+}
+''');
+  }
+
+  test_method_result_conditional() async {
+    await assertNoErrorsInCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @useResult
+  bool foo() => false;
+}
+
+void f(A a) {
+  if (a.foo()) {}
+}
+''');
+  }
+
+  test_method_result_constructorCall() async {
+    await assertNoErrorsInCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @useResult
+  int foo() => 1;
+}
+
+class B {
+  B(int i);
+}
+
+void f(A a) {
+  new B(a.foo());
+}
+''');
+  }
+
+  test_method_result_doWhile() async {
+    await assertNoErrorsInCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @useResult
+  bool foo() => false;
+}
+
+void f(A a) {
+  do {}
+  while (a.foo());
+}
+''');
+  }
+
+  test_method_result_fieldInitializer() async {
+    await assertNoErrorsInCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @useResult
+  int foo() => 1;
+}
+
+class B {
+  int i;
+  B(A a) : i = a.foo();
+}
+''');
+  }
+
+  test_method_result_for() async {
+    await assertNoErrorsInCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @useResult
+  int foo() => 1;
+}
+
+void f(A a) {
+  for (var i = 1; i < a.foo(); i++) {}
+}
+''');
+  }
+
+  test_method_result_for_updaters() async {
+    await assertErrorsInCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @useResult
+  int foo() => 1;
+}
+
+void f(A a) {
+  for (var i = 1; i < 7; a.foo()) {}
+}
+''', [
+      error(HintCode.UNUSED_RESULT, 119, 3),
+    ]);
+  }
+
+  test_method_result_forElement() async {
+    await assertNoErrorsInCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @useResult
+  List<int> foo() => [];
+}
+
+void f(A a) {
+  // Note that the list literal is unused, but we unconditionally consider use
+  // within a list literal to be "use of result."
+  [
+    for (var e in a.foo()) e,
+  ];
+}
+''');
+  }
+
+  test_method_result_forIn() async {
+    await assertNoErrorsInCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @useResult
+  List<int> foo() => [];
+}
+
+void f(A a) {
+  for (var _ in a.foo()) {}
+}
+''');
+  }
+
+  test_method_result_ifElement() async {
+    await assertNoErrorsInCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @useResult
+  bool foo() => false;
+}
+
+void f(A a) {
+  // Note that the list literal is unused, but we unconditionally consider use
+  // within a list literal to be "use of result."
+  [
+    if (a.foo()) 1,
+  ];
+}
+''');
+  }
+
+  test_method_result_ifNull() async {
+    await assertNoErrorsInCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @useResult
+  int? foo() => 1;
+}
+
+int f(A a) {
+  return a.foo() ?? 7;
+}
+''');
+  }
+
+  test_method_result_indexExpression() async {
+    await assertNoErrorsInCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @useResult
+  int foo() => 1;
+}
+
+void f(A a, List<int> list) {
+  list[a.foo()];
+}
+''');
+  }
+
+  test_method_result_nullCheck_isUsed() async {
+    await assertNoErrorsInCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @useResult
+  int? foo() => 1;
+}
+
+int f(A a) {
+  return a.foo()!;
+}
+''');
+  }
+
+  test_method_result_nullCheck_notUsed() async {
+    await assertErrorsInCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @useResult
+  int? foo() => 1;
+}
+
+void f(A a) {
+  a.foo()!;
+}
+''', [
+      error(HintCode.UNUSED_RESULT, 97, 3),
+    ]);
+  }
+
   test_method_result_passed() async {
     await assertNoErrorsInCode(r'''
 import 'package:meta/meta.dart';
@@ -544,6 +803,57 @@
 ''');
   }
 
+  test_method_result_spread() async {
+    await assertNoErrorsInCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @useResult
+  List<int> foo() => [];
+}
+
+void f(A a) {
+  // Note that the list literal is unused, but we unconditionally consider use
+  // within a list literal to be "use of result."
+  [...a.foo()];
+}
+''');
+  }
+
+  test_method_result_superInitializer() async {
+    await assertNoErrorsInCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @useResult
+  int foo() => 1;
+}
+
+class B {
+  B(int i);
+}
+
+class C extends B {
+  C(A a) : super(a.foo());
+}
+''');
+  }
+
+  test_method_result_switchCondition() async {
+    await assertNoErrorsInCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @useResult
+  bool foo() => false;
+}
+
+void f(A a) {
+  switch (a.foo()) {}
+}
+''');
+  }
+
   test_method_result_targetedMethod() async {
     await assertNoErrorsInCode(r'''
 import 'package:meta/meta.dart';
@@ -572,6 +882,21 @@
 ''');
   }
 
+  test_method_result_thrown() async {
+    await assertNoErrorsInCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @useResult
+  bool foo() => false;
+}
+
+void f(A a) {
+  throw a.foo();
+}
+''');
+  }
+
   test_method_result_unassigned() async {
     await assertErrorsInCode(r'''
 import 'package:meta/meta.dart';
@@ -639,6 +964,36 @@
 ''');
   }
 
+  test_method_result_while() async {
+    await assertNoErrorsInCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @useResult
+  bool foo() => false;
+}
+
+void f(A a) {
+  while (a.foo()) {}
+}
+''');
+  }
+
+  test_method_result_yield() async {
+    await assertNoErrorsInCode('''
+import 'package:meta/meta.dart';
+
+class A {
+  @useResult
+  int foo() => 1;
+}
+
+Stream<int> f(A a) async* {
+  yield a.foo();
+}
+''');
+  }
+
   test_topLevelFunction_result_assigned() async {
     await assertNoErrorsInCode(r'''
 import 'package:meta/meta.dart';
diff --git a/pkg/compiler/lib/src/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart
index c6e7a5e..b25e1c4 100644
--- a/pkg/compiler/lib/src/kernel/dart2js_target.dart
+++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart
@@ -70,8 +70,10 @@
   final String name;
 
   final CompilerOptions? options;
+  final bool canPerformGlobalTransforms;
 
-  Dart2jsTarget(this.name, this.flags, {this.options});
+  Dart2jsTarget(this.name, this.flags,
+      {this.options, this.canPerformGlobalTransforms = true});
 
   @override
   bool get enableNoSuchMethodForwarders => true;
@@ -157,8 +159,10 @@
     }
     lowering.transformLibraries(libraries, coreTypes, hierarchy, options);
     logger?.call("Lowering transformations performed");
-    transformMixins.transformLibraries(libraries);
-    logger?.call("Mixin transformations performed");
+    if (canPerformGlobalTransforms) {
+      transformMixins.transformLibraries(libraries);
+      logger?.call("Mixin transformations performed");
+    }
   }
 
   @override
diff --git a/pkg/compiler/lib/src/options.dart b/pkg/compiler/lib/src/options.dart
index 3f926b2..18d2044 100644
--- a/pkg/compiler/lib/src/options.dart
+++ b/pkg/compiler/lib/src/options.dart
@@ -170,9 +170,7 @@
 
   bool get fromDill {
     var targetPath = compilationTarget!.path;
-    return targetPath.endsWith('.dill') ||
-        targetPath.endsWith('.gdill') ||
-        targetPath.endsWith('.mdill');
+    return targetPath.endsWith('.dill');
   }
 
   /// Location of the package configuration file.
diff --git a/pkg/compiler/lib/src/phase/load_kernel.dart b/pkg/compiler/lib/src/phase/load_kernel.dart
index 17795f7..44314a7 100644
--- a/pkg/compiler/lib/src/phase/load_kernel.dart
+++ b/pkg/compiler/lib/src/phase/load_kernel.dart
@@ -17,6 +17,8 @@
 import '../common.dart';
 import '../kernel/front_end_adapter.dart';
 import '../kernel/dart2js_target.dart' show Dart2jsTarget;
+import '../kernel/transformations/clone_mixin_methods_with_super.dart'
+    as transformMixins show transformLibraries;
 import '../options.dart';
 
 class Input {
@@ -121,6 +123,10 @@
       this.component, this.entryLibrary, this.moduleLibraries);
 }
 
+void _doGlobalTransforms(Component component) {
+  transformMixins.transformLibraries(component.libraries);
+}
+
 Future<_LoadFromKernelResult> _loadFromKernel(CompilerOptions options,
     api.CompilerInput compilerInput, String targetName) async {
   Library entryLibrary;
@@ -178,6 +184,11 @@
     var mainMethod = _findMainMethod(entryLibrary);
     component.setMainMethodAndMode(mainMethod, true, component.mode);
   }
+
+  // We apply global transforms when running phase 0.
+  if (options.cfeOnly) {
+    _doGlobalTransforms(component);
+  }
   return _LoadFromKernelResult(component, entryLibrary, moduleLibraries);
 }
 
@@ -195,7 +206,8 @@
     fe.InitializedCompilerState initializedCompilerState,
     String targetName) async {
   bool verbose = false;
-  Target target = Dart2jsTarget(targetName, TargetFlags(), options: options);
+  Target target = Dart2jsTarget(targetName, TargetFlags(),
+      options: options, canPerformGlobalTransforms: true);
   fe.FileSystem fileSystem = CompilerFileSystem(compilerInput);
   fe.Verbosity verbosity = options.verbosity;
   fe.DiagnosticMessageHandler onDiagnostic = (fe.DiagnosticMessage message) {
diff --git a/pkg/compiler/tool/modular_test_suite.dart b/pkg/compiler/tool/modular_test_suite.dart
index 7a20a67..cf0d52c 100644
--- a/pkg/compiler/tool/modular_test_suite.dart
+++ b/pkg/compiler/tool/modular_test_suite.dart
@@ -23,6 +23,7 @@
           OutlineDillCompilationStep(),
           FullDillCompilationStep(),
           ModularAnalysisStep(),
+          ConcatenateDillsStep(useModularAnalysis: true),
           ComputeClosedWorldStep(useModularAnalysis: true),
           GlobalAnalysisStep(),
           Dart2jsCodegenStep(codeId0),
diff --git a/pkg/compiler/tool/modular_test_suite_helper.dart b/pkg/compiler/tool/modular_test_suite_helper.dart
index d4ff09e..357b1da 100644
--- a/pkg/compiler/tool/modular_test_suite_helper.dart
+++ b/pkg/compiler/tool/modular_test_suite_helper.dart
@@ -25,13 +25,14 @@
 String _dart2jsScript;
 String _kernelWorkerScript;
 
-const dillSummaryId = DataId("sdill");
-const dillId = DataId("dill");
-const modularUpdatedDillId = DataId("mdill");
-const modularDataId = DataId("mdata");
+const dillSummaryId = DataId("summary.dill");
+const dillId = DataId("full.dill");
+const fullDillId = DataId("concatenate.dill");
+const modularUpdatedDillId = DataId("modular.dill");
+const modularDataId = DataId("modular.data");
 const closedWorldId = DataId("world");
-const globalUpdatedDillId = DataId("gdill");
-const globalDataId = DataId("gdata");
+const globalUpdatedDillId = DataId("global.dill");
+const globalDataId = DataId("global.data");
 const codeId = ShardsDataId("code", 2);
 const codeId0 = ShardDataId(codeId, 0);
 const codeId1 = ShardDataId(codeId, 1);
@@ -305,44 +306,36 @@
   }
 }
 
-DataId idForDill({bool useModularAnalysis}) =>
-    useModularAnalysis ? modularUpdatedDillId : dillId;
-
-List<DataId> inputFromAnalysis({bool useModularAnalysis = false}) => [
-      idForDill(useModularAnalysis: useModularAnalysis),
-      if (useModularAnalysis) modularDataId,
-    ];
-
-// Step that invokes the dart2js closed world computation.
-class ComputeClosedWorldStep implements IOModularStep {
+class ConcatenateDillsStep implements IOModularStep {
   final bool useModularAnalysis;
 
-  ComputeClosedWorldStep({this.useModularAnalysis});
+  DataId get idForDill => useModularAnalysis ? modularUpdatedDillId : dillId;
+
+  List<DataId> get dependencies => [idForDill];
 
   @override
-  List<DataId> get resultData => const [closedWorldId, globalUpdatedDillId];
+  List<DataId> get resultData => const [fullDillId];
 
   @override
   bool get needsSources => false;
 
   @override
-  List<DataId> get dependencyDataNeeded =>
-      inputFromAnalysis(useModularAnalysis: useModularAnalysis);
+  List<DataId> get dependencyDataNeeded => dependencies;
 
   @override
-  List<DataId> get moduleDataNeeded =>
-      inputFromAnalysis(useModularAnalysis: useModularAnalysis);
+  List<DataId> get moduleDataNeeded => dependencies;
 
   @override
   bool get onlyOnMain => true;
 
+  ConcatenateDillsStep({this.useModularAnalysis});
+
   @override
   Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
       List<String> flags) async {
-    if (_options.verbose)
-      print("\nstep: dart2js compute closed world on $module");
+    if (_options.verbose) print("\nstep: dart2js concatenate dills on $module");
     Set<Module> transitiveDependencies = computeTransitiveDependencies(module);
-    DataId dillId = idForDill(useModularAnalysis: useModularAnalysis);
+    DataId dillId = idForDill;
     Iterable<String> dillDependencies =
         transitiveDependencies.map((m) => '${toUri(m, dillId)}');
     List<String> dataDependencies = transitiveDependencies
@@ -358,8 +351,66 @@
       '${Flags.inputDill}=${toUri(module, dillId)}',
       for (String flag in flags) '--enable-experiment=$flag',
       '${Flags.dillDependencies}=${dillDependencies.join(',')}',
-      if (useModularAnalysis)
-        '${Flags.readModularAnalysis}=${dataDependencies.join(',')}',
+      '${Flags.cfeOnly}',
+      '--out=${toUri(module, fullDillId)}',
+    ];
+    var result =
+        await _runProcess(Platform.resolvedExecutable, args, root.toFilePath());
+
+    _checkExitCode(result, this, module);
+  }
+
+  @override
+  void notifyCached(Module module) {
+    if (_options.verbose)
+      print("\ncached step: dart2js concatenate dills on $module");
+  }
+}
+
+// Step that invokes the dart2js closed world computation.
+class ComputeClosedWorldStep implements IOModularStep {
+  final bool useModularAnalysis;
+
+  ComputeClosedWorldStep({this.useModularAnalysis});
+
+  List<DataId> get dependencies => [
+        fullDillId,
+        if (useModularAnalysis) modularDataId,
+      ];
+
+  @override
+  List<DataId> get resultData => const [closedWorldId, globalUpdatedDillId];
+
+  @override
+  bool get needsSources => false;
+
+  @override
+  List<DataId> get dependencyDataNeeded => dependencies;
+
+  @override
+  List<DataId> get moduleDataNeeded => dependencies;
+
+  @override
+  bool get onlyOnMain => true;
+
+  @override
+  Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
+      List<String> flags) async {
+    if (_options.verbose)
+      print("\nstep: dart2js compute closed world on $module");
+    Set<Module> transitiveDependencies = computeTransitiveDependencies(module);
+    List<String> dataDependencies = transitiveDependencies
+        .map((m) => '${toUri(m, modularDataId)}')
+        .toList();
+    dataDependencies.add('${toUri(module, modularDataId)}');
+    List<String> args = [
+      '--packages=${sdkRoot.toFilePath()}/.packages',
+      _dart2jsScript,
+      // TODO(sigmund): remove this dependency on libraries.json
+      if (_options.useSdk) '--libraries-spec=$_librarySpecForSnapshot',
+      '${Flags.entryUri}=$fakeRoot${module.mainSource}',
+      '${Flags.inputDill}=${toUri(module, fullDillId)}',
+      for (String flag in flags) '--enable-experiment=$flag',
       '${Flags.writeClosedWorld}=${toUri(module, closedWorldId)}',
       Flags.noClosedWorldInData,
       '--out=${toUri(module, globalUpdatedDillId)}',
diff --git a/pkg/compiler/tool/modular_test_suite_legacy.dart b/pkg/compiler/tool/modular_test_suite_legacy.dart
index 875a64a..f170069 100644
--- a/pkg/compiler/tool/modular_test_suite_legacy.dart
+++ b/pkg/compiler/tool/modular_test_suite_legacy.dart
@@ -22,6 +22,7 @@
         IOPipeline([
           OutlineDillCompilationStep(),
           FullDillCompilationStep(),
+          ConcatenateDillsStep(useModularAnalysis: false),
           ComputeClosedWorldStep(useModularAnalysis: false),
           GlobalAnalysisStep(),
           Dart2jsCodegenStep(codeId0),
diff --git a/pkg/compiler/tool/szcmp b/pkg/compiler/tool/szcmp
new file mode 100755
index 0000000..1b2db57
--- /dev/null
+++ b/pkg/compiler/tool/szcmp
@@ -0,0 +1,82 @@
+#!/bin/bash
+# Copyright (c) 2022, 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.
+#
+# Usage: szcmp files
+#
+# Compare sizes of files as percentages in a table.  Column header is the
+# baseline (i.e. all sizes in the column are in terms of the file in the column
+# header).
+#
+# Example with dart2js workflow:
+#
+#     $ dart2js  ...  --out=v1/m.js
+#     # change dart2js
+#     $ dart2js  ...  --out=v2/m.js
+#     # change dart2js again
+#     $ dart2js  ...  --out=v3/m.js
+#
+#     $ szcmp v?/m.js
+#                       v1/m.js  v2/m.js  v3/m.js
+#     5793879  v1/m.js        =  -4.499%  +4.461%
+#     6066812  v2/m.js  +4.711%        =  +9.382%
+#     5546433  v3/m.js  -4.271%  -8.577%        =
+#
+# The first column of the matrix is usually the most relevant - `v2` is 4.7%
+# larger than `v1` and `v3` is 4.2% smaller than `v1`. The other columns can be
+# useful for understanding the differential changes. Sizes relative to `v2` are
+# in the next column, e.g. the step from `v2` to `v3` was a reduction of
+# 8.6%. Above-diagonal values show the percentage change in the 'undo'
+# direction. While changing from `v2` to `v3` reduces by 8.6%, reverting that
+# change would be an increase of 9.4%.
+
+wc -c "$@" |
+awk '
+$2 == "total" { next; }
+
+{
+  N++
+  r[N,0] = $1;
+  r[N,1] = $2;
+}
+
+END {
+  r[0,0] = ""  # header col 0
+  r[0,1] = ""  # header col 1
+
+  for (i = 1; i <= N; i++) { #skips "total" line
+    r[0, i+1] = r[i,1]  # name header
+
+    for (j = 1; j <= N; j++) {
+      s1 = r[i,0]
+      s2 = r[j,0];
+      if (s1 == s2) {
+        r[i, j+1] = "="
+      } else {
+        x = sprintf("%.3f%%", (s1 - s2) / s2 * 100)
+        if (s1 > s2) x = "+" x
+        r[i, j+1] = x
+      }
+    }
+  }
+
+  # find column widths
+  for (cc = 0; cc <= N+1; cc++) {
+    w[cc] = 0;
+    for (rr = 0; rr <= N; rr++) {
+      if (length(r[rr,cc]) > w[cc]) w[cc] = length(r[rr,cc])
+    }
+  }
+
+  for (rr = 0; rr <= N; rr++) {
+    line = ""
+    sep = ""
+    for (cc = 0; cc <= N+1; cc++) {
+      line = line sep sprintf("%*s", w[cc], r[rr, cc])
+      sep = "  "
+    }
+    print line
+  }
+}
+'
diff --git a/pkg/js_ast/analysis_options.yaml b/pkg/js_ast/analysis_options.yaml
new file mode 100644
index 0000000..a1888bc
--- /dev/null
+++ b/pkg/js_ast/analysis_options.yaml
@@ -0,0 +1,16 @@
+# Copyright (c) 2022, 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.
+
+analyzer:
+  errors:
+    todo: ignore
+    # Allow deprecated calls from within the same package
+    deprecated_member_use_from_same_package: ignore
+
+linter:
+  rules:
+    - annotate_overrides
+    - prefer_final_fields
+    - prefer_if_null_operators
+    - prefer_null_aware_operators
diff --git a/pkg/js_ast/lib/src/builder.dart b/pkg/js_ast/lib/src/builder.dart
index 83bb11f..7774925 100644
--- a/pkg/js_ast/lib/src/builder.dart
+++ b/pkg/js_ast/lib/src/builder.dart
@@ -356,6 +356,7 @@
   final MiniJsParser parser;
   final String message;
 
+  @override
   String toString() {
     int pos = parser.lastPosition;
 
diff --git a/pkg/js_ast/lib/src/nodes.dart b/pkg/js_ast/lib/src/nodes.dart
index 7c62eab..06330a1 100644
--- a/pkg/js_ast/lib/src/nodes.dart
+++ b/pkg/js_ast/lib/src/nodes.dart
@@ -89,118 +89,185 @@
   const BaseVisitor();
 
   T visitNode(Node node);
+  @override
   T visitComment(Comment node);
 
+  @override
   T visitProgram(Program node) => visitNode(node);
 
   T visitStatement(Statement node) => visitNode(node);
   T visitLoop(Loop node) => visitStatement(node);
   T visitJump(Statement node) => visitStatement(node);
 
+  @override
   T visitBlock(Block node) => visitStatement(node);
+  @override
   T visitExpressionStatement(ExpressionStatement node) => visitStatement(node);
+  @override
   T visitEmptyStatement(EmptyStatement node) => visitStatement(node);
+  @override
   T visitIf(If node) => visitStatement(node);
+  @override
   T visitFor(For node) => visitLoop(node);
+  @override
   T visitForIn(ForIn node) => visitLoop(node);
+  @override
   T visitWhile(While node) => visitLoop(node);
+  @override
   T visitDo(Do node) => visitLoop(node);
+  @override
   T visitContinue(Continue node) => visitJump(node);
+  @override
   T visitBreak(Break node) => visitJump(node);
+  @override
   T visitReturn(Return node) => visitJump(node);
+  @override
   T visitThrow(Throw node) => visitJump(node);
+  @override
   T visitTry(Try node) => visitStatement(node);
+  @override
   T visitSwitch(Switch node) => visitStatement(node);
+  @override
   T visitFunctionDeclaration(FunctionDeclaration node) => visitStatement(node);
+  @override
   T visitLabeledStatement(LabeledStatement node) => visitStatement(node);
+  @override
   T visitLiteralStatement(LiteralStatement node) => visitStatement(node);
 
+  @override
   T visitCatch(Catch node) => visitNode(node);
+  @override
   T visitCase(Case node) => visitNode(node);
+  @override
   T visitDefault(Default node) => visitNode(node);
 
   T visitExpression(Expression node) => visitNode(node);
   T visitVariableReference(VariableReference node) => visitExpression(node);
 
+  @override
   T visitLiteralExpression(LiteralExpression node) => visitExpression(node);
+  @override
   T visitVariableDeclarationList(VariableDeclarationList node) =>
       visitExpression(node);
+  @override
   T visitAssignment(Assignment node) => visitExpression(node);
+  @override
   T visitVariableInitialization(VariableInitialization node) =>
       visitExpression(node);
 
+  @override
   T visitConditional(Conditional node) => visitExpression(node);
+  @override
   T visitNew(New node) => visitExpression(node);
+  @override
   T visitCall(Call node) => visitExpression(node);
+  @override
   T visitBinary(Binary node) => visitExpression(node);
+  @override
   T visitPrefix(Prefix node) => visitExpression(node);
+  @override
   T visitPostfix(Postfix node) => visitExpression(node);
+  @override
   T visitAccess(PropertyAccess node) => visitExpression(node);
 
+  @override
   T visitVariableUse(VariableUse node) => visitVariableReference(node);
+  @override
   T visitVariableDeclaration(VariableDeclaration node) =>
       visitVariableReference(node);
+  @override
   T visitParameter(Parameter node) => visitVariableDeclaration(node);
+  @override
   T visitThis(This node) => visitParameter(node);
 
+  @override
   T visitNamedFunction(NamedFunction node) => visitExpression(node);
   T visitFunctionExpression(FunctionExpression node) => visitExpression(node);
+  @override
   T visitFun(Fun node) => visitFunctionExpression(node);
+  @override
   T visitArrowFunction(ArrowFunction node) => visitFunctionExpression(node);
 
   T visitToken(DeferredToken node) => visitExpression(node);
 
+  @override
   T visitDeferredStatement(DeferredStatement node) => visitStatement(node);
+  @override
   T visitDeferredExpression(DeferredExpression node) => visitExpression(node);
+  @override
   T visitDeferredNumber(DeferredNumber node) => visitToken(node);
+  @override
   T visitDeferredString(DeferredString node) => visitToken(node);
 
   T visitLiteral(Literal node) => visitExpression(node);
 
+  @override
   T visitLiteralBool(LiteralBool node) => visitLiteral(node);
+  @override
   T visitLiteralString(LiteralString node) => visitLiteral(node);
+  @override
   T visitLiteralNumber(LiteralNumber node) => visitLiteral(node);
+  @override
   T visitLiteralNull(LiteralNull node) => visitLiteral(node);
 
+  @override
   T visitStringConcatenation(StringConcatenation node) => visitLiteral(node);
 
+  @override
   T visitName(Name node) => visitNode(node);
 
+  @override
   T visitParentheses(Parentheses node) => visitExpression(node);
 
+  @override
   T visitArrayInitializer(ArrayInitializer node) => visitExpression(node);
+  @override
   T visitArrayHole(ArrayHole node) => visitExpression(node);
+  @override
   T visitObjectInitializer(ObjectInitializer node) => visitExpression(node);
+  @override
   T visitProperty(Property node) => visitNode(node);
+  @override
   T visitMethodDefinition(MethodDefinition node) => visitNode(node);
+  @override
   T visitRegExpLiteral(RegExpLiteral node) => visitExpression(node);
 
   T visitInterpolatedNode(InterpolatedNode node) => visitNode(node);
 
+  @override
   T visitInterpolatedExpression(InterpolatedExpression node) =>
       visitInterpolatedNode(node);
+  @override
   T visitInterpolatedLiteral(InterpolatedLiteral node) =>
       visitInterpolatedNode(node);
+  @override
   T visitInterpolatedParameter(InterpolatedParameter node) =>
       visitInterpolatedNode(node);
+  @override
   T visitInterpolatedSelector(InterpolatedSelector node) =>
       visitInterpolatedNode(node);
+  @override
   T visitInterpolatedStatement(InterpolatedStatement node) =>
       visitInterpolatedNode(node);
+  @override
   T visitInterpolatedDeclaration(InterpolatedDeclaration node) {
     return visitInterpolatedNode(node);
   }
 
+  @override
   T visitAwait(Await node) => visitExpression(node);
+  @override
   T visitDartYield(DartYield node) => visitStatement(node);
 }
 
 class BaseVisitorVoid extends BaseVisitor<void> {
+  @override
   void visitNode(Node node) {
     node.visitChildren(this);
   }
 
   // Ignore comments by default.
+  @override
   void visitComment(Comment node) {}
 }
 
@@ -289,131 +356,198 @@
   const BaseVisitor1();
 
   R visitNode(Node node, A arg);
+  @override
   R visitComment(Comment node, A arg);
 
+  @override
   R visitProgram(Program node, A arg) => visitNode(node, arg);
 
   R visitStatement(Statement node, A arg) => visitNode(node, arg);
   R visitLoop(Loop node, A arg) => visitStatement(node, arg);
   R visitJump(Statement node, A arg) => visitStatement(node, arg);
 
+  @override
   R visitBlock(Block node, A arg) => visitStatement(node, arg);
+  @override
   R visitExpressionStatement(ExpressionStatement node, A arg) =>
       visitStatement(node, arg);
+  @override
   R visitEmptyStatement(EmptyStatement node, A arg) =>
       visitStatement(node, arg);
+  @override
   R visitIf(If node, A arg) => visitStatement(node, arg);
+  @override
   R visitFor(For node, A arg) => visitLoop(node, arg);
+  @override
   R visitForIn(ForIn node, A arg) => visitLoop(node, arg);
+  @override
   R visitWhile(While node, A arg) => visitLoop(node, arg);
+  @override
   R visitDo(Do node, A arg) => visitLoop(node, arg);
+  @override
   R visitContinue(Continue node, A arg) => visitJump(node, arg);
+  @override
   R visitBreak(Break node, A arg) => visitJump(node, arg);
+  @override
   R visitReturn(Return node, A arg) => visitJump(node, arg);
+  @override
   R visitThrow(Throw node, A arg) => visitJump(node, arg);
+  @override
   R visitTry(Try node, A arg) => visitStatement(node, arg);
+  @override
   R visitSwitch(Switch node, A arg) => visitStatement(node, arg);
+  @override
   R visitFunctionDeclaration(FunctionDeclaration node, A arg) =>
       visitStatement(node, arg);
+  @override
   R visitLabeledStatement(LabeledStatement node, A arg) =>
       visitStatement(node, arg);
+  @override
   R visitLiteralStatement(LiteralStatement node, A arg) =>
       visitStatement(node, arg);
 
+  @override
   R visitCatch(Catch node, A arg) => visitNode(node, arg);
+  @override
   R visitCase(Case node, A arg) => visitNode(node, arg);
+  @override
   R visitDefault(Default node, A arg) => visitNode(node, arg);
 
   R visitExpression(Expression node, A arg) => visitNode(node, arg);
   R visitVariableReference(VariableReference node, A arg) =>
       visitExpression(node, arg);
 
+  @override
   R visitLiteralExpression(LiteralExpression node, A arg) =>
       visitExpression(node, arg);
+  @override
   R visitVariableDeclarationList(VariableDeclarationList node, A arg) =>
       visitExpression(node, arg);
+  @override
   R visitAssignment(Assignment node, A arg) => visitExpression(node, arg);
+  @override
   R visitVariableInitialization(VariableInitialization node, A arg) =>
       visitExpression(node, arg);
 
+  @override
   R visitConditional(Conditional node, A arg) => visitExpression(node, arg);
+  @override
   R visitNew(New node, A arg) => visitExpression(node, arg);
+  @override
   R visitCall(Call node, A arg) => visitExpression(node, arg);
+  @override
   R visitBinary(Binary node, A arg) => visitExpression(node, arg);
+  @override
   R visitPrefix(Prefix node, A arg) => visitExpression(node, arg);
+  @override
   R visitPostfix(Postfix node, A arg) => visitExpression(node, arg);
+  @override
   R visitAccess(PropertyAccess node, A arg) => visitExpression(node, arg);
 
+  @override
   R visitVariableUse(VariableUse node, A arg) =>
       visitVariableReference(node, arg);
+  @override
   R visitVariableDeclaration(VariableDeclaration node, A arg) =>
       visitVariableReference(node, arg);
+  @override
   R visitParameter(Parameter node, A arg) =>
       visitVariableDeclaration(node, arg);
+  @override
   R visitThis(This node, A arg) => visitParameter(node, arg);
 
+  @override
   R visitNamedFunction(NamedFunction node, A arg) => visitExpression(node, arg);
+  @override
   R visitFun(Fun node, A arg) => visitExpression(node, arg);
+  @override
   R visitArrowFunction(ArrowFunction node, A arg) => visitExpression(node, arg);
 
   R visitToken(DeferredToken node, A arg) => visitExpression(node, arg);
 
+  @override
   R visitDeferredStatement(DeferredStatement node, A arg) =>
       visitStatement(node, arg);
+  @override
   R visitDeferredExpression(DeferredExpression node, A arg) =>
       visitExpression(node, arg);
+  @override
   R visitDeferredNumber(DeferredNumber node, A arg) => visitToken(node, arg);
+  @override
   R visitDeferredString(DeferredString node, A arg) => visitToken(node, arg);
 
   R visitLiteral(Literal node, A arg) => visitExpression(node, arg);
 
+  @override
   R visitLiteralBool(LiteralBool node, A arg) => visitLiteral(node, arg);
+  @override
   R visitLiteralString(LiteralString node, A arg) => visitLiteral(node, arg);
+  @override
   R visitLiteralNumber(LiteralNumber node, A arg) => visitLiteral(node, arg);
+  @override
   R visitLiteralNull(LiteralNull node, A arg) => visitLiteral(node, arg);
 
+  @override
   R visitStringConcatenation(StringConcatenation node, A arg) =>
       visitLiteral(node, arg);
 
+  @override
   R visitName(Name node, A arg) => visitNode(node, arg);
 
+  @override
   R visitParentheses(Parentheses node, A arg) => visitExpression(node, arg);
 
+  @override
   R visitArrayInitializer(ArrayInitializer node, A arg) =>
       visitExpression(node, arg);
+  @override
   R visitArrayHole(ArrayHole node, A arg) => visitExpression(node, arg);
+  @override
   R visitObjectInitializer(ObjectInitializer node, A arg) =>
       visitExpression(node, arg);
+  @override
   R visitProperty(Property node, A arg) => visitNode(node, arg);
+  @override
   R visitMethodDefinition(MethodDefinition node, A arg) => visitNode(node, arg);
+  @override
   R visitRegExpLiteral(RegExpLiteral node, A arg) => visitExpression(node, arg);
 
   R visitInterpolatedNode(InterpolatedNode node, A arg) => visitNode(node, arg);
 
+  @override
   R visitInterpolatedExpression(InterpolatedExpression node, A arg) =>
       visitInterpolatedNode(node, arg);
+  @override
   R visitInterpolatedLiteral(InterpolatedLiteral node, A arg) =>
       visitInterpolatedNode(node, arg);
+  @override
   R visitInterpolatedParameter(InterpolatedParameter node, A arg) =>
       visitInterpolatedNode(node, arg);
+  @override
   R visitInterpolatedSelector(InterpolatedSelector node, A arg) =>
       visitInterpolatedNode(node, arg);
+  @override
   R visitInterpolatedStatement(InterpolatedStatement node, A arg) =>
       visitInterpolatedNode(node, arg);
+  @override
   R visitInterpolatedDeclaration(InterpolatedDeclaration node, A arg) {
     return visitInterpolatedNode(node, arg);
   }
 
+  @override
   R visitAwait(Await node, A arg) => visitExpression(node, arg);
+  @override
   R visitDartYield(DartYield node, A arg) => visitStatement(node, arg);
 }
 
 class BaseVisitor1Void<A> extends BaseVisitor1<void, A> {
+  @override
   void visitNode(Node node, A arg) {
     node.visitChildren1(this, arg);
   }
 
   // Ignore comments by default.
+  @override
   void visitComment(Comment node, A arg) {}
 }
 
@@ -477,38 +611,48 @@
   final List<Statement> body;
   Program(this.body);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitProgram(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitProgram(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     for (Statement statement in body) statement.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     for (Statement statement in body) statement.accept1(visitor, arg);
   }
 
+  @override
   Program _clone() => Program(body);
 }
 
 abstract class Statement extends Node {
+  @override
   Statement toStatement() => this;
 }
 
 /// Interface for a deferred [Statement] value. An implementation has to provide
 /// a value via the [statement] getter the latest when the ast is printed.
 abstract class DeferredStatement extends Statement {
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitDeferredStatement(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitDeferredStatement(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     statement.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     statement.accept1(visitor, arg);
   }
@@ -523,19 +667,24 @@
 
   Block.empty() : this.statements = [];
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitBlock(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitBlock(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     for (Statement statement in statements) statement.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     for (Statement statement in statements) statement.accept1(visitor, arg);
   }
 
+  @override
   Block _clone() => Block(statements);
 }
 
@@ -546,34 +695,44 @@
     assert(this.expression != null);
   }
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitExpressionStatement(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitExpressionStatement(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     expression.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     expression.accept1(visitor, arg);
   }
 
+  @override
   ExpressionStatement _clone() => ExpressionStatement(expression);
 }
 
 class EmptyStatement extends Statement {
   EmptyStatement();
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitEmptyStatement(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitEmptyStatement(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {}
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {}
 
+  @override
   EmptyStatement _clone() => EmptyStatement();
 }
 
@@ -588,23 +747,28 @@
 
   bool get hasElse => otherwise is! EmptyStatement;
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitIf(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitIf(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     condition.accept(visitor);
     then.accept(visitor);
     otherwise.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     condition.accept1(visitor, arg);
     then.accept1(visitor, arg);
     otherwise.accept1(visitor, arg);
   }
 
+  @override
   If _clone() => If(condition, then, otherwise);
 }
 
@@ -621,11 +785,14 @@
 
   For(this.init, this.condition, this.update, Statement body) : super(body);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitFor(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitFor(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     if (init != null) init.accept(visitor);
     if (condition != null) condition.accept(visitor);
@@ -633,6 +800,7 @@
     body.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     if (init != null) init.accept1(visitor, arg);
     if (condition != null) condition.accept1(visitor, arg);
@@ -640,6 +808,7 @@
     body.accept1(visitor, arg);
   }
 
+  @override
   For _clone() => For(init, condition, update, body);
 }
 
@@ -651,23 +820,28 @@
 
   ForIn(this.leftHandSide, this.object, Statement body) : super(body);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitForIn(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitForIn(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     leftHandSide.accept(visitor);
     object.accept(visitor);
     body.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     leftHandSide.accept1(visitor, arg);
     object.accept1(visitor, arg);
     body.accept1(visitor, arg);
   }
 
+  @override
   ForIn _clone() => ForIn(leftHandSide, object, body);
 }
 
@@ -676,21 +850,26 @@
 
   While(this.condition, Statement body) : super(body);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitWhile(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitWhile(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     condition.accept(visitor);
     body.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     condition.accept1(visitor, arg);
     body.accept1(visitor, arg);
   }
 
+  @override
   While _clone() => While(condition, body);
 }
 
@@ -699,21 +878,26 @@
 
   Do(Statement body, this.condition) : super(body);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitDo(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitDo(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     body.accept(visitor);
     condition.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     body.accept1(visitor, arg);
     condition.accept1(visitor, arg);
   }
 
+  @override
   Do _clone() => Do(body, condition);
 }
 
@@ -722,15 +906,20 @@
 
   Continue(this.targetLabel);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitContinue(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitContinue(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {}
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {}
 
+  @override
   Continue _clone() => Continue(targetLabel);
 }
 
@@ -739,15 +928,20 @@
 
   Break(this.targetLabel);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitBreak(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitBreak(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {}
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {}
 
+  @override
   Break _clone() => Break(targetLabel);
 }
 
@@ -756,19 +950,24 @@
 
   Return([this.value = null]);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitReturn(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitReturn(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     if (value != null) value.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     if (value != null) value.accept1(visitor, arg);
   }
 
+  @override
   Return _clone() => Return(value);
 }
 
@@ -777,19 +976,24 @@
 
   Throw(this.expression);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitThrow(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitThrow(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     expression.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     expression.accept1(visitor, arg);
   }
 
+  @override
   Throw _clone() => Throw(expression);
 }
 
@@ -802,23 +1006,28 @@
     assert(catchPart != null || finallyPart != null);
   }
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitTry(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitTry(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     body.accept(visitor);
     if (catchPart != null) catchPart.accept(visitor);
     if (finallyPart != null) finallyPart.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     body.accept1(visitor, arg);
     if (catchPart != null) catchPart.accept1(visitor, arg);
     if (finallyPart != null) finallyPart.accept1(visitor, arg);
   }
 
+  @override
   Try _clone() => Try(body, catchPart, finallyPart);
 }
 
@@ -828,21 +1037,26 @@
 
   Catch(this.declaration, this.body);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitCatch(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitCatch(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     declaration.accept(visitor);
     body.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     declaration.accept1(visitor, arg);
     body.accept1(visitor, arg);
   }
 
+  @override
   Catch _clone() => Catch(declaration, body);
 }
 
@@ -852,21 +1066,26 @@
 
   Switch(this.key, this.cases);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitSwitch(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitSwitch(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     key.accept(visitor);
     for (SwitchClause clause in cases) clause.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     key.accept1(visitor, arg);
     for (SwitchClause clause in cases) clause.accept1(visitor, arg);
   }
 
+  @override
   Switch _clone() => Switch(key, cases);
 }
 
@@ -881,40 +1100,50 @@
 
   Case(this.expression, Block body) : super(body);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitCase(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitCase(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     expression.accept(visitor);
     body.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     expression.accept1(visitor, arg);
     body.accept1(visitor, arg);
   }
 
+  @override
   Case _clone() => Case(expression, body);
 }
 
 class Default extends SwitchClause {
   Default(Block body) : super(body);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitDefault(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitDefault(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     body.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     body.accept1(visitor, arg);
   }
 
+  @override
   Default _clone() => Default(body);
 }
 
@@ -924,21 +1153,26 @@
 
   FunctionDeclaration(this.name, this.function);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitFunctionDeclaration(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitFunctionDeclaration(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     name.accept(visitor);
     function.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     name.accept1(visitor, arg);
     function.accept1(visitor, arg);
   }
 
+  @override
   FunctionDeclaration _clone() => FunctionDeclaration(name, function);
 }
 
@@ -948,19 +1182,24 @@
 
   LabeledStatement(this.label, this.body);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitLabeledStatement(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitLabeledStatement(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     body.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     body.accept1(visitor, arg);
   }
 
+  @override
   LabeledStatement _clone() => LabeledStatement(label, body);
 }
 
@@ -969,15 +1208,20 @@
 
   LiteralStatement(this.code);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitLiteralStatement(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitLiteralStatement(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {}
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {}
 
+  @override
   LiteralStatement _clone() => LiteralStatement(code);
 }
 
@@ -990,19 +1234,24 @@
 
   DartYield(this.expression, this.hasStar);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitDartYield(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitDartYield(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     expression.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     expression.accept1(visitor, arg);
   }
 
+  @override
   DartYield _clone() => DartYield(expression, hasStar);
 }
 
@@ -1011,6 +1260,7 @@
   // have precedence depending on how the deferred node is resolved.
   int get precedenceLevel;
 
+  @override
   Statement toStatement() => ExpressionStatement(this);
 }
 
@@ -1025,17 +1275,21 @@
 // that is used. How should the printer know if an occurrence of a Name is meant
 // to be a Literal or a Declaration (which includes a VariableUse)?
 abstract class Name extends Literal implements Declaration, Parameter {
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitName(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitName(this, arg);
 
+  @override
   Name _clone();
 
   /// Returns the text of this name.
   ///
   /// May throw if the text has not been decided. Typically the text is decided
   /// in some finalization phase that happens before the AST is printed.
+  @override
   String get name;
 
   /// Returns a unique [key] for this name.
@@ -1044,6 +1298,7 @@
   /// consumption. As such, it might be long or cryptic.
   String get key;
 
+  @override
   bool get allowRename => false;
 }
 
@@ -1060,10 +1315,12 @@
   @override
   String get value => name.name;
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     name.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     name.accept1(visitor, arg);
   }
@@ -1073,19 +1330,25 @@
   final String template;
   LiteralExpression(this.template);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitLiteralExpression(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitLiteralExpression(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {}
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {}
 
+  @override
   LiteralExpression _clone() => LiteralExpression(template);
 
   // Code that uses LiteralExpression must take care of operator precedences,
   // and put parenthesis if needed.
+  @override
   int get precedenceLevel => PRIMARY;
 }
 
@@ -1101,26 +1364,32 @@
 
   VariableDeclarationList(this.declarations, {this.indentSplits = true});
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) =>
       visitor.visitVariableDeclarationList(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitVariableDeclarationList(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     for (VariableInitialization declaration in declarations) {
       declaration.accept(visitor);
     }
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     for (VariableInitialization declaration in declarations) {
       declaration.accept1(visitor, arg);
     }
   }
 
+  @override
   VariableDeclarationList _clone() => VariableDeclarationList(declarations);
 
+  @override
   int get precedenceLevel => EXPRESSION;
 }
 
@@ -1131,21 +1400,27 @@
 
   Parentheses(this.enclosed);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitParentheses(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitParentheses(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     enclosed.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     enclosed.accept1(visitor, arg);
   }
 
+  @override
   Parentheses _clone() => Parentheses(enclosed);
 
+  @override
   int get precedenceLevel => PRIMARY;
 }
 
@@ -1160,25 +1435,31 @@
   Assignment.compound(this.leftHandSide, this.op, this.value)
       : assert(value != null);
 
+  @override
   int get precedenceLevel => ASSIGNMENT;
 
   bool get isCompound => op != null;
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitAssignment(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitAssignment(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     leftHandSide.accept(visitor);
     value.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     leftHandSide.accept1(visitor, arg);
     value.accept1(visitor, arg);
   }
 
+  @override
   Assignment _clone() => Assignment.compound(leftHandSide, op, value);
 }
 
@@ -1190,24 +1471,30 @@
 
   VariableInitialization(this.declaration, this.value);
 
+  @override
   int get precedenceLevel => ASSIGNMENT;
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) =>
       visitor.visitVariableInitialization(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitVariableInitialization(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     declaration.accept(visitor);
     value?.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     declaration.accept1(visitor, arg);
     value?.accept1(visitor, arg);
   }
 
+  @override
   VariableInitialization _clone() => VariableInitialization(declaration, value);
 }
 
@@ -1218,25 +1505,31 @@
 
   Conditional(this.condition, this.then, this.otherwise);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitConditional(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitConditional(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     condition.accept(visitor);
     then.accept(visitor);
     otherwise.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     condition.accept1(visitor, arg);
     then.accept1(visitor, arg);
     otherwise.accept1(visitor, arg);
   }
 
+  @override
   Conditional _clone() => Conditional(condition, then, otherwise);
 
+  @override
   int get precedenceLevel => ASSIGNMENT;
 }
 
@@ -1249,11 +1542,14 @@
     this._sourceInformation = sourceInformation;
   }
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitCall(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitCall(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     target.accept(visitor);
     for (Expression arg in arguments) {
@@ -1261,6 +1557,7 @@
     }
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     target.accept1(visitor, arg);
     for (Expression arg in arguments) {
@@ -1268,19 +1565,24 @@
     }
   }
 
+  @override
   Call _clone() => Call(target, arguments);
 
+  @override
   int get precedenceLevel => CALL;
 }
 
 class New extends Call {
   New(Expression cls, List<Expression> arguments) : super(cls, arguments);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitNew(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitNew(this, arg);
 
+  @override
   New _clone() => New(target, arguments);
 }
 
@@ -1291,25 +1593,32 @@
 
   Binary(this.op, this.left, this.right);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitBinary(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitBinary(this, arg);
 
+  @override
   Binary _clone() => Binary(op, left, right);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     left.accept(visitor);
     right.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     left.accept1(visitor, arg);
     right.accept1(visitor, arg);
   }
 
+  @override
   bool get isCommaOperator => op == ',';
 
+  @override
   int get precedenceLevel {
     // TODO(floitsch): switch to constant map.
     switch (op) {
@@ -1360,21 +1669,27 @@
 
   Prefix(this.op, this.argument);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitPrefix(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitPrefix(this, arg);
 
+  @override
   Prefix _clone() => Prefix(op, argument);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     argument.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     argument.accept1(visitor, arg);
   }
 
+  @override
   int get precedenceLevel => UNARY;
 }
 
@@ -1384,21 +1699,27 @@
 
   Postfix(this.op, this.argument);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitPostfix(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitPostfix(this, arg);
 
+  @override
   Postfix _clone() => Postfix(op, argument);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     argument.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     argument.accept1(visitor, arg);
   }
 
+  @override
   int get precedenceLevel => UNARY;
 }
 
@@ -1411,25 +1732,33 @@
     assert(_identifierRE.hasMatch(name), "Non-identifier name '$name'");
   }
 
+  @override
   T accept<T>(NodeVisitor<T> visitor);
 
+  @override
   int get precedenceLevel => PRIMARY;
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {}
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {}
 }
 
 class VariableUse extends VariableReference {
   VariableUse(String name) : super(name);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitVariableUse(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitVariableUse(this, arg);
 
+  @override
   VariableUse _clone() => VariableUse(name);
 
+  @override
   String toString() => 'VariableUse($name)';
 }
 
@@ -1438,33 +1767,42 @@
 
   VariableDeclaration(String name, {this.allowRename = true}) : super(name);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitVariableDeclaration(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitVariableDeclaration(this, arg);
 
+  @override
   VariableDeclaration _clone() => VariableDeclaration(name);
 }
 
 class Parameter extends VariableDeclaration {
   Parameter(String name) : super(name);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitParameter(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitParameter(this, arg);
 
+  @override
   Parameter _clone() => Parameter(name);
 }
 
 class This extends Parameter {
   This() : super("this");
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitThis(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitThis(this, arg);
 
+  @override
   This _clone() => This();
 }
 
@@ -1474,23 +1812,29 @@
 
   NamedFunction(this.name, this.function);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitNamedFunction(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitNamedFunction(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     name.accept(visitor);
     function.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     name.accept1(visitor, arg);
     function.accept1(visitor, arg);
   }
 
+  @override
   NamedFunction _clone() => NamedFunction(name, function);
 
+  @override
   int get precedenceLevel => LEFT_HAND_SIDE;
 }
 
@@ -1510,23 +1854,29 @@
 
   Fun(this.params, this.body, {this.asyncModifier = AsyncModifier.sync});
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitFun(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitFun(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     for (Parameter param in params) param.accept(visitor);
     body.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     for (Parameter param in params) param.accept1(visitor, arg);
     body.accept1(visitor, arg);
   }
 
+  @override
   Fun _clone() => Fun(params, body, asyncModifier: asyncModifier);
 
+  @override
   int get precedenceLevel => LEFT_HAND_SIDE;
 }
 
@@ -1546,25 +1896,31 @@
       {this.asyncModifier = AsyncModifier.sync,
       this.implicitReturnAllowed = true});
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitArrowFunction(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitArrowFunction(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     for (Parameter param in params) param.accept(visitor);
     body.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     for (Parameter param in params) param.accept1(visitor, arg);
     body.accept1(visitor, arg);
   }
 
+  @override
   ArrowFunction _clone() => ArrowFunction(params, body,
       asyncModifier: asyncModifier,
       implicitReturnAllowed: implicitReturnAllowed);
 
+  @override
   int get precedenceLevel => ASSIGNMENT;
 }
 
@@ -1588,6 +1944,7 @@
 
   static const List<AsyncModifier> values = [sync, async, asyncStar, syncStar];
 
+  @override
   String toString() => description;
 }
 
@@ -1603,23 +1960,29 @@
   PropertyAccess.indexed(this.receiver, int index)
       : selector = LiteralNumber('$index');
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitAccess(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitAccess(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     receiver.accept(visitor);
     selector.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     receiver.accept1(visitor, arg);
     selector.accept1(visitor, arg);
   }
 
+  @override
   PropertyAccess _clone() => PropertyAccess(receiver, selector);
 
+  @override
   int get precedenceLevel => LEFT_HAND_SIDE;
 }
 
@@ -1628,36 +1991,45 @@
 /// [DeferredToken] is not limited to templates but may also occur in
 /// fully instantiated asts.
 abstract class DeferredToken extends Expression {
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {}
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {}
 
+  @override
   DeferredToken _clone() => this;
 }
 
 /// Interface for a deferred integer value. An implementation has to provide
 /// a value via the [value] getter the latest when the ast is printed.
 abstract class DeferredNumber extends DeferredToken implements Literal {
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitDeferredNumber(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitDeferredNumber(this, arg);
 
   int get value;
 
+  @override
   int get precedenceLevel => value.isNegative ? UNARY : PRIMARY;
 }
 
 /// Interface for a deferred string value. An implementation has to provide
 /// a value via the [value] getter the latest when the ast is printed.
 abstract class DeferredString extends DeferredToken implements Literal {
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitDeferredString(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitDeferredString(this, arg);
 
   String get value;
 
+  @override
   int get precedenceLevel => PRIMARY;
 }
 
@@ -1666,8 +2038,10 @@
 /// Also, [precedenceLevel] has to return the same value that
 /// [value.precedenceLevel] returns once [value] is bound to an [Expression].
 abstract class DeferredExpression extends DeferredToken {
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitDeferredExpression(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitDeferredExpression(this, arg);
 
@@ -1675,10 +2049,13 @@
 }
 
 abstract class Literal extends Expression {
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {}
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {}
 
+  @override
   int get precedenceLevel => PRIMARY;
 }
 
@@ -1687,24 +2064,30 @@
 
   LiteralBool(this.value);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitLiteralBool(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitLiteralBool(this, arg);
 
   // [visitChildren] inherited from [Literal].
 
+  @override
   LiteralBool _clone() => LiteralBool(value);
 }
 
 class LiteralNull extends Literal {
   LiteralNull();
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitLiteralNull(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitLiteralNull(this, arg);
 
+  @override
   LiteralNull _clone() => LiteralNull();
 }
 
@@ -1718,11 +2101,14 @@
   /// printer's settings.
   LiteralString(this.value);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitLiteralString(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitLiteralString(this, arg);
 
+  @override
   LiteralString _clone() => LiteralString(value);
 
   @override
@@ -1758,19 +2144,24 @@
   /// concatenated string.
   StringConcatenation(this.parts);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitStringConcatenation(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitStringConcatenation(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     for (Literal part in parts) part.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     for (Literal part in parts) part.accept1(visitor, arg);
   }
 
+  @override
   StringConcatenation _clone() => StringConcatenation(this.parts);
 }
 
@@ -1779,13 +2170,17 @@
 
   LiteralNumber(this.value);
 
+  @override
   int get precedenceLevel => value.startsWith('-') ? UNARY : PRIMARY;
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitLiteralNumber(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitLiteralNumber(this, arg);
 
+  @override
   LiteralNumber _clone() => LiteralNumber(value);
 }
 
@@ -1794,38 +2189,50 @@
 
   ArrayInitializer(this.elements) : assert(!elements.contains(null));
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitArrayInitializer(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitArrayInitializer(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     for (Expression element in elements) element.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     for (Expression element in elements) element.accept1(visitor, arg);
   }
 
+  @override
   ArrayInitializer _clone() => ArrayInitializer(elements);
 
+  @override
   int get precedenceLevel => PRIMARY;
 }
 
 /// An empty place in an [ArrayInitializer].
 /// For example the list [1, , , 2] would contain two holes.
 class ArrayHole extends Expression {
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitArrayHole(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitArrayHole(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {}
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {}
 
+  @override
   ArrayHole _clone() => ArrayHole();
 
+  @override
   int get precedenceLevel => PRIMARY;
 }
 
@@ -1840,22 +2247,28 @@
   /// If false print each property on a seperate line.
   ObjectInitializer(this.properties, {this.isOneLiner = true});
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitObjectInitializer(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitObjectInitializer(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     for (Property init in properties) init.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     for (Property init in properties) init.accept1(visitor, arg);
   }
 
+  @override
   ObjectInitializer _clone() =>
       ObjectInitializer(properties, isOneLiner: isOneLiner);
 
+  @override
   int get precedenceLevel => PRIMARY;
 }
 
@@ -1866,21 +2279,26 @@
   Property(this.name, this.value)
       : assert(name is Literal || name is DeferredExpression);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitProperty(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitProperty(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {
     name.accept(visitor);
     value.accept(visitor);
   }
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {
     name.accept1(visitor, arg);
     value.accept1(visitor, arg);
   }
 
+  @override
   Property _clone() => Property(name, value);
 }
 
@@ -1897,6 +2315,7 @@
   @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitMethodDefinition(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitMethodDefinition(this, arg);
 
@@ -1926,125 +2345,166 @@
 }
 
 class InterpolatedExpression extends Expression with InterpolatedNode {
+  @override
   final nameOrPosition;
 
   InterpolatedExpression(this.nameOrPosition);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) =>
       visitor.visitInterpolatedExpression(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitInterpolatedExpression(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {}
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {}
 
+  @override
   InterpolatedExpression _clone() => InterpolatedExpression(nameOrPosition);
 
+  @override
   int get precedenceLevel => PRIMARY;
 }
 
 class InterpolatedLiteral extends Literal with InterpolatedNode {
+  @override
   final nameOrPosition;
 
   InterpolatedLiteral(this.nameOrPosition);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitInterpolatedLiteral(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitInterpolatedLiteral(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {}
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {}
 
+  @override
   InterpolatedLiteral _clone() => InterpolatedLiteral(nameOrPosition);
 }
 
 class InterpolatedParameter extends Expression
     with InterpolatedNode
     implements Parameter {
+  @override
   final nameOrPosition;
 
   InterpolatedParameter(this.nameOrPosition);
 
+  @override
   String get name {
     throw "InterpolatedParameter.name must not be invoked";
   }
 
+  @override
   bool get allowRename => false;
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) =>
       visitor.visitInterpolatedParameter(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitInterpolatedParameter(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {}
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {}
 
+  @override
   InterpolatedParameter _clone() => InterpolatedParameter(nameOrPosition);
 
+  @override
   int get precedenceLevel => PRIMARY;
 }
 
 class InterpolatedSelector extends Expression with InterpolatedNode {
+  @override
   final nameOrPosition;
 
   InterpolatedSelector(this.nameOrPosition);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) =>
       visitor.visitInterpolatedSelector(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitInterpolatedSelector(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {}
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {}
 
+  @override
   InterpolatedSelector _clone() => InterpolatedSelector(nameOrPosition);
 
+  @override
   int get precedenceLevel => PRIMARY;
 }
 
 class InterpolatedStatement extends Statement with InterpolatedNode {
+  @override
   final nameOrPosition;
 
   InterpolatedStatement(this.nameOrPosition);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) =>
       visitor.visitInterpolatedStatement(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitInterpolatedStatement(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {}
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {}
 
+  @override
   InterpolatedStatement _clone() => InterpolatedStatement(nameOrPosition);
 }
 
 class InterpolatedDeclaration extends Expression
     with InterpolatedNode
     implements Declaration {
+  @override
   final nameOrPosition;
 
   InterpolatedDeclaration(this.nameOrPosition);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) =>
       visitor.visitInterpolatedDeclaration(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitInterpolatedDeclaration(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {}
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {}
 
+  @override
   InterpolatedDeclaration _clone() {
     return InterpolatedDeclaration(nameOrPosition);
   }
@@ -2065,17 +2525,23 @@
 
   RegExpLiteral(this.pattern);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitRegExpLiteral(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitRegExpLiteral(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {}
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {}
 
+  @override
   RegExpLiteral _clone() => RegExpLiteral(pattern);
 
+  @override
   int get precedenceLevel => PRIMARY;
 }
 
@@ -2089,18 +2555,24 @@
 
   Await(this.expression);
 
+  @override
   int get precedenceLevel => UNARY;
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitAwait(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitAwait(this, arg);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) => expression.accept(visitor);
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       expression.accept1(visitor, arg);
 
+  @override
   Await _clone() => Await(expression);
 }
 
@@ -2113,15 +2585,20 @@
 
   Comment(this.comment);
 
+  @override
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitComment(this);
 
+  @override
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
       visitor.visitComment(this, arg);
 
+  @override
   Comment _clone() => Comment(comment);
 
+  @override
   void visitChildren<T>(NodeVisitor<T> visitor) {}
 
+  @override
   void visitChildren1<R, A>(NodeVisitor1<R, A> visitor, A arg) {}
 }
 
diff --git a/pkg/js_ast/lib/src/printer.dart b/pkg/js_ast/lib/src/printer.dart
index 562107f..6234daf 100644
--- a/pkg/js_ast/lib/src/printer.dart
+++ b/pkg/js_ast/lib/src/printer.dart
@@ -55,6 +55,7 @@
 class SimpleJavaScriptPrintingContext extends JavaScriptPrintingContext {
   final StringBuffer buffer = StringBuffer();
 
+  @override
   void emit(String string) {
     buffer.write(string);
   }
@@ -63,6 +64,7 @@
 }
 
 class _DebugJavaScriptPrintingContext extends SimpleJavaScriptPrintingContext {
+  @override
   bool get isDebugContext => true;
 }
 
@@ -1589,31 +1591,38 @@
     }
   }
 
+  @override
   void visitFunctionDeclaration(FunctionDeclaration declaration) {
     // Note that we don't bother collecting the name of the function.
     collectVarsInFunction(declaration.function);
   }
 
+  @override
   void visitNamedFunction(NamedFunction namedFunction) {
     // Note that we don't bother collecting the name of the function.
     collectVarsInFunction(namedFunction.function);
   }
 
+  @override
   void visitMethodDefinition(MethodDefinition method) {
     // Note that we don't bother collecting the name of the function.
     collectVarsInFunction(method.function);
   }
 
+  @override
   void visitFun(Fun fun) {
     collectVarsInFunction(fun);
   }
 
+  @override
   void visitArrowFunction(ArrowFunction fun) {
     collectVarsInFunction(fun);
   }
 
+  @override
   void visitThis(This node) {}
 
+  @override
   void visitComment(Comment node) {
     if (node.comment.contains(disableVariableMinificationPattern)) {
       enableRenaming = false;
@@ -1622,6 +1631,7 @@
     }
   }
 
+  @override
   void visitVariableDeclaration(VariableDeclaration decl) {
     if (enableRenaming && decl.allowRename) vars.add(decl.name);
   }
@@ -1634,35 +1644,52 @@
 
   DanglingElseVisitor(this.context);
 
+  @override
   bool visitProgram(Program node) => false;
 
+  @override
   bool visitNode(Node node) {
     context.error("Forgot node: $node");
     return true;
   }
 
+  @override
   bool visitComment(Comment node) => true;
 
+  @override
   bool visitBlock(Block node) => false;
+  @override
   bool visitExpressionStatement(ExpressionStatement node) => false;
+  @override
   bool visitEmptyStatement(EmptyStatement node) => false;
+  @override
   bool visitDeferredStatement(DeferredStatement node) {
     return node.statement.accept(this);
   }
 
+  @override
   bool visitIf(If node) {
     if (!node.hasElse) return true;
     return node.otherwise.accept(this);
   }
 
+  @override
   bool visitFor(For node) => node.body.accept(this);
+  @override
   bool visitForIn(ForIn node) => node.body.accept(this);
+  @override
   bool visitWhile(While node) => node.body.accept(this);
+  @override
   bool visitDo(Do node) => false;
+  @override
   bool visitContinue(Continue node) => false;
+  @override
   bool visitBreak(Break node) => false;
+  @override
   bool visitReturn(Return node) => false;
+  @override
   bool visitThrow(Throw node) => false;
+  @override
   bool visitTry(Try node) {
     if (node.finallyPart != null) {
       return node.finallyPart.accept(this);
@@ -1671,16 +1698,25 @@
     }
   }
 
+  @override
   bool visitCatch(Catch node) => node.body.accept(this);
+  @override
   bool visitSwitch(Switch node) => false;
+  @override
   bool visitCase(Case node) => false;
+  @override
   bool visitDefault(Default node) => false;
+  @override
   bool visitFunctionDeclaration(FunctionDeclaration node) => false;
+  @override
   bool visitLabeledStatement(LabeledStatement node) => node.body.accept(this);
+  @override
   bool visitLiteralStatement(LiteralStatement node) => true;
 
+  @override
   bool visitDartYield(DartYield node) => false;
 
+  @override
   bool visitExpression(Expression node) => false;
 }
 
@@ -1693,10 +1729,15 @@
 }
 
 class IdentityNamer implements LocalNamer {
+  @override
   String getName(String oldName) => oldName;
+  @override
   String declareVariable(String oldName) => oldName;
+  @override
   String declareParameter(String oldName) => oldName;
+  @override
   void enterScope(VarCollector vars) {}
+  @override
   void leaveScope() {}
 }
 
@@ -1709,6 +1750,7 @@
 
   MinifyRenamer();
 
+  @override
   void enterScope(VarCollector vars) {
     maps.add({});
     variableNumberStack.add(variableNumber);
@@ -1717,12 +1759,14 @@
     vars.forEachParam(declareParameter);
   }
 
+  @override
   void leaveScope() {
     maps.removeLast();
     variableNumber = variableNumberStack.removeLast();
     parameterNumber = parameterNumberStack.removeLast();
   }
 
+  @override
   String getName(String oldName) {
     // Go from inner scope to outer looking for mapping of name.
     for (int i = maps.length - 1; i >= 0; i--) {
@@ -1750,6 +1794,7 @@
   // that we give up trying to be nice to the compression algorithm and just
   // use the same namespace for arguments and variables, starting with A, and
   // moving on to a0, a1, etc.
+  @override
   String declareVariable(String oldName) {
     if (avoidRenaming(oldName)) return oldName;
     var newName;
@@ -1764,6 +1809,7 @@
     return newName;
   }
 
+  @override
   String declareParameter(String oldName) {
     if (avoidRenaming(oldName)) return oldName;
     var newName;
diff --git a/pkg/js_ast/lib/src/template.dart b/pkg/js_ast/lib/src/template.dart
index bdcf3c0..de78364 100644
--- a/pkg/js_ast/lib/src/template.dart
+++ b/pkg/js_ast/lib/src/template.dart
@@ -185,6 +185,7 @@
     return VariableDeclaration(value);
   }
 
+  @override
   Instantiator visitInterpolatedExpression(InterpolatedExpression node) {
     var nameOrPosition = node.nameOrPosition;
     return (arguments) {
@@ -196,6 +197,7 @@
     };
   }
 
+  @override
   Instantiator visitInterpolatedDeclaration(InterpolatedDeclaration node) {
     var nameOrPosition = node.nameOrPosition;
     return (arguments) {
@@ -226,6 +228,7 @@
     return visit(node);
   }
 
+  @override
   Instantiator visitInterpolatedLiteral(InterpolatedLiteral node) {
     var nameOrPosition = node.nameOrPosition;
     return (arguments) {
@@ -235,6 +238,7 @@
     };
   }
 
+  @override
   Instantiator visitInterpolatedParameter(InterpolatedParameter node) {
     var nameOrPosition = node.nameOrPosition;
     return (arguments) {
@@ -252,6 +256,7 @@
     };
   }
 
+  @override
   Instantiator visitInterpolatedSelector(InterpolatedSelector node) {
     // A selector is an expression, as in `a[selector]`.
     // A String argument converted into a LiteralString, so `a.#` with argument
@@ -266,6 +271,7 @@
     };
   }
 
+  @override
   Instantiator visitInterpolatedStatement(InterpolatedStatement node) {
     var nameOrPosition = node.nameOrPosition;
     return (arguments) {
@@ -295,6 +301,7 @@
     return visit(node);
   }
 
+  @override
   Instantiator visitProgram(Program node) {
     List<Instantiator> instantiators =
         node.body.map(visitSplayableStatement).toList();
@@ -316,6 +323,7 @@
     };
   }
 
+  @override
   Instantiator visitBlock(Block node) {
     List<Instantiator> instantiators =
         node.statements.map(visitSplayableStatement).toList();
@@ -339,6 +347,7 @@
     };
   }
 
+  @override
   Instantiator visitExpressionStatement(ExpressionStatement node) {
     Instantiator buildExpression = visit(node.expression);
     return (arguments) {
@@ -346,9 +355,11 @@
     };
   }
 
+  @override
   Instantiator visitEmptyStatement(EmptyStatement node) =>
       (arguments) => EmptyStatement();
 
+  @override
   Instantiator visitIf(If node) {
     if (node.condition is InterpolatedExpression) {
       return visitIfConditionalCompilation(node);
@@ -397,6 +408,7 @@
     };
   }
 
+  @override
   Instantiator visitFor(For node) {
     Instantiator makeInit = visitNullable(node.init);
     Instantiator makeCondition = visitNullable(node.condition);
@@ -408,6 +420,7 @@
     };
   }
 
+  @override
   Instantiator visitForIn(ForIn node) {
     Instantiator makeLeftHandSide = visit(node.leftHandSide);
     Instantiator makeObject = visit(node.object);
@@ -422,6 +435,7 @@
     throw UnimplementedError('$this.$name');
   }
 
+  @override
   Instantiator visitWhile(While node) {
     Instantiator makeCondition = visit(node.condition);
     Instantiator makeBody = visit(node.body);
@@ -430,6 +444,7 @@
     };
   }
 
+  @override
   Instantiator visitDo(Do node) {
     Instantiator makeBody = visit(node.body);
     Instantiator makeCondition = visit(node.condition);
@@ -438,26 +453,32 @@
     };
   }
 
+  @override
   Instantiator visitContinue(Continue node) =>
       (arguments) => Continue(node.targetLabel);
 
+  @override
   Instantiator visitBreak(Break node) => (arguments) => Break(node.targetLabel);
 
+  @override
   Instantiator visitReturn(Return node) {
     Instantiator makeExpression = visitNullable(node.value);
     return (arguments) => Return(makeExpression(arguments));
   }
 
+  @override
   Instantiator visitDartYield(DartYield node) {
     Instantiator makeExpression = visit(node.expression);
     return (arguments) => DartYield(makeExpression(arguments), node.hasStar);
   }
 
+  @override
   Instantiator visitThrow(Throw node) {
     Instantiator makeExpression = visit(node.expression);
     return (arguments) => Throw(makeExpression(arguments));
   }
 
+  @override
   Instantiator visitTry(Try node) {
     Instantiator makeBody = visit(node.body);
     Instantiator makeCatch = visitNullable(node.catchPart);
@@ -466,6 +487,7 @@
         Try(makeBody(arguments), makeCatch(arguments), makeFinally(arguments));
   }
 
+  @override
   Instantiator visitCatch(Catch node) {
     Instantiator makeDeclaration = visit(node.declaration);
     Instantiator makeBody = visit(node.body);
@@ -473,6 +495,7 @@
         Catch(makeDeclaration(arguments), makeBody(arguments));
   }
 
+  @override
   Instantiator visitSwitch(Switch node) {
     Instantiator makeKey = visit(node.key);
     Iterable<Instantiator> makeCases = node.cases.map(visit);
@@ -485,6 +508,7 @@
     };
   }
 
+  @override
   Instantiator visitCase(Case node) {
     Instantiator makeExpression = visit(node.expression);
     Instantiator makeBody = visit(node.body);
@@ -493,6 +517,7 @@
     };
   }
 
+  @override
   Instantiator visitDefault(Default node) {
     Instantiator makeBody = visit(node.body);
     return (arguments) {
@@ -500,6 +525,7 @@
     };
   }
 
+  @override
   Instantiator visitFunctionDeclaration(FunctionDeclaration node) {
     Instantiator makeName = visit(node.name);
     Instantiator makeFunction = visit(node.function);
@@ -507,16 +533,20 @@
         FunctionDeclaration(makeName(arguments), makeFunction(arguments));
   }
 
+  @override
   Instantiator visitLabeledStatement(LabeledStatement node) {
     Instantiator makeBody = visit(node.body);
     return (arguments) => LabeledStatement(node.label, makeBody(arguments));
   }
 
+  @override
   Instantiator visitLiteralStatement(LiteralStatement node) =>
       TODO('visitLiteralStatement');
+  @override
   Instantiator visitLiteralExpression(LiteralExpression node) =>
       TODO('visitLiteralExpression');
 
+  @override
   Instantiator visitVariableDeclarationList(VariableDeclarationList node) {
     List<Instantiator> declarationMakers =
         node.declarations.map(visit).toList();
@@ -530,6 +560,7 @@
     };
   }
 
+  @override
   Instantiator visitAssignment(Assignment node) {
     Instantiator makeLeftHandSide = visit(node.leftHandSide);
     String op = node.op;
@@ -540,6 +571,7 @@
     };
   }
 
+  @override
   Instantiator visitVariableInitialization(VariableInitialization node) {
     Instantiator makeDeclaration = visit(node.declaration);
     Instantiator makeValue = visitNullable(node.value);
@@ -549,6 +581,7 @@
     };
   }
 
+  @override
   Instantiator visitConditional(Conditional cond) {
     Instantiator makeCondition = visit(cond.condition);
     Instantiator makeThen = visit(cond.then);
@@ -557,9 +590,11 @@
         makeThen(arguments), makeOtherwise(arguments));
   }
 
+  @override
   Instantiator visitNew(New node) =>
       handleCallOrNew(node, (target, arguments) => New(target, arguments));
 
+  @override
   Instantiator visitCall(Call node) =>
       handleCallOrNew(node, (target, arguments) => Call(target, arguments));
 
@@ -585,6 +620,7 @@
     };
   }
 
+  @override
   Instantiator visitBinary(Binary node) {
     Instantiator makeLeft = visit(node.left);
     Instantiator makeRight = visit(node.right);
@@ -592,29 +628,36 @@
     return (arguments) => Binary(op, makeLeft(arguments), makeRight(arguments));
   }
 
+  @override
   Instantiator visitPrefix(Prefix node) {
     Instantiator makeOperand = visit(node.argument);
     String op = node.op;
     return (arguments) => Prefix(op, makeOperand(arguments));
   }
 
+  @override
   Instantiator visitPostfix(Postfix node) {
     Instantiator makeOperand = visit(node.argument);
     String op = node.op;
     return (arguments) => Postfix(op, makeOperand(arguments));
   }
 
+  @override
   Instantiator visitVariableUse(VariableUse node) =>
       (arguments) => VariableUse(node.name);
 
+  @override
   Instantiator visitThis(This node) => (arguments) => This();
 
+  @override
   Instantiator visitVariableDeclaration(VariableDeclaration node) =>
       (arguments) => VariableDeclaration(node.name);
 
+  @override
   Instantiator visitParameter(Parameter node) =>
       (arguments) => Parameter(node.name);
 
+  @override
   Instantiator visitAccess(PropertyAccess node) {
     Instantiator makeReceiver = visit(node.receiver);
     Instantiator makeSelector = visit(node.selector);
@@ -622,6 +665,7 @@
         PropertyAccess(makeReceiver(arguments), makeSelector(arguments));
   }
 
+  @override
   Instantiator visitNamedFunction(NamedFunction node) {
     Instantiator makeDeclaration = visit(node.name);
     Instantiator makeFunction = visit(node.function);
@@ -629,6 +673,7 @@
         NamedFunction(makeDeclaration(arguments), makeFunction(arguments));
   }
 
+  @override
   Instantiator visitFun(Fun node) {
     List<Instantiator> paramMakers = node.params.map(visitSplayable).toList();
     Instantiator makeBody = visit(node.body);
@@ -648,6 +693,7 @@
     };
   }
 
+  @override
   Instantiator visitArrowFunction(ArrowFunction node) {
     List<Instantiator> paramMakers = node.params.map(visitSplayable).toList();
     Instantiator makeBody = visit(node.body);
@@ -668,26 +714,35 @@
     };
   }
 
+  @override
   Instantiator visitDeferredExpression(DeferredExpression node) => same(node);
 
+  @override
   Instantiator visitDeferredStatement(DeferredStatement node) => same(node);
 
+  @override
   Instantiator visitDeferredNumber(DeferredNumber node) => same(node);
 
+  @override
   Instantiator visitDeferredString(DeferredString node) => (arguments) => node;
 
+  @override
   Instantiator visitLiteralBool(LiteralBool node) =>
       (arguments) => LiteralBool(node.value);
 
+  @override
   Instantiator visitLiteralString(LiteralString node) =>
       (arguments) => LiteralString(node.value);
 
+  @override
   Instantiator visitLiteralNumber(LiteralNumber node) =>
       (arguments) => LiteralNumber(node.value);
 
+  @override
   Instantiator visitLiteralNull(LiteralNull node) =>
       (arguments) => LiteralNull();
 
+  @override
   Instantiator visitStringConcatenation(StringConcatenation node) {
     List<Instantiator> partMakers =
         node.parts.map(visit).toList(growable: false);
@@ -699,8 +754,10 @@
     };
   }
 
+  @override
   Instantiator visitName(Name node) => same(node);
 
+  @override
   Instantiator visitParentheses(Parentheses node) {
     Instantiator makeEnclosed = visit(node.enclosed);
     return (arguments) {
@@ -709,6 +766,7 @@
     };
   }
 
+  @override
   Instantiator visitArrayInitializer(ArrayInitializer node) {
     // TODO(sra): Implement splicing?
     List<Instantiator> elementMakers =
@@ -722,10 +780,12 @@
     };
   }
 
+  @override
   Instantiator visitArrayHole(ArrayHole node) {
     return (arguments) => ArrayHole();
   }
 
+  @override
   Instantiator visitObjectInitializer(ObjectInitializer node) {
     List<Instantiator> propertyMakers =
         node.properties.map(visitSplayable).toList();
@@ -744,6 +804,7 @@
     };
   }
 
+  @override
   Instantiator visitProperty(Property node) {
     Instantiator makeName = visit(node.name);
     Instantiator makeValue = visit(node.value);
@@ -752,6 +813,7 @@
     };
   }
 
+  @override
   Instantiator visitMethodDefinition(MethodDefinition node) {
     Instantiator makeName = visit(node.name);
     Instantiator makeFunction = visit(node.function);
@@ -760,11 +822,14 @@
     };
   }
 
+  @override
   Instantiator visitRegExpLiteral(RegExpLiteral node) =>
       (arguments) => RegExpLiteral(node.pattern);
 
+  @override
   Instantiator visitComment(Comment node) => TODO('visitComment');
 
+  @override
   Instantiator visitAwait(Await node) {
     Instantiator makeExpression = visit(node.expression);
     return (arguments) {
@@ -789,12 +854,14 @@
     node.accept(this);
   }
 
+  @override
   void visitNode(Node node) {
     int before = count;
     node.visitChildren(this);
     if (count != before) containsInterpolatedNode.add(node);
   }
 
+  @override
   visitInterpolatedNode(InterpolatedNode node) {
     containsInterpolatedNode.add(node);
     if (node.isNamed) holeNames.add(node.nameOrPosition);
diff --git a/pkg/js_ast/test/deferred_expression_test.dart b/pkg/js_ast/test/deferred_expression_test.dart
index 29fc4d1..ecdb774 100644
--- a/pkg/js_ast/test/deferred_expression_test.dart
+++ b/pkg/js_ast/test/deferred_expression_test.dart
@@ -112,6 +112,7 @@
 }
 
 class _DeferredExpression extends DeferredExpression {
+  @override
   final Expression value;
 
   _DeferredExpression(this.value);
@@ -162,11 +163,13 @@
 
   _Position(this.startPosition, this.endPosition, this.closingPosition);
 
+  @override
   int get hashCode =>
       13 * startPosition.hashCode +
       17 * endPosition.hashCode +
       19 * closingPosition.hashCode;
 
+  @override
   bool operator ==(Object other) {
     if (identical(this, other)) return true;
     return other is _Position &&
@@ -175,6 +178,7 @@
         closingPosition == other.closingPosition;
   }
 
+  @override
   String toString() {
     return '_Position(start=$startPosition,'
         'end=$endPosition,closing=$closingPosition)';
diff --git a/pkg/js_ast/test/deferred_statement_test.dart b/pkg/js_ast/test/deferred_statement_test.dart
index 9628cdf..0d309ae 100644
--- a/pkg/js_ast/test/deferred_statement_test.dart
+++ b/pkg/js_ast/test/deferred_statement_test.dart
@@ -6,6 +6,7 @@
 import 'package:js_ast/js_ast.dart';
 
 class _DeferredStatement extends DeferredStatement {
+  @override
   final Statement statement;
 
   _DeferredStatement(this.statement);
diff --git a/pkg/js_ast/test/printer_callback_test.dart b/pkg/js_ast/test/printer_callback_test.dart
index a54ddbc..f5477ba 100644
--- a/pkg/js_ast/test/printer_callback_test.dart
+++ b/pkg/js_ast/test/printer_callback_test.dart
@@ -166,7 +166,9 @@
 ];
 
 class FixedName extends Name {
+  @override
   final String name;
+  @override
   String get key => name;
 
   FixedName(this.name);
@@ -207,6 +209,7 @@
 
   String tag(int value) => '@$value';
 
+  @override
   void enterNode(Node node, int startPosition) {
     int value = id(node);
     if (mode == TestMode.ENTER) {
@@ -214,6 +217,7 @@
     }
   }
 
+  @override
   void exitNode(
       Node node, int startPosition, int endPosition, int delimiterPosition) {
     int value = id(node);
@@ -224,6 +228,7 @@
     }
   }
 
+  @override
   String getText() {
     String text = super.getText();
     int offset = 0;
diff --git a/runtime/vm/app_snapshot.cc b/runtime/vm/app_snapshot.cc
index f694613b..e2b8a40 100644
--- a/runtime/vm/app_snapshot.cc
+++ b/runtime/vm/app_snapshot.cc
@@ -5140,7 +5140,7 @@
       Deserializer::InitializeHeader(property, kWeakPropertyCid,
                                      WeakProperty::InstanceSize());
       ReadFromTo(property);
-      property->untag()->next_ = WeakProperty::null();
+      property->untag()->next_seen_by_gc_ = WeakProperty::null();
     }
   }
 };
diff --git a/runtime/vm/compiler/assembler/assembler_arm.cc b/runtime/vm/compiler/assembler/assembler_arm.cc
index cab9a8e..e7778e6 100644
--- a/runtime/vm/compiler/assembler/assembler_arm.cc
+++ b/runtime/vm/compiler/assembler/assembler_arm.cc
@@ -3280,6 +3280,12 @@
 #undef __
 #define __ assembler_->
 
+#if defined(VFPv3_D32)
+static const RegisterSet kVolatileFpuRegisters(0, 0xFF0F);  // Q0-Q3, Q8-Q15
+#else
+static const RegisterSet kVolatileFpuRegisters(0, 0x000F);  // Q0-Q3
+#endif
+
 LeafRuntimeScope::LeafRuntimeScope(Assembler* assembler,
                                    intptr_t frame_size,
                                    bool preserve_registers)
@@ -3291,16 +3297,7 @@
         kDartVolatileCpuRegs | (1 << PP) | (1 << FP) | (1 << LR), 0));
     COMPILE_ASSERT((kDartVolatileCpuRegs & (1 << PP)) == 0);
 
-    // Preserve all volatile FPU registers.
-    DRegister firstv = EvenDRegisterOf(kDartFirstVolatileFpuReg);
-    DRegister lastv = OddDRegisterOf(kDartLastVolatileFpuReg);
-    if ((lastv - firstv + 1) >= 16) {
-      DRegister mid = static_cast<DRegister>(firstv + 16);
-      __ vstmd(DB_W, SP, mid, lastv - mid + 1);
-      __ vstmd(DB_W, SP, firstv, 16);
-    } else {
-      __ vstmd(DB_W, SP, firstv, lastv - firstv + 1);
-    }
+    __ PushRegisters(kVolatileFpuRegisters);
   } else {
     SPILLS_LR_TO_FRAME(__ EnterFrame((1 << FP) | (1 << LR), 0));
     // These registers must always be preserved.
@@ -3330,7 +3327,7 @@
     // and ensure proper alignment of the stack frame.
     // We need to restore it before restoring registers.
     const intptr_t kPushedFpuRegisterSize =
-        kDartVolatileFpuRegCount * kFpuRegisterSize;
+        kVolatileFpuRegisters.FpuRegisterCount() * kFpuRegisterSize;
 
     COMPILE_ASSERT(PP < FP);
     COMPILE_ASSERT((kDartVolatileCpuRegs & (1 << PP)) == 0);
@@ -3340,16 +3337,7 @@
         kDartVolatileCpuRegCount * target::kWordSize + kPushedFpuRegisterSize;
     __ AddImmediate(SP, FP, -kPushedRegistersSize);
 
-    // Restore all volatile FPU registers.
-    DRegister firstv = EvenDRegisterOf(kDartFirstVolatileFpuReg);
-    DRegister lastv = OddDRegisterOf(kDartLastVolatileFpuReg);
-    if ((lastv - firstv + 1) >= 16) {
-      DRegister mid = static_cast<DRegister>(firstv + 16);
-      __ vldmd(IA_W, SP, firstv, 16);
-      __ vldmd(IA_W, SP, mid, lastv - mid + 1);
-    } else {
-      __ vldmd(IA_W, SP, firstv, lastv - firstv + 1);
-    }
+    __ PopRegisters(kVolatileFpuRegisters);
 
     // Restore volatile CPU registers.
     RESTORES_LR_FROM_FRAME(__ LeaveFrame(kDartVolatileCpuRegs | (1 << PP) |
diff --git a/runtime/vm/constants_arm.h b/runtime/vm/constants_arm.h
index a8d33d5..7b43812 100644
--- a/runtime/vm/constants_arm.h
+++ b/runtime/vm/constants_arm.h
@@ -571,9 +571,6 @@
 #else
 const int kDartVolatileCpuRegCount = 5;
 #endif
-const QRegister kDartFirstVolatileFpuReg = Q0;
-const QRegister kDartLastVolatileFpuReg = Q3;
-const int kDartVolatileFpuRegCount = 4;
 
 #define R(reg) (static_cast<RegList>(1) << (reg))
 
diff --git a/runtime/vm/heap/gc_shared.cc b/runtime/vm/heap/gc_shared.cc
new file mode 100644
index 0000000..65f56c3
--- /dev/null
+++ b/runtime/vm/heap/gc_shared.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 2022, 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.
+
+// Logic shared between the Scavenger and Marker.
+
+#include "vm/heap/gc_shared.h"
+
+#include "vm/dart_api_state.h"
+#include "vm/heap/scavenger.h"
+#include "vm/log.h"
+#include "vm/message_handler.h"
+#include "vm/object.h"
+
+namespace dart {
+
+void GCLinkedLists::Release() {
+#define FOREACH(type, var) var.Release();
+  GC_LINKED_LIST(FOREACH)
+#undef FOREACH
+}
+
+bool GCLinkedLists::IsEmpty() {
+#define FOREACH(type, var)                                                     \
+  if (!var.IsEmpty()) {                                                        \
+    return false;                                                              \
+  }
+  GC_LINKED_LIST(FOREACH)
+  return true;
+#undef FOREACH
+}
+
+void GCLinkedLists::FlushInto(GCLinkedLists* to) {
+#define FOREACH(type, var) var.FlushInto(&to->var);
+  GC_LINKED_LIST(FOREACH)
+#undef FOREACH
+}
+
+}  // namespace dart
diff --git a/runtime/vm/heap/gc_shared.h b/runtime/vm/heap/gc_shared.h
new file mode 100644
index 0000000..3b42241
--- /dev/null
+++ b/runtime/vm/heap/gc_shared.h
@@ -0,0 +1,86 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for detail_s. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Logic shared between the Scavenger and Marker.
+
+#ifndef RUNTIME_VM_HEAP_GC_SHARED_H_
+#define RUNTIME_VM_HEAP_GC_SHARED_H_
+
+#include "vm/compiler/runtime_api.h"
+#if defined(SHOULD_NOT_INCLUDE_RUNTIME)
+#error "Should not include runtime"
+#endif
+
+#include "vm/dart_api_state.h"
+#include "vm/heap/scavenger.h"
+#include "vm/log.h"
+#include "vm/message_handler.h"
+#include "vm/object.h"
+
+namespace dart {
+
+// These object types have a linked list chaining all pending objects when
+// processing these in the GC.
+// The field should not be visited by pointer visitors.
+// The field should only be set during a GC.
+//
+// Macro params:
+// - type
+// - variable name
+#define GC_LINKED_LIST(V)                                                      \
+  V(WeakProperty, weak_properties)                                             \
+  V(WeakReference, weak_references)
+
+template <typename Type, typename PtrType>
+class GCLinkedList {
+ public:
+  void Enqueue(PtrType ptr) {
+    ptr->untag()->next_seen_by_gc_ = head_;
+    if (head_ == Type::null()) {
+      tail_ = ptr;
+    }
+    head_ = ptr;
+  }
+
+  void FlushInto(GCLinkedList<Type, PtrType>* to) {
+    if (to->head_ == Type::null()) {
+      ASSERT(to->tail_ == Type::null());
+      to->head_ = head_;
+      to->tail_ = tail_;
+    } else {
+      ASSERT(to->tail_ != Type::null());
+      ASSERT(to->tail_->untag()->next_seen_by_gc() == Type::null());
+      to->tail_->untag()->next_seen_by_gc_ = head_;
+      to->tail_ = tail_;
+    }
+    Release();
+  }
+
+  PtrType Release() {
+    PtrType return_value = head_;
+    head_ = Type::null();
+    tail_ = Type::null();
+    return return_value;
+  }
+
+  bool IsEmpty() { return head_ == Type::null() && tail_ == Type::null(); }
+
+ private:
+  PtrType head_ = Type::null();
+  PtrType tail_ = Type::null();
+};
+
+struct GCLinkedLists {
+  void Release();
+  bool IsEmpty();
+  void FlushInto(GCLinkedLists* to);
+
+#define FOREACH(type, var) GCLinkedList<type, type##Ptr> var;
+  GC_LINKED_LIST(FOREACH)
+#undef FOREACH
+};
+
+}  // namespace dart
+
+#endif  // RUNTIME_VM_HEAP_GC_SHARED_H_
diff --git a/runtime/vm/heap/heap_sources.gni b/runtime/vm/heap/heap_sources.gni
index de6d25e..a8e8d99 100644
--- a/runtime/vm/heap/heap_sources.gni
+++ b/runtime/vm/heap/heap_sources.gni
@@ -11,6 +11,8 @@
   "compactor.h",
   "freelist.cc",
   "freelist.h",
+  "gc_shared.cc",
+  "gc_shared.h",
   "heap.cc",
   "heap.h",
   "marker.cc",
diff --git a/runtime/vm/heap/marker.cc b/runtime/vm/heap/marker.cc
index 3309815..a797c39 100644
--- a/runtime/vm/heap/marker.cc
+++ b/runtime/vm/heap/marker.cc
@@ -4,9 +4,11 @@
 
 #include "vm/heap/marker.h"
 
+#include "platform/assert.h"
 #include "platform/atomic.h"
 #include "vm/allocation.h"
 #include "vm/dart_api_state.h"
+#include "vm/heap/gc_shared.h"
 #include "vm/heap/pages.h"
 #include "vm/heap/pointer_block.h"
 #include "vm/isolate.h"
@@ -14,6 +16,7 @@
 #include "vm/object_id_ring.h"
 #include "vm/raw_object.h"
 #include "vm/stack_frame.h"
+#include "vm/tagged_pointer.h"
 #include "vm/thread_barrier.h"
 #include "vm/thread_pool.h"
 #include "vm/thread_registry.h"
@@ -34,34 +37,31 @@
         page_space_(page_space),
         work_list_(marking_stack),
         deferred_work_list_(deferred_marking_stack),
-        delayed_weak_properties_(WeakProperty::null()),
-        delayed_weak_properties_tail_(WeakProperty::null()),
-        delayed_weak_references_(WeakReference::null()),
-        delayed_weak_references_tail_(WeakReference::null()),
         marked_bytes_(0),
         marked_micros_(0) {
     ASSERT(thread_->isolate_group() == isolate_group);
   }
-  ~MarkingVisitorBase() {
-    ASSERT(delayed_weak_properties_ == WeakProperty::null());
-    ASSERT(delayed_weak_references_ == WeakReference::null());
-  }
+  ~MarkingVisitorBase() { ASSERT(delayed_.IsEmpty()); }
 
   uintptr_t marked_bytes() const { return marked_bytes_; }
   int64_t marked_micros() const { return marked_micros_; }
   void AddMicros(int64_t micros) { marked_micros_ += micros; }
 
+  static bool IsMarked(ObjectPtr raw) {
+    ASSERT(raw->IsHeapObject());
+    ASSERT(raw->IsOldObject());
+    return raw->untag()->IsMarked();
+  }
+
   bool ProcessPendingWeakProperties() {
     bool more_to_mark = false;
-    WeakPropertyPtr cur_weak = delayed_weak_properties_;
-    delayed_weak_properties_tail_ = delayed_weak_properties_ =
-        WeakProperty::null();
+    WeakPropertyPtr cur_weak = delayed_.weak_properties.Release();
     while (cur_weak != WeakProperty::null()) {
       WeakPropertyPtr next_weak =
-          cur_weak->untag()->next_.Decompress(cur_weak->heap_base());
+          cur_weak->untag()->next_seen_by_gc_.Decompress(cur_weak->heap_base());
       ObjectPtr raw_key = cur_weak->untag()->key();
       // Reset the next pointer in the weak property.
-      cur_weak->untag()->next_ = WeakProperty::null();
+      cur_weak->untag()->next_seen_by_gc_ = WeakProperty::null();
       if (raw_key->IsSmiOrNewObject() || raw_key->untag()->IsMarked()) {
         ObjectPtr raw_val = cur_weak->untag()->value();
         if (!raw_val->IsSmiOrNewObject() && !raw_val->untag()->IsMarked()) {
@@ -73,7 +73,8 @@
         cur_weak->untag()->VisitPointersNonvirtual(this);
       } else {
         // Requeue this weak property to be handled later.
-        EnqueueWeakProperty(cur_weak);
+        ASSERT(IsMarked(cur_weak));
+        delayed_.weak_properties.Enqueue(cur_weak);
       }
       // Advance to next weak property in the queue.
       cur_weak = next_weak;
@@ -182,34 +183,6 @@
     }
   }
 
-  void EnqueueWeakProperty(WeakPropertyPtr raw_weak) {
-    ASSERT(raw_weak->IsHeapObject());
-    ASSERT(raw_weak->IsOldObject());
-    ASSERT(raw_weak->IsWeakProperty());
-    ASSERT(raw_weak->untag()->IsMarked());
-    ASSERT(raw_weak->untag()->next_ ==
-           CompressedWeakPropertyPtr(WeakProperty::null()));
-    raw_weak->untag()->next_ = delayed_weak_properties_;
-    if (delayed_weak_properties_ == WeakProperty::null()) {
-      delayed_weak_properties_tail_ = raw_weak;
-    }
-    delayed_weak_properties_ = raw_weak;
-  }
-
-  void EnqueueWeakReference(WeakReferencePtr raw_weak) {
-    ASSERT(raw_weak->IsHeapObject());
-    ASSERT(raw_weak->IsOldObject());
-    ASSERT(raw_weak->IsWeakReference());
-    ASSERT(raw_weak->untag()->IsMarked());
-    ASSERT(raw_weak->untag()->next_ ==
-           CompressedWeakReferencePtr(WeakReference::null()));
-    raw_weak->untag()->next_ = delayed_weak_references_;
-    if (delayed_weak_references_ == WeakReference::null()) {
-      delayed_weak_references_tail_ = raw_weak;
-    }
-    delayed_weak_references_ = raw_weak;
-  }
-
   intptr_t ProcessWeakProperty(WeakPropertyPtr raw_weak) {
     // The fate of the weak property is determined by its key.
     ObjectPtr raw_key =
@@ -218,7 +191,8 @@
     if (raw_key->IsHeapObject() && raw_key->IsOldObject() &&
         !raw_key->untag()->IsMarked()) {
       // Key was white. Enqueue the weak property.
-      EnqueueWeakProperty(raw_weak);
+      ASSERT(IsMarked(raw_weak));
+      delayed_.weak_properties.Enqueue(raw_weak);
       return raw_weak->untag()->HeapSize();
     }
     // Key is gray or black. Make the weak property black.
@@ -235,7 +209,8 @@
         !raw_target->untag()->IsMarked()) {
       // Target was white. Enqueue the weak reference. It is potentially dead.
       // It might still be made alive by weak properties in next rounds.
-      EnqueueWeakReference(raw_weak);
+      ASSERT(IsMarked(raw_weak));
+      delayed_.weak_references.Enqueue(raw_weak);
     }
     // Always visit the type argument.
     ObjectPtr raw_type_arguments =
@@ -282,12 +257,11 @@
   }
 
   void MournWeakProperties() {
-    WeakPropertyPtr cur_weak = delayed_weak_properties_;
-    delayed_weak_properties_ = WeakProperty::null();
+    WeakPropertyPtr cur_weak = delayed_.weak_properties.Release();
     while (cur_weak != WeakProperty::null()) {
       WeakPropertyPtr next_weak =
-          cur_weak->untag()->next_.Decompress(cur_weak->heap_base());
-      cur_weak->untag()->next_ = WeakProperty::null();
+          cur_weak->untag()->next_seen_by_gc_.Decompress(cur_weak->heap_base());
+      cur_weak->untag()->next_seen_by_gc_ = WeakProperty::null();
       RELEASE_ASSERT(!cur_weak->untag()->key()->untag()->IsMarked());
       WeakProperty::Clear(cur_weak);
       cur_weak = next_weak;
@@ -295,72 +269,58 @@
   }
 
   void MournWeakReferences() {
-    WeakReferencePtr cur_weak = delayed_weak_references_;
-    delayed_weak_references_ = WeakReference::null();
+    WeakReferencePtr cur_weak = delayed_.weak_references.Release();
     while (cur_weak != WeakReference::null()) {
       WeakReferencePtr next_weak =
-          cur_weak->untag()->next_.Decompress(cur_weak->heap_base());
-      cur_weak->untag()->next_ = WeakReference::null();
+          cur_weak->untag()->next_seen_by_gc_.Decompress(cur_weak->heap_base());
+      cur_weak->untag()->next_seen_by_gc_ = WeakReference::null();
+
       // If we did not mark the target through a weak property in a later round,
       // then the target is dead and we should clear it.
-      if (!cur_weak->untag()->target()->untag()->IsMarked()) {
-        WeakReference::Clear(cur_weak);
-      }
+      SetNullIfCollected(cur_weak->heap_base(), &cur_weak->untag()->target_);
+
       cur_weak = next_weak;
     }
   }
 
+  // Returns whether the object referred to in `ptr_address` was GCed this GC.
+  static bool SetNullIfCollected(uword heap_base,
+                                 CompressedObjectPtr* ptr_address) {
+    ObjectPtr raw = ptr_address->Decompress(heap_base);
+    if (raw.IsRawNull()) {
+      // Object already null before this GC.
+      return false;
+    }
+    if (raw.IsNewObject()) {
+      // Object not touched during this GC.
+      return false;
+    }
+    if (raw->untag()->IsMarked()) {
+      return false;
+    }
+    *ptr_address = Object::null();
+    return true;
+  }
+
   bool WaitForWork(RelaxedAtomic<uintptr_t>* num_busy) {
     return work_list_.WaitForWork(num_busy);
   }
 
-  void Flush(WeakPropertyPtr* weak_properties_head,
-             WeakPropertyPtr* weak_properties_tail,
-             WeakReferencePtr* weak_references_head,
-             WeakReferencePtr* weak_references_tail) {
+  void Flush(GCLinkedLists* global_list) {
     work_list_.Flush();
     deferred_work_list_.Flush();
-
-    if (*weak_properties_head == WeakProperty::null()) {
-      *weak_properties_head = delayed_weak_properties_;
-      *weak_properties_tail = delayed_weak_properties_tail_;
-    } else {
-      (*weak_properties_tail)->untag()->next_ = delayed_weak_properties_;
-      *weak_properties_tail = delayed_weak_properties_tail_;
-    }
-    delayed_weak_properties_tail_ = delayed_weak_properties_ =
-        WeakProperty::null();
-
-    if (*weak_references_head == WeakReference::null()) {
-      *weak_references_head = delayed_weak_references_;
-      *weak_references_tail = delayed_weak_references_tail_;
-    } else {
-      (*weak_references_tail)->untag()->next_ = delayed_weak_references_;
-      *weak_references_tail = delayed_weak_references_tail_;
-    }
-    delayed_weak_references_tail_ = delayed_weak_references_ =
-        WeakReference::null();
+    delayed_.FlushInto(global_list);
   }
 
-  void Adopt(WeakPropertyPtr weak_properties_head,
-             WeakPropertyPtr weak_properties_tail,
-             WeakReferencePtr weak_references_head,
-             WeakReferencePtr weak_references_tail) {
-    ASSERT(delayed_weak_properties_ == WeakProperty::null());
-    ASSERT(delayed_weak_properties_tail_ == WeakProperty::null());
-    ASSERT(delayed_weak_references_ == WeakReference::null());
-    ASSERT(delayed_weak_references_tail_ == WeakReference::null());
-    delayed_weak_properties_ = weak_properties_head;
-    delayed_weak_properties_tail_ = weak_properties_tail;
-    delayed_weak_references_ = weak_references_head;
-    delayed_weak_references_tail_ = weak_references_tail;
+  void Adopt(GCLinkedLists* other) {
+    ASSERT(delayed_.IsEmpty());
+    other->FlushInto(&delayed_);
   }
 
   void AbandonWork() {
     work_list_.AbandonWork();
     deferred_work_list_.AbandonWork();
-    delayed_weak_properties_ = WeakProperty::null();
-    delayed_weak_references_ = WeakReference::null();
+    delayed_.Release();
   }
 
  private:
@@ -430,10 +390,7 @@
   PageSpace* page_space_;
   MarkerWorkList work_list_;
   MarkerWorkList deferred_work_list_;
-  WeakPropertyPtr delayed_weak_properties_;
-  WeakPropertyPtr delayed_weak_properties_tail_;
-  WeakReferencePtr delayed_weak_references_;
-  WeakReferencePtr delayed_weak_references_tail_;
+  GCLinkedLists delayed_;
   uintptr_t marked_bytes_;
   int64_t marked_micros_;
 
@@ -746,6 +703,7 @@
       // Phase 3: Weak processing and statistics.
       visitor_->MournWeakProperties();
       visitor_->MournWeakReferences();
+
       marker_->IterateWeakRoots(thread);
       int64_t stop = OS::GetCurrentMonotonicMicros();
       visitor_->AddMicros(stop - start);
@@ -998,10 +956,7 @@
       RelaxedAtomic<uintptr_t> num_busy = 0;
       // Phase 1: Iterate over roots and drain marking stack in tasks.
 
-      WeakPropertyPtr weak_properties_head = WeakProperty::null();
-      WeakPropertyPtr weak_properties_tail = WeakProperty::null();
-      WeakReferencePtr weak_references_head = WeakReference::null();
-      WeakReferencePtr weak_references_tail = WeakReference::null();
+      GCLinkedLists global_list;
 
       for (intptr_t i = 0; i < num_tasks; ++i) {
         SyncMarkingVisitor* visitor = visitors_[i];
@@ -1013,12 +968,12 @@
                                      &marking_stack_, &deferred_marking_stack_);
           visitors_[i] = visitor;
         }
+
         // Move all work from local blocks to the global list. Any given
         // visitor might not get to run if it fails to reach TryEnter soon
         // enough, and we must fail to visit objects but they're sitting in
         // such a visitor's local blocks.
-        visitor->Flush(&weak_properties_head, &weak_properties_tail,
-                       &weak_references_head, &weak_references_tail);
+        visitor->Flush(&global_list);
         // Need to move weak property list too.
 
         if (i < (num_tasks - 1)) {
@@ -1029,8 +984,7 @@
           ASSERT(result);
         } else {
           // Last worker is the main thread.
-          visitor->Adopt(weak_properties_head, weak_properties_tail,
-                         weak_references_head, weak_references_tail);
+          visitor->Adopt(&global_list);
           ParallelMarkTask task(this, isolate_group_, &marking_stack_, barrier,
                                 visitor, &num_busy);
           task.RunEnteredIsolateGroup();
diff --git a/runtime/vm/heap/scavenger.cc b/runtime/vm/heap/scavenger.cc
index e8b433a..75b2519 100644
--- a/runtime/vm/heap/scavenger.cc
+++ b/runtime/vm/heap/scavenger.cc
@@ -4,11 +4,14 @@
 
 #include "vm/heap/scavenger.h"
 
+#include "platform/assert.h"
 #include "platform/leak_sanitizer.h"
+#include "vm/class_id.h"
 #include "vm/dart.h"
 #include "vm/dart_api_state.h"
 #include "vm/flag_list.h"
 #include "vm/heap/become.h"
+#include "vm/heap/gc_shared.h"
 #include "vm/heap/pages.h"
 #include "vm/heap/pointer_block.h"
 #include "vm/heap/safepoint.h"
@@ -20,6 +23,7 @@
 #include "vm/object.h"
 #include "vm/object_id_ring.h"
 #include "vm/object_set.h"
+#include "vm/port.h"
 #include "vm/stack_frame.h"
 #include "vm/thread_barrier.h"
 #include "vm/thread_registry.h"
@@ -131,13 +135,8 @@
         freelist_(freelist),
         bytes_promoted_(0),
         visiting_old_object_(nullptr),
-        promoted_list_(promotion_stack),
-        delayed_weak_properties_(WeakProperty::null()),
-        delayed_weak_references_(WeakReference::null()) {}
-  ~ScavengerVisitorBase() {
-    ASSERT(delayed_weak_properties_ == WeakProperty::null());
-    ASSERT(delayed_weak_references_ == WeakReference::null());
-  }
+        promoted_list_(promotion_stack) {}
+  ~ScavengerVisitorBase() { ASSERT(delayed_.IsEmpty()); }
 
   virtual void VisitTypedDataViewPointers(TypedDataViewPtr view,
                                           CompressedObjectPtr* first,
@@ -308,13 +307,15 @@
 
   void AbandonWork() {
     promoted_list_.AbandonWork();
-    delayed_weak_properties_ = WeakProperty::null();
-    delayed_weak_references_ = WeakReference::null();
+    delayed_.Release();
   }
 
   NewPage* head() const { return head_; }
   NewPage* tail() const { return tail_; }
 
+  static bool SetNullIfCollected(uword heap_base,
+                                 CompressedObjectPtr* ptr_address);
+
  private:
   void UpdateStoreBuffer(ObjectPtr obj) {
     ASSERT(obj->IsHeapObject());
@@ -511,8 +512,13 @@
   inline void ProcessToSpace();
   DART_FORCE_INLINE intptr_t ProcessCopied(ObjectPtr raw_obj);
   inline void ProcessPromotedList();
-  inline void EnqueueWeakProperty(WeakPropertyPtr raw_weak);
-  inline void EnqueueWeakReference(WeakReferencePtr raw_weak);
+
+  bool IsNotForwarding(ObjectPtr raw) {
+    ASSERT(raw->IsHeapObject());
+    ASSERT(raw->IsNewObject());
+    return !IsForwarding(ReadHeaderRelaxed(raw));
+  }
+
   inline void MournWeakProperties();
   inline void MournOrUpdateWeakReferences();
 
@@ -523,10 +529,8 @@
   FreeList* freelist_;
   intptr_t bytes_promoted_;
   ObjectPtr visiting_old_object_;
-
   PromotionWorkList promoted_list_;
-  WeakPropertyPtr delayed_weak_properties_;
-  WeakReferencePtr delayed_weak_references_;
+  GCLinkedLists delayed_;
 
   NewPage* head_ = nullptr;
   NewPage* tail_ = nullptr;  // Allocating from here.
@@ -1323,11 +1327,10 @@
   // Finished this round of scavenging. Process the pending weak properties
   // for which the keys have become reachable. Potentially this adds more
   // objects to the to space.
-  WeakPropertyPtr cur_weak = delayed_weak_properties_;
-  delayed_weak_properties_ = WeakProperty::null();
+  WeakPropertyPtr cur_weak = delayed_.weak_properties.Release();
   while (cur_weak != WeakProperty::null()) {
     WeakPropertyPtr next_weak =
-        cur_weak->untag()->next_.Decompress(cur_weak->heap_base());
+        cur_weak->untag()->next_seen_by_gc_.Decompress(cur_weak->heap_base());
     // Promoted weak properties are not enqueued. So we can guarantee that
     // we do not need to think about store barriers here.
     ASSERT(cur_weak->IsNewObject());
@@ -1341,11 +1344,12 @@
     ASSERT(from_->Contains(raw_addr));
     uword header = ReadHeaderRelaxed(raw_key);
     // Reset the next pointer in the weak property.
-    cur_weak->untag()->next_ = WeakProperty::null();
+    cur_weak->untag()->next_seen_by_gc_ = WeakProperty::null();
     if (IsForwarding(header)) {
       cur_weak->untag()->VisitPointersNonvirtual(this);
     } else {
-      EnqueueWeakProperty(cur_weak);
+      ASSERT(IsNotForwarding(cur_weak));
+      delayed_.weak_properties.Enqueue(cur_weak);
     }
     // Advance to next weak property in the queue.
     cur_weak = next_weak;
@@ -1378,38 +1382,6 @@
 }
 
 template <bool parallel>
-void ScavengerVisitorBase<parallel>::EnqueueWeakProperty(
-    WeakPropertyPtr raw_weak) {
-  ASSERT(raw_weak->IsHeapObject());
-  ASSERT(raw_weak->IsNewObject());
-  ASSERT(raw_weak->IsWeakProperty());
-#if defined(DEBUG)
-  uword header = ReadHeaderRelaxed(raw_weak);
-  ASSERT(!IsForwarding(header));
-#endif  // defined(DEBUG)
-  ASSERT(raw_weak->untag()->next_ ==
-         CompressedWeakPropertyPtr(WeakProperty::null()));
-  raw_weak->untag()->next_ = delayed_weak_properties_;
-  delayed_weak_properties_ = raw_weak;
-}
-
-template <bool parallel>
-void ScavengerVisitorBase<parallel>::EnqueueWeakReference(
-    WeakReferencePtr raw_weak) {
-  ASSERT(raw_weak->IsHeapObject());
-  ASSERT(raw_weak->IsNewObject());
-  ASSERT(raw_weak->IsWeakReference());
-#if defined(DEBUG)
-  uword header = ReadHeaderRelaxed(raw_weak);
-  ASSERT(!IsForwarding(header));
-#endif  // defined(DEBUG)
-  ASSERT(raw_weak->untag()->next_ ==
-         CompressedWeakReferencePtr(WeakReference::null()));
-  raw_weak->untag()->next_ = delayed_weak_references_;
-  delayed_weak_references_ = raw_weak;
-}
-
-template <bool parallel>
 intptr_t ScavengerVisitorBase<parallel>::ProcessCopied(ObjectPtr raw_obj) {
   intptr_t class_id = raw_obj->GetClassId();
   if (UNLIKELY(class_id == kWeakPropertyCid)) {
@@ -1420,7 +1392,8 @@
       uword header = ReadHeaderRelaxed(raw_key);
       if (!IsForwarding(header)) {
         // Key is white.  Enqueue the weak property.
-        EnqueueWeakProperty(raw_weak);
+        ASSERT(IsNotForwarding(raw_weak));
+        delayed_.weak_properties.Enqueue(raw_weak);
         return raw_weak->untag()->HeapSize();
       }
     }
@@ -1434,7 +1407,8 @@
       if (!IsForwarding(header)) {
         // Target is white. Enqueue the weak reference. Always visit type
         // arguments.
-        EnqueueWeakReference(raw_weak);
+        ASSERT(IsNotForwarding(raw_weak));
+        delayed_.weak_references.Enqueue(raw_weak);
 #if !defined(DART_COMPRESSED_POINTERS)
         ScavengePointer(&raw_weak->untag()->type_arguments_);
 #else
@@ -1507,13 +1481,12 @@
 
   // The queued weak properties at this point do not refer to reachable keys,
   // so we clear their key and value fields.
-  WeakPropertyPtr cur_weak = delayed_weak_properties_;
-  delayed_weak_properties_ = WeakProperty::null();
+  WeakPropertyPtr cur_weak = delayed_.weak_properties.Release();
   while (cur_weak != WeakProperty::null()) {
     WeakPropertyPtr next_weak =
-        cur_weak->untag()->next_.Decompress(cur_weak->heap_base());
+        cur_weak->untag()->next_seen_by_gc_.Decompress(cur_weak->heap_base());
     // Reset the next pointer in the weak property.
-    cur_weak->untag()->next_ = WeakProperty::null();
+    cur_weak->untag()->next_seen_by_gc_ = WeakProperty::null();
 
 #if defined(DEBUG)
     ObjectPtr raw_key = cur_weak->untag()->key();
@@ -1537,31 +1510,48 @@
 
   // The queued weak references at this point either should have their target
   // updated or should be cleared.
-  WeakReferencePtr cur_weak = delayed_weak_references_;
-  delayed_weak_references_ = WeakReference::null();
+  WeakReferencePtr cur_weak = delayed_.weak_references.Release();
   while (cur_weak != WeakReference::null()) {
     WeakReferencePtr next_weak =
-        cur_weak->untag()->next_.Decompress(cur_weak->heap_base());
+        cur_weak->untag()->next_seen_by_gc_.Decompress(cur_weak->heap_base());
     // Reset the next pointer in the weak reference.
-    cur_weak->untag()->next_ = WeakReference::null();
+    cur_weak->untag()->next_seen_by_gc_ = WeakReference::null();
 
-    ObjectPtr raw_target = cur_weak->untag()->target();
-    uword raw_addr = UntaggedObject::ToAddr(raw_target);
-    uword header = *reinterpret_cast<uword*>(raw_addr);
-    if (IsForwarding(header)) {
-      // Get the new location of the object.
-      cur_weak->untag()->target_ = ForwardedObj(header);
-    } else {
-      ASSERT(raw_target->IsHeapObject());
-      ASSERT(raw_target->IsNewObject());
-      WeakReference::Clear(cur_weak);
-    }
+    // If we did not mark the target through a weak property in a later round,
+    // then the target is dead and we should clear it.
+    SetNullIfCollected(cur_weak->heap_base(), &cur_weak->untag()->target_);
 
     // Advance to next weak reference in the queue.
     cur_weak = next_weak;
   }
 }
 
+// Returns whether the object referred to in `ptr_address` was GCed this GC.
+template <bool parallel>
+bool ScavengerVisitorBase<parallel>::SetNullIfCollected(
+    uword heap_base,
+    CompressedObjectPtr* ptr_address) {
+  ObjectPtr raw = ptr_address->Decompress(heap_base);
+  if (raw.IsRawNull()) {
+    // Object already null before this GC.
+    return false;
+  }
+  if (raw.IsOldObject()) {
+    // Object not touched during this GC.
+    return false;
+  }
+  uword header = *reinterpret_cast<uword*>(UntaggedObject::ToAddr(raw));
+  if (IsForwarding(header)) {
+    // Get the new location of the object.
+    *ptr_address = ForwardedObj(header);
+    return false;
+  }
+  ASSERT(raw->IsHeapObject());
+  ASSERT(raw->IsNewObject());
+  *ptr_address = Object::null();
+  return true;
+}
+
 void Scavenger::VisitObjectPointers(ObjectPointerVisitor* visitor) const {
   ASSERT(Thread::Current()->IsAtSafepoint() ||
          (Thread::Current()->task_kind() == Thread::kMarkerTask) ||
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 2f8c8cc..59450e8 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -2422,15 +2422,15 @@
     head = next;
   } while (head != nullptr);
   head = reversed_head;
-
   if (Service::profiler_stream.enabled() && !IsSystemIsolate(this)) {
-    SampleBlockListProcessor buffer(head);
     StackZone zone(thread);
-    HandleScope handle_scope(thread);
-    StreamableSampleFilter filter(main_port());
-    Profile profile;
-    profile.Build(thread, &filter, &buffer);
-    if (profile.sample_count() > 0) {
+    SampleBlockListProcessor buffer(head);
+    if (buffer.HasStreamableSamples(thread)) {
+      HandleScope handle_scope(thread);
+      StreamableSampleFilter filter(main_port());
+      Profile profile;
+      profile.Build(thread, &filter, &buffer);
+      ASSERT(profile.sample_count() > 0);
       ServiceEvent event(this, ServiceEvent::kCpuSamples);
       event.set_cpu_profile(&profile);
       Service::HandleEvent(&event);
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 683dd4d..b1c471e 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -5919,9 +5919,7 @@
   static intptr_t UnroundedSize(intptr_t length) {
     return HeaderSize() + length;
   }
-  static intptr_t InstanceSize() {
-    return 0;
-  }
+  static intptr_t InstanceSize() { return 0; }
   static intptr_t InstanceSize(intptr_t length) {
     return RoundedAllocationSize(UnroundedSize(length));
   }
@@ -11979,7 +11977,7 @@
   }
 
   static void Clear(WeakPropertyPtr raw_weak) {
-    ASSERT(raw_weak->untag()->next_ ==
+    ASSERT(raw_weak->untag()->next_seen_by_gc_ ==
            CompressedWeakPropertyPtr(WeakProperty::null()));
     // This action is performed by the GC. No barrier.
     raw_weak->untag()->key_ = Object::null();
@@ -12012,7 +12010,7 @@
   }
 
   static void Clear(WeakReferencePtr raw_weak) {
-    ASSERT(raw_weak->untag()->next_ ==
+    ASSERT(raw_weak->untag()->next_seen_by_gc_ ==
            CompressedWeakReferencePtr(WeakReference::null()));
     // This action is performed by the GC. No barrier.
     raw_weak->untag()->target_ = Object::null();
diff --git a/runtime/vm/object_graph_copy.cc b/runtime/vm/object_graph_copy.cc
index 638febe..0998320 100644
--- a/runtime/vm/object_graph_copy.cc
+++ b/runtime/vm/object_graph_copy.cc
@@ -1362,8 +1362,8 @@
         Object::null());
     // To satisfy some ASSERT()s in GC we'll use Object:null() explicitly here.
     Base::StoreCompressedPointerNoBarrier(
-        Types::GetWeakPropertyPtr(to), OFFSET_OF(UntaggedWeakProperty, next_),
-        Object::null());
+        Types::GetWeakPropertyPtr(to),
+        OFFSET_OF(UntaggedWeakProperty, next_seen_by_gc_), Object::null());
     Base::EnqueueWeakProperty(from);
   }
 
@@ -1380,8 +1380,8 @@
         from, to, OFFSET_OF(UntaggedWeakReference, type_arguments_));
     // To satisfy some ASSERT()s in GC we'll use Object:null() explicitly here.
     Base::StoreCompressedPointerNoBarrier(
-        Types::GetWeakReferencePtr(to), OFFSET_OF(UntaggedWeakReference, next_),
-        Object::null());
+        Types::GetWeakReferencePtr(to),
+        OFFSET_OF(UntaggedWeakReference, next_seen_by_gc_), Object::null());
     Base::EnqueueWeakReference(from);
   }
 
diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc
index b0261f0..d8ae4af 100644
--- a/runtime/vm/profiler.cc
+++ b/runtime/vm/profiler.cc
@@ -713,6 +713,38 @@
   return buffer;
 }
 
+bool SampleBlockListProcessor::HasStreamableSamples(Thread* thread) {
+  ReusableGrowableObjectArrayHandleScope reusable_array_handle_scope(thread);
+  Zone* zone = thread->zone();
+  Isolate* isolate = thread->isolate();
+  ASSERT(isolate->tag_table() != GrowableObjectArray::null());
+  GrowableObjectArray& tag_table = reusable_array_handle_scope.Handle();
+  tag_table ^= isolate->tag_table();
+  UserTag& tag = UserTag::Handle(zone);
+  while (head_ != nullptr) {
+    if (head_->HasStreamableSamples(tag_table, &tag)) {
+      return true;
+    }
+    head_ = head_->next_free_;
+  }
+  return false;
+}
+
+bool SampleBlock::HasStreamableSamples(const GrowableObjectArray& tag_table,
+                                       UserTag* tag) {
+  for (intptr_t i = 0; i < capacity_; ++i) {
+    Sample* sample = At(i);
+    uword sample_tag = sample->user_tag();
+    for (intptr_t j = 0; j < tag_table.Length(); ++j) {
+      *tag ^= tag_table.At(j);
+      if (tag->tag() == sample_tag && tag->streamable()) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
 ProcessedSampleBuffer* SampleBlockBuffer::BuildProcessedSampleBuffer(
     SampleFilter* filter,
     ProcessedSampleBuffer* buffer) {
diff --git a/runtime/vm/profiler.h b/runtime/vm/profiler.h
index c68c792..dc7e10b 100644
--- a/runtime/vm/profiler.h
+++ b/runtime/vm/profiler.h
@@ -756,6 +756,8 @@
   virtual Sample* ReserveSampleAndLink(Sample* previous);
 
  protected:
+  bool HasStreamableSamples(const GrowableObjectArray& tag_table, UserTag* tag);
+
   Isolate* owner_ = nullptr;
   bool allocation_block_ = false;
 
@@ -882,6 +884,10 @@
       SampleFilter* filter,
       ProcessedSampleBuffer* buffer = nullptr);
 
+  // Returns true when at least one sample in the sample block list has a user
+  // tag with CPU sample streaming enabled.
+  bool HasStreamableSamples(Thread* thread);
+
  private:
   SampleBlock* head_;
 
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 5a084c5..98c4e48 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -3265,7 +3265,7 @@
 class UntaggedWeakProperty : public UntaggedInstance {
   RAW_HEAP_OBJECT_IMPLEMENTATION(WeakProperty);
 
-  COMPRESSED_POINTER_FIELD(ObjectPtr, key)
+  COMPRESSED_POINTER_FIELD(ObjectPtr, key)  // Weak reference.
   VISIT_FROM(key)
   COMPRESSED_POINTER_FIELD(ObjectPtr, value)
   VISIT_TO(value)
@@ -3273,8 +3273,10 @@
 
   // Linked list is chaining all pending weak properties. Not visited by
   // pointer visitors.
-  CompressedWeakPropertyPtr next_;
+  COMPRESSED_POINTER_FIELD(WeakPropertyPtr, next_seen_by_gc)
 
+  template <typename Type, typename PtrType>
+  friend class GCLinkedList;
   friend class GCMarker;
   template <bool>
   friend class MarkingVisitorBase;
@@ -3288,7 +3290,7 @@
 class UntaggedWeakReference : public UntaggedInstance {
   RAW_HEAP_OBJECT_IMPLEMENTATION(WeakReference);
 
-  COMPRESSED_POINTER_FIELD(ObjectPtr, target)
+  COMPRESSED_POINTER_FIELD(ObjectPtr, target)  // Weak reference.
   VISIT_FROM(target)
   COMPRESSED_POINTER_FIELD(TypeArgumentsPtr, type_arguments)
   VISIT_TO(type_arguments)
@@ -3296,8 +3298,10 @@
 
   // Linked list is chaining all pending weak properties. Not visited by
   // pointer visitors.
-  CompressedWeakReferencePtr next_;
+  COMPRESSED_POINTER_FIELD(WeakReferencePtr, next_seen_by_gc)
 
+  template <typename Type, typename PtrType>
+  friend class GCLinkedList;
   friend class GCMarker;
   template <bool>
   friend class MarkingVisitorBase;
diff --git a/tests/modular/issue226161959/b.dart b/tests/modular/issue226161959/b.dart
new file mode 100644
index 0000000..fdcd5b5
--- /dev/null
+++ b/tests/modular/issue226161959/b.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2022, 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 'm.dart';
+
+class B extends A {}
+
+class C extends B with M {}
diff --git a/tests/modular/issue226161959/m.dart b/tests/modular/issue226161959/m.dart
new file mode 100644
index 0000000..56cb29a
--- /dev/null
+++ b/tests/modular/issue226161959/m.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2022, 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.
+
+class A {
+  String foo() => 'A';
+}
+
+mixin M on A {
+  @override
+  String foo() => 'M' + super.foo();
+}
diff --git a/tests/modular/issue226161959/main.dart b/tests/modular/issue226161959/main.dart
new file mode 100644
index 0000000..4315bee
--- /dev/null
+++ b/tests/modular/issue226161959/main.dart
@@ -0,0 +1,12 @@
+// 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:expect/expect.dart';
+
+import 'b.dart';
+
+main() {
+  C c = C();
+  Expect.equals('MA', c.foo());
+}
diff --git a/tests/modular/issue226161959/modules.yaml b/tests/modular/issue226161959/modules.yaml
new file mode 100644
index 0000000..31e04b2
--- /dev/null
+++ b/tests/modular/issue226161959/modules.yaml
@@ -0,0 +1,8 @@
+# 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.
+
+dependencies:
+  main: [b, m, expect]
+  b: [m]
+  m: []
diff --git a/tools/VERSION b/tools/VERSION
index 0258b7a..6056e75 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 17
 PATCH 0
-PRERELEASE 232
+PRERELEASE 233
 PRERELEASE_PATCH 0
\ No newline at end of file