Version 2.18.0-204.0.dev

Merge commit '459d57c8b84a6d8f7b0955326b1fdc39c6d502e2' into 'dev'
diff --git a/pkg/_js_interop_checks/lib/src/transformations/static_interop_class_eraser.dart b/pkg/_js_interop_checks/lib/src/transformations/static_interop_class_eraser.dart
index b499ca5..c98694e 100644
--- a/pkg/_js_interop_checks/lib/src/transformations/static_interop_class_eraser.dart
+++ b/pkg/_js_interop_checks/lib/src/transformations/static_interop_class_eraser.dart
@@ -28,6 +28,7 @@
   final Class _javaScriptObject;
   final CloneVisitorNotMembers _cloner = CloneVisitorNotMembers();
   late final _TypeSubstitutor _typeSubstitutor;
+  late Library currLibrary;
 
   StaticInteropClassEraser(CoreTypes coreTypes,
       {String libraryForJavaScriptObject = 'dart:_interceptors',
@@ -41,9 +42,10 @@
       '${factoryTarget.name}|staticInteropFactoryStub';
 
   /// Either finds or creates a static method stub to replace factories with a
-  /// body in a static interop class.
+  /// body with in a static interop class.
   ///
-  /// Modifies [factoryTarget]'s enclosing class to include the new method.
+  /// Modifies [factoryTarget]'s enclosing class to include the new method if we
+  /// create one.
   Procedure _findOrCreateFactoryStub(Procedure factoryTarget) {
     assert(factoryTarget.isFactory);
     var factoryClass = factoryTarget.enclosingClass!;
@@ -52,6 +54,12 @@
     var stubs = factoryClass.procedures
         .where((procedure) => procedure.name.text == stubName);
     if (stubs.isEmpty) {
+      // We should only create the stub if we're processing the library in which
+      // the stub should exist. Any static invocation of the factory that
+      // doesn't exist in the same library as the factory should be processed
+      // after the library in which the factory exists. In modular compilation,
+      // the outline of that library should already contain the needed stub.
+      assert(factoryClass.enclosingLibrary == currLibrary);
       // Note that the return type of the cloned function is transformed.
       var functionNode = super
               .visitFunctionNode(_cloner.cloneInContext(factoryTarget.function))
@@ -69,6 +77,12 @@
   }
 
   @override
+  TreeNode visitLibrary(Library node) {
+    currLibrary = node;
+    return super.visitLibrary(node);
+  }
+
+  @override
   TreeNode visitConstructor(Constructor node) {
     if (hasStaticInteropAnnotation(node.enclosingClass)) {
       // Transform children of the constructor node excluding the return type.
@@ -158,9 +172,10 @@
         // case where we visit the factory later. Also note that a cast is not
         // needed since the static method already has its type erased.
         var args = super.visitArguments(node.arguments) as Arguments;
-        return StaticInvocation(_findOrCreateFactoryStub(factoryTarget), args,
-            isConst: node.isConst)
-          ..fileOffset = node.fileOffset;
+        var stub = _findOrCreateFactoryStub(factoryTarget);
+        return StaticInvocation(stub, args, isConst: node.isConst)
+          ..fileOffset = node.fileOffset
+          ..targetReference = stub.reference;
       } else {
         // Add a cast so that the result gets typed as `JavaScriptObject`.
         var newInvocation = super.visitStaticInvocation(node) as Expression;
@@ -182,3 +197,23 @@
     return substitutedType != null ? substitutedType : type;
   }
 }
+
+/// Used to create stubs for factories when computing outlines.
+///
+/// These stubs can then be used in downstream dependencies in modular
+/// compilation.
+class StaticInteropStubCreator extends RecursiveVisitor {
+  final StaticInteropClassEraser _eraser;
+  StaticInteropStubCreator(this._eraser);
+
+  @override
+  void visitLibrary(Library node) {
+    _eraser.currLibrary = node;
+    super.visitLibrary(node);
+  }
+
+  @override
+  void visitProcedure(Procedure node) {
+    _eraser.visitProcedure(node);
+  }
+}
diff --git a/pkg/analyzer/lib/src/context/context.dart b/pkg/analyzer/lib/src/context/context.dart
index 1b9c657..29448a0 100644
--- a/pkg/analyzer/lib/src/context/context.dart
+++ b/pkg/analyzer/lib/src/context/context.dart
@@ -2,7 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import 'package:analyzer/src/dart/analysis/session.dart';
 import 'package:analyzer/src/dart/element/type_provider.dart';
 import 'package:analyzer/src/dart/element/type_system.dart';
 import 'package:analyzer/src/generated/constant.dart';
@@ -11,52 +10,103 @@
 
 /// An [AnalysisContext] in which analysis can be performed.
 class AnalysisContextImpl implements AnalysisContext {
-  final SynchronousSession _synchronousSession;
+  AnalysisOptionsImpl _analysisOptions;
+
+  @override
+  final DeclaredVariables declaredVariables;
 
   @override
   final SourceFactory sourceFactory;
 
-  AnalysisContextImpl(this._synchronousSession, this.sourceFactory);
+  TypeProviderImpl? _typeProviderLegacy;
+  TypeProviderImpl? _typeProviderNonNullableByDefault;
+
+  TypeSystemImpl? _typeSystemLegacy;
+  TypeSystemImpl? _typeSystemNonNullableByDefault;
+
+  AnalysisContextImpl({
+    required AnalysisOptionsImpl analysisOptions,
+    required this.declaredVariables,
+    required this.sourceFactory,
+  }) : _analysisOptions = analysisOptions;
 
   @override
   AnalysisOptionsImpl get analysisOptions {
-    return _synchronousSession.analysisOptions;
+    return _analysisOptions;
   }
 
-  @override
-  DeclaredVariables get declaredVariables {
-    return _synchronousSession.declaredVariables;
+  /// TODO(scheglov) Remove it, exists only for Cider.
+  set analysisOptions(AnalysisOptionsImpl analysisOptions) {
+    _analysisOptions = analysisOptions;
+
+    // TODO() remove this method as well
+    _typeSystemLegacy?.updateOptions(
+      implicitCasts: analysisOptions.implicitCasts,
+      strictCasts: analysisOptions.strictCasts,
+      strictInference: analysisOptions.strictInference,
+    );
+
+    _typeSystemNonNullableByDefault?.updateOptions(
+      implicitCasts: analysisOptions.implicitCasts,
+      strictCasts: analysisOptions.strictCasts,
+      strictInference: analysisOptions.strictInference,
+    );
   }
 
-  bool get hasTypeProvider => _synchronousSession.hasTypeProvider;
+  bool get hasTypeProvider {
+    return _typeProviderNonNullableByDefault != null;
+  }
 
   TypeProviderImpl get typeProviderLegacy {
-    return _synchronousSession.typeProviderLegacy;
+    return _typeProviderLegacy!;
   }
 
   TypeProviderImpl get typeProviderNonNullableByDefault {
-    return _synchronousSession.typeProviderNonNullableByDefault;
+    return _typeProviderNonNullableByDefault!;
   }
 
   TypeSystemImpl get typeSystemLegacy {
-    return _synchronousSession.typeSystemLegacy;
+    return _typeSystemLegacy!;
   }
 
   TypeSystemImpl get typeSystemNonNullableByDefault {
-    return _synchronousSession.typeSystemNonNullableByDefault;
+    return _typeSystemNonNullableByDefault!;
   }
 
   void clearTypeProvider() {
-    _synchronousSession.clearTypeProvider();
+    _typeProviderLegacy = null;
+    _typeProviderNonNullableByDefault = null;
+
+    _typeSystemLegacy = null;
+    _typeSystemNonNullableByDefault = null;
   }
 
   void setTypeProviders({
     required TypeProviderImpl legacy,
     required TypeProviderImpl nonNullableByDefault,
   }) {
-    _synchronousSession.setTypeProviders(
-      legacy: legacy,
-      nonNullableByDefault: nonNullableByDefault,
+    if (_typeProviderLegacy != null ||
+        _typeProviderNonNullableByDefault != null) {
+      throw StateError('TypeProvider(s) can be set only once.');
+    }
+
+    _typeSystemLegacy = TypeSystemImpl(
+      implicitCasts: analysisOptions.implicitCasts,
+      isNonNullableByDefault: false,
+      strictCasts: analysisOptions.strictCasts,
+      strictInference: analysisOptions.strictInference,
+      typeProvider: legacy,
     );
+
+    _typeSystemNonNullableByDefault = TypeSystemImpl(
+      implicitCasts: analysisOptions.implicitCasts,
+      isNonNullableByDefault: true,
+      strictCasts: analysisOptions.strictCasts,
+      strictInference: analysisOptions.strictInference,
+      typeProvider: nonNullableByDefault,
+    );
+
+    _typeProviderLegacy = legacy;
+    _typeProviderNonNullableByDefault = nonNullableByDefault;
   }
 }
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_context.dart b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
index 9442649..9f4aeb5 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_context.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
@@ -59,9 +59,11 @@
     required this.macroExecutor,
     required SummaryDataStore? externalSummaries,
   }) {
-    var synchronousSession =
-        SynchronousSession(analysisOptions, declaredVariables);
-    analysisContext = AnalysisContextImpl(synchronousSession, sourceFactory);
+    analysisContext = AnalysisContextImpl(
+      analysisOptions: analysisOptions,
+      declaredVariables: declaredVariables,
+      sourceFactory: sourceFactory,
+    );
 
     elementFactory = LinkedElementFactory(
       analysisContext,
diff --git a/pkg/analyzer/lib/src/dart/analysis/session.dart b/pkg/analyzer/lib/src/dart/analysis/session.dart
index 91f7c6f..d69fa9c 100644
--- a/pkg/analyzer/lib/src/dart/analysis/session.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/session.dart
@@ -13,9 +13,6 @@
 import 'package:analyzer/src/dart/analysis/uri_converter.dart';
 import 'package:analyzer/src/dart/element/class_hierarchy.dart';
 import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
-import 'package:analyzer/src/dart/element/type_provider.dart';
-import 'package:analyzer/src/dart/element/type_system.dart';
-import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
 import 'package:analyzer/src/summary2/linked_element_factory.dart';
 
 /// A concrete implementation of an analysis session.
@@ -139,92 +136,3 @@
     }
   }
 }
-
-/// Data structure containing information about the analysis session that is
-/// available synchronously.
-class SynchronousSession {
-  AnalysisOptionsImpl _analysisOptions;
-
-  final DeclaredVariables declaredVariables;
-
-  TypeProviderImpl? _typeProviderLegacy;
-  TypeProviderImpl? _typeProviderNonNullableByDefault;
-
-  TypeSystemImpl? _typeSystemLegacy;
-  TypeSystemImpl? _typeSystemNonNullableByDefault;
-
-  SynchronousSession(this._analysisOptions, this.declaredVariables);
-
-  AnalysisOptionsImpl get analysisOptions => _analysisOptions;
-
-  set analysisOptions(AnalysisOptionsImpl analysisOptions) {
-    _analysisOptions = analysisOptions;
-
-    _typeSystemLegacy?.updateOptions(
-      implicitCasts: analysisOptions.implicitCasts,
-      strictCasts: analysisOptions.strictCasts,
-      strictInference: analysisOptions.strictInference,
-    );
-
-    _typeSystemNonNullableByDefault?.updateOptions(
-      implicitCasts: analysisOptions.implicitCasts,
-      strictCasts: analysisOptions.strictCasts,
-      strictInference: analysisOptions.strictInference,
-    );
-  }
-
-  bool get hasTypeProvider => _typeProviderNonNullableByDefault != null;
-
-  TypeProviderImpl get typeProviderLegacy {
-    return _typeProviderLegacy!;
-  }
-
-  TypeProviderImpl get typeProviderNonNullableByDefault {
-    return _typeProviderNonNullableByDefault!;
-  }
-
-  TypeSystemImpl get typeSystemLegacy {
-    return _typeSystemLegacy!;
-  }
-
-  TypeSystemImpl get typeSystemNonNullableByDefault {
-    return _typeSystemNonNullableByDefault!;
-  }
-
-  void clearTypeProvider() {
-    _typeProviderLegacy = null;
-    _typeProviderNonNullableByDefault = null;
-
-    _typeSystemLegacy = null;
-    _typeSystemNonNullableByDefault = null;
-  }
-
-  void setTypeProviders({
-    required TypeProviderImpl legacy,
-    required TypeProviderImpl nonNullableByDefault,
-  }) {
-    if (_typeProviderLegacy != null ||
-        _typeProviderNonNullableByDefault != null) {
-      throw StateError('TypeProvider(s) can be set only once.');
-    }
-
-    _typeSystemLegacy = TypeSystemImpl(
-      implicitCasts: _analysisOptions.implicitCasts,
-      isNonNullableByDefault: false,
-      strictCasts: _analysisOptions.strictCasts,
-      strictInference: _analysisOptions.strictInference,
-      typeProvider: legacy,
-    );
-
-    _typeSystemNonNullableByDefault = TypeSystemImpl(
-      implicitCasts: _analysisOptions.implicitCasts,
-      isNonNullableByDefault: true,
-      strictCasts: _analysisOptions.strictCasts,
-      strictInference: _analysisOptions.strictInference,
-      typeProvider: nonNullableByDefault,
-    );
-
-    _typeProviderLegacy = legacy;
-    _typeProviderNonNullableByDefault = nonNullableByDefault;
-  }
-}
diff --git a/pkg/analyzer/lib/src/dart/micro/analysis_context.dart b/pkg/analyzer/lib/src/dart/micro/analysis_context.dart
index a507543..7f33467 100644
--- a/pkg/analyzer/lib/src/dart/micro/analysis_context.dart
+++ b/pkg/analyzer/lib/src/dart/micro/analysis_context.dart
@@ -30,14 +30,11 @@
   required ResourceProvider resourceProvider,
 }) {
   var declaredVariables = DeclaredVariables();
-  var synchronousSession = SynchronousSession(
-    analysisOptions,
-    declaredVariables,
-  );
 
   var analysisContext = AnalysisContextImpl(
-    synchronousSession,
-    sourceFactory,
+    analysisOptions: analysisOptions,
+    declaredVariables: declaredVariables,
+    sourceFactory: sourceFactory,
   );
 
   var analysisSession = _MicroAnalysisSessionImpl(
@@ -48,7 +45,7 @@
 
   var analysisContext2 = _MicroAnalysisContextImpl(
     fileResolver,
-    synchronousSession,
+    analysisOptions,
     root,
     declaredVariables,
     sourceFactory,
@@ -66,7 +63,6 @@
 
   return MicroContextObjects._(
     declaredVariables: declaredVariables,
-    synchronousSession: synchronousSession,
     analysisSession: analysisSession,
     analysisContext: analysisContext,
   );
@@ -74,19 +70,17 @@
 
 class MicroContextObjects {
   final DeclaredVariables declaredVariables;
-  final SynchronousSession synchronousSession;
   final _MicroAnalysisSessionImpl analysisSession;
   final AnalysisContextImpl analysisContext;
 
   MicroContextObjects._({
     required this.declaredVariables,
-    required this.synchronousSession,
     required this.analysisSession,
     required this.analysisContext,
   });
 
   set analysisOptions(AnalysisOptionsImpl analysisOptions) {
-    synchronousSession.analysisOptions = analysisOptions;
+    analysisContext.analysisOptions = analysisOptions;
   }
 
   InheritanceManager3 get inheritanceManager {
@@ -114,7 +108,9 @@
 
 class _MicroAnalysisContextImpl implements AnalysisContext {
   final FileResolver fileResolver;
-  final SynchronousSession synchronousSession;
+
+  @override
+  AnalysisOptionsImpl analysisOptions;
 
   final ResourceProvider resourceProvider;
 
@@ -130,7 +126,7 @@
 
   _MicroAnalysisContextImpl(
     this.fileResolver,
-    this.synchronousSession,
+    this.analysisOptions,
     this.contextRoot,
     this.declaredVariables,
     this.sourceFactory,
@@ -138,11 +134,6 @@
   );
 
   @override
-  AnalysisOptionsImpl get analysisOptions {
-    return synchronousSession.analysisOptions;
-  }
-
-  @override
   Folder? get sdkRoot => null;
 
   @override
diff --git a/pkg/compiler/lib/src/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart
index 5b51960..df7624c 100644
--- a/pkg/compiler/lib/src/kernel/dart2js_target.dart
+++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart
@@ -138,6 +138,13 @@
   bool get errorOnUnexactWebIntLiterals => true;
 
   @override
+  void performOutlineTransformations(
+      ir.Component component, CoreTypes coreTypes) {
+    component
+        .accept(StaticInteropStubCreator(StaticInteropClassEraser(coreTypes)));
+  }
+
+  @override
   void performModularTransformationsOnLibraries(
       ir.Component component,
       CoreTypes coreTypes,
diff --git a/pkg/dart2js_info/bin/src/code_deps.dart b/pkg/dart2js_info/bin/src/code_deps.dart
index 5a57686..d11a043 100644
--- a/pkg/dart2js_info/bin/src/code_deps.dart
+++ b/pkg/dart2js_info/bin/src/code_deps.dart
@@ -2,8 +2,6 @@
 // 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.
 
-// @dart = 2.11
-
 /// Command to query for code dependencies. Currently this tool only
 /// supports the `some_path` query, which gives you the shortest path for how
 /// one function depends on another.
@@ -30,6 +28,7 @@
 import 'dart:collection';
 
 import 'package:args/command_runner.dart';
+import 'package:collection/collection.dart';
 
 import 'package:dart2js_info/info.dart';
 import 'package:dart2js_info/src/graph.dart';
@@ -57,7 +56,7 @@
 
   @override
   void run() async {
-    var args = argResults.rest;
+    var args = argResults!.rest;
     if (args.length < 3) {
       usageException("Missing arguments for some_path, expected: "
           "info.data <element-regexp-1> <element-regexp-2>");
@@ -66,10 +65,10 @@
     var info = await infoFromFile(args.first);
     var graph = graphFromInfo(info);
 
-    var source = info.functions
-        .firstWhere(_longNameMatcher(RegExp(args[1])), orElse: () => null);
-    var target = info.functions
-        .firstWhere(_longNameMatcher(RegExp(args[2])), orElse: () => null);
+    final source =
+        info.functions.firstWhereOrNull(_longNameMatcher(RegExp(args[1])));
+    final target =
+        info.functions.firstWhereOrNull(_longNameMatcher(RegExp(args[2])));
     print('query: some_path');
     if (source == null) {
       usageException("source '${args[1]}' not found in '${args[0]}'");
@@ -106,9 +105,9 @@
 
   SomePathQuery(this.source, this.target);
 
-  List<Info> run(Graph<Info> graph) {
-    var seen = <Info, Info>{source: null};
-    var queue = Queue<Info>();
+  List<Info> run(Graph<Info?> graph) {
+    var seen = <Info?, Info?>{source: null};
+    var queue = Queue<Info?>();
     queue.addLast(source);
     while (queue.isNotEmpty) {
       var node = queue.removeFirst();
diff --git a/pkg/dart2js_info/bin/src/common_command.dart b/pkg/dart2js_info/bin/src/common_command.dart
index e8cba55..8e2aa48 100644
--- a/pkg/dart2js_info/bin/src/common_command.dart
+++ b/pkg/dart2js_info/bin/src/common_command.dart
@@ -2,8 +2,6 @@
 // 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.
 
-// @dart = 2.11
-
 import 'package:args/command_runner.dart';
 
 import 'package:dart2js_info/info.dart';
@@ -33,7 +31,8 @@
 
   @override
   void run() async {
-    var args = argResults.rest;
+    final argRes = argResults!;
+    final args = argRes.rest;
     if (args.length < 2) {
       usageException(
           'Missing arguments, expected two dump-info files to compare');
@@ -41,8 +40,8 @@
 
     var oldInfo = await infoFromFile(args[0]);
     var newInfo = await infoFromFile(args[1]);
-    var packagesOnly = argResults['packages-only'];
-    var orderBySize = argResults['order-by-size'];
+    var packagesOnly = argRes['packages-only'];
+    var orderBySize = argRes['order-by-size'];
 
     var commonElements = findCommonalities(oldInfo, newInfo);
 
@@ -112,7 +111,7 @@
 
   var oldSizeTotal = 0, newSizeTotal = 0;
   oldPackageInfo.forEach((oldPackageName, oldPackageSize) {
-    var newPackageSize = newPackageInfo[oldPackageName];
+    var newPackageSize = newPackageInfo[oldPackageName]!;
     oldSizeTotal += oldPackageSize;
     newSizeTotal += newPackageSize;
   });
@@ -142,7 +141,9 @@
 }
 
 void _section(String title,
-    {int elementCount, int oldSizeTotal, int newSizeTotal}) {
+    {required int elementCount,
+    required int oldSizeTotal,
+    required int newSizeTotal}) {
   if (oldSizeTotal == newSizeTotal) {
     print('$title ($elementCount common elements, $oldSizeTotal bytes)');
   } else {
diff --git a/pkg/dart2js_info/bin/src/coverage_log_server.dart b/pkg/dart2js_info/bin/src/coverage_log_server.dart
index 375731a..683a935 100644
--- a/pkg/dart2js_info/bin/src/coverage_log_server.dart
+++ b/pkg/dart2js_info/bin/src/coverage_log_server.dart
@@ -2,8 +2,6 @@
 // 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.
 
-// @dart = 2.11
-
 /// A tool to gather coverage data from an app generated with dart2js. This
 /// depends on code that has been landed in the bleeding_edge version of dart2js
 /// and that we expect to become publicly visible in version 0.13.0 of the Dart
@@ -55,19 +53,20 @@
 
   @override
   void run() async {
-    if (argResults.rest.isEmpty) {
+    final args = argResults!;
+    if (args.rest.isEmpty) {
       usageException('Missing arguments: <dart2js-out-file> [<html-file>]');
     }
 
-    var jsPath = argResults.rest[0];
-    String htmlPath;
-    if (argResults.rest.length > 1) {
-      htmlPath = argResults.rest[1];
+    var jsPath = args.rest[0];
+    String? htmlPath;
+    if (args.rest.length > 1) {
+      htmlPath = args.rest[1];
     }
-    var outPath = argResults['out'];
+    var outPath = args['out'];
     if (outPath == _defaultOutTemplate) outPath = '$jsPath.coverage.json';
-    var server = _Server(argResults['host'], int.parse(argResults['port']),
-        jsPath, htmlPath, outPath, argResults['uri-prefix']);
+    var server = _Server(args['host'], int.parse(args['port']), jsPath,
+        htmlPath, outPath, args['uri-prefix']);
     await server.run();
   }
 }
@@ -85,7 +84,7 @@
   final String jsPath;
 
   /// HTML file to serve, if any.
-  final String htmlPath;
+  final String? htmlPath;
 
   /// Contents of jsPath, adjusted to use the appropriate server url.
   String jsCode;
@@ -115,7 +114,7 @@
   run() async {
     await shelf.serve(_handler, hostname, port);
     var urlBase = "http://$hostname:$port${prefix == '' ? '/' : '/$prefix/'}";
-    var htmlFilename = htmlPath == null ? '' : path.basename(htmlPath);
+    var htmlFilename = htmlPath == null ? '' : path.basename(htmlPath!);
     print("Server is listening\n"
         "  - html page: $urlBase$htmlFilename\n"
         "  - js code: $urlBase${path.basename(jsPath)}\n"
@@ -128,7 +127,7 @@
     var urlPath = request.url.path;
     print('received request: $urlPath');
     var baseJsName = path.basename(jsPath);
-    var baseHtmlName = htmlPath == null ? '' : path.basename(htmlPath);
+    var baseHtmlName = htmlPath == null ? '' : path.basename(htmlPath!);
 
     // Serve an HTML file at the default prefix, or a path matching the HTML
     // file name
@@ -137,7 +136,7 @@
         urlPath == _expectedPath(baseHtmlName)) {
       var contents = htmlPath == null
           ? '<html><script src="$baseJsName"></script>'
-          : await File(htmlPath).readAsString();
+          : await File(htmlPath!).readAsString();
       return shelf.Response.ok(contents, headers: _htmlHeaders);
     }
 
diff --git a/pkg/dart2js_info/bin/src/deferred_library_check.dart b/pkg/dart2js_info/bin/src/deferred_library_check.dart
index 2aba53c..ce3da3c 100644
--- a/pkg/dart2js_info/bin/src/deferred_library_check.dart
+++ b/pkg/dart2js_info/bin/src/deferred_library_check.dart
@@ -2,8 +2,6 @@
 // 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.
 
-// @dart = 2.11
-
 /// A command that verifies that deferred libraries split the code as expected.
 ///
 /// This tool checks that the output from dart2js meets a given specification,
@@ -57,7 +55,7 @@
 
   @override
   void run() async {
-    var args = argResults.rest;
+    var args = argResults!.rest;
     if (args.length < 2) {
       usageException('Missing arguments, expected: info.data manifest.yaml');
     }
diff --git a/pkg/dart2js_info/bin/src/show_inferred_types.dart b/pkg/dart2js_info/bin/src/show_inferred_types.dart
index 7c43318..417d839 100644
--- a/pkg/dart2js_info/bin/src/show_inferred_types.dart
+++ b/pkg/dart2js_info/bin/src/show_inferred_types.dart
@@ -2,8 +2,6 @@
 // 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.
 
-// @dart = 2.11
-
 /// Simple script that shows the inferred types of a function.
 library compiler.tool.show_inferred_types;
 
@@ -29,12 +27,13 @@
 
   @override
   void run() async {
-    var args = argResults.rest;
+    final argRes = argResults!;
+    var args = argRes.rest;
     if (args.length < 2) {
       usageException(
           'Missing arguments, expected: info.data <function-name-regex>');
     }
-    _showInferredTypes(args[0], args[1], argResults['long-names']);
+    _showInferredTypes(args[0], args[1], argRes['long-names']);
   }
 }
 
diff --git a/pkg/dart2js_info/lib/json_info_codec.dart b/pkg/dart2js_info/lib/json_info_codec.dart
index 65b18a3..639776a 100644
--- a/pkg/dart2js_info/lib/json_info_codec.dart
+++ b/pkg/dart2js_info/lib/json_info_codec.dart
@@ -2,8 +2,6 @@
 // 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.
 
-// @dart = 2.11
-
 /// Converters and codecs for converting between JSON and [Info] classes.
 import 'dart:collection';
 import 'dart:convert';
@@ -50,18 +48,17 @@
         (elements['constant'] as Map).values.map((c) => parseConstant(c)));
 
     input['holding'].forEach((k, deps) {
-      CodeInfo src = registry[k];
-      assert(src != null);
+      final src = registry[k] as CodeInfo;
       for (var dep in deps) {
-        var target = registry[dep['id']];
-        assert(target != null);
+        final target = registry[dep['id']]!;
         src.uses.add(DependencyInfo(target, dep['mask']));
       }
     });
 
     input['dependencies']?.forEach((String k, dependencies) {
       List<String> deps = dependencies;
-      result.dependencies[registry[k]] = deps.map((d) => registry[d]).toList();
+      result.dependencies[registry[k]!] =
+          deps.map((d) => registry[d]!).toList();
     });
 
     result.outputUnits
@@ -91,24 +88,23 @@
   }
 
   OutputUnitInfo parseOutputUnit(Map json) {
-    OutputUnitInfo result = parseId(json['id']);
+    final result = parseId(json['id']) as OutputUnitInfo;
     result
       ..filename = json['filename']
       ..name = json['name']
       ..size = json['size'];
-    result.imports
-        .addAll((json['imports'] as List).map((s) => s as String) ?? const []);
+    result.imports.addAll((json['imports'] as List).map((s) => s as String));
     return result;
   }
 
   LibraryInfo parseLibrary(Map json) {
-    LibraryInfo result = parseId(json['id']);
+    final result = parseId(json['id']) as LibraryInfo;
     result
       ..name = json['name']
       ..uri = Uri.parse(json['canonicalUri'])
-      ..outputUnit = parseId(json['outputUnit'])
+      ..outputUnit = parseId(json['outputUnit']) as OutputUnitInfo?
       ..size = json['size'];
-    for (var child in json['children'].map(parseId)) {
+    for (var child in json['children'].map((id) => parseId(id))) {
       if (child is FunctionInfo) {
         result.topLevelFunctions.add(child);
       } else if (child is FieldInfo) {
@@ -126,15 +122,14 @@
   }
 
   ClassInfo parseClass(Map json) {
-    ClassInfo result = parseId(json['id']);
+    final result = parseId(json['id']) as ClassInfo;
     result
       ..name = json['name']
       ..parent = parseId(json['parent'])
-      ..outputUnit = parseId(json['outputUnit'])
+      ..outputUnit = parseId(json['outputUnit']) as OutputUnitInfo?
       ..size = json['size']
       ..isAbstract = json['modifiers']['abstract'] == true;
-    assert(result is ClassInfo);
-    for (var child in json['children'].map(parseId)) {
+    for (var child in json['children'].map((id) => parseId(id))) {
       if (child is FunctionInfo) {
         result.functions.add(child);
       } else {
@@ -146,44 +141,44 @@
   }
 
   ClassTypeInfo parseClassType(Map json) {
-    ClassTypeInfo result = parseId(json['id']);
+    final result = parseId(json['id']) as ClassTypeInfo;
     result
       ..name = json['name']
       ..parent = parseId(json['parent'])
-      ..outputUnit = parseId(json['outputUnit'])
+      ..outputUnit = parseId(json['outputUnit']) as OutputUnitInfo?
       ..size = json['size'];
     return result;
   }
 
   FieldInfo parseField(Map json) {
-    FieldInfo result = parseId(json['id']);
+    final result = parseId(json['id']) as FieldInfo;
     return result
       ..name = json['name']
       ..parent = parseId(json['parent'])
       ..coverageId = json['coverageId']
-      ..outputUnit = parseId(json['outputUnit'])
+      ..outputUnit = parseId(json['outputUnit']) as OutputUnitInfo?
       ..size = json['size']
       ..type = json['type']
       ..inferredType = json['inferredType']
       ..code = parseCode(json['code'])
       ..isConst = json['const'] ?? false
-      ..initializer = parseId(json['initializer'])
+      ..initializer = parseId(json['initializer']) as ConstantInfo?
       ..closures = (json['children'] as List)
-          .map<ClosureInfo>((c) => parseId(c))
+          .map<ClosureInfo>((c) => parseId(c) as ClosureInfo)
           .toList();
   }
 
   ConstantInfo parseConstant(Map json) {
-    ConstantInfo result = parseId(json['id']);
+    final result = parseId(json['id']) as ConstantInfo;
     return result
       ..name = json['name']
       ..code = parseCode(json['code'])
       ..size = json['size']
-      ..outputUnit = parseId(json['outputUnit']);
+      ..outputUnit = parseId(json['outputUnit']) as OutputUnitInfo?;
   }
 
   TypedefInfo parseTypedef(Map json) {
-    TypedefInfo result = parseId(json['id']);
+    final result = parseId(json['id']) as TypedefInfo;
     return result
       ..name = json['name']
       ..parent = parseId(json['parent'])
@@ -209,7 +204,7 @@
         : Duration(microseconds: dumpInfoDuration as int);
 
     final programInfo = ProgramInfo(
-        entrypoint: parseId(json['entrypoint']),
+        entrypoint: parseId(json['entrypoint']) as FunctionInfo,
         size: json['size'],
         compilationMoment: DateTime.parse(json['compilationMoment']),
         dart2jsVersion: json['dart2jsVersion'],
@@ -245,12 +240,12 @@
   }
 
   FunctionInfo parseFunction(Map json) {
-    FunctionInfo result = parseId(json['id']);
+    final result = parseId(json['id']) as FunctionInfo;
     return result
       ..name = json['name']
       ..parent = parseId(json['parent'])
       ..coverageId = json['coverageId']
-      ..outputUnit = parseId(json['outputUnit'])
+      ..outputUnit = parseId(json['outputUnit']) as OutputUnitInfo?
       ..size = json['size']
       ..functionKind = json['functionKind']
       ..type = json['type']
@@ -263,7 +258,7 @@
       ..inlinedCount = json['inlinedCount']
       ..modifiers = parseModifiers(Map<String, bool>.from(json['modifiers']))
       ..closures = (json['children'] as List)
-          .map<ClosureInfo>((c) => parseId(c))
+          .map<ClosureInfo>((c) => parseId(c) as ClosureInfo)
           .toList();
   }
 
@@ -279,17 +274,16 @@
   }
 
   ClosureInfo parseClosure(Map json) {
-    ClosureInfo result = parseId(json['id']);
+    final result = parseId(json['id']) as ClosureInfo;
     return result
       ..name = json['name']
       ..parent = parseId(json['parent'])
-      ..outputUnit = parseId(json['outputUnit'])
+      ..outputUnit = parseId(json['outputUnit']) as OutputUnitInfo?
       ..size = json['size']
-      ..function = parseId(json['function']);
+      ..function = parseId(json['function']) as FunctionInfo;
   }
 
-  Info parseId(id) {
-    String serializedId = id;
+  Info? parseId(String? serializedId) {
     if (serializedId == null) {
       return null;
     }
@@ -313,8 +307,7 @@
       } else if (serializedId.startsWith('outputUnit/')) {
         return OutputUnitInfo.internal();
       }
-      assert(false);
-      return null;
+      throw StateError('Invalid serialized ID found: $serializedId');
     });
   }
 
@@ -364,7 +357,6 @@
       // No name and no parent, so `longName` isn't helpful
       assert(info.name.isEmpty);
       assert(info.parent == null);
-      assert(info.code != null);
       // Instead, use the content of the code.
       name = info.code.first.text ?? '';
     } else {
@@ -454,7 +446,7 @@
       'dump_version': isBackwardCompatible ? 5 : info.version,
       'deferredFiles': info.deferredFiles,
       'dump_minor_version': isBackwardCompatible ? 1 : info.minorVersion,
-      'program': info.program.accept(this)
+      'program': info.program!.accept(this)
     };
   }
 
@@ -487,10 +479,10 @@
     // TODO(sigmund): Omit this also when outputUnit.id == 0 (most code is in
     // the main output unit by default).
     if (info.outputUnit != null) {
-      res['outputUnit'] = idFor(info.outputUnit).serializedId;
+      res['outputUnit'] = idFor(info.outputUnit!).serializedId;
     }
     if (info.coverageId != null) res['coverageId'] = info.coverageId;
-    if (info.parent != null) res['parent'] = idFor(info.parent).serializedId;
+    if (info.parent != null) res['parent'] = idFor(info.parent!).serializedId;
     return res;
   }
 
@@ -537,7 +529,7 @@
     if (info.isConst) {
       result['const'] = true;
       if (info.initializer != null) {
-        result['initializer'] = idFor(info.initializer).serializedId;
+        result['initializer'] = idFor(info.initializer!).serializedId;
       }
     }
     return result;
@@ -567,7 +559,7 @@
   @override
   Map visitFunction(FunctionInfo info) {
     return _visitBasicInfo(info)
-      ..addAll(<String, Object>{
+      ..addAll(<String, Object?>{
         'children': _toSortedSerializedIds(info.closures, idFor),
         'modifiers': _visitFunctionModifiers(info.modifiers),
         'returnType': info.returnType,
diff --git a/pkg/dart2js_info/lib/src/io.dart b/pkg/dart2js_info/lib/src/io.dart
index f006e87..a80da7c 100644
--- a/pkg/dart2js_info/lib/src/io.dart
+++ b/pkg/dart2js_info/lib/src/io.dart
@@ -1,4 +1,6 @@
-// @dart = 2.11
+// 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 'dart:convert';
 import 'dart:io';
diff --git a/pkg/dart2wasm/lib/target.dart b/pkg/dart2wasm/lib/target.dart
index 12b4408..3be12c7 100644
--- a/pkg/dart2wasm/lib/target.dart
+++ b/pkg/dart2wasm/lib/target.dart
@@ -81,6 +81,21 @@
       ..parent = host;
   }
 
+  StaticInteropClassEraser _staticInteropClassEraser(CoreTypes coreTypes) =>
+      StaticInteropClassEraser(coreTypes,
+          libraryForJavaScriptObject: 'dart:_js_helper',
+          classNameOfJavaScriptObject: 'JSValue');
+
+  void _performJSInteropTransformations(CoreTypes coreTypes,
+      ClassHierarchy hierarchy, List<Library> interopDependentLibraries) {
+    final jsUtilOptimizer = JsUtilWasmOptimizer(coreTypes, hierarchy);
+    final staticInteropClassEraser = _staticInteropClassEraser(coreTypes);
+    for (Library library in interopDependentLibraries) {
+      jsUtilOptimizer.visitLibrary(library);
+      staticInteropClassEraser.visitLibrary(library);
+    }
+  }
+
   @override
   void performPreConstantEvaluationTransformations(
       Component component,
@@ -93,6 +108,12 @@
   }
 
   @override
+  void performOutlineTransformations(Component component, CoreTypes coreTypes) {
+    component
+        .accept(StaticInteropStubCreator(_staticInteropClassEraser(coreTypes)));
+  }
+
+  @override
   void performModularTransformationsOnLibraries(
       Component component,
       CoreTypes coreTypes,
@@ -109,7 +130,7 @@
     if (transitiveImportingJSInterop == null) {
       logger?.call("Skipped JS interop transformations");
     } else {
-      performJSInteropTransformations(
+      _performJSInteropTransformations(
           coreTypes, hierarchy, transitiveImportingJSInterop);
       logger?.call("Transformed JS interop classes");
     }
@@ -242,15 +263,3 @@
   @override
   bool isSupportedPragma(String pragmaName) => pragmaName.startsWith("wasm:");
 }
-
-void performJSInteropTransformations(CoreTypes coreTypes,
-    ClassHierarchy hierarchy, List<Library> interopDependentLibraries) {
-  final jsUtilOptimizer = JsUtilWasmOptimizer(coreTypes, hierarchy);
-  final staticInteropClassEraser = StaticInteropClassEraser(coreTypes,
-      libraryForJavaScriptObject: 'dart:_js_helper',
-      classNameOfJavaScriptObject: 'JSValue');
-  for (Library library in interopDependentLibraries) {
-    jsUtilOptimizer.visitLibrary(library);
-    staticInteropClassEraser.visitLibrary(library);
-  }
-}
diff --git a/pkg/dds/lib/src/dap/protocol_generated.dart b/pkg/dds/lib/src/dap/protocol_generated.dart
index 105ca1f..e413f0a 100644
--- a/pkg/dds/lib/src/dap/protocol_generated.dart
+++ b/pkg/dds/lib/src/dap/protocol_generated.dart
@@ -641,8 +641,7 @@
         supportTerminateDebuggee = obj['supportTerminateDebuggee'] as bool?,
         supportedChecksumAlgorithms =
             (obj['supportedChecksumAlgorithms'] as List?)
-                ?.map((item) =>
-                    ChecksumAlgorithm.fromJson(item as Map<String, Object?>))
+                ?.map((item) => item as ChecksumAlgorithm)
                 .toList(),
         supportsBreakpointLocationsRequest =
             obj['supportsBreakpointLocationsRequest'] as bool?,
@@ -716,7 +715,7 @@
     }
     if ((obj['supportedChecksumAlgorithms'] is! List ||
         (obj['supportedChecksumAlgorithms']
-            .any((item) => !ChecksumAlgorithm.canParse(item))))) {
+            .any((item) => item is! ChecksumAlgorithm)))) {
       return false;
     }
     if (obj['supportsBreakpointLocationsRequest'] is! bool?) {
@@ -909,15 +908,14 @@
   });
 
   Checksum.fromMap(Map<String, Object?> obj)
-      : algorithm = ChecksumAlgorithm.fromJson(
-            obj['algorithm'] as Map<String, Object?>),
+      : algorithm = obj['algorithm'] as ChecksumAlgorithm,
         checksum = obj['checksum'] as String;
 
   static bool canParse(Object? obj) {
     if (obj is! Map<String, dynamic>) {
       return false;
     }
-    if (!ChecksumAlgorithm.canParse(obj['algorithm'])) {
+    if (obj['algorithm'] is! ChecksumAlgorithm) {
       return false;
     }
     if (obj['checksum'] is! String) {
@@ -933,23 +931,7 @@
 }
 
 /// Names of checksum algorithms that may be supported by a debug adapter.
-class ChecksumAlgorithm {
-  static ChecksumAlgorithm fromJson(Map<String, Object?> obj) =>
-      ChecksumAlgorithm.fromMap(obj);
-
-  ChecksumAlgorithm();
-
-  ChecksumAlgorithm.fromMap(Map<String, Object?> obj);
-
-  static bool canParse(Object? obj) {
-    if (obj is! Map<String, dynamic>) {
-      return false;
-    }
-    return true;
-  }
-
-  Map<String, Object?> toJson() => {};
-}
+typedef ChecksumAlgorithm = String;
 
 /// A ColumnDescriptor specifies what module attribute to show in a column of
 /// the ModulesView, how to format it,
@@ -1085,9 +1067,7 @@
         sortText = obj['sortText'] as String?,
         start = obj['start'] as int?,
         text = obj['text'] as String?,
-        type = obj['type'] == null
-            ? null
-            : CompletionItemType.fromJson(obj['type'] as Map<String, Object?>);
+        type = obj['type'] as CompletionItemType?;
 
   static bool canParse(Object? obj) {
     if (obj is! Map<String, dynamic>) {
@@ -1114,7 +1094,7 @@
     if (obj['text'] is! String?) {
       return false;
     }
-    if (!CompletionItemType.canParse(obj['type'])) {
+    if (obj['type'] is! CompletionItemType?) {
       return false;
     }
     return true;
@@ -1134,23 +1114,7 @@
 
 /// Some predefined types for the CompletionItem. Please note that not all
 /// clients have specific icons for all of them.
-class CompletionItemType {
-  static CompletionItemType fromJson(Map<String, Object?> obj) =>
-      CompletionItemType.fromMap(obj);
-
-  CompletionItemType();
-
-  CompletionItemType.fromMap(Map<String, Object?> obj);
-
-  static bool canParse(Object? obj) {
-    if (obj is! Map<String, dynamic>) {
-      return false;
-    }
-    return true;
-  }
-
-  Map<String, Object?> toJson() => {};
-}
+typedef CompletionItemType = String;
 
 /// Arguments for 'completions' request.
 class CompletionsArguments extends RequestArguments {
@@ -1408,10 +1372,7 @@
   });
 
   DataBreakpoint.fromMap(Map<String, Object?> obj)
-      : accessType = obj['accessType'] == null
-            ? null
-            : DataBreakpointAccessType.fromJson(
-                obj['accessType'] as Map<String, Object?>),
+      : accessType = obj['accessType'] as DataBreakpointAccessType?,
         condition = obj['condition'] as String?,
         dataId = obj['dataId'] as String,
         hitCondition = obj['hitCondition'] as String?;
@@ -1420,7 +1381,7 @@
     if (obj is! Map<String, dynamic>) {
       return false;
     }
-    if (!DataBreakpointAccessType.canParse(obj['accessType'])) {
+    if (obj['accessType'] is! DataBreakpointAccessType?) {
       return false;
     }
     if (obj['condition'] is! String?) {
@@ -1444,23 +1405,7 @@
 }
 
 /// This enumeration defines all possible access types for data breakpoints.
-class DataBreakpointAccessType {
-  static DataBreakpointAccessType fromJson(Map<String, Object?> obj) =>
-      DataBreakpointAccessType.fromMap(obj);
-
-  DataBreakpointAccessType();
-
-  DataBreakpointAccessType.fromMap(Map<String, Object?> obj);
-
-  static bool canParse(Object? obj) {
-    if (obj is! Map<String, dynamic>) {
-      return false;
-    }
-    return true;
-  }
-
-  Map<String, Object?> toJson() => {};
-}
+typedef DataBreakpointAccessType = String;
 
 /// Arguments for 'dataBreakpointInfo' request.
 class DataBreakpointInfoArguments extends RequestArguments {
@@ -2056,23 +2001,7 @@
 /// always: always breaks,
 /// unhandled: breaks when exception unhandled,
 /// userUnhandled: breaks if the exception is not handled by user code.
-class ExceptionBreakMode {
-  static ExceptionBreakMode fromJson(Map<String, Object?> obj) =>
-      ExceptionBreakMode.fromMap(obj);
-
-  ExceptionBreakMode();
-
-  ExceptionBreakMode.fromMap(Map<String, Object?> obj);
-
-  static bool canParse(Object? obj) {
-    if (obj is! Map<String, dynamic>) {
-      return false;
-    }
-    return true;
-  }
-
-  Map<String, Object?> toJson() => {};
-}
+typedef ExceptionBreakMode = String;
 
 /// An ExceptionBreakpointsFilter is shown in the UI as an filter option for
 /// configuring how exceptions are dealt with.
@@ -2370,8 +2299,7 @@
   });
 
   ExceptionOptions.fromMap(Map<String, Object?> obj)
-      : breakMode = ExceptionBreakMode.fromJson(
-            obj['breakMode'] as Map<String, Object?>),
+      : breakMode = obj['breakMode'] as ExceptionBreakMode,
         path = (obj['path'] as List?)
             ?.map((item) =>
                 ExceptionPathSegment.fromJson(item as Map<String, Object?>))
@@ -2381,7 +2309,7 @@
     if (obj is! Map<String, dynamic>) {
       return false;
     }
-    if (!ExceptionBreakMode.canParse(obj['breakMode'])) {
+    if (obj['breakMode'] is! ExceptionBreakMode) {
       return false;
     }
     if ((obj['path'] is! List ||
@@ -3002,23 +2930,7 @@
 }
 
 /// Logical areas that can be invalidated by the 'invalidated' event.
-class InvalidatedAreas {
-  static InvalidatedAreas fromJson(Map<String, Object?> obj) =>
-      InvalidatedAreas.fromMap(obj);
-
-  InvalidatedAreas();
-
-  InvalidatedAreas.fromMap(Map<String, Object?> obj);
-
-  static bool canParse(Object? obj) {
-    if (obj is! Map<String, dynamic>) {
-      return false;
-    }
-    return true;
-  }
-
-  Map<String, Object?> toJson() => {};
-}
+typedef InvalidatedAreas = String;
 
 /// Arguments for 'launch' request. Additional attributes are implementation
 /// specific.
@@ -3508,17 +3420,14 @@
   });
 
   NextArguments.fromMap(Map<String, Object?> obj)
-      : granularity = obj['granularity'] == null
-            ? null
-            : SteppingGranularity.fromJson(
-                obj['granularity'] as Map<String, Object?>),
+      : granularity = obj['granularity'] as SteppingGranularity?,
         threadId = obj['threadId'] as int;
 
   static bool canParse(Object? obj) {
     if (obj is! Map<String, dynamic>) {
       return false;
     }
-    if (!SteppingGranularity.canParse(obj['granularity'])) {
+    if (obj['granularity'] is! SteppingGranularity?) {
       return false;
     }
     if (obj['threadId'] is! int) {
@@ -5686,17 +5595,14 @@
   });
 
   StepBackArguments.fromMap(Map<String, Object?> obj)
-      : granularity = obj['granularity'] == null
-            ? null
-            : SteppingGranularity.fromJson(
-                obj['granularity'] as Map<String, Object?>),
+      : granularity = obj['granularity'] as SteppingGranularity?,
         threadId = obj['threadId'] as int;
 
   static bool canParse(Object? obj) {
     if (obj is! Map<String, dynamic>) {
       return false;
     }
-    if (!SteppingGranularity.canParse(obj['granularity'])) {
+    if (obj['granularity'] is! SteppingGranularity?) {
       return false;
     }
     if (obj['threadId'] is! int) {
@@ -5770,10 +5676,7 @@
   });
 
   StepInArguments.fromMap(Map<String, Object?> obj)
-      : granularity = obj['granularity'] == null
-            ? null
-            : SteppingGranularity.fromJson(
-                obj['granularity'] as Map<String, Object?>),
+      : granularity = obj['granularity'] as SteppingGranularity?,
         targetId = obj['targetId'] as int?,
         threadId = obj['threadId'] as int;
 
@@ -5781,7 +5684,7 @@
     if (obj is! Map<String, dynamic>) {
       return false;
     }
-    if (!SteppingGranularity.canParse(obj['granularity'])) {
+    if (obj['granularity'] is! SteppingGranularity?) {
       return false;
     }
     if (obj['targetId'] is! int?) {
@@ -5964,17 +5867,14 @@
   });
 
   StepOutArguments.fromMap(Map<String, Object?> obj)
-      : granularity = obj['granularity'] == null
-            ? null
-            : SteppingGranularity.fromJson(
-                obj['granularity'] as Map<String, Object?>),
+      : granularity = obj['granularity'] as SteppingGranularity?,
         threadId = obj['threadId'] as int;
 
   static bool canParse(Object? obj) {
     if (obj is! Map<String, dynamic>) {
       return false;
     }
-    if (!SteppingGranularity.canParse(obj['granularity'])) {
+    if (obj['granularity'] is! SteppingGranularity?) {
       return false;
     }
     if (obj['threadId'] is! int) {
@@ -6028,23 +5928,7 @@
 
 /// The granularity of one 'step' in the stepping requests 'next', 'stepIn',
 /// 'stepOut', and 'stepBack'.
-class SteppingGranularity {
-  static SteppingGranularity fromJson(Map<String, Object?> obj) =>
-      SteppingGranularity.fromMap(obj);
-
-  SteppingGranularity();
-
-  SteppingGranularity.fromMap(Map<String, Object?> obj);
-
-  static bool canParse(Object? obj) {
-    if (obj is! Map<String, dynamic>) {
-      return false;
-    }
-    return true;
-  }
-
-  Map<String, Object?> toJson() => {};
-}
+typedef SteppingGranularity = String;
 
 /// Arguments for 'terminate' request.
 class TerminateArguments extends RequestArguments {
@@ -6887,8 +6771,7 @@
 
   DataBreakpointInfoResponseBody.fromMap(Map<String, Object?> obj)
       : accessTypes = (obj['accessTypes'] as List?)
-            ?.map((item) =>
-                DataBreakpointAccessType.fromJson(item as Map<String, Object?>))
+            ?.map((item) => item as DataBreakpointAccessType)
             .toList(),
         canPersist = obj['canPersist'] as bool?,
         dataId = obj['dataId'] is String
@@ -6902,7 +6785,7 @@
     }
     if ((obj['accessTypes'] is! List ||
         (obj['accessTypes']
-            .any((item) => !DataBreakpointAccessType.canParse(item))))) {
+            .any((item) => item is! DataBreakpointAccessType)))) {
       return false;
     }
     if (obj['canPersist'] is! bool?) {
@@ -7138,8 +7021,7 @@
   });
 
   ExceptionInfoResponseBody.fromMap(Map<String, Object?> obj)
-      : breakMode = ExceptionBreakMode.fromJson(
-            obj['breakMode'] as Map<String, Object?>),
+      : breakMode = obj['breakMode'] as ExceptionBreakMode,
         description = obj['description'] as String?,
         details = obj['details'] == null
             ? null
@@ -7150,7 +7032,7 @@
     if (obj is! Map<String, dynamic>) {
       return false;
     }
-    if (!ExceptionBreakMode.canParse(obj['breakMode'])) {
+    if (obj['breakMode'] is! ExceptionBreakMode) {
       return false;
     }
     if (obj['description'] is! String?) {
@@ -7319,8 +7201,7 @@
 
   InvalidatedEventBody.fromMap(Map<String, Object?> obj)
       : areas = (obj['areas'] as List?)
-            ?.map((item) =>
-                InvalidatedAreas.fromJson(item as Map<String, Object?>))
+            ?.map((item) => item as InvalidatedAreas)
             .toList(),
         stackFrameId = obj['stackFrameId'] as int?,
         threadId = obj['threadId'] as int?;
@@ -7330,7 +7211,7 @@
       return false;
     }
     if ((obj['areas'] is! List ||
-        (obj['areas'].any((item) => !InvalidatedAreas.canParse(item))))) {
+        (obj['areas'].any((item) => item is! InvalidatedAreas)))) {
       return false;
     }
     if (obj['stackFrameId'] is! int?) {
diff --git a/pkg/dds/test/dap/integration/debug_breakpoints_test.dart b/pkg/dds/test/dap/integration/debug_breakpoints_test.dart
index ecfb94b..3038e06 100644
--- a/pkg/dds/test/dap/integration/debug_breakpoints_test.dart
+++ b/pkg/dds/test/dap/integration/debug_breakpoints_test.dart
@@ -75,6 +75,28 @@
     });
 
     test(
+        'stops at a line breakpoint and can step over (next) '
+        'when stepping granularity was included', () async {
+      final testFile = dap.createTestFile('''
+void main(List<String> args) async {
+  print('Hello!'); $breakpointMarker
+  print('Hello!'); $stepMarker
+}
+    ''');
+      final breakpointLine = lineWith(testFile, breakpointMarker);
+      final stepLine = lineWith(testFile, stepMarker);
+
+      // Hit the initial breakpoint.
+      final stop = await dap.client.hitBreakpoint(testFile, breakpointLine);
+
+      // Step and expect stopping on the next line with a 'step' stop type.
+      await Future.wait([
+        dap.client.expectStop('step', file: testFile, line: stepLine),
+        dap.client.next(stop.threadId!, granularity: 'statement'),
+      ], eagerError: true);
+    });
+
+    test(
         'stops at a line breakpoint and can step over (next) an async boundary',
         () async {
       final client = dap.client;
diff --git a/pkg/dds/test/dap/integration/test_client.dart b/pkg/dds/test/dap/integration/test_client.dart
index 08afc51..4fd429b 100644
--- a/pkg/dds/test/dap/integration/test_client.dart
+++ b/pkg/dds/test/dap/integration/test_client.dart
@@ -261,10 +261,14 @@
 
   /// Sends a next (step over) request for the given thread.
   ///
+  /// [granularity] is always ignored because the Dart debugger does not support
+  /// it (indicated in its capabilities), but it is used by tests to ensure the
+  /// adapter does not crash on the presence of it.
+  ///
   /// Returns a Future that completes when the server returns a corresponding
   /// response.
-  Future<Response> next(int threadId) =>
-      sendRequest(NextArguments(threadId: threadId));
+  Future<Response> next(int threadId, {SteppingGranularity? granularity}) =>
+      sendRequest(NextArguments(threadId: threadId, granularity: granularity));
 
   /// Sends a request to the server for variables scopes available for a given
   /// stack frame.
diff --git a/pkg/dds/tool/dap/codegen.dart b/pkg/dds/tool/dap/codegen.dart
index bd021a5..1fd68cb7 100644
--- a/pkg/dds/tool/dap/codegen.dart
+++ b/pkg/dds/tool/dap/codegen.dart
@@ -178,6 +178,15 @@
     JsonType? resolvedBaseType, {
     Map<String, String> additionalValues = const {},
   }) {
+    _writeTypeDescription(buffer, type);
+
+    // Types that are just aliases to simple value types should be written as
+    // typedefs.
+    if (type.isSimpleValue) {
+      buffer.writeln('typedef $name = ${type.asDartType()};');
+      return;
+    }
+
     // Some properties are defined in both the base and the class, because the
     // type may be narrowed, but sometimes we only want those that are defined
     // only in this class.
@@ -186,7 +195,6 @@
         if (!baseProperties.containsKey(property.key))
           property.key: property.value,
     };
-    _writeTypeDescription(buffer, type);
     buffer.write('class $name ');
     if (baseType != null) {
       buffer.write('extends ${baseType.refName} ');
@@ -466,11 +474,12 @@
   void _writeFromJsonExpression(
       IndentableStringBuffer buffer, JsonType type, String valueCode,
       {bool isOptional = false}) {
+    final baseType = type.aliasFor ?? type;
     final dartType = type.asDartType(isOptional: isOptional);
     final dartTypeNotNullable = type.asDartType();
     final nullOp = isOptional ? '?' : '';
 
-    if (type.isAny || type.isSimple) {
+    if (baseType.isAny || baseType.isSimple) {
       buffer.write('$valueCode');
       if (dartType != 'Object?') {
         buffer.write(' as $dartType');
@@ -605,6 +614,7 @@
   void _writeTypeCheckCondition(
       IndentableStringBuffer buffer, JsonType type, String valueCode,
       {required bool isOptional, bool invert = false}) {
+    final baseType = type.aliasFor ?? type;
     final dartType = type.asDartType(isOptional: isOptional);
 
     // When the expression is inverted, invert the operators so the generated
@@ -617,11 +627,11 @@
     final opOr = invert ? '&&' : '||';
     final opEvery = invert ? 'any' : 'every';
 
-    if (type.isAny) {
+    if (baseType.isAny) {
       buffer.write(opTrue);
     } else if (dartType == 'Null') {
       buffer.write('$valueCode $opEquals null');
-    } else if (type.isSimple) {
+    } else if (baseType.isSimple) {
       buffer.write('$valueCode $opIs $dartType');
     } else if (type.isList) {
       buffer.write('($valueCode $opIs List');
diff --git a/pkg/dds/tool/dap/json_schema_extensions.dart b/pkg/dds/tool/dap/json_schema_extensions.dart
index a84cafd..6e3bf69 100644
--- a/pkg/dds/tool/dap/json_schema_extensions.dart
+++ b/pkg/dds/tool/dap/json_schema_extensions.dart
@@ -72,9 +72,11 @@
         ? _toDartType(dollarRef!)
         : oneOf != null
             ? _toDartUnionType(oneOf!.map((item) => item.asDartType()).toList())
-            : type!.valueEquals('array')
-                ? 'List<${items!.asDartType()}>'
-                : type!.map(_toDartType, _toDartUnionType);
+            : type == null
+                ? refName
+                : type!.valueEquals('array')
+                    ? 'List<${items!.asDartType()}>'
+                    : type!.map(_toDartType, _toDartUnionType);
 
     return isOptional ? '$dartType?' : dartType;
   }
@@ -85,7 +87,21 @@
   /// Whether this type represents a List.
   bool get isList => type?.valueEquals('array') ?? false;
 
-  /// Whether this type is a simple value that does not need any special handling.
+  /// Whether this type is a simple value like `String`, `bool`, `int`.
+  bool get isSimpleValue => isSimple && asDartType() != 'Map<String, Object?>';
+
+  /// If this type is an alias to a simple value type, returns that type.
+  /// Otherwise, returns `null`.
+  JsonType? get aliasFor {
+    final targetType = dollarRef != null ? root.typeFor(this) : null;
+    if (targetType == null) {
+      return null;
+    }
+    return targetType.isSimpleValue ? targetType : null;
+  }
+
+  /// Whether this type is a simple type that needs no special handling for
+  /// deserialisation (such as `String`, `bool`, `int`, `Map<String, Object?>`).
   bool get isSimple {
     const _dartSimpleTypes = {
       'bool',
@@ -95,7 +111,8 @@
       'Map<String, Object?>',
       'Null',
     };
-    return _dartSimpleTypes.contains(asDartType());
+    return type != null &&
+        _dartSimpleTypes.contains(type!.map(_toDartType, _toDartUnionType));
   }
 
   /// Whether this type is a Union type using JSON schema's "oneOf" of where its
diff --git a/pkg/dev_compiler/lib/src/kernel/target.dart b/pkg/dev_compiler/lib/src/kernel/target.dart
index 0a3e3ff..fa648bb 100644
--- a/pkg/dev_compiler/lib/src/kernel/target.dart
+++ b/pkg/dev_compiler/lib/src/kernel/target.dart
@@ -153,6 +153,12 @@
   bool get enableNoSuchMethodForwarders => true;
 
   @override
+  void performOutlineTransformations(Component component, CoreTypes coreTypes) {
+    component
+        .accept(StaticInteropStubCreator(StaticInteropClassEraser(coreTypes)));
+  }
+
+  @override
   void performModularTransformationsOnLibraries(
       Component component,
       CoreTypes coreTypes,
diff --git a/pkg/front_end/lib/src/kernel_generator_impl.dart b/pkg/front_end/lib/src/kernel_generator_impl.dart
index b8eb49e..0a41e53 100644
--- a/pkg/front_end/lib/src/kernel_generator_impl.dart
+++ b/pkg/front_end/lib/src/kernel_generator_impl.dart
@@ -188,7 +188,8 @@
     // summaries without building a full component (at this time, that's
     // the only need we have for these transformations).
     if (!buildComponent) {
-      options.target.performOutlineTransformations(trimmedSummaryComponent);
+      options.target.performOutlineTransformations(
+          trimmedSummaryComponent, kernelTarget.loader.coreTypes);
       options.ticker.logMs("Transformed outline");
     }
     // Don't include source (but do add it above to include importUris).
diff --git a/pkg/frontend_server/lib/compute_kernel.dart b/pkg/frontend_server/lib/compute_kernel.dart
index 918c891..d9334e3 100644
--- a/pkg/frontend_server/lib/compute_kernel.dart
+++ b/pkg/frontend_server/lib/compute_kernel.dart
@@ -396,7 +396,8 @@
         incrementalComponent.problemsAsJson = null;
         incrementalComponent.setMainMethodAndMode(
             null, true, incrementalComponent.mode);
-        target.performOutlineTransformations(incrementalComponent);
+        target.performOutlineTransformations(
+            incrementalComponent, incrementalCompilerResult.coreTypes!);
         makeStable(incrementalComponent);
         return Future.value(fe.serializeComponent(incrementalComponent,
             includeSources: false, includeOffsets: false));
diff --git a/pkg/kernel/lib/target/targets.dart b/pkg/kernel/lib/target/targets.dart
index 9c46dbb..ced147d 100644
--- a/pkg/kernel/lib/target/targets.dart
+++ b/pkg/kernel/lib/target/targets.dart
@@ -318,7 +318,8 @@
   /// transformation is not applied when compiling full kernel programs to
   /// prevent affecting the internal invariants of the compiler and accidentally
   /// slowing down compilation.
-  void performOutlineTransformations(Component component) {}
+  void performOutlineTransformations(
+      Component component, CoreTypes coreTypes) {}
 
   /// Perform target-specific transformations on the given libraries that must
   /// run before constant evaluation.
@@ -1010,8 +1011,8 @@
   }
 
   @override
-  void performOutlineTransformations(Component component) {
-    _target.performOutlineTransformations(component);
+  void performOutlineTransformations(Component component, CoreTypes coreTypes) {
+    _target.performOutlineTransformations(component, coreTypes);
   }
 
   @override
@@ -1076,8 +1077,8 @@
   bool get excludeNonSources;
 
   @override
-  void performOutlineTransformations(Component component) {
-    super.performOutlineTransformations(component);
+  void performOutlineTransformations(Component component, CoreTypes coreTypes) {
+    super.performOutlineTransformations(component, coreTypes);
     if (!excludeNonSources) return;
 
     List<Library> libraries = new List.of(component.libraries);
diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status
index fb489d4..e0a072b 100644
--- a/runtime/tests/vm/vm.status
+++ b/runtime/tests/vm/vm.status
@@ -262,6 +262,7 @@
 dart/appjit*: SkipSlow # DFE too slow
 dart/b162922506_test: SkipSlow # Generates large input file
 dart/data_uri_spawn_test: Skip # Please triage.
+dart/enable_mirrors_test: SkipByDesign # Separate kernel compilation doesn't respect VM options.
 dart/isolates/fast_object_copy_test*: SkipSlow
 dart/minimal_kernel_test: SkipSlow # gen_kernel is too slow on simulated architectures
 dart/null_safety_autodetection_in_kernel_compiler_test: SkipSlow # gen_kernel is too slow on simulated architectures
@@ -269,6 +270,7 @@
 dart_2/appjit*: SkipSlow # DFE too slow
 dart_2/b162922506_test: SkipSlow # Generates large input file
 dart_2/data_uri_spawn_test: Skip # Please triage.
+dart_2/enable_mirrors_test: SkipByDesign # Separate kernel compilation doesn't respect VM options.
 dart_2/isolates/fast_object_copy_test*: SkipSlow
 dart_2/minimal_kernel_test: SkipSlow # gen_kernel is too slow on simulated architectures
 dart_2/null_safety_autodetection_in_kernel_compiler_test: SkipSlow # gen_kernel is too slow on simulated architectures
diff --git a/tests/modular/static_interop_erasure/main.dart b/tests/modular/static_interop_erasure/main.dart
index 80ccc3b..da54aa9 100644
--- a/tests/modular/static_interop_erasure/main.dart
+++ b/tests/modular/static_interop_erasure/main.dart
@@ -6,5 +6,5 @@
 
 void main() {
   setUp();
-  var staticJs = StaticJSClass.factory();
+  var staticJs = StaticJSClass.factory(StaticJSClass());
 }
diff --git a/tests/modular/static_interop_erasure/static_interop.dart b/tests/modular/static_interop_erasure/static_interop.dart
index a5c93c4..7f25a79 100644
--- a/tests/modular/static_interop_erasure/static_interop.dart
+++ b/tests/modular/static_interop_erasure/static_interop.dart
@@ -14,7 +14,7 @@
 @staticInterop
 class StaticJSClass {
   external StaticJSClass();
-  factory StaticJSClass.factory() {
+  factory StaticJSClass.factory(StaticJSClass _) {
     return StaticJSClass();
   }
 }
diff --git a/tools/VERSION b/tools/VERSION
index 4a55559..6f615bc 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 18
 PATCH 0
-PRERELEASE 203
+PRERELEASE 204
 PRERELEASE_PATCH 0
\ No newline at end of file