Version 2.17.0-275.0.dev

Merge commit '837ee17b43aa2b5601cad637b9348cc8fd848d99' into 'dev'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ce5d29a..7cfff32 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,24 @@
 
 - Add `ref=` and `[]=` methods to the `StructPointer` and `UnionPointer`
   extensions. They copy a compound instance into a native memory region.
+- Add `AbiSpecificInteger`s for common C types:
+  - `char`
+  - `unsigned char`
+  - `signed char`
+  - `short`
+  - `unsigned short`
+  - `int`
+  - `unsigned int`
+  - `long`
+  - `unsigned long`
+  - `long long`
+  - `unsigned long long`
+  - `uintptr_t`
+  - `size_t`
+  - `wchar_t`
+- Add `NativeFinalizer` which can potentially detect when objects are
+  "garbage collected". `NativeFinalizer`s run native code where `dart:core`'s
+  `Finalizer`s run Dart code on finalization.
 
 #### `dart:html`
 
@@ -172,6 +190,11 @@
   error with an existing stack trace, instead of creating
   a new stack trace.
 
+#### `dart:ffi`
+
+- Add `Abi` and `AbiSpecificInteger`. These enable specifying integers which
+  have different sizes/signs per ABI (hardware and OS combination).
+
 #### `dart:io`
 
 - **Security advisory**
@@ -608,6 +631,10 @@
   Code catching the class should move to catching `Error` instead
   (or, for integers, check first for whether it's dividing by zero).
 
+#### `dart:ffi`
+
+- Add `Bool` native type.
+
 #### `dart:io`
 
 - **Breaking change** [#46875](https://github.com/dart-lang/sdk/issues/46875):
@@ -912,8 +939,9 @@
 
 #### `dart:ffi`
 
-- Adds the `DynamicLibrary.providesSymbol` function to check whether a symbol is
+- Add the `DynamicLibrary.providesSymbol` function to check whether a symbol is
   available in a dynamic library.
+- Add `Union` native type for interacting with unions in native memory.
 
 #### `dart:html`
 
@@ -1210,6 +1238,11 @@
 
 - Added `serverWebSocketUri` property to `ServiceProtocolInfo`.
 
+#### `dart:ffi`
+
+- Add `Packed` for interacting with packed structs in native memory.
+- Add `Array` for interacting with structs with inline arrays.
+
 ### Dart VM
 
 ### Tools
@@ -1350,6 +1383,26 @@
 - Add `Set.unmodifiable()` constructor, which allows users to create
   unmodifiable `Set` instances.
 
+#### `dart:ffi`
+
+- **Breaking change** [#44621][]: Invocations with a generic `T` of `sizeOf<T>`,
+  `Pointer<T>.elementAt()`, `Pointer<T extends Struct>.ref`, and
+  `Pointer<T extends Struct>[]` are being deprecated in the current stable
+  release (2.12), and are planned to be fully removed in the following stable
+  release (2.13). Consequently, `allocate` in `package:ffi` will no longer be
+  able to invoke `sizeOf<T>` generically, and will be deprecated as well.
+  Instead, the `Allocator` it is introduced to `dart:ffi`, and also requires a
+  constant `T` on invocations. For migration notes see the breaking change
+  request.
+
+- **Breaking change** [#44622][]: Subtypes of `Struct` without any native member
+  are being deprecated in the current stable release (2.12), and are planned to
+  be fully removed in the following stable release (2.13). Migrate opaque types
+  to extend `Opaque` rather than `Struct`.
+
+[#44621]: https://github.com/dart-lang/sdk/issues/44621
+[#44622]: https://github.com/dart-lang/sdk/issues/44622
+
 #### `dart:io`
 
 - `HttpRequest` now correctly follows HTTP 308 redirects
@@ -1385,26 +1438,6 @@
 
 [#42312]: https://github.com/dart-lang/sdk/issues/42312
 
-### Foreign Function Interface (`dart:ffi`)
-
-- **Breaking change** [#44621][]: Invocations with a generic `T` of `sizeOf<T>`,
-  `Pointer<T>.elementAt()`, `Pointer<T extends Struct>.ref`, and
-  `Pointer<T extends Struct>[]` are being deprecated in the current stable
-  release (2.12), and are planned to be fully removed in the following stable
-  release (2.13). Consequently, `allocate` in `package:ffi` will no longer be
-  able to invoke `sizeOf<T>` generically, and will be deprecated as well.
-  Instead, the `Allocator` it is introduced to `dart:ffi`, and also requires a
-  constant `T` on invocations. For migration notes see the breaking change
-  request.
-
-- **Breaking change** [#44622][]: Subtypes of `Struct` without any native member
-  are being deprecated in the current stable release (2.12), and are planned to
-  be fully removed in the following stable release (2.13). Migrate opaque types
-  to extend `Opaque` rather than `Struct`.
-
-[#44621]: https://github.com/dart-lang/sdk/issues/44621
-[#44622]: https://github.com/dart-lang/sdk/issues/44622
-
 ### Dart2JS
 
 - Remove `--no-defer-class-types` and `--no-new-deferred-split`.
@@ -2028,6 +2061,19 @@
   parameter provided in the constructor. This will be used by tooling to allow
   for better filtering of timeline events.
 
+#### `dart:ffi`
+
+- **Breaking change**: Changed `Pointer.asFunction()` and
+  `DynamicLibrary.lookupFunction()` to extension methods. Invoking them
+  dynamically previously already threw an exception, so the runtime behavior
+  stays the same. However, the extension methods are only visible if `dart:ffi`
+  is imported directly. This breaks code where `dart:ffi` is not directly
+  imported. To fix, add:
+
+  ```dart
+  import 'dart:ffi';
+  ```
+
 #### `dart:html`
 
 - **Breaking change** [#39627][]: Changed the return type of several HTML native
@@ -2167,19 +2213,6 @@
   - `Dart_IsNonNullableType()`
   - `Dart_IsNullableType()`
 
-### Foreign Function Interface (`dart:ffi`)
-
-- **Breaking change**: Changed `Pointer.asFunction()` and
-  `DynamicLibrary.lookupFunction()` to extension methods. Invoking them
-  dynamically previously already threw an exception, so the runtime behavior
-  stays the same. However, the extension methods are only visible if `dart:ffi`
-  is imported directly. This breaks code where `dart:ffi` is not directly
-  imported. To fix, add:
-
-  ```dart
-  import 'dart:ffi';
-  ```
-
 ### Tools
 
 #### Dart Dev Compiler (DDC)
@@ -2561,18 +2594,7 @@
 - Added optional `parent` parameter to `TimelineTask` constructor to allow for
   linking of asynchronous timeline events in the DevTools timeline view.
 
-#### `dart:io`
-
-- Added `enableTimelineLogging` property to `HttpClient` which, when enabled,
-  will post HTTP connection and request information to the developer timeline
-  for all `HttpClient` instances.
-
-### Dart VM
-
-- Added a new tool for AOT compiling Dart programs to native, self-contained
-  executables. See https://dart.dev/tools/dart2native for additional details.
-
-### Foreign Function Interface (`dart:ffi`)
+#### `dart:ffi`
 
 - **Breaking change**: The API now makes use of static extension members. Static
   extension members enable the `dart:ffi` API to be more precise with types, and
@@ -2592,6 +2614,17 @@
 - The dartanalyzer (commandline and IDEs) now reports `dart:ffi` static errors.
 - Callbacks are now supported in AOT (ahead-of-time) compiled code.
 
+#### `dart:io`
+
+- Added `enableTimelineLogging` property to `HttpClient` which, when enabled,
+  will post HTTP connection and request information to the developer timeline
+  for all `HttpClient` instances.
+
+### Dart VM
+
+- Added a new tool for AOT compiling Dart programs to native, self-contained
+  executables. See https://dart.dev/tools/dart2native for additional details.
+
 ### Dart for the Web
 
 #### Dart Dev Compiler (DDC)
diff --git a/DEPS b/DEPS
index fa7dad3..e1d4793 100644
--- a/DEPS
+++ b/DEPS
@@ -44,7 +44,7 @@
   # co19 is a cipd package. Use update.sh in tests/co19[_2] to update these
   # hashes. It requires access to the dart-build-access group, which EngProd
   # has.
-  "co19_rev": "7baaec7cf49188b92ceed2af98beb1caf8939756",
+  "co19_rev": "6604c07f70757e9d081f64a08ab36f5b9aeaf937",
   # This line prevents conflicts when both packages are rolled simultaneously.
   "co19_2_rev": "b2034a17609472e374623f3dbe0efd9f5cb258af",
 
@@ -141,7 +141,7 @@
   "pool_rev": "7abe634002a1ba8a0928eded086062f1307ccfae",
   "process_rev": "56ece43b53b64c63ae51ec184b76bd5360c28d0b",
   "protobuf_rev": "c1eb6cb51af39ccbaa1a8e19349546586a5c8e31",
-  "pub_rev": "a3a102a549388a6dbfecc9252fabb618f9a2f5f7",
+  "pub_rev": "a949b329b1b51f5f3973a790e0a0a45897d837de",
   "pub_semver_rev": "ea6c54019948dc03042c595ce9413e17aaf7aa38",
   "root_certificates_rev": "692f6d6488af68e0121317a9c2c9eb393eb0ee50",
   "rust_revision": "b7856f695d65a8ebc846754f97d15814bcb1c244",
@@ -150,7 +150,7 @@
   "shelf_proxy_tag": "ccd311f64d8689e7a145d703ba267975d6df9e28", # 1.0.0
   "shelf_rev": "78ac724a7944700340a3cef28c84bccbe62e9367",
   "shelf_web_socket_rev": "24fb8a04befa75a94ac63a27047b231d1a22aab4",
-  "source_map_stack_trace_rev": "80709f2d2fe5086ab50d744a28a2d26ea4384a1b",
+  "source_map_stack_trace_rev": "8eabd96b1811e30a11d3c54c9b4afae4fb72e98f",
   "source_maps-0.9.4_rev": "38524",
   "source_maps_rev": "6499ee3adac8d469e2953e2e8ba4bdb4c2fbef90",
   "source_span_rev": "dc189b455d823e2919667f6c5dcb41ab7483bce0",
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart b/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
index e94c61e..fddd932 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
@@ -25,16 +25,16 @@
   macroDeclarations
       .forEach((String macroImport, Map<String, List<String>> macroClasses) {
     imports.writeln('import \'$macroImport\';');
+    constructorEntries.writeln("Uri.parse('$macroImport'): {");
     macroClasses.forEach((String macroName, List<String> constructorNames) {
-      constructorEntries
-          .writeln("MacroClassIdentifierImpl(Uri.parse('$macroImport'), "
-              "'$macroName'): {");
+      constructorEntries.writeln("'$macroName': {");
       for (String constructor in constructorNames) {
         constructorEntries.writeln("'$constructor': "
             "$macroName.${constructor.isEmpty ? 'new' : constructor},");
       }
       constructorEntries.writeln('},');
     });
+    constructorEntries.writeln("}");
   });
   return template
       .replaceFirst(_importMarker, imports.toString())
@@ -169,8 +169,8 @@
   sendResult(serializer);
 }
 
-/// Maps macro identifiers to constructors.
-final _macroConstructors = <MacroClassIdentifierImpl, Map<String, Macro Function()>>{
+/// Maps libraries by uri to macros by name, and then constructors by name.
+final _macroConstructors = <Uri, Map<String, Map<String, Macro Function()>>>{
   $_macroConstructorEntriesMarker
 };
 
@@ -181,15 +181,21 @@
 Future<SerializableResponse> _instantiateMacro(
     InstantiateMacroRequest request) async {
   try {
-    var constructors = _macroConstructors[request.macroClass];
-    if (constructors == null) {
-      throw new ArgumentError('Unrecognized macro class \${request.macroClass}');
+    var classes = _macroConstructors[request.library];
+    if (classes == null) {
+      throw new ArgumentError('Unrecognized macro library \${request.library}');
     }
-    var constructor = constructors[request.constructorName];
+    var constructors = classes[request.name];
+    if (constructors == null) {
+      throw new ArgumentError(
+          'Unrecognized macro class \${request.name} for library '
+          '\${request.library}');
+    }
+    var constructor = constructors[request.constructor];
     if (constructor == null) {
       throw new ArgumentError(
-          'Unrecognized constructor name "\${request.constructorName}" for '
-          'macro class "\${request.macroClass}".');
+          'Unrecognized constructor name "\${request.constructor}" for '
+          'macro class "\${request.name}".');
     }
 
     var instance = Function.apply(constructor, request.arguments.positional, {
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart
index f178c40..8a8e8f2 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'api.dart';
+// ignore: unused_import
 import 'bootstrap.dart'; // For doc comments only.
 import 'executor/serialization.dart';
 
@@ -14,32 +15,15 @@
 /// during macro discovery and expansion, and unifies how augmentation libraries
 /// are produced.
 abstract class MacroExecutor {
-  /// Invoked when an implementation discovers a new macro definition in a
-  /// [library] with [name], and prepares this executor to run the macro.
-  ///
-  /// May be invoked more than once for the same macro, which will cause the
-  /// macro to be re-loaded. Previous [MacroClassIdentifier]s and
-  /// [MacroInstanceIdentifier]s given for this macro will be invalid after
-  /// that point and should be discarded.
-  ///
-  /// The [precompiledKernelUri] if passed must point to a kernel program for
-  /// the given macro. A bootstrap Dart program can be generated with the
-  /// [bootstrapMacroIsolate] function, and the result should be compiled to
-  /// kernel and passed here.
-  ///
-  /// Some implementations may require [precompiledKernelUri] to be passed, and
-  /// will throw an [UnsupportedError] if it is not.
-  ///
-  /// Throws an exception if the macro fails to load.
-  Future<MacroClassIdentifier> loadMacro(Uri library, String name,
-      {Uri? precompiledKernelUri});
-
-  /// Creates an instance of [macroClass] in the executor, and returns an
-  /// identifier for that instance.
+  /// Creates an instance of the macro [name] from [library] in the executor,
+  /// and returns an identifier for that instance.
   ///
   /// Throws an exception if an instance is not created.
+  ///
+  /// Instances may be re-used throughout a single build, but should be
+  /// re-created on subsequent builds (even incremental ones).
   Future<MacroInstanceIdentifier> instantiateMacro(
-      MacroClassIdentifier macroClass, String constructor, Arguments arguments);
+      Uri library, String name, String constructor, Arguments arguments);
 
   /// Runs the type phase for [macro] on a given [declaration].
   ///
@@ -93,7 +77,7 @@
 
   /// Tell the executor to shut down and clean up any resources it may have
   /// allocated.
-  void close();
+  Future<void> close();
 }
 
 /// The arguments passed to a macro constructor.
@@ -266,12 +250,6 @@
   topLevelMember,
 }
 
-/// An opaque identifier for a macro class, retrieved by
-/// [MacroExecutor.loadMacro].
-///
-/// Used to execute or reload this macro in the future.
-abstract class MacroClassIdentifier implements Serializable {}
-
 /// An opaque identifier for an instance of a macro class, retrieved by
 /// [MacroExecutor.instantiateMacro].
 ///
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/executor_base.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/executor_base.dart
index 5609883..2e3c5d2 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/executor_base.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/executor_base.dart
@@ -392,21 +392,11 @@
 
   @override
   Future<MacroInstanceIdentifier> instantiateMacro(
-          MacroClassIdentifier macroClass,
-          String constructor,
-          Arguments arguments) =>
+          Uri library, String name, String constructor, Arguments arguments) =>
       _sendRequest((zoneId) => new InstantiateMacroRequest(
-          macroClass, constructor, arguments, RemoteInstance.uniqueId,
+          library, name, constructor, arguments, RemoteInstance.uniqueId,
           serializationZoneId: zoneId));
 
-  /// These calls are handled by the higher level executor.
-  @override
-  Future<MacroClassIdentifier> loadMacro(Uri library, String name,
-          {Uri? precompiledKernelUri}) =>
-      throw new StateError(
-          'This executor should be wrapped in a MultiMacroExecutor which will '
-          'handle load requests.');
-
   /// Sends [serializer.result] to [sendPort], possibly wrapping it in a
   /// [TransferableTypedData] object.
   void sendResult(Serializer serializer);
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/isolated_executor.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/isolated_executor.dart
index ae15038..35facb9 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/isolated_executor.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/isolated_executor.dart
@@ -6,30 +6,26 @@
 import 'dart:isolate';
 import 'dart:typed_data';
 
-import '../executor/multi_executor.dart';
 import '../executor/executor_base.dart';
 import '../executor/serialization.dart';
 import '../executor.dart';
 
-/// Returns a [MacroExecutor] which loads macros into isolates using precompiled
-/// kernel files and communicates with that isolate using [serializationMode].
+/// Spawns a [MacroExecutor] as an isolate by passing [uriToSpawn] to
+/// [Isolate.spawnUri], and communicating using [serializationMode].
 ///
-/// The [serializationMode] must be a `server` variant, and any precompiled
-/// programs must use the corresponding `client` variant.
+/// The [uriToSpawn] can be any valid Uri for [Isolate.spawnUri].
+///
+/// Both [arguments] and [packageConfigUri] will be forwarded to
+/// [Isolate.spawnUri] if provided.
+///
+/// The [serializationMode] must be a `server` variant, and [uriToSpawn] must
+/// use the corresponding `client` variant.
 ///
 /// This is the only public api exposed by this library.
-Future<MacroExecutor> start(SerializationMode serializationMode) async =>
-    new MultiMacroExecutor((Uri library, String name,
-        {Uri? precompiledKernelUri}) {
-      if (precompiledKernelUri == null) {
-        throw new UnsupportedError(
-            'This environment requires a non-null `precompiledKernelUri` to be '
-            'passed when loading macros.');
-      }
-
-      return _SingleIsolatedMacroExecutor.start(
-          library, name, precompiledKernelUri, serializationMode);
-    });
+Future<MacroExecutor> start(SerializationMode serializationMode, Uri uriToSpawn,
+        {List<String> arguments = const [], Uri? packageConfigUri}) async =>
+    _SingleIsolatedMacroExecutor.start(
+        uriToSpawn, serializationMode, arguments, packageConfigUri);
 
 /// Actual implementation of the isolate based macro executor.
 class _SingleIsolatedMacroExecutor extends ExternalMacroExecutorBase {
@@ -48,11 +44,15 @@
       : super(
             messageStream: messageStream, serializationMode: serializationMode);
 
-  static Future<_SingleIsolatedMacroExecutor> start(Uri library, String name,
-      Uri precompiledKernelUri, SerializationMode serializationMode) async {
+  static Future<_SingleIsolatedMacroExecutor> start(
+      Uri uriToSpawn,
+      SerializationMode serializationMode,
+      List<String> arguments,
+      Uri? packageConfig) async {
     ReceivePort receivePort = new ReceivePort();
-    Isolate isolate =
-        await Isolate.spawnUri(precompiledKernelUri, [], receivePort.sendPort);
+    Isolate isolate = await Isolate.spawnUri(
+        uriToSpawn, arguments, receivePort.sendPort,
+        packageConfig: packageConfig);
     Completer<SendPort> sendPortCompleter = new Completer();
     StreamController<Object> messageStreamController =
         new StreamController(sync: true);
@@ -79,7 +79,7 @@
   }
 
   @override
-  void close() => onClose();
+  Future<void> close() => new Future.sync(onClose);
 
   /// Sends the [Serializer.result] to [sendPort], possibly wrapping it in a
   /// [TransferableTypedData] object.
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/multi_executor.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/multi_executor.dart
index 654669d..e5b11af 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/multi_executor.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/multi_executor.dart
@@ -7,29 +7,91 @@
 import '../api.dart';
 import '../executor/augmentation_library.dart';
 import '../executor/introspection_impls.dart';
-import '../executor/response_impls.dart';
 import '../executor.dart';
 
 /// A [MacroExecutor] implementation which delegates most work to other
 /// executors which are spawned through a provided callback.
 class MultiMacroExecutor extends MacroExecutor with AugmentationLibraryBuilder {
-  /// Individual executors indexed by [MacroClassIdentifier] or
-  /// [MacroInstanceIdentifier].
-  final _executors = <Object, MacroExecutor>{};
+  /// Executors by [MacroInstanceIdentifier].
+  ///
+  /// Using an expando means we don't have to worry about cleaning  up instances
+  /// for executors that were shut down.
+  final Expando<ExecutorFactoryToken> _instanceExecutors = new Expando();
 
-  /// The function to spawn an actual macro executor for a given [loadMacro]
-  /// request.
-  final Future<MacroExecutor> Function(Uri library, String name,
-      {Uri? precompiledKernelUri}) _spawnExecutor;
+  /// Registered factories for starting up a new macro executor for a library.
+  final Map<Uri, ExecutorFactoryToken> _libraryExecutorFactories = {};
 
-  MultiMacroExecutor(this._spawnExecutor);
+  /// All known registered executor factories.
+  final Set<ExecutorFactoryToken> _executorFactoryTokens = {};
 
-  @override
-  void close() {
-    for (MacroExecutor executor in _executors.values) {
-      executor.close();
+  /// Whether or not an executor factory for [library] is currently registered.
+  bool libraryIsRegistered(Uri library) =>
+      _libraryExecutorFactories.containsKey(library);
+
+  /// Registers a [factory] which can produce a [MacroExecutor] that can be
+  /// used to run any macro defined in [libraries].
+  ///
+  /// Throws an [ArgumentError] if a library in [libraries] already has a
+  /// factory registered.
+  ///
+  /// Returns a token which can be used to shut down any executors spawned in
+  /// this way via [unregisterExecutorFactory].
+  ExecutorFactoryToken registerExecutorFactory(
+      FutureOr<MacroExecutor> Function() factory, Set<Uri> libraries) {
+    ExecutorFactoryToken token = new ExecutorFactoryToken._(factory, libraries);
+    _executorFactoryTokens.add(token);
+    for (Uri library in libraries) {
+      if (_libraryExecutorFactories.containsKey(library)) {
+        throw new ArgumentError(
+            'Attempted to register a macro executor factory for library '
+            '$library which already has one assigned.');
+      }
+      _libraryExecutorFactories[library] = token;
     }
-    _executors.clear();
+    return token;
+  }
+
+  /// Unregisters [token] for all [libraries].
+  ///
+  /// If [libraries] is not passed (or `null`), then the token is unregistered
+  /// for all libraries.
+  ///
+  /// If no libraries are registered for [token] after this call, then the
+  /// executor mapped to [token] will be shut down and the token will be freed.
+  ///
+  /// This should be called whenever the executors might be stale, or as an
+  /// optimization to shut them down when they are known to be not used any
+  /// longer.
+  Future<void> unregisterExecutorFactory(ExecutorFactoryToken token,
+      {Set<Uri>? libraries}) async {
+    bool shouldClose;
+    if (libraries == null) {
+      libraries = token._libraries;
+      shouldClose = true;
+    } else {
+      token._libraries.removeAll(libraries);
+      shouldClose = token._libraries.isEmpty;
+    }
+
+    for (Uri library in libraries) {
+      _libraryExecutorFactories.remove(library);
+    }
+
+    if (shouldClose) {
+      _executorFactoryTokens.remove(token);
+      token._libraries.clear();
+      await token._close();
+    }
+  }
+
+  /// Shuts down all executors, but does not clear [_libraryExecutorFactories]
+  /// or [_executorFactoryTokens].
+  @override
+  Future<void> close() {
+    Future done = Future.wait([
+      for (ExecutorFactoryToken token in _executorFactoryTokens) token._close(),
+    ]);
+    return done;
   }
 
   @override
@@ -39,8 +101,9 @@
           IdentifierResolver identifierResolver,
           TypeResolver typeResolver,
           ClassIntrospector classIntrospector) =>
-      _executors[macro]!.executeDeclarationsPhase(macro, declaration,
-          identifierResolver, typeResolver, classIntrospector);
+      _instanceExecutors[macro]!._withInstance((executor) =>
+          executor.executeDeclarationsPhase(macro, declaration,
+              identifierResolver, typeResolver, classIntrospector));
 
   @override
   Future<MacroExecutionResult> executeDefinitionsPhase(
@@ -51,43 +114,75 @@
           ClassIntrospector classIntrospector,
           TypeDeclarationResolver typeDeclarationResolver,
           TypeInferrer typeInferrer) =>
-      _executors[macro]!.executeDefinitionsPhase(
-          macro,
-          declaration,
-          identifierResolver,
-          typeResolver,
-          classIntrospector,
-          typeDeclarationResolver,
-          typeInferrer);
+      _instanceExecutors[macro]!._withInstance((executor) =>
+          executor.executeDefinitionsPhase(
+              macro,
+              declaration,
+              identifierResolver,
+              typeResolver,
+              classIntrospector,
+              typeDeclarationResolver,
+              typeInferrer));
 
   @override
   Future<MacroExecutionResult> executeTypesPhase(MacroInstanceIdentifier macro,
           DeclarationImpl declaration, IdentifierResolver identifierResolver) =>
-      _executors[macro]!
-          .executeTypesPhase(macro, declaration, identifierResolver);
+      _instanceExecutors[macro]!._withInstance((executor) =>
+          executor.executeTypesPhase(macro, declaration, identifierResolver));
 
   @override
   Future<MacroInstanceIdentifier> instantiateMacro(
-      MacroClassIdentifier macroClass,
-      String constructor,
-      Arguments arguments) async {
-    MacroExecutor executor = _executors[macroClass]!;
-    MacroInstanceIdentifier instance =
-        await executor.instantiateMacro(macroClass, constructor, arguments);
-    _executors[instance] = executor;
-    return instance;
+      Uri library, String name, String constructor, Arguments arguments) {
+    ExecutorFactoryToken? token = _libraryExecutorFactories[library];
+    if (token == null) {
+      throw new ArgumentError(
+          'No executor registered to run macros from $library');
+    }
+    return token._withInstance((executor) async {
+      MacroInstanceIdentifier instance = await executor.instantiateMacro(
+          library, name, constructor, arguments);
+      _instanceExecutors[instance] = token;
+      return instance;
+    });
+  }
+}
+
+/// A token to track registered [MacroExecutor] factories.
+///
+/// Used to unregister them later on, and also handles bookkeeping for the
+/// factory and actual instances.
+class ExecutorFactoryToken {
+  final FutureOr<MacroExecutor> Function() _factory;
+  FutureOr<MacroExecutor>? _instance;
+  final Set<Uri> _libraries;
+
+  ExecutorFactoryToken._(this._factory, this._libraries);
+
+  /// Runs [callback] with an actual instance once available.
+  ///
+  /// This will spin up an instance if one is not currently running.
+  Future<T> _withInstance<T>(Future<T> Function(MacroExecutor) callback) {
+    FutureOr<MacroExecutor>? instance = _instance;
+    if (instance == null) {
+      instance = _instance = _factory();
+    }
+    if (instance is Future<MacroExecutor>) {
+      return instance.then(callback);
+    } else {
+      return callback(instance);
+    }
   }
 
-  @override
-  Future<MacroClassIdentifier> loadMacro(Uri library, String name,
-      {Uri? precompiledKernelUri}) async {
-    MacroClassIdentifier identifier =
-        new MacroClassIdentifierImpl(library, name);
-    _executors.remove(identifier)?.close();
-
-    MacroExecutor executor = await _spawnExecutor(library, name,
-        precompiledKernelUri: precompiledKernelUri);
-    _executors[identifier] = executor;
-    return identifier;
+  /// Closes [_instance] if non-null, and sets it to `null`.
+  Future<void> _close() async {
+    FutureOr<MacroExecutor>? instance = _instance;
+    _instance = null;
+    if (instance != null) {
+      if (instance is Future<MacroExecutor>) {
+        await (await instance).close();
+      } else {
+        await instance.close();
+      }
+    }
   }
 }
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/process_executor.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/process_executor.dart
index 07b02f0..7205ecd 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/process_executor.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/process_executor.dart
@@ -10,39 +10,29 @@
 import 'package:_fe_analyzer_shared/src/macros/executor/protocol.dart';
 
 import '../executor/message_grouper.dart';
-import '../executor/multi_executor.dart';
 import '../executor/executor_base.dart';
 import '../executor/serialization.dart';
 import '../executor.dart';
 
-/// Returns a [MacroExecutor] which loads macros as separate processes using
-/// precompiled binaries and communicates with that program using
-/// [serializationMode].
+/// Spawns a [MacroExecutor] as a separate process, by running [program] with
+/// [arguments], and communicating using [serializationMode].
 ///
-/// The [serializationMode] must be a `server` variant, and any precompiled
-/// programs spawned must use the corresponding `client` variant.
+/// The [serializationMode] must be a `server` variant, and [program] must use
+/// the corresponding `client` variant.
 ///
 /// This is the only public api exposed by this library.
 Future<MacroExecutor> start(SerializationMode serializationMode,
-        CommunicationChannel communicationChannel) async =>
-    new MultiMacroExecutor((Uri library, String name,
-        {Uri? precompiledKernelUri}) {
-      // TODO: We actually assume this is a full precompiled AOT binary, and
-      // not a kernel file. We launch it directly using `Process.start`.
-      if (precompiledKernelUri == null) {
-        throw new UnsupportedError(
-            'This environment requires a non-null `precompiledKernelUri` to be '
-            'passed when loading macros.');
-      }
-      switch (communicationChannel) {
-        case CommunicationChannel.stdio:
-          return _SingleProcessMacroExecutor.startWithStdio(library, name,
-              serializationMode, precompiledKernelUri.toFilePath());
-        case CommunicationChannel.socket:
-          return _SingleProcessMacroExecutor.startWithSocket(library, name,
-              serializationMode, precompiledKernelUri.toFilePath());
-      }
-    });
+    CommunicationChannel communicationChannel, String program,
+    [List<String> arguments = const []]) {
+  switch (communicationChannel) {
+    case CommunicationChannel.stdio:
+      return _SingleProcessMacroExecutor.startWithStdio(
+          serializationMode, program, arguments);
+    case CommunicationChannel.socket:
+      return _SingleProcessMacroExecutor.startWithSocket(
+          serializationMode, program, arguments);
+  }
+}
 
 /// Actual implementation of the separate process based macro executor.
 class _SingleProcessMacroExecutor extends ExternalMacroExecutorBase {
@@ -62,10 +52,9 @@
             messageStream: messageStream, serializationMode: serializationMode);
 
   static Future<_SingleProcessMacroExecutor> startWithSocket(
-      Uri library,
-      String name,
       SerializationMode serializationMode,
-      String programPath) async {
+      String programPath,
+      List<String> arguments) async {
     late ServerSocket serverSocket;
     // Try an ipv6 address loopback first, and fall back on ipv4.
     try {
@@ -74,6 +63,7 @@
       serverSocket = await ServerSocket.bind(InternetAddress.loopbackIPv4, 0);
     }
     Process process = await Process.start(programPath, [
+      ...arguments,
       serverSocket.address.address,
       serverSocket.port.toString(),
     ]);
@@ -116,11 +106,10 @@
   }
 
   static Future<_SingleProcessMacroExecutor> startWithStdio(
-      Uri library,
-      String name,
       SerializationMode serializationMode,
-      String programPath) async {
-    Process process = await Process.start(programPath, []);
+      String programPath,
+      List<String> arguments) async {
+    Process process = await Process.start(programPath, arguments);
     process.stderr
         .transform(const Utf8Decoder())
         .listen((content) => throw new RemoteException(content));
@@ -150,7 +139,7 @@
   }
 
   @override
-  void close() => onClose();
+  Future<void> close() => new Future.sync(onClose);
 
   /// Sends the [Serializer.result] to [stdin].
   ///
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/protocol.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/protocol.dart
index b868726..d763d12 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/protocol.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/protocol.dart
@@ -96,9 +96,6 @@
         deserializer.moveNext();
         error = deserializer.expectString();
         break;
-      case MessageType.macroClassIdentifier:
-        response = new MacroClassIdentifierImpl.deserialize(deserializer);
-        break;
       case MessageType.macroInstanceIdentifier:
         response = new MacroInstanceIdentifierImpl.deserialize(deserializer);
         break;
@@ -219,34 +216,38 @@
 
 /// A request to instantiate a macro instance.
 class InstantiateMacroRequest extends Request {
-  final MacroClassIdentifier macroClass;
-  final String constructorName;
+  final Uri library;
+  final String name;
+  final String constructor;
   final Arguments arguments;
 
   /// The ID to assign to the identifier, this needs to come from the requesting
   /// side so that it is unique.
   final int instanceId;
 
-  InstantiateMacroRequest(
-      this.macroClass, this.constructorName, this.arguments, this.instanceId,
+  InstantiateMacroRequest(this.library, this.name, this.constructor,
+      this.arguments, this.instanceId,
       {required int serializationZoneId})
       : super(serializationZoneId: serializationZoneId);
 
   InstantiateMacroRequest.deserialize(
       Deserializer deserializer, int serializationZoneId)
-      : macroClass = new MacroClassIdentifierImpl.deserialize(deserializer),
-        constructorName = (deserializer..moveNext()).expectString(),
+      : library = (deserializer..moveNext()).expectUri(),
+        name = (deserializer..moveNext()).expectString(),
+        constructor = (deserializer..moveNext()).expectString(),
         arguments = new Arguments.deserialize(deserializer),
         instanceId = (deserializer..moveNext()).expectInt(),
         super.deserialize(deserializer, serializationZoneId);
 
   @override
   void serialize(Serializer serializer) {
-    serializer.addInt(MessageType.instantiateMacroRequest.index);
-    macroClass.serialize(serializer);
-    serializer.addString(constructorName);
-    arguments.serialize(serializer);
-    serializer.addInt(instanceId);
+    serializer
+      ..addInt(MessageType.instantiateMacroRequest.index)
+      ..addUri(library)
+      ..addString(name)
+      ..addString(constructor)
+      ..addSerializable(arguments)
+      ..addInt(instanceId);
     super.serialize(serializer);
   }
 }
@@ -845,7 +846,6 @@
   isSubtypeOfRequest,
   loadMacroRequest,
   remoteInstance,
-  macroClassIdentifier,
   macroInstanceIdentifier,
   macroExecutionResult,
   namedStaticType,
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/response_impls.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/response_impls.dart
index eedc0bc..89fb92e 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/response_impls.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/response_impls.dart
@@ -7,22 +7,6 @@
 import 'serialization.dart';
 import 'serialization_extensions.dart';
 
-/// Implementation of [MacroClassIdentifier].
-class MacroClassIdentifierImpl implements MacroClassIdentifier {
-  final String id;
-
-  MacroClassIdentifierImpl(Uri library, String name) : id = '$library#$name';
-
-  MacroClassIdentifierImpl.deserialize(Deserializer deserializer)
-      : id = (deserializer..moveNext()).expectString();
-
-  void serialize(Serializer serializer) => serializer.addString(id);
-
-  operator ==(other) => other is MacroClassIdentifierImpl && id == other.id;
-
-  int get hashCode => id.hashCode;
-}
-
 /// Implementation of [MacroInstanceIdentifier].
 class MacroInstanceIdentifierImpl implements MacroInstanceIdentifier {
   /// Unique identifier for this instance, passed in from the server.
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/serialization_extensions.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/serialization_extensions.dart
index 8cf9207..c51175a 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/serialization_extensions.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/serialization_extensions.dart
@@ -67,6 +67,8 @@
     }
   }
 
+  Uri expectUri() => Uri.parse(expectString());
+
   /// Helper method to read a list of [RemoteInstance]s.
   List<T> _expectRemoteInstanceList<T extends RemoteInstance>() {
     expectList();
@@ -413,6 +415,13 @@
   }
 }
 
+extension Helpers on Serializer {
+  void addUri(Uri uri) => addString('$uri');
+
+  void addSerializable(Serializable serializable) =>
+      serializable.serialize(this);
+}
+
 enum _CodePartKind {
   string,
   code,
diff --git a/pkg/_fe_analyzer_shared/lib/src/testing/id_testing.dart b/pkg/_fe_analyzer_shared/lib/src/testing/id_testing.dart
index f29fc9f..04fe99d 100644
--- a/pkg/_fe_analyzer_shared/lib/src/testing/id_testing.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/testing/id_testing.dart
@@ -7,6 +7,7 @@
 import 'id.dart';
 import 'id_generation.dart';
 import '../util/colors.dart' as colors;
+import '../util/options.dart';
 
 const String cfeMarker = 'cfe';
 const String cfeWithNnbdMarker = '$cfeMarker:nnbd';
@@ -504,9 +505,10 @@
 /// Checks [compiledData] against the expected data in [expectedMaps] derived
 /// from [code].
 Future<TestResult<T>> checkCode<T>(
+    MarkerOptions markerOptions,
+    String marker,
     String modeName,
-    Uri mainFileUri,
-    Map<Uri, AnnotatedCode> code,
+    TestData testData,
     MemberAnnotations<IdValue> expectedMaps,
     CompiledData<T> compiledData,
     DataInterpreter<T> dataInterpreter,
@@ -514,6 +516,9 @@
     bool fatalErrors: true,
     bool succinct: false,
     required void onFailure(String message)}) async {
+  String testName = testData.name;
+  Map<Uri, AnnotatedCode> code = testData.code;
+
   bool hasFailure = false;
   Set<Uri> neededDiffs = new Set<Uri>();
 
@@ -630,12 +635,18 @@
     hasFailure = true;
   }
   if (hasFailure && fatalErrors) {
-    onFailure('Errors found.');
+    onFailure(generateErrorMessage(
+      markerOptions,
+      mismatches: {
+        testName: {marker}
+      },
+    ));
   }
   return new TestResult<T>(dataInterpreter, compiledData, hasFailure);
 }
 
-typedef Future<Map<String, TestResult<T>>> RunTestFunction<T>(TestData testData,
+typedef Future<Map<String, TestResult<T>>> RunTestFunction<T>(
+    MarkerOptions markerOptions, TestData testData,
     {required bool testAfterFailures,
     required bool verbose,
     required bool succinct,
@@ -657,8 +668,37 @@
   ]);
 }
 
+/// Description of a tester for a specific marker.
+///
+/// A tester is the runnable dart script used to run the tests for a certain
+/// configurations. For instance
+/// `pkg/front_end/test/id_tests/constant_test.dart` is used to run the test
+/// data in `pkg/_fe_analyzer_shared/test/constants/data/` using the front end
+/// and `pkg/analyzer/test/id_tests/constant_test.dart` is used to run the same
+/// test data using the analyzer.
+class MarkerTester {
+  /// The marker supported by this tester, for instance 'cfe' for front end
+  /// testing.
+  final String marker;
+
+  /// The path of the tester relative to the sdk repo root, for instance
+  /// `pkg/front_end/test/id_tests/constant_test.dart`.
+  final String path;
+
+  /// The resolved file uri for the test.
+  final Uri uri;
+
+  MarkerTester(this.marker, this.path, this.uri);
+
+  @override
+  String toString() => 'MarkerTester($marker,$path,$uri)';
+}
+
+/// A model of the testers defined in a `marker.options` file, located in the
+/// data folder for id tests.
 class MarkerOptions {
-  final Map<String, Uri> markers;
+  /// The supported markers and their corresponding testers.
+  final Map<String, MarkerTester> markers;
 
   MarkerOptions.internal(this.markers);
 
@@ -670,7 +710,7 @@
       throw new ArgumentError("Marker option file '$file' doesn't exist.");
     }
 
-    Map<String, Uri> markers = {};
+    Map<String, MarkerTester> markers = {};
     String text = file.readAsStringSync();
     bool isScriptFound = false;
     for (String line in text.split('\n')) {
@@ -692,7 +732,7 @@
       if (markers.containsKey(marker)) {
         throw new ArgumentError("Duplicate marker '$marker' in ${file.uri}");
       }
-      markers[marker] = testerFile.uri;
+      markers[marker] = new MarkerTester(marker, tester, testerFile.uri);
       if (testerFile.absolute.uri == script.absolute.uri) {
         isScriptFound = true;
       }
@@ -707,14 +747,14 @@
   Iterable<String> get supportedMarkers => markers.keys;
 
   Future<void> runAll(List<String> args) async {
-    Set<Uri> testers = markers.values.toSet();
+    Set<MarkerTester> testers = markers.values.toSet();
     bool allOk = true;
-    for (Uri tester in testers) {
+    for (MarkerTester tester in testers) {
       print('================================================================');
-      print('Running tester: $tester ${args.join(' ')}');
+      print('Running tester: ${tester.path} ${args.join(' ')}');
       print('================================================================');
       Process process = await Process.start(
-          Platform.resolvedExecutable, [tester.toString(), ...args],
+          Platform.resolvedExecutable, [tester.uri.toString(), ...args],
           mode: ProcessStartMode.inheritStdio);
       if (await process.exitCode != 0) {
         allOk = false;
@@ -734,6 +774,40 @@
   }
 }
 
+// TODO(johnniwinther): Support --show to show actual data for an input.
+class Options {
+  static const Option<bool> runAll =
+      const Option<bool>('--run-all', const BoolValue(false));
+  static const Option<bool> verbose =
+      const Option<bool>('--verbose', const BoolValue(false), aliases: ['-v']);
+  static const Option<bool> succinct =
+      const Option<bool>('--succinct', const BoolValue(false), aliases: ['-s']);
+  static const Option<bool> shouldContinue =
+      const Option<bool>('--continue', const BoolValue(false), aliases: ['-c']);
+  static const Option<bool> stopAfterFailures = const Option<bool>(
+      '--stop-after-failures', const BoolValue(false),
+      aliases: ['-a']);
+  static const Option<bool> printCode = const Option<bool>(
+      '--print-code', const BoolValue(false),
+      aliases: ['-p']);
+  static const Option<bool> generateAnnotations =
+      const Option<bool>('--generate', const BoolValue(false), aliases: ['-g']);
+  static const Option<bool> forceUpdate = const Option<bool>(
+      '--force-update', const BoolValue(false),
+      aliases: ['-f']);
+}
+
+const List<Option> idTestOptions = [
+  Options.runAll,
+  Options.verbose,
+  Options.succinct,
+  Options.shouldContinue,
+  Options.stopAfterFailures,
+  Options.printCode,
+  Options.generateAnnotations,
+  Options.forceUpdate,
+];
+
 /// Check code for all tests in [dataDir] using [runTest].
 Future<void> runTests<T>(Directory dataDir,
     {List<String> args: const <String>[],
@@ -747,24 +821,26 @@
     Map<String, List<String>>? skipMap,
     bool preserveWhitespaceInAnnotations: false,
     bool preserveInfixWhitespaceInAnnotations: false}) async {
+  ParsedOptions parsedOptions = ParsedOptions.parse(args, idTestOptions);
   MarkerOptions markerOptions =
       new MarkerOptions.fromDataDir(dataDir, shouldFindScript: shards == 1);
-  // TODO(johnniwinther): Support --show to show actual data for an input.
-  args = args.toList();
-  bool runAll = args.remove('--run-all');
-  if (runAll) {
-    await markerOptions.runAll(args);
+  if (Options.runAll.read(parsedOptions)) {
+    await markerOptions.runAll(Options.runAll.remove(args.toList()));
     return;
   }
-  bool verbose = args.remove('-v');
-  bool succinct = args.remove('-s');
-  bool shouldContinue = args.remove('-c');
-  bool testAfterFailures = args.remove('-a');
-  bool printCode = args.remove('-p');
+  bool verbose = Options.verbose.read(parsedOptions);
+  bool succinct = Options.succinct.read(parsedOptions);
+  bool shouldContinue = Options.shouldContinue.read(parsedOptions);
+  bool stopAfterFailures = Options.stopAfterFailures.read(parsedOptions);
+  bool printCode = Options.printCode.read(parsedOptions);
+  bool generateAnnotations = Options.generateAnnotations.read(parsedOptions);
+  bool forceUpdate = Options.forceUpdate.read(parsedOptions);
+  List<String> arguments = parsedOptions.arguments;
+
   bool continued = false;
-  bool hasFailures = false;
-  bool generateAnnotations = args.remove('-g');
-  bool forceUpdate = args.remove('-f');
+
+  Map<String, Set<String>> mismatches = {};
+  Map<String, Set<String>> errors = {};
 
   String relativeDir = dataDir.uri.path.replaceAll(Uri.base.path, '');
   print('Data dir: ${relativeDir}');
@@ -783,13 +859,17 @@
   }
   int testCount = 0;
   for (FileSystemEntity entity in entities) {
-    String name = getTestName(entity);
-    if (args.isNotEmpty && !args.contains(name) && !continued) continue;
+    String testName = getTestName(entity);
+    if (arguments.isNotEmpty && !arguments.contains(testName) && !continued) {
+      continue;
+    }
     if (shouldContinue) continued = true;
     testCount++;
 
-    if (skipList != null && skipList.contains(name) && !args.contains(name)) {
-      print('Skip: ${name}');
+    if (skipList != null &&
+        skipList.contains(testName) &&
+        !arguments.contains(testName)) {
+      print('Skip: ${testName}');
       continue;
     }
     if (onTest != null) {
@@ -814,27 +894,31 @@
             preserveInfixWhitespaceInAnnotations);
     print('Test: ${testData.testFileUri}');
 
-    Map<String, TestResult<T>> results = await runTest(testData,
-        testAfterFailures: testAfterFailures || generateAnnotations,
+    Map<String, TestResult<T>> results = await runTest(markerOptions, testData,
+        testAfterFailures: !stopAfterFailures || generateAnnotations,
         verbose: verbose,
         succinct: succinct,
         printCode: printCode,
         skipMap: skipMap,
         nullUri: createTestUri(entity.uri.resolve("null"), "null"));
 
-    bool hasMismatches = false;
-    bool hasErrors = false;
+    Set<String> mismatchMarkers = {};
+    Set<String> errorMarkers = {};
     results.forEach((String marker, TestResult<T> result) {
       if (result.hasMismatches) {
-        hasMismatches = true;
+        mismatchMarkers.add(marker);
       } else if (result.isErroneous) {
-        hasErrors = true;
+        errorMarkers.add(marker);
       }
     });
-    if (hasErrors) {
+    if (errorMarkers.isNotEmpty) {
       // Cannot generate annotations for erroneous tests.
-      hasFailures = true;
-    } else if (hasMismatches || (forceUpdate && generateAnnotations)) {
+      if (mismatchMarkers.isNotEmpty) {
+        mismatches[testName] = mismatchMarkers;
+      }
+      errors[testName] = errorMarkers;
+    } else if (mismatchMarkers.isNotEmpty ||
+        (forceUpdate && generateAnnotations)) {
       if (generateAnnotations) {
         DataInterpreter? dataInterpreter;
         Map<String, Map<Uri, Map<Id, ActualData<T>>>> actualData = {};
@@ -885,18 +969,93 @@
           print('Generated annotations for ${fileUri}');
         });
       } else {
-        hasFailures = true;
+        mismatches[testName] = mismatchMarkers;
       }
     }
   }
-  if (hasFailures) {
-    onFailure('Errors found.');
+  if (mismatches.isNotEmpty || errors.isNotEmpty) {
+    onFailure(generateErrorMessage(markerOptions,
+        mismatches: mismatches, errors: errors));
   }
   if (testCount == 0) {
     onFailure("No files were tested.");
   }
 }
 
+/// Generates an error message for the [mismatches] and [errors] using
+/// [markerOptions] to provide a re-run/re-generation command.
+///
+/// [mismatches] and [errors] are maps from the test names of the failing tests
+/// to the markers for the configurations in which they failed.
+String generateErrorMessage(MarkerOptions markerOptions,
+    {Map<String, Set<String>> mismatches = const {},
+    Map<String, Set<String>> errors = const {}}) {
+  Set<String> testNames = {...mismatches.keys, ...errors.keys};
+  StringBuffer message = new StringBuffer();
+  message.writeln();
+  message.writeln('==========================================================');
+  message.writeln();
+  if (testNames.length > 1) {
+    message.writeln("Errors found in tests:");
+    message.writeln();
+    for (String testName in testNames.toList()..sort()) {
+      message.writeln("  ${testName}");
+    }
+  } else {
+    message.writeln("Errors found in test '${testNames.single}'");
+  }
+  if (mismatches.isNotEmpty) {
+    Map<MarkerTester, Set<String>> regenerations = {};
+    mismatches.forEach((String testName, Set<String> markers) {
+      for (String marker in markers) {
+        MarkerTester tester = markerOptions.markers[marker]!;
+        (regenerations[tester] ??= {}).add(testName);
+      }
+    });
+    message.writeln();
+    if (regenerations.length > 1) {
+      message.writeln('Run these commands to generate new expectations:');
+    } else {
+      message.writeln('Run this command to generate new expectations:');
+    }
+    message.writeln();
+    regenerations.forEach((MarkerTester tester, Set<String> testNames) {
+      message.write('  dart ${tester.path} -g');
+      for (String testName in testNames) {
+        message.write(' ${testName}');
+      }
+      message.writeln();
+    });
+  }
+  message.writeln();
+  if (errors.isNotEmpty) {
+    Map<MarkerTester, Set<String>> erroneous = {};
+    errors.forEach((String testName, Set<String> markers) {
+      for (String marker in markers) {
+        MarkerTester tester = markerOptions.markers[marker]!;
+        (erroneous[tester] ??= {}).add(testName);
+      }
+    });
+    message.writeln();
+    if (erroneous.length > 1) {
+      message.writeln('Run these commands to re-run the failing tests:');
+    } else {
+      message.writeln('Run this command to re-run the failing test(s):');
+    }
+    message.writeln();
+    erroneous.forEach((MarkerTester tester, Set<String> testNames) {
+      message.write('  dart ${tester.path}');
+      for (String testName in testNames) {
+        message.write(' ${testName}');
+      }
+      message.writeln();
+    });
+  }
+  message.writeln();
+  message.writeln('==========================================================');
+  return message.toString();
+}
+
 /// Returns `true` if [testName] is marked as skipped in [skipMap] for
 /// the given [configMarker].
 bool skipForConfig(
diff --git a/pkg/_fe_analyzer_shared/lib/src/util/options.dart b/pkg/_fe_analyzer_shared/lib/src/util/options.dart
index 17c3c01..59a8442 100644
--- a/pkg/_fe_analyzer_shared/lib/src/util/options.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/util/options.dart
@@ -307,4 +307,12 @@
 
   T read(ParsedOptions parsedOptions) =>
       (isDefines ? parsedOptions.defines : parsedOptions.options[flag]) as T;
+
+  List<String> remove(List<String> arguments) {
+    arguments.remove(flag);
+    for (String alias in aliases) {
+      arguments.remove(alias);
+    }
+    return arguments;
+  }
 }
diff --git a/pkg/_fe_analyzer_shared/test/macros/executor/executor_test.dart b/pkg/_fe_analyzer_shared/test/macros/executor/executor_test.dart
index c70ad25..127791c 100644
--- a/pkg/_fe_analyzer_shared/test/macros/executor/executor_test.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/executor/executor_test.dart
@@ -46,14 +46,6 @@
           setUpAll(() async {
             simpleMacroFile =
                 File(Platform.script.resolve('simple_macro.dart').toFilePath());
-            executor = executorKind == 'Isolated'
-                ? await isolatedExecutor.start(mode)
-                : executorKind == 'ProcessSocket'
-                    ? await processExecutor.start(
-                        mode, CommunicationChannel.socket)
-                    : await processExecutor.start(
-                        mode, CommunicationChannel.stdio);
-
             tmpDir = Directory.systemTemp.createTempSync('executor_test');
             macroUri = simpleMacroFile.absolute.uri;
 
@@ -86,22 +78,26 @@
                 reason: 'stdout: ${buildSnapshotResult.stdout}\n'
                     'stderr: ${buildSnapshotResult.stderr}');
 
-            var clazzId = await executor.loadMacro(macroUri, macroName,
-                precompiledKernelUri: kernelOutputFile.uri);
-            expect(clazzId, isNotNull, reason: 'Can load a macro.');
+            executor = executorKind == 'Isolated'
+                ? await isolatedExecutor.start(mode, kernelOutputFile.uri)
+                : executorKind == 'ProcessSocket'
+                    ? await processExecutor.start(mode,
+                        CommunicationChannel.socket, kernelOutputFile.path)
+                    : await processExecutor.start(mode,
+                        CommunicationChannel.stdio, kernelOutputFile.path);
 
-            instanceId =
-                await executor.instantiateMacro(clazzId, '', Arguments([], {}));
+            instanceId = await executor.instantiateMacro(
+                macroUri, macroName, '', Arguments([], {}));
             expect(instanceId, isNotNull,
                 reason: 'Can create an instance with no arguments.');
 
             instanceId = await executor.instantiateMacro(
-                clazzId, '', Arguments([1, 2], {}));
+                macroUri, macroName, '', Arguments([1, 2], {}));
             expect(instanceId, isNotNull,
                 reason: 'Can create an instance with positional arguments.');
 
             instanceId = await executor.instantiateMacro(
-                clazzId, 'named', Arguments([], {'x': 1, 'y': 2}));
+                macroUri, macroName, 'named', Arguments([], {'x': 1, 'y': 2}));
             expect(instanceId, isNotNull,
                 reason: 'Can create an instance with named arguments.');
           });
diff --git a/pkg/_js_interop_checks/OWNERS b/pkg/_js_interop_checks/OWNERS
new file mode 100644
index 0000000..f5bd90c
--- /dev/null
+++ b/pkg/_js_interop_checks/OWNERS
@@ -0,0 +1 @@
+file:/tools/OWNERS_WEB
diff --git a/pkg/analysis_server/benchmark/benchmarks.dart b/pkg/analysis_server/benchmark/benchmarks.dart
index 7037a85..cf25eb4 100644
--- a/pkg/analysis_server/benchmark/benchmarks.dart
+++ b/pkg/analysis_server/benchmark/benchmarks.dart
@@ -75,6 +75,7 @@
   Future oneTimeSetup() => Future.value();
 
   Future<BenchMarkResult> run({
+    required String dartSdkPath,
     bool quick = false,
     bool verbose = false,
   });
@@ -194,6 +195,8 @@
   final List<Benchmark> benchmarks;
 
   RunCommand(this.benchmarks) {
+    argParser.addOption('dart-sdk',
+        help: 'The absolute normalized path of the Dart SDK.');
     argParser.addOption('flutter-repository',
         help: 'The absolute normalized path of the Flutter repository.');
     argParser.addFlag('quick',
@@ -230,6 +233,7 @@
 
     var benchmarkId = args.rest.first;
     var repeatCount = int.parse(args['repeat'] as String);
+    var dartSdkPath = args['dart-sdk'] as String?;
     var flutterRepository = args['flutter-repository'] as String?;
     var quick = args['quick'] as bool;
     var verbose = args['verbose'] as bool;
@@ -240,6 +244,8 @@
       exit(1);
     });
 
+    dartSdkPath ??= path.dirname(path.dirname(Platform.resolvedExecutable));
+
     if (benchmark is FlutterBenchmark) {
       if (flutterRepository != null) {
         if (path.isAbsolute(flutterRepository) &&
@@ -274,6 +280,7 @@
 
       for (var iteration = 0; iteration < actualIterations; iteration++) {
         var newResult = await benchmark.run(
+          dartSdkPath: dartSdkPath,
           quick: quick,
           verbose: verbose,
         );
diff --git a/pkg/analysis_server/benchmark/integration/driver.dart b/pkg/analysis_server/benchmark/integration/driver.dart
index 9f40d74..b52e80a 100644
--- a/pkg/analysis_server/benchmark/integration/driver.dart
+++ b/pkg/analysis_server/benchmark/integration/driver.dart
@@ -3,9 +3,11 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:async';
+import 'dart:io';
 import 'dart:math' show max, sqrt;
 
 import 'package:logging/logging.dart';
+import 'package:path/path.dart';
 
 import '../../test/integration/support/integration_test_methods.dart';
 import '../../test/integration/support/integration_tests.dart';
@@ -90,8 +92,9 @@
       serverConnected.complete();
     });
     running = true;
+    var dartSdkPath = dirname(dirname(Platform.resolvedExecutable));
     return server
-        .start(diagnosticPort: diagnosticPort /*profileServer: true*/)
+        .start(dartSdkPath: dartSdkPath, diagnosticPort: diagnosticPort)
         .then((params) {
       server.listenToOutput(dispatchNotification);
       server.exitCode.then((_) {
diff --git a/pkg/analysis_server/benchmark/perf/benchmarks_impl.dart b/pkg/analysis_server/benchmark/perf/benchmarks_impl.dart
index fab0850..f0d8533 100644
--- a/pkg/analysis_server/benchmark/perf/benchmarks_impl.dart
+++ b/pkg/analysis_server/benchmark/perf/benchmarks_impl.dart
@@ -27,6 +27,7 @@
 
   @override
   Future<BenchMarkResult> run({
+    required String dartSdkPath,
     bool quick = false,
     bool verbose = false,
   }) async {
@@ -36,7 +37,7 @@
     if (verbose) {
       test.debugStdio();
     }
-    await test.setUp(getProjectRoots(quick: quick));
+    await test.setUp(dartSdkPath, getProjectRoots(quick: quick));
     await test.analysisFinished;
 
     stopwatch.stop();
@@ -156,6 +157,7 @@
 
   @override
   Future<BenchMarkResult> run({
+    required String dartSdkPath,
     bool quick = false,
     bool verbose = false,
   }) async {
@@ -166,7 +168,7 @@
     var stopwatch = Stopwatch()..start();
 
     var test = testConstructor();
-    await test.setUp(getProjectRoots(quick: quick));
+    await test.setUp(dartSdkPath, getProjectRoots(quick: quick));
     await test.analysisFinished;
 
     stopwatch.stop();
diff --git a/pkg/analysis_server/benchmark/perf/flutter_analyze_benchmark.dart b/pkg/analysis_server/benchmark/perf/flutter_analyze_benchmark.dart
index f2d157b3..3c4ae77 100644
--- a/pkg/analysis_server/benchmark/perf/flutter_analyze_benchmark.dart
+++ b/pkg/analysis_server/benchmark/perf/flutter_analyze_benchmark.dart
@@ -5,8 +5,6 @@
 import 'dart:convert';
 import 'dart:io';
 
-import 'package:path/path.dart' as path;
-
 import '../benchmarks.dart';
 
 Future<int> _runProcess(
@@ -57,6 +55,7 @@
 
   @override
   Future<BenchMarkResult> run({
+    required String dartSdkPath,
     bool quick = false,
     bool verbose = false,
   }) async {
@@ -64,12 +63,10 @@
       deleteServerCache();
     }
 
-    var dartSdkPath = path.dirname(path.dirname(Platform.resolvedExecutable));
-
     var stopwatch = Stopwatch()..start();
 
     await _runProcess(
-      Platform.resolvedExecutable,
+      '$dartSdkPath/bin/dart',
       [
         'packages/flutter_tools/bin/flutter_tools.dart',
         'analyze',
diff --git a/pkg/analysis_server/benchmark/perf/flutter_completion_benchmark.dart b/pkg/analysis_server/benchmark/perf/flutter_completion_benchmark.dart
index 7863130..a874443 100644
--- a/pkg/analysis_server/benchmark/perf/flutter_completion_benchmark.dart
+++ b/pkg/analysis_server/benchmark/perf/flutter_completion_benchmark.dart
@@ -37,6 +37,7 @@
 
   @override
   Future<BenchMarkResult> run({
+    required String dartSdkPath,
     bool quick = false,
     bool verbose = false,
   }) async {
@@ -55,7 +56,7 @@
     // Open a small directory, but with the package config that allows us
     // to analyze any file in `package:flutter`, including tests.
     var startTimer = Stopwatch()..start();
-    await test.setUp([
+    await test.setUp(dartSdkPath, [
       '$flutterPkgPath/lib/src/physics',
     ]);
 
diff --git a/pkg/analysis_server/benchmark/perf/memory_tests.dart b/pkg/analysis_server/benchmark/perf/memory_tests.dart
index 1837834..307b009 100644
--- a/pkg/analysis_server/benchmark/perf/memory_tests.dart
+++ b/pkg/analysis_server/benchmark/perf/memory_tests.dart
@@ -25,7 +25,7 @@
   void debugStdio();
   Future<int> getMemoryUsage();
   Future<void> openFile(String filePath, String contents);
-  Future<void> setUp(List<String> roots);
+  Future<void> setUp(String dartSdkPath, List<String> roots);
   Future<void> shutdown();
 
   Future<void> updateFile(String filePath, String contents);
@@ -76,7 +76,8 @@
   }
 
   @override
-  Future<void> setUp(List<String> roots) async {
+  Future<void> setUp(String dartSdkPath, List<String> roots) async {
+    _test.dartSdkPath = dartSdkPath;
     await _test.setUp();
     await _test.subscribeToStatusNotifications();
     await _test.subscribeToAvailableSuggestions();
@@ -187,7 +188,8 @@
   }
 
   @override
-  Future<void> setUp(List<String> roots) async {
+  Future<void> setUp(String dartSdkPath, List<String> roots) async {
+    _test.dartSdkPath = dartSdkPath;
     _test.instrumentationService = InstrumentationLogAdapter(_logger);
     await _test.setUp();
     _test.projectFolderPath = roots.single;
diff --git a/pkg/analysis_server/test/integration/analysis/get_errors_non_standard_sdk_test.dart b/pkg/analysis_server/test/integration/analysis/get_errors_non_standard_sdk_test.dart
index 4a0e09d..455d2c5 100644
--- a/pkg/analysis_server/test/integration/analysis/get_errors_non_standard_sdk_test.dart
+++ b/pkg/analysis_server/test/integration/analysis/get_errors_non_standard_sdk_test.dart
@@ -22,8 +22,42 @@
 class AnalysisDomainGetErrorsTest
     extends AbstractAnalysisServerIntegrationTest {
   String createNonStandardSdk() {
+    String executableFilePathIn(String sdkPath) {
+      var name = Platform.isWindows ? 'dart.exe' : 'dart';
+      return path.join(sdkPath, 'bin', name);
+    }
+
+    String serverSnapshotPathIn(String sdkPath) {
+      return path.join(
+        sdkPath,
+        'bin',
+        'snapshots',
+        'analysis_server.dart.snapshot',
+      );
+    }
+
     var sdkPath = path.join(sourceDirectory.path, 'sdk');
 
+    var standardSdkPath = path.dirname(
+      path.dirname(Platform.resolvedExecutable),
+    );
+
+    Directory(
+      path.join(sdkPath, 'bin', 'snapshots'),
+    ).createSync(recursive: true);
+
+    File(
+      executableFilePathIn(standardSdkPath),
+    ).copySync(
+      executableFilePathIn(sdkPath),
+    );
+
+    File(
+      serverSnapshotPathIn(standardSdkPath),
+    ).copySync(
+      serverSnapshotPathIn(sdkPath),
+    );
+
     Directory(path.join(sdkPath, 'lib', 'core')).createSync(recursive: true);
     Directory(path.join(sdkPath, 'lib', 'async')).createSync(recursive: true);
     Directory(path.join(sdkPath, 'lib', 'fake')).createSync(recursive: true);
@@ -81,7 +115,7 @@
     var sdkPath = createNonStandardSdk();
     return server.start(
         diagnosticPort: diagnosticPort,
-        sdkPath: sdkPath,
+        dartSdkPath: sdkPath,
         servicesPort: servicesPort);
   }
 
diff --git a/pkg/analysis_server/test/integration/analysis/highlights_test.dart b/pkg/analysis_server/test/integration/analysis/highlights_test.dart
index 57c3736..2f1fbd3 100644
--- a/pkg/analysis_server/test/integration/analysis/highlights_test.dart
+++ b/pkg/analysis_server/test/integration/analysis/highlights_test.dart
@@ -52,6 +52,7 @@
     int? servicesPort,
   }) {
     return server.start(
+        dartSdkPath: dartSdkPath,
         diagnosticPort: diagnosticPort,
         servicesPort: servicesPort,
         useAnalysisHighlight2: true);
diff --git a/pkg/analysis_server/test/integration/lsp_server/integration_tests.dart b/pkg/analysis_server/test/integration/lsp_server/integration_tests.dart
index cbf06cd..c09c72e 100644
--- a/pkg/analysis_server/test/integration/lsp_server/integration_tests.dart
+++ b/pkg/analysis_server/test/integration/lsp_server/integration_tests.dart
@@ -20,6 +20,8 @@
   LspServerClient? client;
   InstrumentationService? instrumentationService;
   final Map<num, Completer<ResponseMessage>> _completers = {};
+  String dartSdkPath = dirname(dirname(Platform.resolvedExecutable));
+
   LspByteStreamServerChannel get channel => client!.channel!;
 
   @override
@@ -84,7 +86,7 @@
 
     final client = LspServerClient(instrumentationService);
     this.client = client;
-    await client.start(vmArgs: vmArgs);
+    await client.start(dartSdkPath: dartSdkPath, vmArgs: vmArgs);
     client.serverToClient.listen((message) {
       if (message is ResponseMessage) {
         final id = message.id!.map((number) => number,
@@ -138,26 +140,22 @@
     return dirname(pathname);
   }
 
-  Future start({List<String>? vmArgs}) async {
+  Future start({
+    required String dartSdkPath,
+    List<String>? vmArgs,
+  }) async {
     if (_process != null) {
       throw Exception('Process already started');
     }
 
-    var dartBinary = Platform.executable;
+    var dartBinary = join(dartSdkPath, 'bin', 'dart');
 
     var useSnapshot = true;
     String serverPath;
 
     if (useSnapshot) {
-      // Look for snapshots/analysis_server.dart.snapshot.
-      serverPath = normalize(join(dirname(Platform.resolvedExecutable),
-          'snapshots', 'analysis_server.dart.snapshot'));
-
-      if (!FileSystemEntity.isFileSync(serverPath)) {
-        // Look for dart-sdk/bin/snapshots/analysis_server.dart.snapshot.
-        serverPath = normalize(join(dirname(Platform.resolvedExecutable),
-            'dart-sdk', 'bin', 'snapshots', 'analysis_server.dart.snapshot'));
-      }
+      serverPath = normalize(join(
+          dartSdkPath, 'bin', 'snapshots', 'analysis_server.dart.snapshot'));
     } else {
       final rootDir =
           findRoot(Platform.script.toFilePath(windows: Platform.isWindows));
diff --git a/pkg/analysis_server/test/integration/server/command_line_options_test.dart b/pkg/analysis_server/test/integration/server/command_line_options_test.dart
index 0b085ea..475bf9a 100644
--- a/pkg/analysis_server/test/integration/server/command_line_options_test.dart
+++ b/pkg/analysis_server/test/integration/server/command_line_options_test.dart
@@ -43,6 +43,7 @@
 ''');
 
     return server.start(
+      dartSdkPath: dartSdkPath,
       diagnosticPort: diagnosticPort,
       servicesPort: servicesPort,
       packagesFile: packagesPath,
diff --git a/pkg/analysis_server/test/integration/support/integration_tests.dart b/pkg/analysis_server/test/integration/support/integration_tests.dart
index 46858e0..ceb7185 100644
--- a/pkg/analysis_server/test/integration/support/integration_tests.dart
+++ b/pkg/analysis_server/test/integration/support/integration_tests.dart
@@ -108,6 +108,8 @@
   /// updates.
   bool _subscribedToServerStatus = false;
 
+  String dartSdkPath = path.dirname(path.dirname(Platform.resolvedExecutable));
+
   AbstractAnalysisServerIntegrationTest() {
     initializeInttestMixin();
   }
@@ -236,6 +238,7 @@
     int? servicesPort,
   }) {
     return server.start(
+      dartSdkPath: dartSdkPath,
       diagnosticPort: diagnosticPort,
       servicesPort: servicesPort,
     );
@@ -607,16 +610,17 @@
   /// with "--observe" and "--pause-isolates-on-exit", allowing the observatory
   /// to be used.
   Future start({
+    required String dartSdkPath,
     int? diagnosticPort,
     String? instrumentationLogFile,
     String? packagesFile,
     bool profileServer = false,
-    String? sdkPath,
     int? servicesPort,
     bool useAnalysisHighlight2 = false,
   }) async {
     _time.start();
-    var dartBinary = Platform.executable;
+
+    var dartBinary = path.join(dartSdkPath, 'bin', 'dart');
 
     // The integration tests run 3x faster when run from snapshots (you need to
     // run test.py with --use-sdk).
@@ -624,21 +628,8 @@
     String serverPath;
 
     if (useSnapshot) {
-      // Look for snapshots/analysis_server.dart.snapshot.
       serverPath = path.normalize(path.join(
-          path.dirname(Platform.resolvedExecutable),
-          'snapshots',
-          'analysis_server.dart.snapshot'));
-
-      if (!FileSystemEntity.isFileSync(serverPath)) {
-        // Look for dart-sdk/bin/snapshots/analysis_server.dart.snapshot.
-        serverPath = path.normalize(path.join(
-            path.dirname(Platform.resolvedExecutable),
-            'dart-sdk',
-            'bin',
-            'snapshots',
-            'analysis_server.dart.snapshot'));
-      }
+          dartSdkPath, 'bin', 'snapshots', 'analysis_server.dart.snapshot'));
     } else {
       var rootDir =
           findRoot(Platform.script.toFilePath(windows: Platform.isWindows));
@@ -673,6 +664,7 @@
     // Add server arguments.
     //
     arguments.add('--suppress-analytics');
+    arguments.add('--sdk=$dartSdkPath');
     if (diagnosticPort != null) {
       arguments.add('--port');
       arguments.add(diagnosticPort.toString());
@@ -683,9 +675,6 @@
     if (packagesFile != null) {
       arguments.add('--packages=$packagesFile');
     }
-    if (sdkPath != null) {
-      arguments.add('--sdk=$sdkPath');
-    }
     if (useAnalysisHighlight2) {
       arguments.add('--useAnalysisHighlight2');
     }
diff --git a/pkg/analysis_server/test/timing/timing_framework.dart b/pkg/analysis_server/test/timing/timing_framework.dart
index b9eb6d0..31ed32a 100644
--- a/pkg/analysis_server/test/timing/timing_framework.dart
+++ b/pkg/analysis_server/test/timing/timing_framework.dart
@@ -147,7 +147,8 @@
       serverConnected.complete();
     });
     skipShutdown = true;
-    return server.start(/*profileServer: true*/).then((_) {
+    var dartSdkPath = dirname(dirname(Platform.resolvedExecutable));
+    return server.start(dartSdkPath: dartSdkPath).then((_) {
       server.listenToOutput(dispatchNotification);
       server.exitCode.then((_) {
         skipShutdown = true;
diff --git a/pkg/analyzer/test/id_tests/nullability_test.dart b/pkg/analyzer/test/id_tests/nullability_test.dart
index 0abde94..659e605 100644
--- a/pkg/analyzer/test/id_tests/nullability_test.dart
+++ b/pkg/analyzer/test/id_tests/nullability_test.dart
@@ -6,16 +6,13 @@
 
 import 'package:_fe_analyzer_shared/src/testing/id.dart' show ActualData, Id;
 import 'package:_fe_analyzer_shared/src/testing/id_testing.dart';
-import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/dart/element/type_system.dart';
 import 'package:analyzer/src/dart/analysis/testing_data.dart';
 import 'package:analyzer/src/dart/ast/extensions.dart';
-import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
 import 'package:analyzer/src/util/ast_data_extractor.dart';
-import 'package:test/test.dart';
 
 import '../util/id_testing_helper.dart';
 
@@ -30,19 +27,6 @@
           const _NullabilityDataComputer(), [analyzerDefaultConfig]));
 }
 
-class FlowTestBase {
-  late final FlowAnalysisDataForTesting flowResult;
-
-  /// Resolve the given [code] and track nullability in the unit.
-  Future<void> trackCode(String code) async {
-    TestResult<String> testResult = await checkTests(code,
-        const _NullabilityDataComputer(), FeatureSet.latestLanguageVersion());
-    if (testResult.hasFailures) {
-      fail('Failure(s)');
-    }
-  }
-}
-
 class _NullabilityDataComputer extends DataComputer<String> {
   const _NullabilityDataComputer();
 
diff --git a/pkg/analyzer/test/id_tests/reachability_test.dart b/pkg/analyzer/test/id_tests/reachability_test.dart
index d5b03f8..6997d16 100644
--- a/pkg/analyzer/test/id_tests/reachability_test.dart
+++ b/pkg/analyzer/test/id_tests/reachability_test.dart
@@ -6,12 +6,10 @@
 
 import 'package:_fe_analyzer_shared/src/testing/id.dart' show ActualData, Id;
 import 'package:_fe_analyzer_shared/src/testing/id_testing.dart';
-import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/src/dart/analysis/testing_data.dart';
 import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
 import 'package:analyzer/src/util/ast_data_extractor.dart';
-import 'package:test/test.dart';
 
 import '../util/id_testing_helper.dart';
 
@@ -26,19 +24,6 @@
           const _ReachabilityDataComputer(), [analyzerDefaultConfig]));
 }
 
-class FlowTestBase {
-  late final FlowAnalysisDataForTesting flowResult;
-
-  /// Resolve the given [code] and track nullability in the unit.
-  Future<void> trackCode(String code) async {
-    TestResult<Set<_ReachabilityAssertion>> testResult = await checkTests(code,
-        const _ReachabilityDataComputer(), FeatureSet.latestLanguageVersion());
-    if (testResult.hasFailures) {
-      fail('Failure(s)');
-    }
-  }
-}
-
 enum _ReachabilityAssertion {
   doesNotComplete,
   unreachable,
diff --git a/pkg/analyzer/test/util/id_testing_helper.dart b/pkg/analyzer/test/util/id_testing_helper.dart
index 69780494..f5e4f87 100644
--- a/pkg/analyzer/test/util/id_testing_helper.dart
+++ b/pkg/analyzer/test/util/id_testing_helper.dart
@@ -36,28 +36,6 @@
 /// tests.
 Uri _defaultDir = Uri.parse('file:///a/b/c/');
 
-Future<TestResult<T>> checkTests<T>(
-    String rawCode, DataComputer<T> dataComputer, FeatureSet featureSet) async {
-  AnnotatedCode code =
-      AnnotatedCode.fromText(rawCode, commentStart, commentEnd);
-  String testFileName = 'test.dart';
-  var testFileUri = _toTestUri(testFileName);
-  var memorySourceFiles = {testFileName: code.sourceCode};
-  var marker = 'analyzer';
-  Map<String, MemberAnnotations<IdValue>> expectedMaps = {
-    marker: MemberAnnotations<IdValue>(),
-  };
-  computeExpectedMap(testFileUri, testFileName, code, expectedMaps,
-      onFailure: onFailure);
-  Map<Uri, AnnotatedCode> codeMap = {testFileUri: code};
-  var testData = TestData(testFileName, testFileUri, testFileUri,
-      memorySourceFiles, codeMap, expectedMaps);
-  var config =
-      TestConfig(marker, 'provisional test config', featureSet: featureSet);
-  return runTestForConfig<T>(testData, dataComputer, config,
-      onFailure: onFailure);
-}
-
 /// Creates the testing URI used for [fileName] in annotated tests.
 Uri createUriForFileName(String fileName) => _toTestUri(fileName);
 
@@ -68,8 +46,11 @@
 /// Runs [dataComputer] on [testData] for all [testedConfigs].
 ///
 /// Returns `true` if an error was encountered.
-Future<Map<String, TestResult<T>>> runTest<T>(TestData testData,
-    DataComputer<T> dataComputer, List<TestConfig> testedConfigs,
+Future<Map<String, TestResult<T>>> runTest<T>(
+    MarkerOptions markerOptions,
+    TestData testData,
+    DataComputer<T> dataComputer,
+    List<TestConfig> testedConfigs,
     {required bool testAfterFailures,
     bool forUserLibrariesOnly = true,
     Iterable<Id> globalIds = const <Id>[],
@@ -88,7 +69,7 @@
       continue;
     }
     results[config.marker] = await runTestForConfig(
-        testData, dataComputer, config,
+        markerOptions, testData, dataComputer, config,
         fatalErrors: !testAfterFailures, onFailure: onFailure);
   }
   return results;
@@ -97,14 +78,14 @@
 /// Creates a test runner for [dataComputer] on [testedConfigs].
 RunTestFunction<T> runTestFor<T>(
     DataComputer<T> dataComputer, List<TestConfig> testedConfigs) {
-  return (TestData testData,
+  return (MarkerOptions markerOptions, TestData testData,
       {required bool testAfterFailures,
       bool? verbose,
       bool? succinct,
       bool? printCode,
       Map<String, List<String>>? skipMap,
       Uri? nullUri}) {
-    return runTest(testData, dataComputer, testedConfigs,
+    return runTest(markerOptions, testData, dataComputer, testedConfigs,
         testAfterFailures: testAfterFailures,
         onFailure: onFailure,
         skipMap: skipMap);
@@ -114,7 +95,7 @@
 /// Runs [dataComputer] on [testData] for [config].
 ///
 /// Returns `true` if an error was encountered.
-Future<TestResult<T>> runTestForConfig<T>(
+Future<TestResult<T>> runTestForConfig<T>(MarkerOptions markerOptions,
     TestData testData, DataComputer<T> dataComputer, TestConfig config,
     {bool fatalErrors = true,
     required void Function(String message) onFailure,
@@ -213,7 +194,7 @@
   });
   var compiledData = AnalyzerCompiledData<T>(
       testData.code, testData.entryPoint, actualMaps, globalData);
-  return checkCode(config.name, testData.testFileUri, testData.code,
+  return checkCode(markerOptions, config.marker, config.name, testData,
       memberAnnotations, compiledData, dataComputer.dataValidator,
       fatalErrors: fatalErrors, onFailure: onFailure);
 }
diff --git a/pkg/compiler/lib/src/common/elements.dart b/pkg/compiler/lib/src/common/elements.dart
index 0f88c4e..aea3172 100644
--- a/pkg/compiler/lib/src/common/elements.dart
+++ b/pkg/compiler/lib/src/common/elements.dart
@@ -911,14 +911,8 @@
   FunctionEntity get checkDeferredIsLoaded =>
       _findHelperFunction('checkDeferredIsLoaded');
 
-  FunctionEntity get throwNoSuchMethod =>
-      _findHelperFunction('throwNoSuchMethod');
-
   FunctionEntity get createRuntimeType => _findRtiFunction('createRuntimeType');
 
-  FunctionEntity get fallThroughError =>
-      _findHelperFunction("getFallThroughError");
-
   FunctionEntity get createInvocationMirror =>
       _findHelperFunction('createInvocationMirror');
 
diff --git a/pkg/compiler/lib/src/common/resolution.dart b/pkg/compiler/lib/src/common/resolution.dart
deleted file mode 100644
index 0156152..0000000
--- a/pkg/compiler/lib/src/common/resolution.dart
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-library dart2js.common.resolution;
-
-import '../constants/values.dart' show ConstantValue;
-import '../elements/entities.dart';
-import '../native/behavior.dart' show NativeBehavior;
-import '../universe/world_impact.dart' show WorldImpact;
-import '../universe/feature.dart';
-
-class ResolutionImpact extends WorldImpact {
-  const ResolutionImpact();
-
-  Iterable<Feature> get features => const <Feature>[];
-  Iterable<MapLiteralUse> get mapLiterals => const <MapLiteralUse>[];
-  Iterable<SetLiteralUse> get setLiterals => const <SetLiteralUse>[];
-  Iterable<ListLiteralUse> get listLiterals => const <ListLiteralUse>[];
-  Iterable<String> get constSymbolNames => const <String>[];
-  Iterable<ConstantValue> get constantLiterals => const <ConstantValue>[];
-  Iterable<ClassEntity> get seenClasses => const <ClassEntity>[];
-  Iterable<RuntimeTypeUse> get runtimeTypeUses => const <RuntimeTypeUse>[];
-  Iterable<NativeBehavior> get nativeData => const <NativeBehavior>[];
-
-  Iterable<GenericInstantiation> get genericInstantiations =>
-      const <GenericInstantiation>[];
-}
diff --git a/pkg/compiler/lib/src/dump_info.dart b/pkg/compiler/lib/src/dump_info.dart
index 77d8144..0101783 100644
--- a/pkg/compiler/lib/src/dump_info.dart
+++ b/pkg/compiler/lib/src/dump_info.dart
@@ -121,7 +121,7 @@
   AbstractValue _resultOfParameter(Local e) =>
       _globalInferenceResults.resultOfParameter(e);
 
-  FieldInfo visitField(FieldEntity field, {ClassEntity containingClass}) {
+  FieldInfo visitField(FieldEntity field) {
     AbstractValue inferredType = _resultOfMember(field).type;
     // If a field has an empty inferred type it is never used.
     if (inferredType == null ||
@@ -201,7 +201,7 @@
           }
         }
       } else if (member.isField) {
-        FieldInfo fieldInfo = visitField(member, containingClass: clazz);
+        FieldInfo fieldInfo = visitField(member);
         if (fieldInfo != null) {
           classInfo.fields.add(fieldInfo);
           fieldInfo.parent = classInfo;
@@ -436,7 +436,7 @@
       _constantToInfo[constant] = info;
       result.constants.add(info);
     });
-    environment.libraries.forEach(visitLibrary);
+    component.libraries.forEach(visitLibrary);
   }
 
   /// Whether to emit information about [entity].
@@ -449,24 +449,32 @@
         dumpInfoTask.inlineCount.containsKey(entity);
   }
 
-  LibraryInfo visitLibrary(LibraryEntity lib) {
-    String libname = environment.getLibraryName(lib);
-    if (libname.isEmpty) {
+  LibraryInfo visitLibrary(ir.Library lib) {
+    final libEntity = environment.lookupLibrary(lib.importUri);
+    if (libEntity == null) return null;
+
+    String libname = lib.name;
+    if (libname == null || libname.isEmpty) {
       libname = '<unnamed>';
     }
-    int size = dumpInfoTask.sizeOf(lib);
-    LibraryInfo info = LibraryInfo(libname, lib.canonicalUri, null, size);
-    _entityToInfo[lib] = info;
 
-    environment.forEachLibraryMember(lib, (MemberEntity member) {
-      if (member.isFunction || member.isGetter || member.isSetter) {
-        FunctionInfo functionInfo = visitFunction(member);
+    int size = dumpInfoTask.sizeOf(libEntity);
+    LibraryInfo info = LibraryInfo(libname, lib.importUri, null, size);
+    _entityToInfo[libEntity] = info;
+
+    lib.members.forEach((ir.Member member) {
+      final memberEntity =
+          environment.lookupLibraryMember(libEntity, member.name.text);
+      if (memberEntity == null) return;
+
+      if (member.function != null) {
+        FunctionInfo functionInfo = visitFunction(memberEntity);
         if (functionInfo != null) {
           info.topLevelFunctions.add(functionInfo);
           functionInfo.parent = info;
         }
-      } else if (member.isField) {
-        FieldInfo fieldInfo = visitField(member);
+      } else {
+        FieldInfo fieldInfo = visitField(member, fieldEntity: memberEntity);
         if (fieldInfo != null) {
           info.topLevelVariables.add(fieldInfo);
           fieldInfo.parent = info;
@@ -474,21 +482,24 @@
       }
     });
 
-    environment.forEachClass(lib, (ClassEntity clazz) {
-      ClassTypeInfo classTypeInfo = visitClassType(clazz);
+    lib.classes.forEach((ir.Class clazz) {
+      final classEntity = environment.lookupClass(libEntity, clazz.name);
+      if (classEntity == null) return;
+
+      ClassTypeInfo classTypeInfo = visitClassType(classEntity);
       if (classTypeInfo != null) {
         info.classTypes.add(classTypeInfo);
         classTypeInfo.parent = info;
       }
 
-      ClassInfo classInfo = visitClass(clazz);
+      ClassInfo classInfo = visitClass(clazz, classEntity: classEntity);
       if (classInfo != null) {
         info.classes.add(classInfo);
         classInfo.parent = info;
       }
     });
 
-    if (info.isEmpty && !shouldKeep(lib)) return null;
+    if (info.isEmpty && !shouldKeep(libEntity)) return null;
     result.libraries.add(info);
     return info;
   }
@@ -499,8 +510,8 @@
   AbstractValue _resultOfParameter(Local e) =>
       _globalInferenceResults.resultOfParameter(e);
 
-  FieldInfo visitField(FieldEntity field, {ClassEntity containingClass}) {
-    AbstractValue inferredType = _resultOfMember(field).type;
+  FieldInfo visitField(ir.Field field, {FieldEntity fieldEntity}) {
+    AbstractValue inferredType = _resultOfMember(fieldEntity).type;
     // If a field has an empty inferred type it is never used.
     if (inferredType == null ||
         closedWorld.abstractValueDomain
@@ -509,21 +520,22 @@
       return null;
     }
 
-    int size = dumpInfoTask.sizeOf(field);
-    List<CodeSpan> code = dumpInfoTask.codeOf(field);
+    int size = dumpInfoTask.sizeOf(fieldEntity);
+    List<CodeSpan> code = dumpInfoTask.codeOf(fieldEntity);
 
     // TODO(het): Why doesn't `size` account for the code size already?
     if (code != null) size += code.length;
 
     FieldInfo info = FieldInfo(
-        name: field.name,
-        type: '${environment.getFieldType(field)}',
+        name: field.name.text,
+        type: field.type.toStringInternal(),
         inferredType: '$inferredType',
         code: code,
-        outputUnit: _unitInfoForMember(field),
+        outputUnit: _unitInfoForMember(fieldEntity),
         isConst: field.isConst);
-    _entityToInfo[field] = info;
-    FieldAnalysisData fieldData = closedWorld.fieldAnalysis.getFieldData(field);
+    _entityToInfo[fieldEntity] = info;
+    FieldAnalysisData fieldData =
+        closedWorld.fieldAnalysis.getFieldData(fieldEntity);
     if (fieldData.initialValue != null) {
       info.initializer = _constantToInfo[fieldData.initialValue];
     }
@@ -534,7 +546,7 @@
       info.coverageId = '${field.hashCode}';
     }
 
-    int closureSize = _addClosureInfo(info, field);
+    int closureSize = _addClosureInfo(info, fieldEntity);
     info.size = size + closureSize;
 
     result.fields.add(info);
@@ -559,18 +571,28 @@
     return classTypeInfo;
   }
 
-  ClassInfo visitClass(ClassEntity clazz) {
+  ClassInfo visitClass(ir.Class clazz, {ClassEntity classEntity}) {
     // Omit class if it is not needed.
     ClassInfo classInfo = ClassInfo(
         name: clazz.name,
         isAbstract: clazz.isAbstract,
-        outputUnit: _unitInfoForClass(clazz));
-    _entityToInfo[clazz] = classInfo;
+        outputUnit: _unitInfoForClass(classEntity));
+    _entityToInfo[classEntity] = classInfo;
 
-    int size = dumpInfoTask.sizeOf(clazz);
-    environment.forEachLocalClassMember(clazz, (member) {
-      if (member.isFunction || member.isGetter || member.isSetter) {
-        FunctionInfo functionInfo = visitFunction(member);
+    int size = dumpInfoTask.sizeOf(classEntity);
+    clazz.members.forEach((ir.Member member) {
+      MemberEntity memberEntity =
+          environment.lookupClassMember(classEntity, member.name.text);
+      if (memberEntity == null) return;
+      // Note: JWorld representations of a kernel member may omit the field or
+      // getter. Filter those cases here.
+      if ((member is ir.Field && memberEntity is! FieldEntity) ||
+          (member is ir.Procedure && memberEntity is! FunctionEntity)) {
+        return;
+      }
+
+      if (member.function != null) {
+        FunctionInfo functionInfo = visitFunction(memberEntity);
         if (functionInfo != null) {
           classInfo.functions.add(functionInfo);
           functionInfo.parent = classInfo;
@@ -578,8 +600,8 @@
             size += closureInfo.size;
           }
         }
-      } else if (member.isField) {
-        FieldInfo fieldInfo = visitField(member, containingClass: clazz);
+      } else {
+        FieldInfo fieldInfo = visitField(member, fieldEntity: memberEntity);
         if (fieldInfo != null) {
           classInfo.fields.add(fieldInfo);
           fieldInfo.parent = classInfo;
@@ -587,12 +609,14 @@
             size += closureInfo.size;
           }
         }
-      } else {
-        throw StateError('Class member not a function or field');
       }
     });
-    environment.forEachConstructor(clazz, (constructor) {
-      FunctionInfo functionInfo = visitFunction(constructor);
+
+    clazz.constructors.forEach((ir.Constructor constructor) {
+      final constructorEntity =
+          environment.lookupConstructor(classEntity, constructor.name.text);
+      if (constructorEntity == null) return;
+      FunctionInfo functionInfo = visitFunction(constructorEntity);
       if (functionInfo != null) {
         classInfo.functions.add(functionInfo);
         functionInfo.parent = classInfo;
@@ -604,7 +628,8 @@
 
     classInfo.size = size;
 
-    if (!compiler.backendStrategy.emitterTask.neededClasses.contains(clazz) &&
+    if (!compiler.backendStrategy.emitterTask.neededClasses
+            .contains(classEntity) &&
         classInfo.fields.isEmpty &&
         classInfo.functions.isEmpty) {
       return null;
diff --git a/pkg/compiler/lib/src/js_backend/backend_impact.dart b/pkg/compiler/lib/src/js_backend/backend_impact.dart
index 56d4682..2211c3c 100644
--- a/pkg/compiler/lib/src/js_backend/backend_impact.dart
+++ b/pkg/compiler/lib/src/js_backend/backend_impact.dart
@@ -166,13 +166,6 @@
     ]);
   }
 
-  BackendImpact _fallThroughError;
-
-  BackendImpact get fallThroughError {
-    return _fallThroughError ??=
-        BackendImpact(staticUses: [_commonElements.fallThroughError]);
-  }
-
   BackendImpact _asCheck;
 
   BackendImpact get asCheck {
@@ -181,19 +174,6 @@
     ]);
   }
 
-  BackendImpact _throwNoSuchMethod;
-
-  BackendImpact get throwNoSuchMethod {
-    return _throwNoSuchMethod ??= BackendImpact(staticUses: [
-      _commonElements.throwNoSuchMethod,
-    ], otherImpacts: [
-      // Also register the types of the arguments passed to this method.
-      _needsList('Needed to encode the arguments for throw NoSuchMethodError.'),
-      _needsString('Needed to encode the name for throw NoSuchMethodError.'),
-      mapLiteralClass, // noSuchMethod helpers are passed a Map.
-    ]);
-  }
-
   BackendImpact _stringValues;
 
   BackendImpact get stringValues {
@@ -244,15 +224,6 @@
     ]);
   }
 
-  BackendImpact _throwRuntimeError;
-
-  BackendImpact get throwRuntimeError {
-    return _throwRuntimeError ??= BackendImpact(otherImpacts: [
-      // Also register the types of the arguments passed to this method.
-      stringValues
-    ]);
-  }
-
   BackendImpact _throwUnsupportedError;
 
   BackendImpact get throwUnsupportedError {
diff --git a/pkg/compiler/lib/src/js_backend/impact_transformer.dart b/pkg/compiler/lib/src/js_backend/impact_transformer.dart
index e340b31..8e0ff82 100644
--- a/pkg/compiler/lib/src/js_backend/impact_transformer.dart
+++ b/pkg/compiler/lib/src/js_backend/impact_transformer.dart
@@ -4,12 +4,8 @@
 
 library js_backend.backend.impact_transformer;
 
-import '../universe/class_hierarchy.dart' show ClassHierarchyBuilder;
-
-import '../common.dart';
 import '../common/elements.dart';
 import '../common/codegen.dart' show CodegenImpact;
-import '../common/resolution.dart' show ResolutionImpact;
 import '../constants/values.dart';
 import '../elements/entities.dart';
 import '../elements/types.dart';
@@ -23,335 +19,14 @@
 import '../universe/world_impact.dart' show TransformedWorldImpact, WorldImpact;
 import '../util/util.dart';
 import '../world.dart';
-import 'annotations.dart';
 import 'backend_impact.dart';
 import 'backend_usage.dart';
-import 'custom_elements_analysis.dart';
 import 'interceptor_data.dart';
 import 'namer.dart';
 import 'native_data.dart';
 import 'runtime_types.dart';
 import 'runtime_types_resolution.dart';
 
-/// JavaScript specific transformation for resolution world impacts.
-///
-/// This processes target-agnostic [ResolutionImpact]s and creates [WorldImpact]
-/// in which JavaScript specific impact data is added, for example: if
-/// a certain feature is used that requires some helper code from the backend
-/// libraries, this will be included by the impact transformer.
-class JavaScriptImpactTransformer {
-  final ElementEnvironment _elementEnvironment;
-  final CommonElements _commonElements;
-  final BackendImpacts _impacts;
-  final NativeBasicData _nativeBasicData;
-  final NativeResolutionEnqueuer _nativeResolutionEnqueuer;
-  final BackendUsageBuilder _backendUsageBuilder;
-  final CustomElementsResolutionAnalysis _customElementsResolutionAnalysis;
-  final RuntimeTypesNeedBuilder _rtiNeedBuilder;
-  final ClassHierarchyBuilder _classHierarchyBuilder;
-  final AnnotationsData _annotationsData;
-
-  JavaScriptImpactTransformer(
-      this._elementEnvironment,
-      this._commonElements,
-      this._impacts,
-      this._nativeBasicData,
-      this._nativeResolutionEnqueuer,
-      this._backendUsageBuilder,
-      this._customElementsResolutionAnalysis,
-      this._rtiNeedBuilder,
-      this._classHierarchyBuilder,
-      this._annotationsData);
-
-  DartTypes get _dartTypes => _commonElements.dartTypes;
-
-  /// Transform the [ResolutionImpact] into a [WorldImpact] adding the
-  /// backend dependencies for features used in [worldImpact].
-  WorldImpact transformResolutionImpact(ResolutionImpact worldImpact) {
-    TransformedWorldImpact transformed = TransformedWorldImpact(worldImpact);
-
-    void registerImpact(BackendImpact impact) {
-      impact.registerImpact(transformed, _elementEnvironment);
-      _backendUsageBuilder.processBackendImpact(impact);
-    }
-
-    for (Feature feature in worldImpact.features) {
-      switch (feature) {
-        case Feature.ASSERT:
-          registerImpact(_impacts.assertWithoutMessage);
-          break;
-        case Feature.ASSERT_WITH_MESSAGE:
-          registerImpact(_impacts.assertWithMessage);
-          break;
-        case Feature.ASYNC:
-          registerImpact(_impacts.asyncBody);
-          break;
-        case Feature.ASYNC_FOR_IN:
-          registerImpact(_impacts.asyncForIn);
-          break;
-        case Feature.ASYNC_STAR:
-          registerImpact(_impacts.asyncStarBody);
-          break;
-        case Feature.CATCH_STATEMENT:
-          registerImpact(_impacts.catchStatement);
-          break;
-        case Feature.FALL_THROUGH_ERROR:
-          registerImpact(_impacts.fallThroughError);
-          break;
-        case Feature.FIELD_WITHOUT_INITIALIZER:
-        case Feature.LOCAL_WITHOUT_INITIALIZER:
-          transformed
-              .registerTypeUse(TypeUse.instantiation(_commonElements.nullType));
-          registerImpact(_impacts.nullLiteral);
-          break;
-        case Feature.LAZY_FIELD:
-          registerImpact(_impacts.lazyField);
-          break;
-        case Feature.STACK_TRACE_IN_CATCH:
-          registerImpact(_impacts.stackTraceInCatch);
-          break;
-        case Feature.STRING_INTERPOLATION:
-          registerImpact(_impacts.stringInterpolation);
-          break;
-        case Feature.STRING_JUXTAPOSITION:
-          registerImpact(_impacts.stringJuxtaposition);
-          break;
-        case Feature.SUPER_NO_SUCH_METHOD:
-          registerImpact(_impacts.superNoSuchMethod);
-          break;
-        case Feature.SYNC_FOR_IN:
-          registerImpact(_impacts.syncForIn);
-          break;
-        case Feature.SYNC_STAR:
-          registerImpact(_impacts.syncStarBody);
-          break;
-        case Feature.THROW_EXPRESSION:
-          registerImpact(_impacts.throwExpression);
-          break;
-        case Feature.THROW_NO_SUCH_METHOD:
-          registerImpact(_impacts.throwNoSuchMethod);
-          break;
-        case Feature.THROW_RUNTIME_ERROR:
-          registerImpact(_impacts.throwRuntimeError);
-          break;
-        case Feature.THROW_UNSUPPORTED_ERROR:
-          registerImpact(_impacts.throwUnsupportedError);
-          break;
-        case Feature.TYPE_VARIABLE_BOUNDS_CHECK:
-          registerImpact(_impacts.typeVariableBoundCheck);
-          break;
-        case Feature.LOAD_LIBRARY:
-          registerImpact(_impacts.loadLibrary);
-          break;
-      }
-    }
-
-    bool hasAsCast = false;
-    bool hasTypeLiteral = false;
-    for (TypeUse typeUse in worldImpact.typeUses) {
-      DartType type = typeUse.type;
-      switch (typeUse.kind) {
-        case TypeUseKind.INSTANTIATION:
-        case TypeUseKind.CONST_INSTANTIATION:
-        case TypeUseKind.NATIVE_INSTANTIATION:
-          break;
-        case TypeUseKind.IS_CHECK:
-        case TypeUseKind.CATCH_TYPE:
-          onIsCheck(type, transformed);
-          break;
-        case TypeUseKind.AS_CAST:
-          if (_annotationsData
-              .getExplicitCastCheckPolicy(worldImpact.member)
-              .isEmitted) {
-            onIsCheck(type, transformed);
-            hasAsCast = true;
-          }
-          break;
-        case TypeUseKind.IMPLICIT_CAST:
-          if (_annotationsData
-              .getImplicitDowncastCheckPolicy(worldImpact.member)
-              .isEmitted) {
-            onIsCheck(type, transformed);
-          }
-          break;
-        case TypeUseKind.PARAMETER_CHECK:
-        case TypeUseKind.TYPE_VARIABLE_BOUND_CHECK:
-          if (_annotationsData
-              .getParameterCheckPolicy(worldImpact.member)
-              .isEmitted) {
-            onIsCheck(type, transformed);
-          }
-          break;
-        case TypeUseKind.TYPE_LITERAL:
-          _customElementsResolutionAnalysis.registerTypeLiteral(type);
-          type.forEachTypeVariable((TypeVariableType variable) {
-            TypeVariableType typeWithoutNullability =
-                variable.withoutNullability;
-            Entity typeDeclaration =
-                typeWithoutNullability.element.typeDeclaration;
-            if (typeDeclaration is ClassEntity) {
-              _rtiNeedBuilder
-                  .registerClassUsingTypeVariableLiteral(typeDeclaration);
-            } else if (typeDeclaration is FunctionEntity) {
-              _rtiNeedBuilder
-                  .registerMethodUsingTypeVariableLiteral(typeDeclaration);
-            } else if (typeDeclaration is Local) {
-              _rtiNeedBuilder.registerLocalFunctionUsingTypeVariableLiteral(
-                  typeDeclaration);
-            }
-            registerImpact(_impacts.typeVariableExpression);
-          });
-          hasTypeLiteral = true;
-          break;
-        case TypeUseKind.RTI_VALUE:
-        case TypeUseKind.TYPE_ARGUMENT:
-        case TypeUseKind.NAMED_TYPE_VARIABLE_NEW_RTI:
-        case TypeUseKind.CONSTRUCTOR_REFERENCE:
-          failedAt(CURRENT_ELEMENT_SPANNABLE, "Unexpected type use: $typeUse.");
-          break;
-      }
-    }
-
-    if (hasAsCast) {
-      registerImpact(_impacts.asCheck);
-    }
-
-    if (hasTypeLiteral) {
-      transformed
-          .registerTypeUse(TypeUse.instantiation(_commonElements.typeType));
-      registerImpact(_impacts.typeLiteral);
-    }
-
-    for (MapLiteralUse mapLiteralUse in worldImpact.mapLiterals) {
-      // TODO(johnniwinther): Use the [isEmpty] property when factory
-      // constructors are registered directly.
-      if (mapLiteralUse.isConstant) {
-        registerImpact(_impacts.constantMapLiteral);
-      } else {
-        transformed.registerTypeUse(TypeUse.instantiation(mapLiteralUse.type));
-      }
-    }
-
-    for (SetLiteralUse setLiteralUse in worldImpact.setLiterals) {
-      if (setLiteralUse.isConstant) {
-        registerImpact(_impacts.constantSetLiteral);
-      } else {
-        transformed.registerTypeUse(TypeUse.instantiation(setLiteralUse.type));
-      }
-    }
-
-    for (ListLiteralUse listLiteralUse in worldImpact.listLiterals) {
-      // TODO(johnniwinther): Use the [isConstant] and [isEmpty] property when
-      // factory constructors are registered directly.
-      transformed.registerTypeUse(TypeUse.instantiation(listLiteralUse.type));
-    }
-
-    for (RuntimeTypeUse runtimeTypeUse in worldImpact.runtimeTypeUses) {
-      // Enable runtime type support if we discover a getter called
-      // runtimeType. We have to enable runtime type before hitting the
-      // codegen, so that constructors know whether they need to generate code
-      // for runtime type.
-      _backendUsageBuilder.registerRuntimeTypeUse(runtimeTypeUse);
-    }
-
-    if (worldImpact.constSymbolNames.isNotEmpty) {
-      registerImpact(_impacts.constSymbol);
-    }
-
-    for (StaticUse staticUse in worldImpact.staticUses) {
-      switch (staticUse.kind) {
-        case StaticUseKind.CLOSURE:
-          registerImpact(_impacts.closure);
-          Local closure = staticUse.element;
-          FunctionType type = _elementEnvironment.getLocalFunctionType(closure);
-          if (type.containsTypeVariables ||
-              // TODO(johnniwinther): Can we avoid the need for signatures in
-              // Dart 2?
-              true) {
-            registerImpact(_impacts.computeSignature);
-          }
-          break;
-        default:
-      }
-    }
-
-    for (ConstantValue constant in worldImpact.constantLiterals) {
-      switch (constant.kind) {
-        case ConstantValueKind.NULL:
-          registerImpact(_impacts.nullLiteral);
-          break;
-        case ConstantValueKind.BOOL:
-          registerImpact(_impacts.boolLiteral);
-          break;
-        case ConstantValueKind.INT:
-          registerImpact(_impacts.intLiteral);
-          break;
-        case ConstantValueKind.DOUBLE:
-          registerImpact(_impacts.doubleLiteral);
-          break;
-        case ConstantValueKind.STRING:
-          registerImpact(_impacts.stringLiteral);
-          break;
-        default:
-          assert(
-              false,
-              failedAt(NO_LOCATION_SPANNABLE,
-                  "Unexpected constant literal: ${constant.kind}."));
-      }
-    }
-
-    for (NativeBehavior behavior in worldImpact.nativeData) {
-      _nativeResolutionEnqueuer.registerNativeBehavior(
-          transformed, behavior, worldImpact);
-    }
-
-    for (ClassEntity classEntity in worldImpact.seenClasses) {
-      _classHierarchyBuilder.registerClass(classEntity);
-    }
-
-    if (worldImpact.genericInstantiations.isNotEmpty) {
-      for (GenericInstantiation instantiation
-          in worldImpact.genericInstantiations) {
-        registerImpact(_impacts
-            .getGenericInstantiation(instantiation.typeArguments.length));
-        _rtiNeedBuilder.registerGenericInstantiation(instantiation);
-      }
-    }
-
-    return transformed;
-  }
-
-  // TODO(johnniwinther): Maybe split this into [onAssertType] and [onTestType].
-  void onIsCheck(DartType type, TransformedWorldImpact transformed) {
-    void registerImpact(BackendImpact impact) {
-      impact.registerImpact(transformed, _elementEnvironment);
-      _backendUsageBuilder.processBackendImpact(impact);
-    }
-
-    registerImpact(_impacts.typeCheck);
-
-    var typeWithoutNullability = type.withoutNullability;
-    if (!_dartTypes.treatAsRawType(typeWithoutNullability) ||
-        typeWithoutNullability.containsTypeVariables ||
-        typeWithoutNullability is FunctionType) {
-      registerImpact(_impacts.genericTypeCheck);
-      if (typeWithoutNullability is TypeVariableType) {
-        registerImpact(_impacts.typeVariableTypeCheck);
-      }
-    }
-    if (typeWithoutNullability is FunctionType) {
-      registerImpact(_impacts.functionTypeCheck);
-    }
-    if (typeWithoutNullability is InterfaceType &&
-        _nativeBasicData.isNativeClass(typeWithoutNullability.element)) {
-      registerImpact(_impacts.nativeTypeCheck);
-    }
-    if (typeWithoutNullability is FutureOrType) {
-      registerImpact(_impacts.futureOrTypeCheck);
-    }
-  }
-}
-
 class CodegenImpactTransformer {
   final JClosedWorld _closedWorld;
   final ElementEnvironment _elementEnvironment;
diff --git a/pkg/compiler/lib/src/js_backend/runtime_types_resolution.dart b/pkg/compiler/lib/src/js_backend/runtime_types_resolution.dart
index c53fa5f..391a9fbcc 100644
--- a/pkg/compiler/lib/src/js_backend/runtime_types_resolution.dart
+++ b/pkg/compiler/lib/src/js_backend/runtime_types_resolution.dart
@@ -886,6 +886,9 @@
   /// Registers that a generic [instantiation] is used.
   void registerGenericInstantiation(GenericInstantiation instantiation);
 
+  /// Registers a [TypeVariableType] literal on this [RuntimeTypesNeedBuilder].
+  void registerTypeVariableLiteral(TypeVariableType variable);
+
   /// Computes the [RuntimeTypesNeed] for the data registered with this builder.
   RuntimeTypesNeed computeRuntimeTypesNeed(
       KClosedWorld closedWorld, CompilerOptions options);
@@ -907,6 +910,9 @@
   void registerGenericInstantiation(GenericInstantiation instantiation) {}
 
   @override
+  void registerTypeVariableLiteral(TypeVariableType variable) {}
+
+  @override
   RuntimeTypesNeed computeRuntimeTypesNeed(
       KClosedWorld closedWorld, CompilerOptions options) {
     return TrivialRuntimeTypesNeed(closedWorld.elementEnvironment);
@@ -957,6 +963,18 @@
   }
 
   @override
+  void registerTypeVariableLiteral(TypeVariableType variable) {
+    Entity typeDeclaration = variable.element.typeDeclaration;
+    if (typeDeclaration is ClassEntity) {
+      registerClassUsingTypeVariableLiteral(typeDeclaration);
+    } else if (typeDeclaration is FunctionEntity) {
+      registerMethodUsingTypeVariableLiteral(typeDeclaration);
+    } else if (typeDeclaration is Local) {
+      registerLocalFunctionUsingTypeVariableLiteral(typeDeclaration);
+    }
+  }
+
+  @override
   RuntimeTypesNeed computeRuntimeTypesNeed(
       KClosedWorld closedWorld, CompilerOptions options) {
     TypeVariableTests typeVariableTests = TypeVariableTests(
diff --git a/pkg/compiler/lib/src/kernel/element_map_impl.dart b/pkg/compiler/lib/src/kernel/element_map_impl.dart
index 4bfe3529..6dc3606 100644
--- a/pkg/compiler/lib/src/kernel/element_map_impl.dart
+++ b/pkg/compiler/lib/src/kernel/element_map_impl.dart
@@ -15,7 +15,6 @@
 import '../common.dart';
 import '../common/elements.dart';
 import '../common/names.dart';
-import '../common/resolution.dart';
 import '../constants/values.dart';
 import '../elements/entities.dart';
 import '../elements/indexed.dart';
@@ -33,15 +32,22 @@
 import '../ir/visitors.dart';
 import '../ir/util.dart';
 import '../js/js.dart' as js;
+import '../js_backend/annotations.dart';
+import '../js_backend/backend_impact.dart';
+import '../js_backend/backend_usage.dart';
+import '../js_backend/custom_elements_analysis.dart';
 import '../js_backend/namer.dart';
 import '../js_backend/native_data.dart';
+import '../js_backend/runtime_types_resolution.dart';
 import '../js_model/locals.dart';
 import '../kernel/dart2js_target.dart';
 import '../native/behavior.dart';
+import '../native/enqueue.dart';
 import '../options.dart';
 import '../ordered_typeset.dart';
 import '../universe/call_structure.dart';
 import '../universe/selector.dart';
+import '../universe/world_impact.dart';
 
 import 'element_map.dart';
 import 'env.dart';
@@ -103,6 +109,7 @@
   BehaviorBuilder _nativeBehaviorBuilder;
 
   Map<KMember, Map<ir.Expression, TypeMap>> typeMapsForTesting;
+  Map<ir.Member, ImpactData> impactDataForTesting;
 
   KernelToElementMap(this.reporter, this._environment, this.options) {
     _elementEnvironment = KernelElementEnvironment(this);
@@ -1443,8 +1450,15 @@
       _nativeBehaviorBuilder ??= BehaviorBuilder(elementEnvironment,
           commonElements, nativeBasicData, reporter, options);
 
-  ResolutionImpact computeWorldImpact(
-      KMember member, ImpactBuilderData impactBuilderData) {
+  WorldImpact computeWorldImpact(
+      KMember member,
+      BackendImpacts impacts,
+      NativeResolutionEnqueuer nativeResolutionEnqueuer,
+      BackendUsageBuilder backendUsageBuilder,
+      CustomElementsResolutionAnalysis customElementsResolutionAnalysis,
+      RuntimeTypesNeedBuilder rtiNeedBuilder,
+      AnnotationsData annotationsData,
+      ImpactBuilderData impactBuilderData) {
     KMemberData memberData = members.getData(member);
     ir.Member node = memberData.node;
 
@@ -1454,6 +1468,10 @@
     }
     ImpactData impactData = impactBuilderData.impactData;
     memberData.staticTypes = impactBuilderData.cachedStaticTypes;
+    if (retainDataForTesting) {
+      impactDataForTesting ??= {};
+      impactDataForTesting[node] = impactData;
+    }
     KernelImpactConverter converter = KernelImpactConverter(
         this,
         member,
@@ -1462,7 +1480,13 @@
         _constantValuefier,
         // TODO(johnniwinther): Pull the static type context from the cached
         // static types.
-        ir.StaticTypeContext(node, typeEnvironment));
+        ir.StaticTypeContext(node, typeEnvironment),
+        impacts,
+        nativeResolutionEnqueuer,
+        backendUsageBuilder,
+        customElementsResolutionAnalysis,
+        rtiNeedBuilder,
+        annotationsData);
     return converter.convert(impactData);
   }
 
diff --git a/pkg/compiler/lib/src/kernel/kernel_impact.dart b/pkg/compiler/lib/src/kernel/kernel_impact.dart
index 7b863ab..11fa918 100644
--- a/pkg/compiler/lib/src/kernel/kernel_impact.dart
+++ b/pkg/compiler/lib/src/kernel/kernel_impact.dart
@@ -7,7 +7,6 @@
 
 import '../common.dart';
 import '../common/names.dart';
-import '../common/resolution.dart';
 import '../common/elements.dart';
 import '../constants/values.dart';
 import '../elements/entities.dart';
@@ -19,32 +18,54 @@
 import '../ir/static_type.dart';
 import '../ir/util.dart';
 import '../ir/visitors.dart';
+import '../js_backend/annotations.dart';
+import '../js_backend/backend_impact.dart';
+import '../js_backend/backend_usage.dart';
+import '../js_backend/custom_elements_analysis.dart';
 import '../js_backend/native_data.dart';
+import '../js_backend/runtime_types_resolution.dart';
 import '../native/behavior.dart';
+import '../native/enqueue.dart';
 import '../options.dart';
-import '../resolution/registry.dart' show ResolutionWorldImpactBuilder;
 import '../universe/call_structure.dart';
 import '../universe/feature.dart';
 import '../universe/selector.dart';
 import '../universe/use.dart';
 import '../universe/world_builder.dart';
+import '../universe/world_impact.dart';
 import 'element_map.dart';
 
 /// [ImpactRegistry] that converts kernel based impact data to world impact
 /// object based on the K model.
 class KernelImpactConverter implements ImpactRegistry {
-  final ResolutionWorldImpactBuilder impactBuilder;
+  final WorldImpactBuilder impactBuilder;
   final KernelToElementMap elementMap;
   final DiagnosticReporter reporter;
   final CompilerOptions _options;
   final MemberEntity currentMember;
   final ConstantValuefier _constantValuefier;
   final ir.StaticTypeContext staticTypeContext;
+  final BackendImpacts _impacts;
+  final NativeResolutionEnqueuer _nativeResolutionEnqueuer;
+  final BackendUsageBuilder _backendUsageBuilder;
+  final CustomElementsResolutionAnalysis _customElementsResolutionAnalysis;
+  final RuntimeTypesNeedBuilder _rtiNeedBuilder;
+  final AnnotationsData _annotationsData;
 
-  KernelImpactConverter(this.elementMap, this.currentMember, this.reporter,
-      this._options, this._constantValuefier, this.staticTypeContext)
-      : this.impactBuilder = ResolutionWorldImpactBuilder(
-            elementMap.commonElements.dartTypes, currentMember);
+  KernelImpactConverter(
+      this.elementMap,
+      this.currentMember,
+      this.reporter,
+      this._options,
+      this._constantValuefier,
+      this.staticTypeContext,
+      this._impacts,
+      this._nativeResolutionEnqueuer,
+      this._backendUsageBuilder,
+      this._customElementsResolutionAnalysis,
+      this._rtiNeedBuilder,
+      this._annotationsData)
+      : this.impactBuilder = WorldImpactBuilderImpl(currentMember);
 
   ir.TypeEnvironment get typeEnvironment => elementMap.typeEnvironment;
 
@@ -52,6 +73,8 @@
 
   NativeBasicData get _nativeBasicData => elementMap.nativeBasicData;
 
+  ElementEnvironment get elementEnvironment => elementMap.elementEnvironment;
+
   DartTypes get dartTypes => commonElements.dartTypes;
 
   String typeToString(DartType type) =>
@@ -72,11 +95,48 @@
     return null;
   }
 
+  void registerBackendImpact(BackendImpact impact) {
+    impact.registerImpact(impactBuilder, elementEnvironment);
+    _backendUsageBuilder.processBackendImpact(impact);
+  }
+
+  void registerNativeImpact(NativeBehavior behavior) {
+    _nativeResolutionEnqueuer.registerNativeBehavior(
+        impactBuilder, behavior, impactBuilder);
+  }
+
+  // TODO(johnniwinther): Maybe split this into [onAssertType] and [onTestType].
+  void onIsCheck(DartType type) {
+    registerBackendImpact(_impacts.typeCheck);
+    var typeWithoutNullability = type.withoutNullability;
+    if (!dartTypes.treatAsRawType(typeWithoutNullability) ||
+        typeWithoutNullability.containsTypeVariables ||
+        typeWithoutNullability is FunctionType) {
+      registerBackendImpact(_impacts.genericTypeCheck);
+      if (typeWithoutNullability is TypeVariableType) {
+        registerBackendImpact(_impacts.typeVariableTypeCheck);
+      }
+    }
+    if (typeWithoutNullability is FunctionType) {
+      registerBackendImpact(_impacts.functionTypeCheck);
+    }
+    if (typeWithoutNullability is InterfaceType &&
+        _nativeBasicData.isNativeClass(typeWithoutNullability.element)) {
+      registerBackendImpact(_impacts.nativeTypeCheck);
+    }
+    if (typeWithoutNullability is FutureOrType) {
+      registerBackendImpact(_impacts.futureOrTypeCheck);
+    }
+  }
+
   @override
   void registerParameterCheck(ir.DartType irType) {
     DartType type = elementMap.getDartType(irType);
     if (type is! DynamicType) {
       impactBuilder.registerTypeUse(TypeUse.parameterCheck(type));
+      if (_annotationsData.getParameterCheckPolicy(currentMember).isEmitted) {
+        onIsCheck(type);
+      }
     }
   }
 
@@ -87,7 +147,7 @@
 
   @override
   void registerLazyField() {
-    impactBuilder.registerFeature(Feature.LAZY_FIELD);
+    registerBackendImpact(_impacts.lazyField);
   }
 
   @override
@@ -105,11 +165,10 @@
           getCreatesAnnotations(dartTypes, reporter, commonElements, metadata);
       Iterable<String> returnsAnnotations =
           getReturnsAnnotations(dartTypes, reporter, commonElements, metadata);
-      impactBuilder.registerNativeData(elementMap.getNativeBehaviorForFieldLoad(
+      registerNativeImpact(elementMap.getNativeBehaviorForFieldLoad(
           field, createsAnnotations, returnsAnnotations,
           isJsInterop: isJsInterop));
-      impactBuilder
-          .registerNativeData(elementMap.getNativeBehaviorForFieldStore(field));
+      registerNativeImpact(elementMap.getNativeBehaviorForFieldStore(field));
     }
   }
 
@@ -126,7 +185,7 @@
           getCreatesAnnotations(dartTypes, reporter, commonElements, metadata);
       Iterable<String> returnsAnnotations =
           getReturnsAnnotations(dartTypes, reporter, commonElements, metadata);
-      impactBuilder.registerNativeData(elementMap.getNativeBehaviorForMethod(
+      registerNativeImpact(elementMap.getNativeBehaviorForMethod(
           constructor, createsAnnotations, returnsAnnotations,
           isJsInterop: isJsInterop));
     }
@@ -134,7 +193,7 @@
 
   @override
   void registerSyncStar(ir.DartType elementType) {
-    impactBuilder.registerFeature(Feature.SYNC_STAR);
+    registerBackendImpact(_impacts.syncStarBody);
     impactBuilder.registerStaticUse(StaticUse.staticInvoke(
         commonElements.syncStarIterableFactory,
         CallStructure.unnamed(1, 1),
@@ -143,7 +202,7 @@
 
   @override
   void registerAsync(ir.DartType elementType) {
-    impactBuilder.registerFeature(Feature.ASYNC);
+    registerBackendImpact(_impacts.asyncBody);
     impactBuilder.registerStaticUse(StaticUse.staticInvoke(
         commonElements.asyncAwaitCompleterFactory,
         CallStructure.unnamed(0, 1),
@@ -152,7 +211,7 @@
 
   @override
   void registerAsyncStar(ir.DartType elementType) {
-    impactBuilder.registerFeature(Feature.ASYNC_STAR);
+    registerBackendImpact(_impacts.asyncStarBody);
     impactBuilder.registerStaticUse(StaticUse.staticInvoke(
         commonElements.asyncStarStreamControllerFactory,
         CallStructure.unnamed(1, 1),
@@ -172,7 +231,7 @@
           getCreatesAnnotations(dartTypes, reporter, commonElements, metadata);
       Iterable<String> returnsAnnotations =
           getReturnsAnnotations(dartTypes, reporter, commonElements, metadata);
-      impactBuilder.registerNativeData(elementMap.getNativeBehaviorForMethod(
+      registerNativeImpact(elementMap.getNativeBehaviorForMethod(
           procedure, createsAnnotations, returnsAnnotations,
           isJsInterop: isJsInterop));
     }
@@ -180,61 +239,68 @@
 
   @override
   void registerIntLiteral(int value) {
-    impactBuilder.registerConstantLiteral(
-        IntConstantValue(BigInt.from(value).toUnsigned(64)));
+    registerBackendImpact(_impacts.intLiteral);
   }
 
   @override
   void registerDoubleLiteral(double value) {
-    impactBuilder.registerConstantLiteral(DoubleConstantValue(value));
+    registerBackendImpact(_impacts.doubleLiteral);
   }
 
   @override
   void registerBoolLiteral(bool value) {
-    impactBuilder.registerConstantLiteral(BoolConstantValue(value));
+    registerBackendImpact(_impacts.boolLiteral);
   }
 
   @override
   void registerStringLiteral(String value) {
-    impactBuilder.registerConstantLiteral(StringConstantValue(value));
+    registerBackendImpact(_impacts.stringLiteral);
   }
 
   @override
   void registerSymbolLiteral(String value) {
-    impactBuilder.registerConstSymbolName(value);
+    registerBackendImpact(_impacts.constSymbol);
   }
 
   @override
   void registerNullLiteral() {
-    impactBuilder.registerConstantLiteral(NullConstantValue());
+    registerBackendImpact(_impacts.nullLiteral);
   }
 
   @override
   void registerListLiteral(ir.DartType elementType,
       {bool isConst, bool isEmpty}) {
-    impactBuilder.registerListLiteral(ListLiteralUse(
-        commonElements.listType(elementMap.getDartType(elementType)),
-        isConstant: isConst,
-        isEmpty: isEmpty));
+    // TODO(johnniwinther): Use the [isConstant] and [isEmpty] property when
+    // factory constructors are registered directly.
+    impactBuilder.registerTypeUse(TypeUse.instantiation(
+        commonElements.listType(elementMap.getDartType(elementType))));
   }
 
   @override
   void registerSetLiteral(ir.DartType elementType,
       {bool isConst, bool isEmpty}) {
-    impactBuilder.registerSetLiteral(SetLiteralUse(
-        commonElements.setType(elementMap.getDartType(elementType)),
-        isConstant: isConst,
-        isEmpty: isEmpty));
+    // TODO(johnniwinther): Use the [isEmpty] property when factory
+    // constructors are registered directly.
+    if (isConst) {
+      registerBackendImpact(_impacts.constantSetLiteral);
+    } else {
+      impactBuilder.registerTypeUse(TypeUse.instantiation(
+          commonElements.setType(elementMap.getDartType(elementType))));
+    }
   }
 
   @override
   void registerMapLiteral(ir.DartType keyType, ir.DartType valueType,
       {bool isConst, bool isEmpty}) {
-    impactBuilder.registerMapLiteral(MapLiteralUse(
-        commonElements.mapType(
-            elementMap.getDartType(keyType), elementMap.getDartType(valueType)),
-        isConstant: isConst,
-        isEmpty: isEmpty));
+    // TODO(johnniwinther): Use the [isEmpty] property when factory
+    // constructors are registered directly.
+    if (isConst) {
+      registerBackendImpact(_impacts.constantMapLiteral);
+    } else {
+      impactBuilder.registerTypeUse(TypeUse.instantiation(
+          commonElements.mapType(elementMap.getDartType(keyType),
+              elementMap.getDartType(valueType))));
+    }
   }
 
   @override
@@ -258,13 +324,13 @@
         : StaticUse.typedConstructorInvoke(constructor, callStructure,
             elementMap.getDartType(type).withoutNullability, deferredImport));
     if (type.typeArguments.any((ir.DartType type) => type is! ir.DynamicType)) {
-      impactBuilder.registerFeature(Feature.TYPE_VARIABLE_BOUNDS_CHECK);
+      registerBackendImpact(_impacts.typeVariableBoundCheck);
     }
 
     if (target.isExternal &&
         constructor.isFromEnvironmentConstructor &&
         !isConst) {
-      impactBuilder.registerFeature(Feature.THROW_UNSUPPORTED_ERROR);
+      registerBackendImpact(_impacts.throwUnsupportedError);
       // We need to register the external constructor as live below, so don't
       // return here.
     }
@@ -294,8 +360,7 @@
             {'type': typeToString(value.getType(elementMap.commonElements))});
         return;
       }
-      StringConstantValue stringValue = value;
-      impactBuilder.registerConstSymbolName(stringValue.stringValue);
+      registerBackendImpact(_impacts.constSymbol);
     }
   }
 
@@ -345,15 +410,14 @@
   void registerStaticInvocationNode(ir.StaticInvocation node) {
     switch (elementMap.getForeignKind(node)) {
       case ForeignKind.JS:
-        impactBuilder
-            .registerNativeData(elementMap.getNativeBehaviorForJsCall(node));
+        registerNativeImpact(elementMap.getNativeBehaviorForJsCall(node));
         break;
       case ForeignKind.JS_BUILTIN:
-        impactBuilder.registerNativeData(
+        registerNativeImpact(
             elementMap.getNativeBehaviorForJsBuiltinCall(node));
         break;
       case ForeignKind.JS_EMBEDDED_GLOBAL:
-        impactBuilder.registerNativeData(
+        registerNativeImpact(
             elementMap.getNativeBehaviorForJsEmbeddedGlobalCall(node));
         break;
       case ForeignKind.JS_INTERCEPTOR_CONSTANT:
@@ -387,8 +451,7 @@
     InterfaceType interfaceType = matchedType;
     ClassEntity cls = interfaceType.element;
     InterfaceType thisType = elementMap.elementEnvironment.getThisType(cls);
-
-    impactBuilder.registerTypeUse(TypeUse.isCheck(thisType));
+    _registerIsCheckInternal(thisType);
 
     Selector selector = Selector.callClosure(
         0, const <String>[], thisType.typeArguments.length);
@@ -432,7 +495,7 @@
       impactBuilder.registerStaticUse(StaticUse.superInvoke(
           elementMap.getSuperNoSuchMethod(currentMember.enclosingClass),
           CallStructure.ONE_ARG));
-      impactBuilder.registerFeature(Feature.SUPER_NO_SUCH_METHOD);
+      registerBackendImpact(_impacts.superNoSuchMethod);
     }
   }
 
@@ -451,7 +514,7 @@
       impactBuilder.registerStaticUse(StaticUse.superInvoke(
           elementMap.getSuperNoSuchMethod(currentMember.enclosingClass),
           CallStructure.ONE_ARG));
-      impactBuilder.registerFeature(Feature.SUPER_NO_SUCH_METHOD);
+      registerBackendImpact(_impacts.superNoSuchMethod);
     }
   }
 
@@ -470,7 +533,7 @@
       impactBuilder.registerStaticUse(StaticUse.superInvoke(
           elementMap.getSuperNoSuchMethod(currentMember.enclosingClass),
           CallStructure.ONE_ARG));
-      impactBuilder.registerFeature(Feature.SUPER_NO_SUCH_METHOD);
+      registerBackendImpact(_impacts.superNoSuchMethod);
     }
   }
 
@@ -604,62 +667,89 @@
           break;
       }
     }
-    impactBuilder.registerRuntimeTypeUse(
+
+    // Enable runtime type support if we discover a getter called
+    // runtimeType. We have to enable runtime type before hitting the
+    // codegen, so that constructors know whether they need to generate code
+    // for runtime type.
+    _backendUsageBuilder.registerRuntimeTypeUse(
         RuntimeTypeUse(kind, receiverDartType, argumentDartType));
   }
 
   @override
   void registerAssert({bool withMessage}) {
-    impactBuilder.registerFeature(
-        withMessage ? Feature.ASSERT_WITH_MESSAGE : Feature.ASSERT);
+    registerBackendImpact(withMessage
+        ? _impacts.assertWithMessage
+        : _impacts.assertWithoutMessage);
   }
 
   @override
   void registerGenericInstantiation(
       ir.FunctionType expressionType, List<ir.DartType> typeArguments) {
     // TODO(johnniwinther): Track which arities are used in instantiation.
-    impactBuilder.registerInstantiation(GenericInstantiation(
+    final instantiation = GenericInstantiation(
         elementMap.getDartType(expressionType).withoutNullability,
-        typeArguments.map(elementMap.getDartType).toList()));
+        typeArguments.map(elementMap.getDartType).toList());
+    registerBackendImpact(
+        _impacts.getGenericInstantiation(instantiation.typeArguments.length));
+    _rtiNeedBuilder.registerGenericInstantiation(instantiation);
   }
 
   @override
   void registerStringConcatenation() {
-    impactBuilder.registerFeature(Feature.STRING_INTERPOLATION);
-    impactBuilder.registerFeature(Feature.STRING_JUXTAPOSITION);
+    registerBackendImpact(_impacts.stringInterpolation);
+    registerBackendImpact(_impacts.stringJuxtaposition);
   }
 
   @override
   void registerLocalFunction(ir.TreeNode node) {
     Local function = elementMap.getLocalFunction(node);
     impactBuilder.registerStaticUse(StaticUse.closure(function));
+    registerBackendImpact(_impacts.closure);
+    registerBackendImpact(_impacts.computeSignature);
   }
 
   @override
   void registerLocalWithoutInitializer() {
-    impactBuilder.registerFeature(Feature.LOCAL_WITHOUT_INITIALIZER);
-  }
-
-  @override
-  void registerIsCheck(ir.DartType type) {
     impactBuilder
-        .registerTypeUse(TypeUse.isCheck(elementMap.getDartType(type)));
+        .registerTypeUse(TypeUse.instantiation(commonElements.nullType));
+    registerBackendImpact(_impacts.nullLiteral);
+  }
+
+  void _registerIsCheckInternal(DartType type) {
+    impactBuilder.registerTypeUse(TypeUse.isCheck(type));
+    onIsCheck(type);
   }
 
   @override
-  void registerImplicitCast(ir.DartType type) {
-    impactBuilder
-        .registerTypeUse(TypeUse.implicitCast(elementMap.getDartType(type)));
+  void registerIsCheck(ir.DartType irType) {
+    _registerIsCheckInternal(elementMap.getDartType(irType));
   }
 
   @override
-  void registerAsCast(ir.DartType type) {
-    impactBuilder.registerTypeUse(TypeUse.asCast(elementMap.getDartType(type)));
+  void registerImplicitCast(ir.DartType irType) {
+    DartType type = elementMap.getDartType(irType);
+    impactBuilder.registerTypeUse(TypeUse.implicitCast(type));
+    if (_annotationsData
+        .getImplicitDowncastCheckPolicy(currentMember)
+        .isEmitted) {
+      onIsCheck(type);
+    }
+  }
+
+  @override
+  void registerAsCast(ir.DartType irType) {
+    DartType type = elementMap.getDartType(irType);
+    impactBuilder.registerTypeUse(TypeUse.asCast(type));
+    if (_annotationsData.getExplicitCastCheckPolicy(currentMember).isEmitted) {
+      onIsCheck(type);
+      registerBackendImpact(_impacts.asCheck);
+    }
   }
 
   @override
   void registerThrow() {
-    impactBuilder.registerFeature(Feature.THROW_EXPRESSION);
+    registerBackendImpact(_impacts.throwExpression);
   }
 
   @override
@@ -667,7 +757,7 @@
       ClassRelation iteratorClassRelation) {
     Object receiverConstraint =
         _computeReceiverConstraint(iteratorType, iteratorClassRelation);
-    impactBuilder.registerFeature(Feature.SYNC_FOR_IN);
+    registerBackendImpact(_impacts.syncForIn);
     impactBuilder.registerDynamicUse(
         DynamicUse(Selectors.iterator, receiverConstraint, const []));
     impactBuilder.registerDynamicUse(
@@ -681,7 +771,7 @@
       ClassRelation iteratorClassRelation) {
     Object receiverConstraint =
         _computeReceiverConstraint(iteratorType, iteratorClassRelation);
-    impactBuilder.registerFeature(Feature.ASYNC_FOR_IN);
+    registerBackendImpact(_impacts.asyncForIn);
     impactBuilder.registerDynamicUse(
         DynamicUse(Selectors.cancel, receiverConstraint, const []));
     impactBuilder.registerDynamicUse(
@@ -692,25 +782,34 @@
 
   @override
   void registerCatch() {
-    impactBuilder.registerFeature(Feature.CATCH_STATEMENT);
+    registerBackendImpact(_impacts.catchStatement);
   }
 
   @override
   void registerStackTrace() {
-    impactBuilder.registerFeature(Feature.STACK_TRACE_IN_CATCH);
+    registerBackendImpact(_impacts.stackTraceInCatch);
   }
 
   @override
-  void registerCatchType(ir.DartType type) {
-    impactBuilder
-        .registerTypeUse(TypeUse.catchType(elementMap.getDartType(type)));
+  void registerCatchType(ir.DartType irType) {
+    DartType type = elementMap.getDartType(irType);
+    impactBuilder.registerTypeUse(TypeUse.catchType(type));
+    onIsCheck(type);
   }
 
   @override
-  void registerTypeLiteral(ir.DartType type, ir.LibraryDependency import) {
+  void registerTypeLiteral(ir.DartType irType, ir.LibraryDependency import) {
     ImportEntity deferredImport = elementMap.getImport(import);
-    impactBuilder.registerTypeUse(
-        TypeUse.typeLiteral(elementMap.getDartType(type), deferredImport));
+    DartType type = elementMap.getDartType(irType);
+    impactBuilder.registerTypeUse(TypeUse.typeLiteral(type, deferredImport));
+    _customElementsResolutionAnalysis.registerTypeLiteral(type);
+    type.forEachTypeVariable((TypeVariableType variable) {
+      _rtiNeedBuilder.registerTypeVariableLiteral(variable);
+      registerBackendImpact(_impacts.typeVariableExpression);
+    });
+    impactBuilder
+        .registerTypeUse(TypeUse.instantiation(commonElements.typeType));
+    registerBackendImpact(_impacts.typeLiteral);
   }
 
   @override
@@ -744,7 +843,7 @@
   void registerLoadLibrary() {
     impactBuilder.registerStaticUse(StaticUse.staticInvoke(
         commonElements.loadDeferredLibrary, CallStructure.ONE_ARG));
-    impactBuilder.registerFeature(Feature.LOAD_LIBRARY);
+    registerBackendImpact(_impacts.loadLibrary);
   }
 
   @override
@@ -794,8 +893,8 @@
   }
 
   /// Converts a [ImpactData] object based on kernel to the corresponding
-  /// [ResolutionImpact] based on the K model.
-  ResolutionImpact convert(ImpactData impactData) {
+  /// [WorldImpact] based on the K model.
+  WorldImpact convert(ImpactData impactData) {
     impactData.apply(this);
     return impactBuilder;
   }
diff --git a/pkg/compiler/lib/src/kernel/kernel_strategy.dart b/pkg/compiler/lib/src/kernel/kernel_strategy.dart
index d45d1bd..6c29404 100644
--- a/pkg/compiler/lib/src/kernel/kernel_strategy.dart
+++ b/pkg/compiler/lib/src/kernel/kernel_strategy.dart
@@ -9,7 +9,6 @@
 import '../common.dart';
 import '../common/elements.dart';
 import '../common/names.dart' show Uris;
-import '../common/resolution.dart';
 import '../common/tasks.dart';
 import '../common/work.dart';
 import '../compiler.dart';
@@ -27,7 +26,6 @@
 import '../js_backend/backend_usage.dart';
 import '../js_backend/custom_elements_analysis.dart';
 import '../js_backend/field_analysis.dart' show KFieldAnalysis;
-import '../js_backend/impact_transformer.dart';
 import '../js_backend/interceptor_data.dart';
 import '../js_backend/native_data.dart';
 import '../js_backend/no_such_method_registry.dart';
@@ -155,17 +153,6 @@
     // before creating the resolution enqueuer.
     AnnotationsData annotationsData = AnnotationsDataImpl(
         compiler.options, annotationsDataBuilder.pragmaAnnotations);
-    final impactTransformer = JavaScriptImpactTransformer(
-        elementEnvironment,
-        commonElements,
-        impacts,
-        nativeBasicData,
-        _nativeResolutionEnqueuer,
-        _backendUsageBuilder,
-        _customElementsResolutionAnalysis,
-        rtiNeedBuilder,
-        classHierarchyBuilder,
-        annotationsData);
     InterceptorDataBuilder interceptorDataBuilder = InterceptorDataBuilderImpl(
         nativeBasicData, elementEnvironment, commonElements);
     return ResolutionEnqueuer(
@@ -207,12 +194,17 @@
             nativeBasicData,
             nativeDataBuilder,
             annotationsDataBuilder,
-            impactTransformer,
             closureModels,
             compiler.impactCache,
             _fieldAnalysis,
             _modularStrategy,
-            _irAnnotationData),
+            _irAnnotationData,
+            impacts,
+            _nativeResolutionEnqueuer,
+            _backendUsageBuilder,
+            _customElementsResolutionAnalysis,
+            rtiNeedBuilder,
+            annotationsData),
         annotationsData);
   }
 
@@ -287,7 +279,6 @@
 class KernelWorkItemBuilder implements WorkItemBuilder {
   final CompilerTask _compilerTask;
   final KernelToElementMap _elementMap;
-  final JavaScriptImpactTransformer _impactTransformer;
   final KernelNativeMemberResolver _nativeMemberResolver;
   final AnnotationsDataBuilder _annotationsDataBuilder;
   final Map<MemberEntity, ClosureScopeModel> _closureModels;
@@ -295,6 +286,12 @@
   final KFieldAnalysis _fieldAnalysis;
   final ModularStrategy _modularStrategy;
   final IrAnnotationData _irAnnotationData;
+  final BackendImpacts _impacts;
+  final NativeResolutionEnqueuer _nativeResolutionEnqueuer;
+  final BackendUsageBuilder _backendUsageBuilder;
+  final CustomElementsResolutionAnalysis _customElementsResolutionAnalysis;
+  final RuntimeTypesNeedBuilder _rtiNeedBuilder;
+  final AnnotationsData _annotationsData;
 
   KernelWorkItemBuilder(
       this._compilerTask,
@@ -302,12 +299,17 @@
       NativeBasicData nativeBasicData,
       NativeDataBuilder nativeDataBuilder,
       this._annotationsDataBuilder,
-      this._impactTransformer,
       this._closureModels,
       this._impactCache,
       this._fieldAnalysis,
       this._modularStrategy,
-      this._irAnnotationData)
+      this._irAnnotationData,
+      this._impacts,
+      this._nativeResolutionEnqueuer,
+      this._backendUsageBuilder,
+      this._customElementsResolutionAnalysis,
+      this._rtiNeedBuilder,
+      this._annotationsData)
       : _nativeMemberResolver = KernelNativeMemberResolver(
             _elementMap, nativeBasicData, nativeDataBuilder);
 
@@ -316,7 +318,6 @@
     return KernelWorkItem(
         _compilerTask,
         _elementMap,
-        _impactTransformer,
         _nativeMemberResolver,
         _annotationsDataBuilder,
         entity,
@@ -324,14 +325,19 @@
         _impactCache,
         _fieldAnalysis,
         _modularStrategy,
-        _irAnnotationData);
+        _irAnnotationData,
+        _impacts,
+        _nativeResolutionEnqueuer,
+        _backendUsageBuilder,
+        _customElementsResolutionAnalysis,
+        _rtiNeedBuilder,
+        _annotationsData);
   }
 }
 
 class KernelWorkItem implements WorkItem {
   final CompilerTask _compilerTask;
   final KernelToElementMap _elementMap;
-  final JavaScriptImpactTransformer _impactTransformer;
   final KernelNativeMemberResolver _nativeMemberResolver;
   final AnnotationsDataBuilder _annotationsDataBuilder;
   @override
@@ -341,11 +347,16 @@
   final KFieldAnalysis _fieldAnalysis;
   final ModularStrategy _modularStrategy;
   final IrAnnotationData _irAnnotationData;
+  final BackendImpacts _impacts;
+  final NativeResolutionEnqueuer _nativeResolutionEnqueuer;
+  final BackendUsageBuilder _backendUsageBuilder;
+  final CustomElementsResolutionAnalysis _customElementsResolutionAnalysis;
+  final RuntimeTypesNeedBuilder _rtiNeedBuilder;
+  final AnnotationsData _annotationsData;
 
   KernelWorkItem(
       this._compilerTask,
       this._elementMap,
-      this._impactTransformer,
       this._nativeMemberResolver,
       this._annotationsDataBuilder,
       this.element,
@@ -353,7 +364,13 @@
       this._impactCache,
       this._fieldAnalysis,
       this._modularStrategy,
-      this._irAnnotationData);
+      this._irAnnotationData,
+      this._impacts,
+      this._nativeResolutionEnqueuer,
+      this._backendUsageBuilder,
+      this._customElementsResolutionAnalysis,
+      this._rtiNeedBuilder,
+      this._annotationsData);
 
   @override
   WorldImpact run() {
@@ -383,10 +400,15 @@
       }
       ImpactBuilderData impactBuilderData = modularMemberData.impactBuilderData;
       return _compilerTask.measureSubtask('worldImpact', () {
-        ResolutionImpact impact =
-            _elementMap.computeWorldImpact(element, impactBuilderData);
-        WorldImpact worldImpact =
-            _impactTransformer.transformResolutionImpact(impact);
+        WorldImpact worldImpact = _elementMap.computeWorldImpact(
+            element,
+            _impacts,
+            _nativeResolutionEnqueuer,
+            _backendUsageBuilder,
+            _customElementsResolutionAnalysis,
+            _rtiNeedBuilder,
+            _annotationsData,
+            impactBuilderData);
         if (_impactCache != null) {
           _impactCache[element] = worldImpact;
         }
diff --git a/pkg/compiler/lib/src/kernel/transformations/async_lowering.dart b/pkg/compiler/lib/src/kernel/transformations/async_lowering.dart
index ec145b0..30dd0b8 100644
--- a/pkg/compiler/lib/src/kernel/transformations/async_lowering.dart
+++ b/pkg/compiler/lib/src/kernel/transformations/async_lowering.dart
@@ -48,7 +48,7 @@
     final futureValueType = node.futureValueType!;
     _updateFunctionBody(
         node,
-        ExpressionStatement(StaticInvocation(
+        ReturnStatement(StaticInvocation(
             _coreTypes.futureSyncFactory,
             Arguments([
               FunctionExpression(FunctionNode(node.body,
diff --git a/pkg/compiler/lib/src/native/enqueue.dart b/pkg/compiler/lib/src/native/enqueue.dart
index 2b6355c..d70d471 100644
--- a/pkg/compiler/lib/src/native/enqueue.dart
+++ b/pkg/compiler/lib/src/native/enqueue.dart
@@ -47,7 +47,7 @@
 
   /// Register [classes] as natively instantiated in [impactBuilder].
   void _registerTypeUses(
-      WorldImpactBuilder impactBuilder, Set<ClassEntity> classes, cause) {
+      WorldImpactBuilder impactBuilder, Set<ClassEntity> classes /*, cause*/) {
     for (ClassEntity cls in classes) {
       if (!_unusedClasses.contains(cls)) {
         // No need to add [classElement] to [impactBuilder]: it has already been
@@ -62,13 +62,13 @@
 
   /// Registers the [nativeBehavior]. Adds the liveness of its instantiated
   /// types to the world.
-  void registerNativeBehavior(
-      WorldImpactBuilder impactBuilder, NativeBehavior nativeBehavior, cause) {
+  void registerNativeBehavior(WorldImpactBuilder impactBuilder,
+      NativeBehavior nativeBehavior, Object cause) {
     _processNativeBehavior(impactBuilder, nativeBehavior, cause);
   }
 
   void _processNativeBehavior(
-      WorldImpactBuilder impactBuilder, NativeBehavior behavior, cause) {
+      WorldImpactBuilder impactBuilder, NativeBehavior behavior, Object cause) {
     void registerInstantiation(InterfaceType type) {
       impactBuilder.registerTypeUse(TypeUse.nativeInstantiation(type));
     }
@@ -123,7 +123,7 @@
     if (matchingClasses.isNotEmpty && _registeredClasses.isEmpty) {
       matchingClasses.addAll(_onFirstNativeClass(impactBuilder));
     }
-    _registerTypeUses(impactBuilder, matchingClasses, cause);
+    _registerTypeUses(impactBuilder, matchingClasses /*, cause*/);
 
     // Give an info so that library developers can compile with -v to find why
     // all the native classes are included.
@@ -188,7 +188,7 @@
     _nativeClasses.addAll(nativeClasses);
     _unusedClasses.addAll(nativeClasses);
     if (!enableLiveTypeAnalysis) {
-      _registerTypeUses(impactBuilder, _nativeClasses, 'forced');
+      _registerTypeUses(impactBuilder, _nativeClasses /*, 'forced'*/);
     }
     return impactBuilder;
   }
@@ -224,7 +224,7 @@
     _unusedClasses.addAll(_nativeClasses);
 
     if (!enableLiveTypeAnalysis) {
-      _registerTypeUses(impactBuilder, _nativeClasses, 'forced');
+      _registerTypeUses(impactBuilder, _nativeClasses /*, 'forced'*/);
     }
 
     // HACK HACK - add all the resolved classes.
@@ -237,14 +237,14 @@
     if (matchingClasses.isNotEmpty && _registeredClasses.isEmpty) {
       matchingClasses.addAll(_onFirstNativeClass(impactBuilder));
     }
-    _registerTypeUses(impactBuilder, matchingClasses, 'was resolved');
+    _registerTypeUses(impactBuilder, matchingClasses /*, 'was resolved'*/);
     return impactBuilder;
   }
 
   @override
   void _registerTypeUses(
-      WorldImpactBuilder impactBuilder, Set<ClassEntity> classes, cause) {
-    super._registerTypeUses(impactBuilder, classes, cause);
+      WorldImpactBuilder impactBuilder, Set<ClassEntity> classes /*, cause*/) {
+    super._registerTypeUses(impactBuilder, classes /*, cause*/);
 
     for (ClassEntity classElement in classes) {
       // Add the information that this class is a subtype of its supertypes. The
diff --git a/pkg/compiler/lib/src/resolution/registry.dart b/pkg/compiler/lib/src/resolution/registry.dart
deleted file mode 100644
index 0d4480f..0000000
--- a/pkg/compiler/lib/src/resolution/registry.dart
+++ /dev/null
@@ -1,180 +0,0 @@
-// Copyright (c) 2014, 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.
-
-library dart2js.resolution.registry;
-
-import '../common/resolution.dart' show ResolutionImpact;
-import '../constants/values.dart';
-import '../elements/entities.dart' show ClassEntity, MemberEntity;
-import '../elements/types.dart';
-import '../native/behavior.dart' show NativeBehavior;
-import '../universe/feature.dart';
-import '../universe/world_impact.dart' show WorldImpact, WorldImpactBuilderImpl;
-import '../util/enumset.dart' show EnumSet;
-import '../util/util.dart' show Setlet;
-
-class ResolutionWorldImpactBuilder extends WorldImpactBuilderImpl
-    implements ResolutionImpact {
-  final DartTypes _dartTypes;
-  @override
-  final MemberEntity member;
-  EnumSet<Feature> _features;
-  Setlet<MapLiteralUse> _mapLiterals;
-  Setlet<SetLiteralUse> _setLiterals;
-  Setlet<ListLiteralUse> _listLiterals;
-  Setlet<String> _constSymbolNames;
-  Setlet<ConstantValue> _constantLiterals;
-  Setlet<NativeBehavior> _nativeData;
-  Setlet<ClassEntity> _seenClasses;
-  Set<RuntimeTypeUse> _runtimeTypeUses;
-  Set<GenericInstantiation> _genericInstantiations;
-
-  ResolutionWorldImpactBuilder(this._dartTypes, this.member);
-
-  @override
-  bool get isEmpty => false;
-
-  void registerMapLiteral(MapLiteralUse mapLiteralUse) {
-    assert(mapLiteralUse != null);
-    _mapLiterals ??= Setlet();
-    _mapLiterals.add(mapLiteralUse);
-  }
-
-  @override
-  Iterable<MapLiteralUse> get mapLiterals => _mapLiterals ?? const [];
-
-  void registerSetLiteral(SetLiteralUse setLiteralUse) {
-    assert(setLiteralUse != null);
-    _setLiterals ??= Setlet();
-    _setLiterals.add(setLiteralUse);
-  }
-
-  @override
-  Iterable<SetLiteralUse> get setLiterals => _setLiterals ?? const [];
-
-  void registerListLiteral(ListLiteralUse listLiteralUse) {
-    assert(listLiteralUse != null);
-    _listLiterals ??= Setlet();
-    _listLiterals.add(listLiteralUse);
-  }
-
-  @override
-  Iterable<ListLiteralUse> get listLiterals => _listLiterals ?? const [];
-
-  void registerRuntimeTypeUse(RuntimeTypeUse runtimeTypeUse) {
-    assert(runtimeTypeUse != null);
-    _runtimeTypeUses ??= Setlet();
-    _runtimeTypeUses.add(runtimeTypeUse);
-  }
-
-  @override
-  Iterable<RuntimeTypeUse> get runtimeTypeUses => _runtimeTypeUses ?? const [];
-
-  void registerConstSymbolName(String name) {
-    _constSymbolNames ??= Setlet();
-    _constSymbolNames.add(name);
-  }
-
-  @override
-  Iterable<String> get constSymbolNames => _constSymbolNames ?? const [];
-
-  void registerFeature(Feature feature) {
-    _features ??= EnumSet();
-    _features.add(feature);
-  }
-
-  @override
-  Iterable<Feature> get features {
-    return _features != null
-        ? _features.iterable(Feature.values)
-        : const <Feature>[];
-  }
-
-  void registerConstantLiteral(ConstantValue constant) {
-    _constantLiterals ??= Setlet();
-    _constantLiterals.add(constant);
-  }
-
-  @override
-  Iterable<ConstantValue> get constantLiterals => _constantLiterals ?? const [];
-
-  void registerNativeData(NativeBehavior nativeData) {
-    assert(nativeData != null);
-    _nativeData ??= Setlet();
-    _nativeData.add(nativeData);
-  }
-
-  @override
-  Iterable<NativeBehavior> get nativeData => _nativeData ?? const [];
-
-  void registerSeenClass(ClassEntity seenClass) {
-    _seenClasses ??= Setlet();
-    _seenClasses.add(seenClass);
-  }
-
-  @override
-  Iterable<ClassEntity> get seenClasses => _seenClasses ?? const [];
-
-  void registerInstantiation(GenericInstantiation instantiation) {
-    _genericInstantiations ??= Setlet();
-    _genericInstantiations.add(instantiation);
-  }
-
-  @override
-  Iterable<GenericInstantiation> get genericInstantiations =>
-      _genericInstantiations ?? const [];
-
-  @override
-  String toString() {
-    StringBuffer sb = StringBuffer();
-    sb.write('ResolutionWorldImpactBuilder($member)');
-    WorldImpact.printOn(sb, this);
-    if (_features != null) {
-      sb.write('\n features:');
-      for (Feature feature in _features.iterable(Feature.values)) {
-        sb.write('\n  $feature');
-      }
-    }
-    if (_mapLiterals != null) {
-      sb.write('\n map-literals:');
-      for (MapLiteralUse use in _mapLiterals) {
-        sb.write('\n  $use');
-      }
-    }
-    if (_setLiterals != null) {
-      sb.write('\n set-literals:');
-      for (SetLiteralUse use in _setLiterals) {
-        sb.write('\n  $use');
-      }
-    }
-    if (_listLiterals != null) {
-      sb.write('\n list-literals:');
-      for (ListLiteralUse use in _listLiterals) {
-        sb.write('\n  $use');
-      }
-    }
-    if (_constantLiterals != null) {
-      sb.write('\n const-literals:');
-      for (ConstantValue constant in _constantLiterals) {
-        sb.write('\n  ${constant.toDartText(_dartTypes)}');
-      }
-    }
-    if (_constSymbolNames != null) {
-      sb.write('\n const-symbol-names: $_constSymbolNames');
-    }
-    if (_nativeData != null) {
-      sb.write('\n native-data:');
-      for (var data in _nativeData) {
-        sb.write('\n  $data');
-      }
-    }
-    if (_genericInstantiations != null) {
-      sb.write('\n instantiations:');
-      for (var data in _genericInstantiations) {
-        sb.write('\n  $data');
-      }
-    }
-    return sb.toString();
-  }
-}
diff --git a/pkg/compiler/lib/src/universe/world_impact.dart b/pkg/compiler/lib/src/universe/world_impact.dart
index 1008089..0d319ea 100644
--- a/pkg/compiler/lib/src/universe/world_impact.dart
+++ b/pkg/compiler/lib/src/universe/world_impact.dart
@@ -74,14 +74,18 @@
   }
 }
 
-abstract class WorldImpactBuilder {
+abstract class WorldImpactBuilder extends WorldImpact {
   void registerDynamicUse(DynamicUse dynamicUse);
   void registerTypeUse(TypeUse typeUse);
   void registerStaticUse(StaticUse staticUse);
   void registerConstantUse(ConstantUse constantUse);
 }
 
-class WorldImpactBuilderImpl extends WorldImpact implements WorldImpactBuilder {
+class WorldImpactBuilderImpl extends WorldImpactBuilder {
+  /// The [MemberEntity] associated with this set of impacts. Maybe null.
+  @override
+  final MemberEntity member;
+
   // TODO(johnniwinther): Do we benefit from lazy initialization of the
   // [Setlet]s?
   Set<DynamicUse> _dynamicUses;
@@ -89,10 +93,11 @@
   Set<TypeUse> _typeUses;
   Set<ConstantUse> _constantUses;
 
-  WorldImpactBuilderImpl();
+  WorldImpactBuilderImpl([this.member]);
 
   WorldImpactBuilderImpl.internal(
-      this._dynamicUses, this._staticUses, this._typeUses, this._constantUses);
+      this._dynamicUses, this._staticUses, this._typeUses, this._constantUses,
+      {this.member});
 
   @override
   bool get isEmpty =>
@@ -161,7 +166,7 @@
 
 /// Mutable implementation of [WorldImpact] used to transform
 /// [ResolutionImpact] or [CodegenImpact] to [WorldImpact].
-class TransformedWorldImpact implements WorldImpact, WorldImpactBuilder {
+class TransformedWorldImpact extends WorldImpactBuilder {
   final WorldImpact worldImpact;
 
   Setlet<StaticUse> _staticUses;
@@ -228,16 +233,6 @@
   }
 
   @override
-  void apply(WorldImpactVisitor visitor) {
-    staticUses.forEach((StaticUse use) => visitor.visitStaticUse(member, use));
-    dynamicUses
-        .forEach((DynamicUse use) => visitor.visitDynamicUse(member, use));
-    typeUses.forEach((TypeUse use) => visitor.visitTypeUse(member, use));
-    constantUses
-        .forEach((ConstantUse use) => visitor.visitConstantUse(member, use));
-  }
-
-  @override
   String toString() {
     StringBuffer sb = StringBuffer();
     sb.write('TransformedWorldImpact($worldImpact)');
diff --git a/pkg/compiler/test/equivalence/id_equivalence_helper.dart b/pkg/compiler/test/equivalence/id_equivalence_helper.dart
index e8c5eae..d93441e 100644
--- a/pkg/compiler/test/equivalence/id_equivalence_helper.dart
+++ b/pkg/compiler/test/equivalence/id_equivalence_helper.dart
@@ -419,7 +419,8 @@
 
   dataComputer.setup();
 
-  Future<Map<String, TestResult<T>>> checkTest(TestData testData,
+  Future<Map<String, TestResult<T>>> checkTest(
+      MarkerOptions markerOptions, TestData testData,
       {bool testAfterFailures,
       bool verbose,
       bool succinct,
@@ -454,7 +455,11 @@
         }
         print('--from (${testConfiguration.name})-------------');
         results[testConfiguration.marker] = await runTestForConfiguration(
-            testConfiguration, dataComputer, testData, testOptions,
+            markerOptions,
+            testConfiguration,
+            dataComputer,
+            testData,
+            testOptions,
             filterActualData: filterActualData,
             verbose: verbose,
             succinct: succinct,
@@ -483,8 +488,12 @@
   return Uri.parse('memory:sdk/tests/web/native/$fileName');
 }
 
-Future<TestResult<T>> runTestForConfiguration<T>(TestConfig testConfiguration,
-    DataComputer<T> dataComputer, TestData testData, List<String> options,
+Future<TestResult<T>> runTestForConfiguration<T>(
+    MarkerOptions markerOptions,
+    TestConfig testConfiguration,
+    DataComputer<T> dataComputer,
+    TestData testData,
+    List<String> options,
     {bool filterActualData(IdValue idValue, ActualData<T> actualData),
     bool verbose: false,
     bool succinct: false,
@@ -503,8 +512,14 @@
       forUserLibrariesOnly: forUserLibrariesOnly,
       globalIds: annotations.globalData.keys,
       verifyCompiler: verifyCompiler);
-  return await checkCode(testConfiguration.name, testData.testFileUri,
-      testData.code, annotations, compiledData, dataComputer.dataValidator,
+  return await checkCode(
+      markerOptions,
+      testConfiguration.marker,
+      testConfiguration.name,
+      testData,
+      annotations,
+      compiledData,
+      dataComputer.dataValidator,
       filterActualData: filterActualData,
       fatalErrors: !testAfterFailures,
       onFailure: Expect.fail,
diff --git a/pkg/compiler/test/impact/data/constants/lib.dart b/pkg/compiler/test/impact/data/constants/lib.dart
index 9e25e3c..279fa49 100644
--- a/pkg/compiler/test/impact/data/constants/lib.dart
+++ b/pkg/compiler/test/impact/data/constants/lib.dart
@@ -37,6 +37,7 @@
 
   const Class(field1, this.field2) : super(field1);
 
+  /*member: Class.staticMethodField:*/
   static staticMethodField() {}
 }
 
@@ -99,6 +100,7 @@
 
 const dynamic instantiationField = _instantiation;
 
+/*member: topLevelMethod:*/
 topLevelMethod() {}
 
 const dynamic topLevelTearOffField = topLevelMethod;
diff --git a/pkg/compiler/test/impact/data/constants/main.dart b/pkg/compiler/test/impact/data/constants/main.dart
index be9a623..b750f7c 100644
--- a/pkg/compiler/test/impact/data/constants/main.dart
+++ b/pkg/compiler/test/impact/data/constants/main.dart
@@ -7,7 +7,52 @@
 import 'lib.dart';
 import 'lib.dart' deferred as defer;
 
-/*member: main:static=%*/
+/*member: main:static=[
+  boolLiteral(0),
+  boolLiteralDeferred(0),
+  boolLiteralRef(0),
+  doubleLiteral(0),
+  doubleLiteralDeferred(0),
+  doubleLiteralRef(0),
+  instanceConstant(0),
+  instanceConstantDeferred(0),
+  instanceConstantRef(0),
+  instantiation(0),
+  instantiationDeferred(0),
+  instantiationRef(0),
+  intLiteral(0),
+  intLiteralDeferred(0),
+  intLiteralRef(0),
+  listLiteral(0),
+  listLiteralDeferred(0),
+  listLiteralRef(0),
+  mapLiteral(0),
+  mapLiteralDeferred(0),
+  mapLiteralRef(0),
+  nullLiteral(0),
+  nullLiteralDeferred(0),
+  nullLiteralRef(0),
+  setLiteral(0),
+  setLiteralDeferred(0),
+  setLiteralRef(0),
+  staticTearOff(0),
+  staticTearOffDeferred(0),
+  staticTearOffRef(0),
+  stringLiteral(0),
+  stringLiteralDeferred(0),
+  stringLiteralRef(0),
+  stringMapLiteral(0),
+  stringMapLiteralDeferred(0),
+  stringMapLiteralRef(0),
+  symbolLiteral(0),
+  symbolLiteralDeferred(0),
+  symbolLiteralRef(0),
+  topLevelTearOff(0),
+  topLevelTearOffDeferred(0),
+  topLevelTearOffRef(0),
+  typeLiteral(0),
+  typeLiteralDeferred(0),
+  typeLiteralRef(0)]*/
 main() {
   nullLiteral();
   boolLiteral();
@@ -100,7 +145,10 @@
   return local;
 }
 
-/*member: symbolLiteral:static=[Symbol.(1)],type=[inst:Symbol]*/
+/*member: symbolLiteral:
+ static=[Symbol.(1)],
+ type=[inst:Symbol]
+*/
 symbolLiteral() => #foo;
 
 /*member: listLiteral:type=[
@@ -132,8 +180,12 @@
 setLiteral() => const {true, false};
 
 /*member: instanceConstant:
- static=[Class.field2=BoolConstant(false),SuperClass.field1=BoolConstant(true)],
- type=[const:Class,inst:JSBool]
+ static=[
+  Class.field2=BoolConstant(false),
+  SuperClass.field1=BoolConstant(true)],
+ type=[
+  const:Class,
+  inst:JSBool]
 */
 instanceConstant() => const Class(true, false);
 
@@ -151,7 +203,14 @@
   return local;
 }
 
-/*member: instantiation:static=[closureFunctionType(1),id,instantiate1(1),instantiatedGenericFunctionType(2)],type=[inst:Instantiation1<dynamic>]*/
+/*member: instantiation:
+ static=[
+  closureFunctionType(1),
+  id,
+  instantiate1(1),
+  instantiatedGenericFunctionType(2)],
+ type=[inst:Instantiation1<dynamic>]
+*/
 instantiation() {
   const int Function(int) local = id;
   return local;
@@ -196,7 +255,10 @@
 /*member: stringLiteralRef:type=[inst:JSString]*/
 stringLiteralRef() => stringLiteralField;
 
-/*member: symbolLiteralRef:static=[Symbol.(1)],type=[inst:Symbol]*/
+/*member: symbolLiteralRef:
+ static=[Symbol.(1)],
+ type=[inst:Symbol]
+*/
 symbolLiteralRef() => symbolLiteralField;
 
 /*member: listLiteralRef:type=[
@@ -228,8 +290,12 @@
 setLiteralRef() => setLiteralField;
 
 /*member: instanceConstantRef:
- static=[Class.field2=BoolConstant(false),SuperClass.field1=BoolConstant(true)],
- type=[const:Class,inst:JSBool]
+ static=[
+  Class.field2=BoolConstant(false),
+  SuperClass.field1=BoolConstant(true)],
+ type=[
+  const:Class,
+  inst:JSBool]
 */
 instanceConstantRef() => instanceConstantField;
 
@@ -244,7 +310,14 @@
 */
 typeLiteralRef() => typeLiteralField;
 
-/*member: instantiationRef:static=[closureFunctionType(1),id,instantiate1(1),instantiatedGenericFunctionType(2)],type=[inst:Instantiation1<dynamic>]*/
+/*member: instantiationRef:
+ static=[
+  closureFunctionType(1),
+  id,
+  instantiate1(1),
+  instantiatedGenericFunctionType(2)],
+ type=[inst:Instantiation1<dynamic>]
+*/
 instantiationRef() => instantiationField;
 
 /*member: topLevelTearOffRef:static=[topLevelMethod]*/
@@ -281,7 +354,10 @@
 stringLiteralDeferred() => defer.stringLiteralField;
 
 // TODO(johnniwinther): Should we record that this is deferred?
-/*member: symbolLiteralDeferred:static=[Symbol.(1)],type=[inst:Symbol]*/
+/*member: symbolLiteralDeferred:
+ static=[Symbol.(1)],
+ type=[inst:Symbol]
+*/
 symbolLiteralDeferred() => defer.symbolLiteralField;
 
 // TODO(johnniwinther): Should we record that this is deferred?
@@ -317,8 +393,12 @@
 setLiteralDeferred() => defer.setLiteralField;
 
 /*member: instanceConstantDeferred:
- static=[Class.field2=BoolConstant(false),SuperClass.field1=BoolConstant(true)],
- type=[const:Class{defer},inst:JSBool]
+ static=[
+  Class.field2=BoolConstant(false),
+  SuperClass.field1=BoolConstant(true)],
+ type=[
+  const:Class{defer},
+  inst:JSBool]
 */
 instanceConstantDeferred() => defer.instanceConstantField;
 
@@ -333,7 +413,14 @@
 */
 typeLiteralDeferred() => defer.typeLiteralField;
 
-/*member: instantiationDeferred:static=[closureFunctionType(1),id{defer},instantiate1(1),instantiatedGenericFunctionType(2)],type=[inst:Instantiation1<dynamic>]*/
+/*member: instantiationDeferred:
+ static=[
+  closureFunctionType(1),
+  id{defer},
+  instantiate1(1),
+  instantiatedGenericFunctionType(2)],
+ type=[inst:Instantiation1<dynamic>]
+*/
 instantiationDeferred() => defer.instantiationField;
 
 /*member: topLevelTearOffDeferred:static=[topLevelMethod{defer}]*/
diff --git a/pkg/compiler/test/impact/impact_test.dart b/pkg/compiler/test/impact/impact_test.dart
index 7cec147..ff5ed69 100644
--- a/pkg/compiler/test/impact/impact_test.dart
+++ b/pkg/compiler/test/impact/impact_test.dart
@@ -7,10 +7,12 @@
 import 'dart:io';
 import 'package:_fe_analyzer_shared/src/testing/features.dart';
 import 'package:async_helper/async_helper.dart';
-import 'package:compiler/src/common/resolution.dart';
 import 'package:compiler/src/compiler.dart';
 import 'package:compiler/src/elements/entities.dart';
+import 'package:compiler/src/kernel/element_map.dart';
 import 'package:compiler/src/kernel/kernel_strategy.dart';
+import 'package:compiler/src/ir/impact.dart';
+import 'package:compiler/src/ir/runtime_type_analysis.dart';
 import 'package:compiler/src/universe/feature.dart';
 import 'package:compiler/src/universe/use.dart';
 import 'package:compiler/src/universe/world_impact.dart';
@@ -21,7 +23,7 @@
 main(List<String> args) {
   asyncTest(() async {
     Directory dataDir = new Directory.fromUri(Platform.script.resolve('data'));
-    print('Testing computation of ResolutionImpact through ImpactData');
+    print('Testing computation of WorldImpact through ImpactData');
     print('==================================================================');
     await checkTests(dataDir, const ImpactDataComputer(),
         args: args, testedConfigs: allSpecConfigs);
@@ -69,13 +71,8 @@
     for (ConstantUse use in impact.constantUses) {
       features.addElement(Tags.constantUse, use.shortText);
     }
-    if (impact is TransformedWorldImpact &&
-        impact.worldImpact is ResolutionImpact) {
-      ResolutionImpact resolutionImpact = impact.worldImpact;
-      for (RuntimeTypeUse use in resolutionImpact.runtimeTypeUses) {
-        features.addElement(Tags.runtimeTypeUse, use.shortText);
-      }
-    }
+    final impactData = frontendStrategy.elementMap.impactDataForTesting[node];
+    impactData.apply(ImpactDataGoldener(frontendStrategy.elementMap, features));
     Id id = computeMemberId(node);
     ir.TreeNode nodeWithOffset = computeTreeNodeWithOffset(node);
     actualMap[id] = new ActualData<Features>(id, features,
@@ -89,3 +86,23 @@
   DataInterpreter<Features> get dataValidator =>
       const FeaturesDataInterpreter(wildcard: wildcard);
 }
+
+class ImpactDataGoldener implements ImpactRegistry {
+  final KernelToElementMap elementMap;
+  final Features features;
+
+  ImpactDataGoldener(this.elementMap, this.features);
+
+  @override
+  void registerRuntimeTypeUse(ir.Expression node, RuntimeTypeUseKind kind,
+      ir.DartType receiverType, ir.DartType argumentType) {
+    final runtimeTypeUse = RuntimeTypeUse(
+        kind,
+        elementMap.getDartType(receiverType),
+        argumentType == null ? null : elementMap.getDartType(argumentType));
+    features.addElement(Tags.runtimeTypeUse, runtimeTypeUse.shortText);
+  }
+
+  @override
+  noSuchMethod(_) {}
+}
diff --git a/pkg/front_end/lib/src/api_prototype/compiler_options.dart b/pkg/front_end/lib/src/api_prototype/compiler_options.dart
index 450b24b..3543755 100644
--- a/pkg/front_end/lib/src/api_prototype/compiler_options.dart
+++ b/pkg/front_end/lib/src/api_prototype/compiler_options.dart
@@ -4,7 +4,7 @@
 
 library front_end.compiler_options;
 
-import 'package:_fe_analyzer_shared/src/macros/executor.dart';
+import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart';
 import 'package:_fe_analyzer_shared/src/messages/diagnostic_message.dart'
     show DiagnosticMessage, DiagnosticMessageHandler;
 import 'package:_fe_analyzer_shared/src/messages/severity.dart' show Severity;
@@ -108,17 +108,10 @@
   /// file system.  TODO(paulberry): fix this.
   FileSystem fileSystem = StandardFileSystem.instance;
 
-  /// Function that creates a [MacroExecutor] if supported.
+  /// The [MultiMacroExecutor] for loading and executing macros if supported.
   ///
   /// This is part of the experimental macro feature.
-  Future<MacroExecutor> Function() macroExecutorProvider =
-      () async => throw 'Macro execution is not supported.';
-
-  /// Map from library import [Uri]s of libraries that declare macros to
-  /// the [Uri] for the precompiled dill that contains the macro code.
-  ///
-  /// This is part of the experimental macro feature.
-  Map<Uri, Uri>? precompiledMacroUris;
+  MultiMacroExecutor? macroExecutor;
 
   /// The [Target] used for compiling macros.
   ///
@@ -131,7 +124,7 @@
   /// [Component].
   ///
   /// This is used to turn a precompiled macro into a [Uri] that can be loaded
-  /// by the macro executor provided by [macroExecutorProvider].
+  /// by the [macroExecutor].
   ///
   /// This is part of the experimental macro feature.
   MacroSerializer? macroSerializer;
diff --git a/pkg/front_end/lib/src/base/processed_options.dart b/pkg/front_end/lib/src/base/processed_options.dart
index 959eb38..2a6e8f6 100644
--- a/pkg/front_end/lib/src/base/processed_options.dart
+++ b/pkg/front_end/lib/src/base/processed_options.dart
@@ -8,7 +8,7 @@
 
 import 'package:_fe_analyzer_shared/src/messages/severity.dart' show Severity;
 
-import 'package:_fe_analyzer_shared/src/macros/executor.dart';
+import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart';
 
 import 'package:_fe_analyzer_shared/src/util/libraries_specification.dart'
     show
@@ -827,11 +827,8 @@
     }
   }
 
-  Future<MacroExecutor> Function() get macroExecutorProvider =>
-      _raw.macroExecutorProvider;
-
-  Map<Uri, Uri> get precompiledMacroUris =>
-      _raw.precompiledMacroUris ?? const {};
+  MultiMacroExecutor get macroExecutor =>
+      _raw.macroExecutor ??= new MultiMacroExecutor();
 
   CompilerOptions get rawOptionsForTesting => _raw;
 }
diff --git a/pkg/front_end/lib/src/fasta/incremental_compiler.dart b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
index 410e9b1..8375411 100644
--- a/pkg/front_end/lib/src/fasta/incremental_compiler.dart
+++ b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
@@ -11,6 +11,9 @@
 import 'package:_fe_analyzer_shared/src/scanner/abstract_scanner.dart'
     show ScannerConfiguration;
 
+import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart'
+    as macros;
+
 import 'package:front_end/src/fasta/kernel/benchmarker.dart'
     show BenchmarkPhases, Benchmarker;
 
@@ -60,8 +63,6 @@
 
 import 'package:package_config/package_config.dart' show Package, PackageConfig;
 
-import '../api_prototype/compiler_options.dart' show CompilerOptions;
-
 import '../api_prototype/file_system.dart' show FileSystem, FileSystemEntity;
 
 import '../api_prototype/incremental_kernel_generator.dart'
@@ -184,6 +185,12 @@
   // (enableIncrementalCompilerBenchmarking).
   Benchmarker? _benchmarker;
 
+  /// Map by library [Uri] to the [macros.ExecutorFactoryToken]s that was
+  /// retrieved when registering the compiled macro executor for that library.
+  ///
+  /// This is primarily used for invalidation.
+  final Map<Uri, macros.ExecutorFactoryToken> macroExecutorFactoryTokens = {};
+
   RecorderForTesting? get recorderForTesting => null;
 
   static final Uri debugExprUri =
@@ -330,7 +337,8 @@
 
       _benchmarker
           ?.enterPhase(BenchmarkPhases.incremental_invalidatePrecompiledMacros);
-      _invalidatePrecompiledMacros(c.options, reusedResult.notReusedLibraries);
+      await _invalidatePrecompiledMacros(
+          c.options, reusedResult.notReusedLibraries);
 
       // Cleanup: After (potentially) removing builders we have stuff to cleanup
       // to not leak, and we might need to re-create the dill target.
@@ -378,9 +386,13 @@
         NeededPrecompilations? neededPrecompilations =
             await currentKernelTarget.computeNeededPrecompilations();
         _benchmarker?.enterPhase(BenchmarkPhases.incremental_precompileMacros);
-        if (enableMacros &&
-            await precompileMacros(neededPrecompilations, c.options)) {
-          continue;
+        if (enableMacros) {
+          Map<Uri, macros.ExecutorFactoryToken>? precompiled =
+              await precompileMacros(neededPrecompilations, c.options);
+          if (precompiled != null) {
+            macroExecutorFactoryTokens.addAll(precompiled);
+            continue;
+          }
         }
         _benchmarker?.enterPhase(
             BenchmarkPhases.incremental_experimentalInvalidationPatchUpScopes);
@@ -1477,21 +1489,19 @@
   }
 
   /// Removes the precompiled macros whose libraries cannot be reused.
-  void _invalidatePrecompiledMacros(ProcessedOptions processedOptions,
-      Set<LibraryBuilder> notReusedLibraries) {
+  Future<void> _invalidatePrecompiledMacros(ProcessedOptions processedOptions,
+      Set<LibraryBuilder> notReusedLibraries) async {
     if (notReusedLibraries.isEmpty) {
       return;
     }
-    CompilerOptions compilerOptions = processedOptions.rawOptionsForTesting;
-    Map<Uri, Uri>? precompiledMacroUris = compilerOptions.precompiledMacroUris;
-    if (precompiledMacroUris != null) {
-      Set<Uri> importUris =
-          notReusedLibraries.map((library) => library.importUri).toSet();
-      for (Uri macroLibraryUri in precompiledMacroUris.keys.toList()) {
-        if (importUris.contains(macroLibraryUri)) {
-          precompiledMacroUris.remove(macroLibraryUri);
-        }
-      }
+    if (macroExecutorFactoryTokens.isNotEmpty) {
+      await Future.wait(notReusedLibraries
+          .map((library) => library.importUri)
+          .where(macroExecutorFactoryTokens.containsKey)
+          .map((importUri) => processedOptions.macroExecutor
+              .unregisterExecutorFactory(
+                  macroExecutorFactoryTokens.remove(importUri)!,
+                  libraries: {importUri})));
     }
   }
 
diff --git a/pkg/front_end/lib/src/fasta/kernel/macro/macro.dart b/pkg/front_end/lib/src/fasta/kernel/macro/macro.dart
index 4bce249..9d67f00 100644
--- a/pkg/front_end/lib/src/fasta/kernel/macro/macro.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/macro/macro.dart
@@ -6,6 +6,8 @@
 import 'package:_fe_analyzer_shared/src/macros/executor.dart' as macro;
 import 'package:_fe_analyzer_shared/src/macros/executor/introspection_impls.dart'
     as macro;
+import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart'
+    as macro;
 import 'package:_fe_analyzer_shared/src/macros/executor/remote_instance.dart'
     as macro;
 import 'package:front_end/src/fasta/kernel/benchmarker.dart'
@@ -126,13 +128,10 @@
   }
 
   static Future<MacroApplications> loadMacroIds(
-      macro.MacroExecutor macroExecutor,
-      Map<Uri, Uri> precompiledMacroUris,
+      macro.MultiMacroExecutor macroExecutor,
       Map<SourceLibraryBuilder, LibraryMacroApplicationData> libraryData,
       MacroApplicationDataForTesting? dataForTesting,
       Benchmarker? benchmarker) async {
-    Map<ClassBuilder, macro.MacroClassIdentifier> classIdCache = {};
-
     Map<MacroApplication, macro.MacroInstanceIdentifier> instanceIdCache = {};
 
     Future<void> ensureMacroClassIds(
@@ -141,21 +140,17 @@
         for (MacroApplication application in applications) {
           Uri libraryUri = application.classBuilder.libraryBuilder.importUri;
           String macroClassName = application.classBuilder.name;
-          Uri? precompiledMacroUri = precompiledMacroUris[libraryUri];
           try {
             benchmarker?.beginSubdivide(
                 BenchmarkSubdivides.macroApplications_macroExecutorLoadMacro);
-            macro.MacroClassIdentifier macroClassIdentifier =
-                classIdCache[application.classBuilder] ??=
-                    await macroExecutor.loadMacro(libraryUri, macroClassName,
-                        precompiledKernelUri: precompiledMacroUri);
             benchmarker?.endSubdivide();
             try {
               benchmarker?.beginSubdivide(BenchmarkSubdivides
                   .macroApplications_macroExecutorInstantiateMacro);
               application.instanceIdentifier = instanceIdCache[application] ??=
                   await macroExecutor.instantiateMacro(
-                      macroClassIdentifier,
+                      libraryUri,
+                      macroClassName,
                       application.constructorName,
                       // TODO(johnniwinther): Support macro arguments.
                       new macro.Arguments([], {}));
diff --git a/pkg/front_end/lib/src/fasta/source/source_loader.dart b/pkg/front_end/lib/src/fasta/source/source_loader.dart
index 3d323b0..c3c8539 100644
--- a/pkg/front_end/lib/src/fasta/source/source_loader.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart
@@ -11,8 +11,6 @@
 import 'package:_fe_analyzer_shared/src/parser/forwarding_listener.dart'
     show ForwardingListener;
 
-import 'package:_fe_analyzer_shared/src/macros/executor.dart'
-    show MacroExecutor;
 import 'package:_fe_analyzer_shared/src/parser/class_member_parser.dart'
     show ClassMemberParser;
 import 'package:_fe_analyzer_shared/src/parser/parser.dart'
@@ -1441,16 +1439,14 @@
     /// [ClassBuilder]s for the macro classes.
     Map<Uri, List<ClassBuilder>> macroLibraries = {};
 
-    Map<Uri, Uri> precompiledMacroUris =
-        target.context.options.precompiledMacroUris;
-
     for (LibraryBuilder libraryBuilder in libraryBuilders) {
       Iterator<Builder> iterator = libraryBuilder.iterator;
       while (iterator.moveNext()) {
         Builder builder = iterator.current;
         if (builder is ClassBuilder && builder.isMacro) {
           Uri libraryUri = builder.libraryBuilder.importUri;
-          if (!precompiledMacroUris.containsKey(libraryUri)) {
+          if (!target.context.options.macroExecutor
+              .libraryIsRegistered(libraryUri)) {
             (macroLibraries[libraryUri] ??= []).add(builder);
             if (retainDataForTesting) {
               (dataForTesting!.macroDeclarationData
@@ -1520,7 +1516,8 @@
       if (builder.importUri.isScheme("dart") && !builder.isSynthetic) {
         // Assume the platform is precompiled.
         addPrecompiledLibrary(builder.importUri);
-      } else if (precompiledMacroUris.containsKey(builder.importUri)) {
+      } else if (target.context.options.macroExecutor
+          .libraryIsRegistered(builder.importUri)) {
         // The precompiled macros given are also precompiled.
         assert(
             !macroLibraries.containsKey(builder.importUri),
@@ -1703,15 +1700,10 @@
     if (libraryData.isNotEmpty) {
       target.benchmarker?.beginSubdivide(
           BenchmarkSubdivides.computeMacroApplications_macroExecutorProvider);
-      MacroExecutor macroExecutor =
-          await target.context.options.macroExecutorProvider();
       target.benchmarker?.endSubdivide();
 
-      Map<Uri, Uri> precompiledMacroUris =
-          target.context.options.precompiledMacroUris;
       MacroApplications result = await MacroApplications.loadMacroIds(
-          macroExecutor,
-          precompiledMacroUris,
+          target.context.options.macroExecutor,
           libraryData,
           dataForTesting?.macroApplicationData,
           target.benchmarker);
diff --git a/pkg/front_end/lib/src/kernel_generator_impl.dart b/pkg/front_end/lib/src/kernel_generator_impl.dart
index 65c8d51..b8eb49e 100644
--- a/pkg/front_end/lib/src/kernel_generator_impl.dart
+++ b/pkg/front_end/lib/src/kernel_generator_impl.dart
@@ -6,6 +6,9 @@
 library front_end.kernel_generator_impl;
 
 import 'package:_fe_analyzer_shared/src/macros/bootstrap.dart';
+import 'package:_fe_analyzer_shared/src/macros/executor/isolated_executor.dart'
+    as isolatedExecutor;
+import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart';
 import 'package:_fe_analyzer_shared/src/macros/executor/serialization.dart';
 import 'package:_fe_analyzer_shared/src/messages/severity.dart' show Severity;
 import 'package:kernel/ast.dart';
@@ -95,7 +98,9 @@
       NeededPrecompilations? neededPrecompilations =
           await kernelTarget.computeNeededPrecompilations();
       kernelTarget.benchmarker?.enterPhase(BenchmarkPhases.precompileMacros);
-      if (await precompileMacros(neededPrecompilations, options)) {
+      Map<Uri, ExecutorFactoryToken>? precompiled =
+          await precompileMacros(neededPrecompilations, options);
+      if (precompiled != null) {
         kernelTarget.benchmarker
             ?.enterPhase(BenchmarkPhases.unknownGenerateKernelInternal);
         continue;
@@ -272,32 +277,34 @@
 
 /// Compiles the libraries for the macro classes in [neededPrecompilations].
 ///
-/// Returns `true` if macro classes were compiled and added to the
-/// [CompilerOptions.precompiledMacroUris] of the provided [options].
+/// Returns a map of library uri to [ExecutorFactoryToken] if macro classes were
+/// compiled and added to the [CompilerOptions.macroExecutor] of the provided
+/// [options].
 ///
-/// Returns `false` if no macro classes needed precompilation or if macro
+/// Returns `null` if no macro classes needed precompilation or if macro
 /// precompilation is not supported.
-Future<bool> precompileMacros(NeededPrecompilations? neededPrecompilations,
+Future<Map<Uri, ExecutorFactoryToken>?> precompileMacros(
+    NeededPrecompilations? neededPrecompilations,
     ProcessedOptions options) async {
   if (neededPrecompilations != null) {
     if (enableMacros) {
       // TODO(johnniwinther): Avoid using [rawOptionsForTesting] to compute
       // the compiler options for the precompilation.
       if (options.rawOptionsForTesting.macroTarget != null) {
-        await _compileMacros(
-            neededPrecompilations, options.rawOptionsForTesting);
         // TODO(johnniwinther): Assert that some works has been done.
         // TODO(johnniwinther): Stop in case of compile-time errors.
-        return true;
+        return await _compileMacros(
+            neededPrecompilations, options.rawOptionsForTesting);
       }
     } else {
       throw new UnsupportedError('Macro precompilation is not supported');
     }
   }
-  return false;
+  return null;
 }
 
-Future<void> _compileMacros(NeededPrecompilations neededPrecompilations,
+Future<Map<Uri, ExecutorFactoryToken>> _compileMacros(
+    NeededPrecompilations neededPrecompilations,
     CompilerOptions options) async {
   assert(options.macroSerializer != null);
   CompilerOptions precompilationOptions = new CompilerOptions();
@@ -309,11 +316,11 @@
   // macros likely need them.
   precompilationOptions.environmentDefines = options.environmentDefines ?? {};
   precompilationOptions.packagesFileUri = options.packagesFileUri;
-  precompilationOptions.precompiledMacroUris = options.precompiledMacroUris;
+  MultiMacroExecutor macroExecutor = precompilationOptions.macroExecutor =
+      options.macroExecutor ??= new MultiMacroExecutor();
   // TODO(johnniwinther): What if sdk root isn't set? How do we then get the
   // right sdk?
   precompilationOptions.sdkRoot = options.sdkRoot;
-  precompilationOptions.macroExecutorProvider = options.macroExecutorProvider;
 
   Map<String, Map<String, List<String>>> macroDeclarations = {};
   neededPrecompilations.macroDeclarations
@@ -332,9 +339,13 @@
       await kernelForProgramInternal(uri, precompilationOptions);
   Uri precompiledUri = await options.macroSerializer!
       .createUriForComponent(compilerResult!.component!);
-  Map<Uri, Uri> precompiledMacroUris = options.precompiledMacroUris ??= {};
-  neededPrecompilations.macroDeclarations
-      .forEach((Uri uri, Map<String, List<String>> macroClasses) {
-    precompiledMacroUris[uri] = precompiledUri;
-  });
+  Set<Uri> macroLibraries =
+      neededPrecompilations.macroDeclarations.keys.toSet();
+  ExecutorFactoryToken executorToken = macroExecutor.registerExecutorFactory(
+      () => isolatedExecutor.start(
+          SerializationMode.byteDataServer, precompiledUri),
+      macroLibraries);
+  return <Uri, ExecutorFactoryToken>{
+    for (Uri library in macroLibraries) library: executorToken,
+  };
 }
diff --git a/pkg/front_end/lib/src/testing/id_testing_helper.dart b/pkg/front_end/lib/src/testing/id_testing_helper.dart
index 4be720bd..07501ef 100644
--- a/pkg/front_end/lib/src/testing/id_testing_helper.dart
+++ b/pkg/front_end/lib/src/testing/id_testing_helper.dart
@@ -270,14 +270,14 @@
 RunTestFunction<T> runTestFor<T>(
     DataComputer<T> dataComputer, List<TestConfig> testedConfigs) {
   retainDataForTesting = true;
-  return (TestData testData,
+  return (MarkerOptions markerOptions, TestData testData,
       {required bool testAfterFailures,
       required bool verbose,
       required bool succinct,
       required bool printCode,
       Map<String, List<String>>? skipMap,
       required Uri nullUri}) {
-    return runTest(testData, dataComputer, testedConfigs,
+    return runTest(markerOptions, testData, dataComputer, testedConfigs,
         testAfterFailures: testAfterFailures,
         verbose: verbose,
         succinct: succinct,
@@ -291,8 +291,11 @@
 /// Runs [dataComputer] on [testData] for all [testedConfigs].
 ///
 /// Returns `true` if an error was encountered.
-Future<Map<String, TestResult<T>>> runTest<T>(TestData testData,
-    DataComputer<T> dataComputer, List<TestConfig> testedConfigs,
+Future<Map<String, TestResult<T>>> runTest<T>(
+    MarkerOptions markerOptions,
+    TestData testData,
+    DataComputer<T> dataComputer,
+    List<TestConfig> testedConfigs,
     {required bool testAfterFailures,
     required bool verbose,
     required bool succinct,
@@ -315,7 +318,7 @@
       continue;
     }
     results[config.marker] = await runTestForConfig(
-        testData, dataComputer, config,
+        markerOptions, testData, dataComputer, config,
         fatalErrors: !testAfterFailures,
         onFailure: onFailure,
         verbose: verbose,
@@ -329,7 +332,7 @@
 /// Runs [dataComputer] on [testData] for [config].
 ///
 /// Returns `true` if an error was encountered.
-Future<TestResult<T>> runTestForConfig<T>(
+Future<TestResult<T>> runTestForConfig<T>(MarkerOptions markerOptions,
     TestData testData, DataComputer<T> dataComputer, TestConfig config,
     {required bool fatalErrors,
     required bool verbose,
@@ -560,7 +563,7 @@
 
   CfeCompiledData<T> compiledData = new CfeCompiledData<T>(
       compilerResult, testData.entryPoint, actualMaps, globalData);
-  return checkCode(config.name, testData.testFileUri, testData.code,
+  return checkCode(markerOptions, config.marker, config.name, testData,
       memberAnnotations, compiledData, dataComputer.dataValidator,
       fatalErrors: fatalErrors, succinct: succinct, onFailure: onFailure);
 }
diff --git a/pkg/front_end/test/macros/application/macro_application_test.dart b/pkg/front_end/test/macros/application/macro_application_test.dart
index 69ae9c6..ca922e9 100644
--- a/pkg/front_end/test/macros/application/macro_application_test.dart
+++ b/pkg/front_end/test/macros/application/macro_application_test.dart
@@ -6,9 +6,6 @@
 
 import 'package:_fe_analyzer_shared/src/macros/api.dart';
 import 'package:_fe_analyzer_shared/src/macros/executor.dart';
-import 'package:_fe_analyzer_shared/src/macros/executor/serialization.dart';
-import 'package:_fe_analyzer_shared/src/macros/executor/isolated_executor.dart'
-    as isolatedExecutor;
 import 'package:_fe_analyzer_shared/src/testing/id.dart'
     show ActualData, ClassId, Id, LibraryId;
 import 'package:_fe_analyzer_shared/src/testing/id_testing.dart';
@@ -60,7 +57,6 @@
   final Directory dataDir;
   final MacroSerializer macroSerializer;
   final bool generateExpectations;
-  final Map<Uri, Uri> precompiledMacroUris = {};
 
   MacroTestConfig(this.dataDir, this.macroSerializer,
       {required this.generateExpectations})
@@ -71,10 +67,6 @@
 
   @override
   void customizeCompilerOptions(CompilerOptions options, TestData testData) {
-    options.macroExecutorProvider = () async {
-      return await isolatedExecutor.start(SerializationMode.byteDataServer);
-    };
-    options.precompiledMacroUris = precompiledMacroUris;
     options.macroTarget = new VmTarget(new TargetFlags());
     options.macroSerializer = macroSerializer;
   }
diff --git a/pkg/front_end/test/macros/declaration/data/tests/all_precompiled.dart b/pkg/front_end/test/macros/declaration/data/tests/all_precompiled.dart
index 9310930..2da2a58 100644
--- a/pkg/front_end/test/macros/declaration/data/tests/all_precompiled.dart
+++ b/pkg/front_end/test/macros/declaration/data/tests/all_precompiled.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 /*library: 
- macroClassIds=[package:precompiled_macro/precompiled_macro.dart/PrecompiledMacro],
  macroInstanceIds=[package:precompiled_macro/precompiled_macro.dart/PrecompiledMacro/()],
  macrosAreApplied,
  macrosAreAvailable
diff --git a/pkg/front_end/test/macros/declaration/data/tests/applications.dart b/pkg/front_end/test/macros/declaration/data/tests/applications.dart
index 6adb8dc..1f03c5a 100644
--- a/pkg/front_end/test/macros/declaration/data/tests/applications.dart
+++ b/pkg/front_end/test/macros/declaration/data/tests/applications.dart
@@ -6,10 +6,6 @@
  compilationSequence=[
   package:_fe_analyzer_shared/src/macros/api.dart|package:macro/macro.dart,
   main.dart],
- macroClassIds=[
-  package:macro/macro.dart/Macro1,
-  package:macro/macro.dart/Macro2,
-  package:macro/macro.dart/Macro3],
  macroInstanceIds=[
   package:macro/macro.dart/Macro1/(),
   package:macro/macro.dart/Macro1/(),
diff --git a/pkg/front_end/test/macros/declaration/data/tests/declare_vs_apply/main.dart b/pkg/front_end/test/macros/declaration/data/tests/declare_vs_apply/main.dart
index 09e50a9..6f2309b 100644
--- a/pkg/front_end/test/macros/declaration/data/tests/declare_vs_apply/main.dart
+++ b/pkg/front_end/test/macros/declaration/data/tests/declare_vs_apply/main.dart
@@ -6,7 +6,6 @@
  compilationSequence=[
   apply_lib_dep.dart|macro_lib.dart|macro_lib_dep.dart|main_lib_dep.dart|package:_fe_analyzer_shared/src/macros/api.dart,
   apply_lib.dart|main.dart],
- macroClassIds=[macro_lib.dart/Macro1],
  macroInstanceIds=[macro_lib.dart/Macro1/()],
  macrosAreAvailable,
  neededPrecompilations=[macro_lib.dart=Macro1(new)]
diff --git a/pkg/front_end/test/macros/declaration/data/tests/multiple_macros/main.dart b/pkg/front_end/test/macros/declaration/data/tests/multiple_macros/main.dart
index 103c1ed..1e9ac81 100644
--- a/pkg/front_end/test/macros/declaration/data/tests/multiple_macros/main.dart
+++ b/pkg/front_end/test/macros/declaration/data/tests/multiple_macros/main.dart
@@ -7,10 +7,6 @@
   macro_lib1.dart|macro_lib2a.dart|package:_fe_analyzer_shared/src/macros/api.dart,
   macro_lib2b.dart,
   main.dart],
- macroClassIds=[
-  macro_lib1.dart/Macro1,
-  macro_lib2a.dart/Macro2a,
-  macro_lib2b.dart/Macro2b],
  macroInstanceIds=[
   macro_lib1.dart/Macro1/(),
   macro_lib2a.dart/Macro2a/(),
diff --git a/pkg/front_end/test/macros/declaration/data/tests/precompiled.dart b/pkg/front_end/test/macros/declaration/data/tests/precompiled.dart
index 16171a8..1311d4c 100644
--- a/pkg/front_end/test/macros/declaration/data/tests/precompiled.dart
+++ b/pkg/front_end/test/macros/declaration/data/tests/precompiled.dart
@@ -6,9 +6,6 @@
  compilationSequence=[
   package:macro/macro.dart,
   main.dart],
- macroClassIds=[
-  package:macro/macro.dart/Macro1,
-  package:precompiled_macro/precompiled_macro.dart/PrecompiledMacro],
  macroInstanceIds=[
   package:macro/macro.dart/Macro1/(),
   package:precompiled_macro/precompiled_macro.dart/PrecompiledMacro/()],
diff --git a/pkg/front_end/test/macros/declaration/data/tests/use_macro_package.dart b/pkg/front_end/test/macros/declaration/data/tests/use_macro_package.dart
index 9fea835..1db43d7 100644
--- a/pkg/front_end/test/macros/declaration/data/tests/use_macro_package.dart
+++ b/pkg/front_end/test/macros/declaration/data/tests/use_macro_package.dart
@@ -6,10 +6,6 @@
  compilationSequence=[
   package:_fe_analyzer_shared/src/macros/api.dart|package:macro/macro.dart,
   main.dart],
- macroClassIds=[
-  package:macro/macro.dart/Macro1,
-  package:macro/macro.dart/Macro2,
-  package:macro/macro.dart/Macro3],
  macroInstanceIds=[
   package:macro/macro.dart/Macro1/(),
   package:macro/macro.dart/Macro1/(),
diff --git a/pkg/front_end/test/macros/declaration/data/tests/use_macro_source/main.dart b/pkg/front_end/test/macros/declaration/data/tests/use_macro_source/main.dart
index 475694d..fb50380 100644
--- a/pkg/front_end/test/macros/declaration/data/tests/use_macro_source/main.dart
+++ b/pkg/front_end/test/macros/declaration/data/tests/use_macro_source/main.dart
@@ -6,9 +6,6 @@
  compilationSequence=[
   macro_lib.dart|package:_fe_analyzer_shared/src/macros/api.dart,
   main.dart],
- macroClassIds=[
-  macro_lib.dart/Macro1,
-  macro_lib.dart/Macro2],
  macroInstanceIds=[
   macro_lib.dart/Macro1/(),
   macro_lib.dart/Macro1/(),
diff --git a/pkg/front_end/test/macros/declaration/macro_declaration_test.dart b/pkg/front_end/test/macros/declaration/macro_declaration_test.dart
index d1e3f3b..746e1a6 100644
--- a/pkg/front_end/test/macros/declaration/macro_declaration_test.dart
+++ b/pkg/front_end/test/macros/declaration/macro_declaration_test.dart
@@ -6,6 +6,7 @@
 
 import 'package:_fe_analyzer_shared/src/macros/api.dart';
 import 'package:_fe_analyzer_shared/src/macros/executor.dart';
+import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart';
 import 'package:_fe_analyzer_shared/src/macros/executor/serialization.dart';
 import 'package:_fe_analyzer_shared/src/testing/features.dart';
 import 'package:_fe_analyzer_shared/src/testing/id.dart' show ActualData, Id;
@@ -42,14 +43,11 @@
   @override
   TestMacroExecutor customizeCompilerOptions(
       CompilerOptions options, TestData testData) {
-    TestMacroExecutor macroExecutor = new TestMacroExecutor();
-    options.macroExecutorProvider = () async => macroExecutor;
-    Uri precompiledPackage =
-        Uri.parse('package:precompiled_macro/precompiled_macro.dart');
-    options.precompiledMacroUris = {
-      precompiledPackage: dummyUri,
-    };
-    return macroExecutor;
+    TestMacroExecutor testExecutor =
+        options.macroExecutor = new TestMacroExecutor();
+    testExecutor.registerExecutorFactory(() => testExecutor,
+        {Uri.parse('package:precompiled_macro/precompiled_macro.dart')});
+    return testExecutor;
   }
 }
 
@@ -101,7 +99,6 @@
   static const String neededPrecompilations = 'neededPrecompilations';
   static const String declaredMacros = 'declaredMacros';
   static const String appliedMacros = 'appliedMacros';
-  static const String macroClassIds = 'macroClassIds';
   static const String macroInstanceIds = 'macroInstanceIds';
   static const String error = 'error';
 }
@@ -260,9 +257,6 @@
         }
         features.addElement(Tags.neededPrecompilations, sb.toString());
       }
-      for (_MacroClassIdentifier id in macroExecutor.macroClasses) {
-        features.addElement(Tags.macroClassIds, id.toText());
-      }
       for (_MacroInstanceIdentifier id in macroExecutor.macroInstances) {
         features.addElement(Tags.macroInstanceIds, id.toText());
       }
@@ -289,8 +283,7 @@
   }
 }
 
-class TestMacroExecutor implements MacroExecutor {
-  List<_MacroClassIdentifier> macroClasses = [];
+class TestMacroExecutor extends MultiMacroExecutor {
   List<_MacroInstanceIdentifier> macroInstances = [];
 
   @override
@@ -303,7 +296,7 @@
   }
 
   @override
-  void close() {
+  Future<void> close() async {
     // TODO: implement close
   }
 
@@ -337,58 +330,24 @@
 
   @override
   Future<MacroInstanceIdentifier> instantiateMacro(
-      MacroClassIdentifier macroClass,
-      String constructor,
-      Arguments arguments) async {
-    _MacroInstanceIdentifier id = new _MacroInstanceIdentifier(
-        macroClass as _MacroClassIdentifier, constructor, arguments);
+      Uri library, String name, String constructor, Arguments arguments) async {
+    _MacroInstanceIdentifier id =
+        new _MacroInstanceIdentifier(library, name, constructor, arguments);
     macroInstances.add(id);
     return id;
   }
-
-  @override
-  Future<MacroClassIdentifier> loadMacro(Uri library, String name,
-      {Uri? precompiledKernelUri}) async {
-    _MacroClassIdentifier id = new _MacroClassIdentifier(library, name);
-    macroClasses.add(id);
-    return id;
-  }
-}
-
-class _MacroClassIdentifier implements MacroClassIdentifier {
-  final Uri uri;
-  final String className;
-
-  _MacroClassIdentifier(this.uri, this.className);
-
-  String toText() => '${importUriToString(uri)}/${className}';
-
-  @override
-  int get hashCode => uri.hashCode * 13 + className.hashCode * 17;
-
-  @override
-  bool operator ==(Object other) {
-    if (identical(this, other)) return true;
-    return other is _MacroClassIdentifier &&
-        uri == other.uri &&
-        className == other.className;
-  }
-
-  @override
-  String toString() => 'MacroClassIdentifier($uri,$className)';
-
-  @override
-  void serialize(Serializer serializer) => throw UnimplementedError();
 }
 
 class _MacroInstanceIdentifier implements MacroInstanceIdentifier {
-  final _MacroClassIdentifier macroClass;
+  final Uri library;
+  final String name;
   final String constructor;
   final Arguments arguments;
 
-  _MacroInstanceIdentifier(this.macroClass, this.constructor, this.arguments);
+  _MacroInstanceIdentifier(
+      this.library, this.name, this.constructor, this.arguments);
 
-  String toText() => '${macroClass.toText()}/${constructor}()';
+  String toText() => '${importUriToString(library)}/${name}/${constructor}()';
 
   @override
   void serialize(Serializer serializer) => throw UnimplementedError();
diff --git a/pkg/front_end/test/macros/incremental/incremental_macro_test.dart b/pkg/front_end/test/macros/incremental/incremental_macro_test.dart
index 6e38046..1c5ba4b 100644
--- a/pkg/front_end/test/macros/incremental/incremental_macro_test.dart
+++ b/pkg/front_end/test/macros/incremental/incremental_macro_test.dart
@@ -4,9 +4,7 @@
 
 import 'dart:io';
 
-import 'package:_fe_analyzer_shared/src/macros/executor/isolated_executor.dart'
-    as isolatedExecutor;
-import 'package:_fe_analyzer_shared/src/macros/executor/serialization.dart';
+import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart';
 import 'package:front_end/src/api_prototype/compiler_options.dart';
 import 'package:front_end/src/api_prototype/experimental_flags.dart';
 import 'package:front_end/src/api_prototype/incremental_kernel_generator.dart';
@@ -95,12 +93,9 @@
       ExperimentalFlag.alternativeInvalidationStrategy: true,
     }
     ..macroSerializer = macroSerializer
-    ..precompiledMacroUris = {}
-    ..macroExecutorProvider = () async {
-      return await isolatedExecutor.start(SerializationMode.byteDataServer);
-    }
     ..macroTarget = new VmTarget(new TargetFlags())
     ..fileSystem = new HybridFileSystem(memoryFileSystem);
+  compilerOptions.macroExecutor ??= new MultiMacroExecutor();
 
   ProcessedOptions processedOptions =
       new ProcessedOptions(options: compilerOptions);
diff --git a/pkg/front_end/test/macros/macro_api_test.dart b/pkg/front_end/test/macros/macro_api_test.dart
index 1392e5f..bfb041e 100644
--- a/pkg/front_end/test/macros/macro_api_test.dart
+++ b/pkg/front_end/test/macros/macro_api_test.dart
@@ -4,9 +4,7 @@
 
 import 'dart:io' show Platform;
 
-import 'package:_fe_analyzer_shared/src/macros/executor/isolated_executor.dart'
-    as isolatedExecutor;
-import 'package:_fe_analyzer_shared/src/macros/executor/serialization.dart';
+import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart';
 import 'package:expect/expect.dart';
 import 'package:front_end/src/api_prototype/experimental_flags.dart';
 import 'package:front_end/src/api_prototype/front_end.dart';
@@ -29,10 +27,7 @@
     options.explicitExperimentalFlags[ExperimentalFlag.macros] = true;
     options.packagesFileUri = Platform.script.resolve(
         '../../../_fe_analyzer_shared/test/macros/api/package_config.json');
-    options.macroExecutorProvider = () async {
-      return await isolatedExecutor.start(SerializationMode.byteDataServer);
-    };
-    options.precompiledMacroUris = {};
+    options.macroExecutor ??= new MultiMacroExecutor();
     options.target = options.macroTarget = new VmTarget(new TargetFlags());
     options.macroSerializer = macroSerializer;
 
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index 88a7263..f822ebb 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -136,6 +136,7 @@
 bof
 bom
 bones
+bookkeeping
 bootstrap
 bother
 boundness
@@ -157,6 +158,7 @@
 c
 caches
 calculations
+callbacks
 callee
 caller's
 callers
@@ -190,7 +192,6 @@
 ci
 circuited
 ck
-callbacks
 cl
 claim
 claimed
@@ -219,8 +220,10 @@
 combinations
 combinator
 combiner
+commands
 communicate
 communicates
+communicating
 communication
 compared
 compares
@@ -502,6 +505,7 @@
 fortunately
 fourth
 framework
+freed
 freely
 freshly
 frontend
@@ -947,6 +951,7 @@
 presubmit
 presumably
 prev
+primarily
 prime
 printer
 printf
@@ -1051,6 +1056,7 @@
 reflectee
 reflective
 reg
+regenerations
 regis
 registering
 rehash
@@ -1168,6 +1174,8 @@
 shrinking
 shru
 shut
+shutdown
+shuts
 shutting
 si
 sibling
@@ -1213,6 +1221,7 @@
 speeding
 spend
 spent
+spin
 spuriously
 sq
 sra
@@ -1423,6 +1432,8 @@
 unqualified
 unreachable
 unregister
+unregistered
+unregisters
 unseen
 unset
 unshadowed
diff --git a/pkg/front_end/testcases/dart2js/async_lowering/no_await.dart.strong.transformed.expect b/pkg/front_end/testcases/dart2js/async_lowering/no_await.dart.strong.transformed.expect
index d8ebe4a..63a5d71 100644
--- a/pkg/front_end/testcases/dart2js/async_lowering/no_await.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/dart2js/async_lowering/no_await.dart.strong.transformed.expect
@@ -4,16 +4,16 @@
 import "dart:core" as core;
 
 static method foo1() → asy::Future<core::int> /* futureValueType= core::int */ /* originally async */ 
-  asy::Future::sync<core::int>(() → FutureOr<core::int> {
+  return asy::Future::sync<core::int>(() → FutureOr<core::int> {
     final core::int c = 3;
     return c;
   });
 static method foo2() → asy::Future<void> /* futureValueType= void */ /* originally async */ 
-  asy::Future::sync<void>(() → FutureOr<void>? {
+  return asy::Future::sync<void>(() → FutureOr<void>? {
     final core::int c = 3;
   });
 static method foo3() → dynamic /* futureValueType= dynamic */ /* originally async */ 
-  asy::Future::sync<dynamic>(() → FutureOr<dynamic>? {
+  return asy::Future::sync<dynamic>(() → FutureOr<dynamic>? {
     return 234;
   });
 static method bar(() → asy::Future<core::int> func) → void {
@@ -21,13 +21,11 @@
 }
 static method foo4() → asy::Future<core::bool> async /* futureValueType= core::bool */ {
   await asy::Future::value<core::int>(2);
-  self::bar(() → asy::Future<core::int> /* futureValueType= core::int */ /* originally async */ 
-    asy::Future::sync<core::int>(() → FutureOr<core::int> => 3);
-);
+  self::bar(() → asy::Future<core::int> /* futureValueType= core::int */ /* originally async */ => asy::Future::sync<core::int>(() → FutureOr<core::int> => 3));
   return true;
 }
 static method foo5(core::bool x) → asy::Future<core::int> /* futureValueType= core::int */ /* originally async */ 
-  asy::Future::sync<core::int>(() → FutureOr<core::int> {
+  return asy::Future::sync<core::int>(() → FutureOr<core::int> {
     if(x)
       return 123;
     return 234;
diff --git a/pkg/front_end/testcases/dart2js/async_lowering/no_await.dart.weak.transformed.expect b/pkg/front_end/testcases/dart2js/async_lowering/no_await.dart.weak.transformed.expect
index d8ebe4a..63a5d71 100644
--- a/pkg/front_end/testcases/dart2js/async_lowering/no_await.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/dart2js/async_lowering/no_await.dart.weak.transformed.expect
@@ -4,16 +4,16 @@
 import "dart:core" as core;
 
 static method foo1() → asy::Future<core::int> /* futureValueType= core::int */ /* originally async */ 
-  asy::Future::sync<core::int>(() → FutureOr<core::int> {
+  return asy::Future::sync<core::int>(() → FutureOr<core::int> {
     final core::int c = 3;
     return c;
   });
 static method foo2() → asy::Future<void> /* futureValueType= void */ /* originally async */ 
-  asy::Future::sync<void>(() → FutureOr<void>? {
+  return asy::Future::sync<void>(() → FutureOr<void>? {
     final core::int c = 3;
   });
 static method foo3() → dynamic /* futureValueType= dynamic */ /* originally async */ 
-  asy::Future::sync<dynamic>(() → FutureOr<dynamic>? {
+  return asy::Future::sync<dynamic>(() → FutureOr<dynamic>? {
     return 234;
   });
 static method bar(() → asy::Future<core::int> func) → void {
@@ -21,13 +21,11 @@
 }
 static method foo4() → asy::Future<core::bool> async /* futureValueType= core::bool */ {
   await asy::Future::value<core::int>(2);
-  self::bar(() → asy::Future<core::int> /* futureValueType= core::int */ /* originally async */ 
-    asy::Future::sync<core::int>(() → FutureOr<core::int> => 3);
-);
+  self::bar(() → asy::Future<core::int> /* futureValueType= core::int */ /* originally async */ => asy::Future::sync<core::int>(() → FutureOr<core::int> => 3));
   return true;
 }
 static method foo5(core::bool x) → asy::Future<core::int> /* futureValueType= core::int */ /* originally async */ 
-  asy::Future::sync<core::int>(() → FutureOr<core::int> {
+  return asy::Future::sync<core::int>(() → FutureOr<core::int> {
     if(x)
       return 123;
     return 234;
diff --git a/pkg/front_end/testcases/general/async_function.dart.weak.transformed.expect b/pkg/front_end/testcases/general/async_function.dart.weak.transformed.expect
index 373a8d8..aea2b09 100644
--- a/pkg/front_end/testcases/general/async_function.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/general/async_function.dart.weak.transformed.expect
@@ -121,10 +121,10 @@
             return null;
           else
             [yield] null;
-          if(:controller.{asy::_AsyncStarStreamController::addStream}(self::asyncStarString2()){(asy::Stream<core::String>) → core::bool})
+          :controller.{asy::_AsyncStarStreamController::addStream}(self::asyncStarString2()){(asy::Stream<core::String>) → void};
+          [yield] null;
+          if(_in::unsafeCast<core::bool>(:result_or_exception))
             return null;
-          else
-            [yield] null;
           [yield] let dynamic #t1 = asy::_awaitHelper(self::asyncString(), :async_op_then, :async_op_error) in null;
           if(:controller.{asy::_AsyncStarStreamController::add}(_in::unsafeCast<core::String>(:result_or_exception)){(core::String) → core::bool})
             return null;
diff --git a/pkg/front_end/testcases/general/await_complex.dart.weak.transformed.expect b/pkg/front_end/testcases/general/await_complex.dart.weak.transformed.expect
index 21decd4..01a1fee 100644
--- a/pkg/front_end/testcases/general/await_complex.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/general/await_complex.dart.weak.transformed.expect
@@ -652,10 +652,10 @@
                       #L16:
                       {
                         [yield] let dynamic #t55 = asy::_awaitHelper(func<asy::Stream<core::int>>(self::intStream()){(asy::Stream<core::int>) → FutureOr<asy::Stream<core::int>>}, :async_op_then, :async_op_error) in null;
-                        if(:controller.{asy::_AsyncStarStreamController::addStream}(_in::unsafeCast<asy::Stream<core::int>>(:result_or_exception)){(asy::Stream<core::int>) → core::bool})
+                        :controller.{asy::_AsyncStarStreamController::addStream}(_in::unsafeCast<asy::Stream<core::int>>(:result_or_exception)){(asy::Stream<core::int>) → void};
+                        [yield] null;
+                        if(_in::unsafeCast<core::bool>(:result_or_exception))
                           return null;
-                        else
-                          [yield] null;
                       }
                       return;
                     }
diff --git a/pkg/front_end/testcases/general/statements.dart.weak.transformed.expect b/pkg/front_end/testcases/general/statements.dart.weak.transformed.expect
index 012142b..2db299f 100644
--- a/pkg/front_end/testcases/general/statements.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/general/statements.dart.weak.transformed.expect
@@ -44,10 +44,10 @@
                       return null;
                     else
                       [yield] null;
-                    if(:controller.{asy::_AsyncStarStreamController::addStream}(x as{TypeError,ForDynamic,ForNonNullableByDefault} asy::Stream<dynamic>){(asy::Stream<dynamic>) → core::bool})
+                    :controller.{asy::_AsyncStarStreamController::addStream}(x as{TypeError,ForDynamic,ForNonNullableByDefault} asy::Stream<dynamic>){(asy::Stream<dynamic>) → void};
+                    [yield] null;
+                    if(_in::unsafeCast<core::bool>(:result_or_exception))
                       return null;
-                    else
-                      [yield] null;
                   }
                 }
                 else
diff --git a/pkg/front_end/testcases/inference/block_bodied_lambdas_async_star.dart.weak.transformed.expect b/pkg/front_end/testcases/inference/block_bodied_lambdas_async_star.dart.weak.transformed.expect
index 2e127fbc..5730a20 100644
--- a/pkg/front_end/testcases/inference/block_bodied_lambdas_async_star.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/inference/block_bodied_lambdas_async_star.dart.weak.transformed.expect
@@ -2,6 +2,7 @@
 import self as self;
 import "dart:async" as asy;
 import "dart:core" as core;
+import "dart:_internal" as _in;
 
 import "dart:async";
 
@@ -25,10 +26,10 @@
             else
               [yield] null;
             asy::Stream<core::double*>* s;
-            if(:controller.{asy::_AsyncStarStreamController::addStream}(s){(asy::Stream<core::num*>) → core::bool})
+            :controller.{asy::_AsyncStarStreamController::addStream}(s){(asy::Stream<core::num*>) → void};
+            [yield] null;
+            if(_in::unsafeCast<core::bool>(:result_or_exception))
               return null;
-            else
-              [yield] null;
           }
           return;
         }
diff --git a/pkg/front_end/testcases/inference/downwards_inference_yield_yield_star.dart.weak.transformed.expect b/pkg/front_end/testcases/inference/downwards_inference_yield_yield_star.dart.weak.transformed.expect
index 7f9840f..02bca84 100644
--- a/pkg/front_end/testcases/inference/downwards_inference_yield_yield_star.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/inference/downwards_inference_yield_yield_star.dart.weak.transformed.expect
@@ -29,6 +29,7 @@
 import self as self;
 import "dart:core" as core;
 import "dart:async" as asy;
+import "dart:_internal" as _in;
 
 import "dart:async";
 
@@ -109,18 +110,18 @@
             return null;
           else
             [yield] null;
-          if(:controller.{asy::_AsyncStarStreamController::addStream}(invalid-expression "pkg/front_end/testcases/inference/downwards_inference_yield_yield_star.dart:17:64: Error: A value of type 'List<dynamic>' can't be assigned to a variable of type 'Stream<List<int>>'.
+          :controller.{asy::_AsyncStarStreamController::addStream}(invalid-expression "pkg/front_end/testcases/inference/downwards_inference_yield_yield_star.dart:17:64: Error: A value of type 'List<dynamic>' can't be assigned to a variable of type 'Stream<List<int>>'.
  - 'List' is from 'dart:core'.
  - 'Stream' is from 'dart:async'.
   yield* /*error:YIELD_OF_INVALID_TYPE*/ /*@typeArgs=dynamic*/ [];
-                                                               ^" in core::_GrowableList::•<dynamic>(0) as{TypeError} asy::Stream<core::List<core::int*>*>*){(asy::Stream<core::List<core::int*>*>) → core::bool})
+                                                               ^" in core::_GrowableList::•<dynamic>(0) as{TypeError} asy::Stream<core::List<core::int*>*>*){(asy::Stream<core::List<core::int*>*>) → void};
+          [yield] null;
+          if(_in::unsafeCast<core::bool>(:result_or_exception))
             return null;
-          else
-            [yield] null;
-          if(:controller.{asy::_AsyncStarStreamController::addStream}(self::MyStream::•<core::List<core::int*>*>()){(asy::Stream<core::List<core::int*>*>) → core::bool})
+          :controller.{asy::_AsyncStarStreamController::addStream}(self::MyStream::•<core::List<core::int*>*>()){(asy::Stream<core::List<core::int*>*>) → void};
+          [yield] null;
+          if(_in::unsafeCast<core::bool>(:result_or_exception))
             return null;
-          else
-            [yield] null;
         }
         return;
       }
diff --git a/pkg/front_end/testcases/inference/local_return_and_yield.dart.weak.transformed.expect b/pkg/front_end/testcases/inference/local_return_and_yield.dart.weak.transformed.expect
index fbcd719..251c281 100644
--- a/pkg/front_end/testcases/inference/local_return_and_yield.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/inference/local_return_and_yield.dart.weak.transformed.expect
@@ -10,6 +10,7 @@
 import self as self;
 import "dart:core" as core;
 import "dart:async" as asy;
+import "dart:_internal" as _in;
 
 import "dart:async";
 
@@ -129,10 +130,10 @@
         try {
           #L3:
           {
-            if(:controller.{asy::_AsyncStarStreamController::addStream}(asy::Stream::fromIterable<(core::int*) →* core::int*>(core::_GrowableList::_literal1<(core::int*) →* core::int*>((core::int* x) → core::int* => x))){(asy::Stream<(core::int*) →* core::int*>) → core::bool})
+            :controller.{asy::_AsyncStarStreamController::addStream}(asy::Stream::fromIterable<(core::int*) →* core::int*>(core::_GrowableList::_literal1<(core::int*) →* core::int*>((core::int* x) → core::int* => x))){(asy::Stream<(core::int*) →* core::int*>) → void};
+            [yield] null;
+            if(_in::unsafeCast<core::bool>(:result_or_exception))
               return null;
-            else
-              [yield] null;
           }
           return;
         }
diff --git a/pkg/front_end/testcases/inference/top_level_return_and_yield.dart.weak.transformed.expect b/pkg/front_end/testcases/inference/top_level_return_and_yield.dart.weak.transformed.expect
index 84ea22c..3d5e979 100644
--- a/pkg/front_end/testcases/inference/top_level_return_and_yield.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/inference/top_level_return_and_yield.dart.weak.transformed.expect
@@ -10,6 +10,7 @@
 import self as self;
 import "dart:core" as core;
 import "dart:async" as asy;
+import "dart:_internal" as _in;
 
 import "dart:async";
 
@@ -128,10 +129,10 @@
       try {
         #L3:
         {
-          if(:controller.{asy::_AsyncStarStreamController::addStream}(asy::Stream::fromIterable<(core::int*) →* core::int*>(core::_GrowableList::_literal1<(core::int*) →* core::int*>((core::int* x) → core::int* => x))){(asy::Stream<(core::int*) →* core::int*>) → core::bool})
+          :controller.{asy::_AsyncStarStreamController::addStream}(asy::Stream::fromIterable<(core::int*) →* core::int*>(core::_GrowableList::_literal1<(core::int*) →* core::int*>((core::int* x) → core::int* => x))){(asy::Stream<(core::int*) →* core::int*>) → void};
+          [yield] null;
+          if(_in::unsafeCast<core::bool>(:result_or_exception))
             return null;
-          else
-            [yield] null;
         }
         return;
       }
diff --git a/pkg/front_end/testcases/nnbd/issue41437c.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/issue41437c.dart.strong.transformed.expect
index cfc0b05..c8058e6 100644
--- a/pkg/front_end/testcases/nnbd/issue41437c.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/issue41437c.dart.strong.transformed.expect
@@ -40,6 +40,7 @@
 import self as self;
 import "dart:async" as asy;
 import "dart:core" as core;
+import "dart:_internal" as _in;
 
 static method getNull() → dynamic
   return null;
@@ -160,13 +161,13 @@
       try {
         #L4:
         {
-          if(:controller.{asy::_AsyncStarStreamController::addStream}(invalid-expression "pkg/front_end/testcases/nnbd/issue41437c.dart:21:10: Error: A value of type 'Stream<dynamic>' can't be assigned to a variable of type 'Stream<bool>'.
+          :controller.{asy::_AsyncStarStreamController::addStream}(invalid-expression "pkg/front_end/testcases/nnbd/issue41437c.dart:21:10: Error: A value of type 'Stream<dynamic>' can't be assigned to a variable of type 'Stream<bool>'.
  - 'Stream' is from 'dart:async'.
   yield* getStreamNull(); // error
-         ^" in self::getStreamNull() as{TypeError,ForNonNullableByDefault} asy::Stream<core::bool>){(asy::Stream<core::bool>) → core::bool})
+         ^" in self::getStreamNull() as{TypeError,ForNonNullableByDefault} asy::Stream<core::bool>){(asy::Stream<core::bool>) → void};
+          [yield] null;
+          if(_in::unsafeCast<core::bool>(:result_or_exception))
             return null;
-          else
-            [yield] null;
         }
         return;
       }
@@ -203,10 +204,10 @@
       try {
         #L5:
         {
-          if(:controller.{asy::_AsyncStarStreamController::addStream}(self::getStreamBool()){(asy::Stream<core::bool>) → core::bool})
+          :controller.{asy::_AsyncStarStreamController::addStream}(self::getStreamBool()){(asy::Stream<core::bool>) → void};
+          [yield] null;
+          if(_in::unsafeCast<core::bool>(:result_or_exception))
             return null;
-          else
-            [yield] null;
         }
         return;
       }
@@ -285,13 +286,13 @@
               try {
                 #L8:
                 {
-                  if(:controller.{asy::_AsyncStarStreamController::addStream}(invalid-expression "pkg/front_end/testcases/nnbd/issue41437c.dart:38:12: Error: A value of type 'Stream<dynamic>' can't be assigned to a variable of type 'Stream<bool>'.
+                  :controller.{asy::_AsyncStarStreamController::addStream}(invalid-expression "pkg/front_end/testcases/nnbd/issue41437c.dart:38:12: Error: A value of type 'Stream<dynamic>' can't be assigned to a variable of type 'Stream<bool>'.
  - 'Stream' is from 'dart:async'.
     yield* getStreamNull(); // error
-           ^" in self::getStreamNull() as{TypeError,ForNonNullableByDefault} asy::Stream<core::bool>){(asy::Stream<core::bool>) → core::bool})
+           ^" in self::getStreamNull() as{TypeError,ForNonNullableByDefault} asy::Stream<core::bool>){(asy::Stream<core::bool>) → void};
+                  [yield] null;
+                  if(_in::unsafeCast<core::bool>(:result_or_exception))
                     return null;
-                  else
-                    [yield] null;
                 }
                 return;
               }
@@ -328,10 +329,10 @@
               try {
                 #L9:
                 {
-                  if(:controller.{asy::_AsyncStarStreamController::addStream}(self::getStreamBool()){(asy::Stream<core::bool>) → core::bool})
+                  :controller.{asy::_AsyncStarStreamController::addStream}(self::getStreamBool()){(asy::Stream<core::bool>) → void};
+                  [yield] null;
+                  if(_in::unsafeCast<core::bool>(:result_or_exception))
                     return null;
-                  else
-                    [yield] null;
                 }
                 return;
               }
@@ -402,10 +403,10 @@
               try {
                 #L11:
                 {
-                  if(:controller.{asy::_AsyncStarStreamController::addStream}(self::getStreamNull()){(asy::Stream<dynamic>) → core::bool})
+                  :controller.{asy::_AsyncStarStreamController::addStream}(self::getStreamNull()){(asy::Stream<dynamic>) → void};
+                  [yield] null;
+                  if(_in::unsafeCast<core::bool>(:result_or_exception))
                     return null;
-                  else
-                    [yield] null;
                 }
                 return;
               }
@@ -440,10 +441,10 @@
               try {
                 #L12:
                 {
-                  if(:controller.{asy::_AsyncStarStreamController::addStream}(self::getStreamBool()){(asy::Stream<core::bool>) → core::bool})
+                  :controller.{asy::_AsyncStarStreamController::addStream}(self::getStreamBool()){(asy::Stream<core::bool>) → void};
+                  [yield] null;
+                  if(_in::unsafeCast<core::bool>(:result_or_exception))
                     return null;
-                  else
-                    [yield] null;
                 }
                 return;
               }
diff --git a/pkg/front_end/testcases/nnbd/issue41437c.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/issue41437c.dart.weak.transformed.expect
index cfc0b05..c8058e6 100644
--- a/pkg/front_end/testcases/nnbd/issue41437c.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/issue41437c.dart.weak.transformed.expect
@@ -40,6 +40,7 @@
 import self as self;
 import "dart:async" as asy;
 import "dart:core" as core;
+import "dart:_internal" as _in;
 
 static method getNull() → dynamic
   return null;
@@ -160,13 +161,13 @@
       try {
         #L4:
         {
-          if(:controller.{asy::_AsyncStarStreamController::addStream}(invalid-expression "pkg/front_end/testcases/nnbd/issue41437c.dart:21:10: Error: A value of type 'Stream<dynamic>' can't be assigned to a variable of type 'Stream<bool>'.
+          :controller.{asy::_AsyncStarStreamController::addStream}(invalid-expression "pkg/front_end/testcases/nnbd/issue41437c.dart:21:10: Error: A value of type 'Stream<dynamic>' can't be assigned to a variable of type 'Stream<bool>'.
  - 'Stream' is from 'dart:async'.
   yield* getStreamNull(); // error
-         ^" in self::getStreamNull() as{TypeError,ForNonNullableByDefault} asy::Stream<core::bool>){(asy::Stream<core::bool>) → core::bool})
+         ^" in self::getStreamNull() as{TypeError,ForNonNullableByDefault} asy::Stream<core::bool>){(asy::Stream<core::bool>) → void};
+          [yield] null;
+          if(_in::unsafeCast<core::bool>(:result_or_exception))
             return null;
-          else
-            [yield] null;
         }
         return;
       }
@@ -203,10 +204,10 @@
       try {
         #L5:
         {
-          if(:controller.{asy::_AsyncStarStreamController::addStream}(self::getStreamBool()){(asy::Stream<core::bool>) → core::bool})
+          :controller.{asy::_AsyncStarStreamController::addStream}(self::getStreamBool()){(asy::Stream<core::bool>) → void};
+          [yield] null;
+          if(_in::unsafeCast<core::bool>(:result_or_exception))
             return null;
-          else
-            [yield] null;
         }
         return;
       }
@@ -285,13 +286,13 @@
               try {
                 #L8:
                 {
-                  if(:controller.{asy::_AsyncStarStreamController::addStream}(invalid-expression "pkg/front_end/testcases/nnbd/issue41437c.dart:38:12: Error: A value of type 'Stream<dynamic>' can't be assigned to a variable of type 'Stream<bool>'.
+                  :controller.{asy::_AsyncStarStreamController::addStream}(invalid-expression "pkg/front_end/testcases/nnbd/issue41437c.dart:38:12: Error: A value of type 'Stream<dynamic>' can't be assigned to a variable of type 'Stream<bool>'.
  - 'Stream' is from 'dart:async'.
     yield* getStreamNull(); // error
-           ^" in self::getStreamNull() as{TypeError,ForNonNullableByDefault} asy::Stream<core::bool>){(asy::Stream<core::bool>) → core::bool})
+           ^" in self::getStreamNull() as{TypeError,ForNonNullableByDefault} asy::Stream<core::bool>){(asy::Stream<core::bool>) → void};
+                  [yield] null;
+                  if(_in::unsafeCast<core::bool>(:result_or_exception))
                     return null;
-                  else
-                    [yield] null;
                 }
                 return;
               }
@@ -328,10 +329,10 @@
               try {
                 #L9:
                 {
-                  if(:controller.{asy::_AsyncStarStreamController::addStream}(self::getStreamBool()){(asy::Stream<core::bool>) → core::bool})
+                  :controller.{asy::_AsyncStarStreamController::addStream}(self::getStreamBool()){(asy::Stream<core::bool>) → void};
+                  [yield] null;
+                  if(_in::unsafeCast<core::bool>(:result_or_exception))
                     return null;
-                  else
-                    [yield] null;
                 }
                 return;
               }
@@ -402,10 +403,10 @@
               try {
                 #L11:
                 {
-                  if(:controller.{asy::_AsyncStarStreamController::addStream}(self::getStreamNull()){(asy::Stream<dynamic>) → core::bool})
+                  :controller.{asy::_AsyncStarStreamController::addStream}(self::getStreamNull()){(asy::Stream<dynamic>) → void};
+                  [yield] null;
+                  if(_in::unsafeCast<core::bool>(:result_or_exception))
                     return null;
-                  else
-                    [yield] null;
                 }
                 return;
               }
@@ -440,10 +441,10 @@
               try {
                 #L12:
                 {
-                  if(:controller.{asy::_AsyncStarStreamController::addStream}(self::getStreamBool()){(asy::Stream<core::bool>) → core::bool})
+                  :controller.{asy::_AsyncStarStreamController::addStream}(self::getStreamBool()){(asy::Stream<core::bool>) → void};
+                  [yield] null;
+                  if(_in::unsafeCast<core::bool>(:result_or_exception))
                     return null;
-                  else
-                    [yield] null;
                 }
                 return;
               }
diff --git a/pkg/front_end/testcases/nnbd/issue48631_1.dart b/pkg/front_end/testcases/nnbd/issue48631_1.dart
new file mode 100644
index 0000000..f995c3e
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_1.dart
@@ -0,0 +1,13 @@
+// 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:async';
+
+FutureOr<T?> foo<T>(T t) {}
+
+bar<S>(bool t, S s) {
+  var x = [foo(s), s];
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_1.dart.strong.expect b/pkg/front_end/testcases/nnbd/issue48631_1.dart.strong.expect
new file mode 100644
index 0000000..a26757b
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_1.dart.strong.expect
@@ -0,0 +1,11 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "dart:async";
+
+static method foo<T extends core::Object? = dynamic>(self::foo::T% t) → FutureOr<self::foo::T?> {}
+static method bar<S extends core::Object? = dynamic>(core::bool t, self::bar::S% s) → dynamic {
+  core::List<FutureOr<self::bar::S?>?> x = <FutureOr<self::bar::S?>?>[self::foo<self::bar::S%>(s), s];
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_1.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/issue48631_1.dart.strong.transformed.expect
new file mode 100644
index 0000000..b425585
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_1.dart.strong.transformed.expect
@@ -0,0 +1,11 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "dart:async";
+
+static method foo<T extends core::Object? = dynamic>(self::foo::T% t) → FutureOr<self::foo::T?> {}
+static method bar<S extends core::Object? = dynamic>(core::bool t, self::bar::S% s) → dynamic {
+  core::List<FutureOr<self::bar::S?>?> x = core::_GrowableList::_literal2<FutureOr<self::bar::S?>?>(self::foo<self::bar::S%>(s), s);
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_1.dart.textual_outline.expect b/pkg/front_end/testcases/nnbd/issue48631_1.dart.textual_outline.expect
new file mode 100644
index 0000000..65faadd
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_1.dart.textual_outline.expect
@@ -0,0 +1,5 @@
+import 'dart:async';
+
+FutureOr<T?> foo<T>(T t) {}
+bar<S>(bool t, S s) {}
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_1.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/nnbd/issue48631_1.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..65faadd
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_1.dart.textual_outline_modelled.expect
@@ -0,0 +1,5 @@
+import 'dart:async';
+
+FutureOr<T?> foo<T>(T t) {}
+bar<S>(bool t, S s) {}
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_1.dart.weak.expect b/pkg/front_end/testcases/nnbd/issue48631_1.dart.weak.expect
new file mode 100644
index 0000000..a26757b
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_1.dart.weak.expect
@@ -0,0 +1,11 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "dart:async";
+
+static method foo<T extends core::Object? = dynamic>(self::foo::T% t) → FutureOr<self::foo::T?> {}
+static method bar<S extends core::Object? = dynamic>(core::bool t, self::bar::S% s) → dynamic {
+  core::List<FutureOr<self::bar::S?>?> x = <FutureOr<self::bar::S?>?>[self::foo<self::bar::S%>(s), s];
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_1.dart.weak.modular.expect b/pkg/front_end/testcases/nnbd/issue48631_1.dart.weak.modular.expect
new file mode 100644
index 0000000..a26757b
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_1.dart.weak.modular.expect
@@ -0,0 +1,11 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "dart:async";
+
+static method foo<T extends core::Object? = dynamic>(self::foo::T% t) → FutureOr<self::foo::T?> {}
+static method bar<S extends core::Object? = dynamic>(core::bool t, self::bar::S% s) → dynamic {
+  core::List<FutureOr<self::bar::S?>?> x = <FutureOr<self::bar::S?>?>[self::foo<self::bar::S%>(s), s];
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_1.dart.weak.outline.expect b/pkg/front_end/testcases/nnbd/issue48631_1.dart.weak.outline.expect
new file mode 100644
index 0000000..f366279
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_1.dart.weak.outline.expect
@@ -0,0 +1,12 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "dart:async";
+
+static method foo<T extends core::Object? = dynamic>(self::foo::T% t) → FutureOr<self::foo::T?>
+  ;
+static method bar<S extends core::Object? = dynamic>(core::bool t, self::bar::S% s) → dynamic
+  ;
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/nnbd/issue48631_1.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/issue48631_1.dart.weak.transformed.expect
new file mode 100644
index 0000000..b425585
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_1.dart.weak.transformed.expect
@@ -0,0 +1,11 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "dart:async";
+
+static method foo<T extends core::Object? = dynamic>(self::foo::T% t) → FutureOr<self::foo::T?> {}
+static method bar<S extends core::Object? = dynamic>(core::bool t, self::bar::S% s) → dynamic {
+  core::List<FutureOr<self::bar::S?>?> x = core::_GrowableList::_literal2<FutureOr<self::bar::S?>?>(self::foo<self::bar::S%>(s), s);
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_2.dart b/pkg/front_end/testcases/nnbd/issue48631_2.dart
new file mode 100644
index 0000000..2cf30ba
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_2.dart
@@ -0,0 +1,29 @@
+// 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:async';
+
+abstract class A {
+  T foo<T>(B<T> b);
+}
+
+class B<X> {
+  B(X x);
+}
+
+class C<Y> {
+  final Bar<FutureOr<Y>, D<Y>> bar;
+
+  C(this.bar);
+}
+
+abstract class D<W> implements A {}
+
+typedef Bar<V, U extends A> = V Function(U);
+
+final baz = C<int>((a) {
+    return a.foo(B(Future.value(0)));
+});
+
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_2.dart.strong.expect b/pkg/front_end/testcases/nnbd/issue48631_2.dart.strong.expect
new file mode 100644
index 0000000..e842794
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_2.dart.strong.expect
@@ -0,0 +1,34 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "dart:async";
+
+typedef Bar<V extends core::Object? = dynamic, contravariant U extends self::A> = (U) → V%;
+abstract class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+  abstract method foo<T extends core::Object? = dynamic>(self::B<self::A::foo::T%> b) → self::A::foo::T%;
+}
+class B<X extends core::Object? = dynamic> extends core::Object {
+  constructor •(self::B::X% x) → self::B<self::B::X%>
+    : super core::Object::•()
+    ;
+}
+class C<Y extends core::Object? = dynamic> extends core::Object {
+  final field (self::D<self::C::Y%>) → FutureOr<self::C::Y%>% bar;
+  constructor •((self::D<self::C::Y%>) → FutureOr<self::C::Y%>% bar) → self::C<self::C::Y%>
+    : self::C::bar = bar, super core::Object::•()
+    ;
+}
+abstract class D<W extends core::Object? = dynamic> extends core::Object implements self::A {
+  synthetic constructor •() → self::D<self::D::W%>
+    : super core::Object::•()
+    ;
+}
+static final field self::C<core::int> baz = new self::C::•<core::int>((self::D<core::int> a) → FutureOr<core::int> {
+  return a.{self::A::foo}<FutureOr<core::int>>(new self::B::•<FutureOr<core::int>>(asy::Future::value<core::int>(0))){(self::B<FutureOr<core::int>>) → FutureOr<core::int>};
+});
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_2.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/issue48631_2.dart.strong.transformed.expect
new file mode 100644
index 0000000..e842794
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_2.dart.strong.transformed.expect
@@ -0,0 +1,34 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "dart:async";
+
+typedef Bar<V extends core::Object? = dynamic, contravariant U extends self::A> = (U) → V%;
+abstract class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+  abstract method foo<T extends core::Object? = dynamic>(self::B<self::A::foo::T%> b) → self::A::foo::T%;
+}
+class B<X extends core::Object? = dynamic> extends core::Object {
+  constructor •(self::B::X% x) → self::B<self::B::X%>
+    : super core::Object::•()
+    ;
+}
+class C<Y extends core::Object? = dynamic> extends core::Object {
+  final field (self::D<self::C::Y%>) → FutureOr<self::C::Y%>% bar;
+  constructor •((self::D<self::C::Y%>) → FutureOr<self::C::Y%>% bar) → self::C<self::C::Y%>
+    : self::C::bar = bar, super core::Object::•()
+    ;
+}
+abstract class D<W extends core::Object? = dynamic> extends core::Object implements self::A {
+  synthetic constructor •() → self::D<self::D::W%>
+    : super core::Object::•()
+    ;
+}
+static final field self::C<core::int> baz = new self::C::•<core::int>((self::D<core::int> a) → FutureOr<core::int> {
+  return a.{self::A::foo}<FutureOr<core::int>>(new self::B::•<FutureOr<core::int>>(asy::Future::value<core::int>(0))){(self::B<FutureOr<core::int>>) → FutureOr<core::int>};
+});
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_2.dart.textual_outline.expect b/pkg/front_end/testcases/nnbd/issue48631_2.dart.textual_outline.expect
new file mode 100644
index 0000000..a409162
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_2.dart.textual_outline.expect
@@ -0,0 +1,22 @@
+import 'dart:async';
+
+abstract class A {
+  T foo<T>(B<T> b);
+}
+
+class B<X> {
+  B(X x);
+}
+
+class C<Y> {
+  final Bar<FutureOr<Y>, D<Y>> bar;
+  C(this.bar);
+}
+
+abstract class D<W> implements A {}
+
+typedef Bar<V, U extends A> = V Function(U);
+final baz = C<int>((a) {
+  return a.foo(B(Future.value(0)));
+});
+main() {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_2.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/nnbd/issue48631_2.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..bee82dc
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_2.dart.textual_outline_modelled.expect
@@ -0,0 +1,22 @@
+import 'dart:async';
+
+abstract class A {
+  T foo<T>(B<T> b);
+}
+
+abstract class D<W> implements A {}
+
+class B<X> {
+  B(X x);
+}
+
+class C<Y> {
+  C(this.bar);
+  final Bar<FutureOr<Y>, D<Y>> bar;
+}
+
+final baz = C<int>((a) {
+  return a.foo(B(Future.value(0)));
+});
+main() {}
+typedef Bar<V, U extends A> = V Function(U);
diff --git a/pkg/front_end/testcases/nnbd/issue48631_2.dart.weak.expect b/pkg/front_end/testcases/nnbd/issue48631_2.dart.weak.expect
new file mode 100644
index 0000000..e842794
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_2.dart.weak.expect
@@ -0,0 +1,34 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "dart:async";
+
+typedef Bar<V extends core::Object? = dynamic, contravariant U extends self::A> = (U) → V%;
+abstract class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+  abstract method foo<T extends core::Object? = dynamic>(self::B<self::A::foo::T%> b) → self::A::foo::T%;
+}
+class B<X extends core::Object? = dynamic> extends core::Object {
+  constructor •(self::B::X% x) → self::B<self::B::X%>
+    : super core::Object::•()
+    ;
+}
+class C<Y extends core::Object? = dynamic> extends core::Object {
+  final field (self::D<self::C::Y%>) → FutureOr<self::C::Y%>% bar;
+  constructor •((self::D<self::C::Y%>) → FutureOr<self::C::Y%>% bar) → self::C<self::C::Y%>
+    : self::C::bar = bar, super core::Object::•()
+    ;
+}
+abstract class D<W extends core::Object? = dynamic> extends core::Object implements self::A {
+  synthetic constructor •() → self::D<self::D::W%>
+    : super core::Object::•()
+    ;
+}
+static final field self::C<core::int> baz = new self::C::•<core::int>((self::D<core::int> a) → FutureOr<core::int> {
+  return a.{self::A::foo}<FutureOr<core::int>>(new self::B::•<FutureOr<core::int>>(asy::Future::value<core::int>(0))){(self::B<FutureOr<core::int>>) → FutureOr<core::int>};
+});
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_2.dart.weak.modular.expect b/pkg/front_end/testcases/nnbd/issue48631_2.dart.weak.modular.expect
new file mode 100644
index 0000000..e842794
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_2.dart.weak.modular.expect
@@ -0,0 +1,34 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "dart:async";
+
+typedef Bar<V extends core::Object? = dynamic, contravariant U extends self::A> = (U) → V%;
+abstract class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+  abstract method foo<T extends core::Object? = dynamic>(self::B<self::A::foo::T%> b) → self::A::foo::T%;
+}
+class B<X extends core::Object? = dynamic> extends core::Object {
+  constructor •(self::B::X% x) → self::B<self::B::X%>
+    : super core::Object::•()
+    ;
+}
+class C<Y extends core::Object? = dynamic> extends core::Object {
+  final field (self::D<self::C::Y%>) → FutureOr<self::C::Y%>% bar;
+  constructor •((self::D<self::C::Y%>) → FutureOr<self::C::Y%>% bar) → self::C<self::C::Y%>
+    : self::C::bar = bar, super core::Object::•()
+    ;
+}
+abstract class D<W extends core::Object? = dynamic> extends core::Object implements self::A {
+  synthetic constructor •() → self::D<self::D::W%>
+    : super core::Object::•()
+    ;
+}
+static final field self::C<core::int> baz = new self::C::•<core::int>((self::D<core::int> a) → FutureOr<core::int> {
+  return a.{self::A::foo}<FutureOr<core::int>>(new self::B::•<FutureOr<core::int>>(asy::Future::value<core::int>(0))){(self::B<FutureOr<core::int>>) → FutureOr<core::int>};
+});
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/nnbd/issue48631_2.dart.weak.outline.expect b/pkg/front_end/testcases/nnbd/issue48631_2.dart.weak.outline.expect
new file mode 100644
index 0000000..4eff2cf
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_2.dart.weak.outline.expect
@@ -0,0 +1,28 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "dart:async";
+
+typedef Bar<V extends core::Object? = dynamic, contravariant U extends self::A> = (U) → V%;
+abstract class A extends core::Object {
+  synthetic constructor •() → self::A
+    ;
+  abstract method foo<T extends core::Object? = dynamic>(self::B<self::A::foo::T%> b) → self::A::foo::T%;
+}
+class B<X extends core::Object? = dynamic> extends core::Object {
+  constructor •(self::B::X% x) → self::B<self::B::X%>
+    ;
+}
+class C<Y extends core::Object? = dynamic> extends core::Object {
+  final field (self::D<self::C::Y%>) → FutureOr<self::C::Y%>% bar;
+  constructor •((self::D<self::C::Y%>) → FutureOr<self::C::Y%>% bar) → self::C<self::C::Y%>
+    ;
+}
+abstract class D<W extends core::Object? = dynamic> extends core::Object implements self::A {
+  synthetic constructor •() → self::D<self::D::W%>
+    ;
+}
+static final field self::C<core::int> baz;
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/nnbd/issue48631_2.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/issue48631_2.dart.weak.transformed.expect
new file mode 100644
index 0000000..e842794
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue48631_2.dart.weak.transformed.expect
@@ -0,0 +1,34 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "dart:async";
+
+typedef Bar<V extends core::Object? = dynamic, contravariant U extends self::A> = (U) → V%;
+abstract class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+  abstract method foo<T extends core::Object? = dynamic>(self::B<self::A::foo::T%> b) → self::A::foo::T%;
+}
+class B<X extends core::Object? = dynamic> extends core::Object {
+  constructor •(self::B::X% x) → self::B<self::B::X%>
+    : super core::Object::•()
+    ;
+}
+class C<Y extends core::Object? = dynamic> extends core::Object {
+  final field (self::D<self::C::Y%>) → FutureOr<self::C::Y%>% bar;
+  constructor •((self::D<self::C::Y%>) → FutureOr<self::C::Y%>% bar) → self::C<self::C::Y%>
+    : self::C::bar = bar, super core::Object::•()
+    ;
+}
+abstract class D<W extends core::Object? = dynamic> extends core::Object implements self::A {
+  synthetic constructor •() → self::D<self::D::W%>
+    : super core::Object::•()
+    ;
+}
+static final field self::C<core::int> baz = new self::C::•<core::int>((self::D<core::int> a) → FutureOr<core::int> {
+  return a.{self::A::foo}<FutureOr<core::int>>(new self::B::•<FutureOr<core::int>>(asy::Future::value<core::int>(0))){(self::B<FutureOr<core::int>>) → FutureOr<core::int>};
+});
+static method main() → dynamic {}
diff --git a/pkg/frontend_server/lib/compute_kernel.dart b/pkg/frontend_server/lib/compute_kernel.dart
index e51cafc..6e89be6 100644
--- a/pkg/frontend_server/lib/compute_kernel.dart
+++ b/pkg/frontend_server/lib/compute_kernel.dart
@@ -314,28 +314,41 @@
             '${parsedArgs['macro-serialization-mode']}');
     }
 
+    // TODO: Handle invalidation of precompiled macros.
+    // TODO: Handle multiple macro libraries compiled to a single precompiled
+    // kernel file.
+    var macroExecutor = state.options.macroExecutor;
     var format = parsedArgs['precompiled-macro-format'];
-    switch (format) {
-      case 'kernel':
-        state.options.macroExecutorProvider =
-            () => isolatedExecutor.start(serializationMode);
-        break;
-      case 'aot':
-        state.options.macroExecutorProvider = () => processExecutor.start(
-            serializationMode, processExecutor.CommunicationChannel.socket);
-        break;
-      default:
-        throw ArgumentError('Unrecognized precompiled macro format $format');
-    }
-    var precompiledMacroUris = state.options.precompiledMacroUris = {};
     for (var parts in (parsedArgs['precompiled-macro'] as List<String>)
         .map((arg) => arg.split(';'))) {
-      precompiledMacroUris[Uri.parse(parts[0])] = toUri(parts[1]);
+      var library = Uri.parse(parts[0]);
+      if (macroExecutor.libraryIsRegistered(library)) {
+        continue;
+      }
+      var programUri = toUri(parts[1]);
+      switch (format) {
+        case 'kernel':
+          macroExecutor.registerExecutorFactory(
+              () => isolatedExecutor.start(serializationMode, programUri),
+              {library});
+          break;
+        case 'aot':
+          macroExecutor.registerExecutorFactory(
+              () => processExecutor.start(
+                  serializationMode,
+                  processExecutor.CommunicationChannel.socket,
+                  programUri.toFilePath()),
+              {library});
+          break;
+        default:
+          throw ArgumentError('Unrecognized precompiled macro format $format');
+      }
     }
   } else {
     enableMacros = false;
     forceEnableMacros = false;
-    state.options.precompiledMacroUris = {};
+    await state.options.macroExecutor?.close();
+    state.options.macroExecutor = null;
   }
 
   void onDiagnostic(fe.DiagnosticMessage message) {
diff --git a/pkg/js_ast/lib/src/characters.dart b/pkg/js_ast/lib/src/characters.dart
index aaa068d..5fd1196 100644
--- a/pkg/js_ast/lib/src/characters.dart
+++ b/pkg/js_ast/lib/src/characters.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.15
-
 library js_character_codes;
 
 const int $EOF = 0;
diff --git a/pkg/js_ast/test/deferred_expression_test.dart b/pkg/js_ast/test/deferred_expression_test.dart
index da1e158..f519964 100644
--- a/pkg/js_ast/test/deferred_expression_test.dart
+++ b/pkg/js_ast/test/deferred_expression_test.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.10
-
 import 'package:expect/expect.dart';
 import 'package:js_ast/js_ast.dart';
 
@@ -55,7 +53,7 @@
 void test(Map<Expression, DeferredExpression> map, String template,
     List<Expression> arguments, String expectedOutput) {
   Expression directExpression =
-      js.expressionTemplateFor(template).instantiate(arguments);
+      js.expressionTemplateFor(template).instantiate(arguments) as Expression;
   _Context directContext = _Context();
   Printer directPrinter =
       Printer(const JavaScriptPrintingOptions(), directContext);
@@ -64,7 +62,7 @@
 
   Expression deferredExpression = js
       .expressionTemplateFor(template)
-      .instantiate(arguments.map((e) => map[e]).toList());
+      .instantiate(arguments.map((e) => map[e]).toList()) as Expression;
   _Context deferredContext = _Context();
   Printer deferredPrinter =
       Printer(const JavaScriptPrintingOptions(), deferredContext);
@@ -72,7 +70,7 @@
   Expect.equals(expectedOutput, deferredContext.text);
 
   for (Expression argument in arguments) {
-    DeferredExpression deferred = map[argument];
+    DeferredExpression deferred = map[argument]!;
     Expect.isTrue(
         directContext.enterPositions.containsKey(argument),
         'Argument ${DebugPrint(argument)} not found in direct enter positions: '
@@ -144,7 +142,7 @@
 
   @override
   void exitNode(
-      Node node, int startPosition, int endPosition, int closingPosition) {
+      Node node, int startPosition, int endPosition, int? closingPosition) {
     exitPositions[node] =
         _Position(startPosition, endPosition, closingPosition);
     Expect.equals(enterPositions[node], startPosition);
@@ -161,7 +159,7 @@
 class _Position {
   final int startPosition;
   final int endPosition;
-  final int closingPosition;
+  final int? closingPosition;
 
   _Position(this.startPosition, this.endPosition, this.closingPosition);
 
diff --git a/pkg/js_ast/test/deferred_statement_test.dart b/pkg/js_ast/test/deferred_statement_test.dart
index 4db27a6..1ec6f13 100644
--- a/pkg/js_ast/test/deferred_statement_test.dart
+++ b/pkg/js_ast/test/deferred_statement_test.dart
@@ -2,16 +2,15 @@
 // 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.10
-
 import 'package:expect/expect.dart';
 import 'package:js_ast/js_ast.dart';
 
 class _DeferredStatement extends DeferredStatement {
+  final Statement? _statement;
   @override
-  final Statement statement;
+  Statement get statement => _statement!;
 
-  _DeferredStatement(this.statement);
+  _DeferredStatement(this._statement);
 }
 
 void main() {
@@ -38,14 +37,21 @@
 
   // Nested Blocks in DeferredStatements are elided.
   Expect.equals(
-      DebugPrint(Block([
-        _DeferredStatement(Block([
-          _DeferredStatement(Block.empty()),
-          Block.empty(),
-          Block([_DeferredStatement(Block.empty()), Block.empty()]),
-          _DeferredStatement(_DeferredStatement(Block.empty()))
-        ]))
-      ])),
+      DebugPrint(
+        Block([
+          _DeferredStatement(
+            Block([
+              _DeferredStatement(Block.empty()),
+              Block.empty(),
+              Block([
+                _DeferredStatement(Block.empty()),
+                Block.empty(),
+              ]),
+              _DeferredStatement(_DeferredStatement(Block.empty())),
+            ]),
+          ),
+        ]),
+      ),
       '{\n}\n');
 
   // DeferredStatement with empty Statement prints semicolon and a newline.
diff --git a/pkg/js_ast/test/printer_callback_test.dart b/pkg/js_ast/test/printer_callback_test.dart
index 433d3ad..45912bb 100644
--- a/pkg/js_ast/test/printer_callback_test.dart
+++ b/pkg/js_ast/test/printer_callback_test.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.10
-
 // Note: This test relies on LF line endings in the source file.
 
 // Test that JS printer callbacks occur when expected.
@@ -178,11 +176,8 @@
 
 void check(TestCase testCase) {
   Map<TestMode, String> map = testCase.data;
-  String code = map[TestMode.INPUT];
-
-  // Input is the same as output.
-  code ??= map[TestMode.NONE];
-
+  // Unspecified input is the same as output.
+  String? code = map[TestMode.INPUT] ?? map[TestMode.NONE]!;
   JavaScriptPrintingOptions options = JavaScriptPrintingOptions();
   Map arguments = {};
   testCase.environment.forEach((String name, String value) {
@@ -221,7 +216,7 @@
 
   @override
   void exitNode(
-      Node node, int startPosition, int endPosition, int delimiterPosition) {
+      Node node, int startPosition, int endPosition, int? delimiterPosition) {
     int value = id(node);
     if (mode == TestMode.DELIMITER && delimiterPosition != null) {
       tagMap.putIfAbsent(delimiterPosition, () => []).add(tag(value));
@@ -239,7 +234,7 @@
       if (offset < position) {
         sb.write(text.substring(offset, position));
       }
-      tagMap[position].forEach((String tag) => sb.write(tag));
+      tagMap[position]!.forEach((String tag) => sb.write(tag));
       offset = position;
     }
     if (offset < text.length) {
diff --git a/pkg/js_ast/test/string_escape_test.dart b/pkg/js_ast/test/string_escape_test.dart
index c483ba9..c61a3ff 100644
--- a/pkg/js_ast/test/string_escape_test.dart
+++ b/pkg/js_ast/test/string_escape_test.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.10
-
 library js_ast.string_escape_test;
 
 import 'package:js_ast/js_ast.dart';
@@ -14,9 +12,11 @@
 const int $RCURLY = $CLOSE_CURLY_BRACKET;
 
 void main() {
-  void check(input, expected, {bool utf8 = false}) {
-    if (input is List) input = String.fromCharCodes(input);
-    String actual = DebugPrint(js.string(input), utf8: utf8);
+  void check(Object input, Object expected, {bool utf8 = false}) {
+    String string = input is String
+        ? input
+        : String.fromCharCodes(List<int>.from(input as Iterable));
+    String actual = DebugPrint(js.string(string), utf8: utf8);
     if (expected is List) {
       expect(actual.codeUnits, expected);
     } else {
diff --git a/pkg/kernel/lib/type_algebra.dart b/pkg/kernel/lib/type_algebra.dart
index 7afdf2d..fe6e356 100644
--- a/pkg/kernel/lib/type_algebra.dart
+++ b/pkg/kernel/lib/type_algebra.dart
@@ -614,7 +614,22 @@
     int before = useCounter;
     DartType typeArgument = node.typeArgument.accept(this);
     if (useCounter == before) return node;
-    return new FutureOrType(typeArgument, node.declaredNullability);
+
+    // The top-level nullability of a FutureOr should remain the same, with the
+    // exception of the case of [Nullability.undetermined].  In that case it
+    // remains undetermined if the nullability of [typeArgument] is
+    // undetermined, and otherwise it should become [Nullability.nonNullable].
+    Nullability nullability;
+    if (node.declaredNullability == Nullability.undetermined) {
+      if (typeArgument.nullability == Nullability.undetermined) {
+        nullability = Nullability.undetermined;
+      } else {
+        nullability = Nullability.nonNullable;
+      }
+    } else {
+      nullability = node.declaredNullability;
+    }
+    return new FutureOrType(typeArgument, nullability);
   }
 
   @override
diff --git a/pkg/test_runner/lib/src/compiler_configuration.dart b/pkg/test_runner/lib/src/compiler_configuration.dart
index b287bcc..d27db05 100644
--- a/pkg/test_runner/lib/src/compiler_configuration.dart
+++ b/pkg/test_runner/lib/src/compiler_configuration.dart
@@ -892,6 +892,8 @@
         exec = "$simBuildDir/gen_snapshot";
       } else if (_isArm64 && _configuration.useQemu) {
         exec = "$buildDir/clang_x64/gen_snapshot";
+      } else if (_isRiscv64 && _configuration.useQemu) {
+        exec = "$buildDir/x64/gen_snapshot";
       } else {
         exec = "$buildDir/gen_snapshot";
       }
diff --git a/pkg/vm/lib/transformations/continuation.dart b/pkg/vm/lib/transformations/continuation.dart
index b5c3949..fa535fc 100644
--- a/pkg/vm/lib/transformations/continuation.dart
+++ b/pkg/vm/lib/transformations/continuation.dart
@@ -1319,10 +1319,21 @@
         functionType: addMethodFunctionType)
       ..fileOffset = stmt.fileOffset;
 
-    statements.add(new IfStatement(
-        addExpression,
-        new ReturnStatement(new NullLiteral()),
-        createContinuationPoint()..fileOffset = stmt.fileOffset));
+    if (stmt.isYieldStar) {
+      statements.add(ExpressionStatement(addExpression));
+      statements.add(createContinuationPoint()..fileOffset = stmt.fileOffset);
+      final wasCancelled = StaticInvocation(
+          helper.unsafeCast,
+          Arguments(<Expression>[VariableGet(expressionRewriter!.asyncResult)],
+              types: <DartType>[helper.coreTypes.boolNonNullableRawType]));
+      statements
+          .add(IfStatement(wasCancelled, ReturnStatement(NullLiteral()), null));
+    } else {
+      statements.add(new IfStatement(
+          addExpression,
+          new ReturnStatement(new NullLiteral()),
+          createContinuationPoint()..fileOffset = stmt.fileOffset));
+    }
     return removalSentinel ?? EmptyStatement();
   }
 
diff --git a/runtime/tests/vm/dart/causal_stacks/flutter_regress_100441_test.dart b/runtime/tests/vm/dart/causal_stacks/flutter_regress_100441_test.dart
new file mode 100644
index 0000000..b5ff8ad
--- /dev/null
+++ b/runtime/tests/vm/dart/causal_stacks/flutter_regress_100441_test.dart
@@ -0,0 +1,77 @@
+// 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.
+//
+// VMOptions=--lazy-async-stacks
+
+import 'dart:async';
+
+import 'package:expect/expect.dart';
+
+import 'utils.dart' show assertStack;
+
+String effectOrder = '';
+StackTrace? stackAfterYield = null;
+
+void emit(String m) => effectOrder += m;
+
+main() async {
+  emit('1');
+  await for (final value in produce()) {
+    emit('5');
+    Expect.equals('|value|', value);
+  }
+  emit('8');
+  Expect.equals('12345678', effectOrder);
+
+  assertStack(const <String>[
+    r'^#0      produceInner .*$',
+    r'^<asynchronous suspension>$',
+    r'^#1      produce .*$',
+    r'^<asynchronous suspension>$',
+    r'^#2      main .*$',
+    r'^<asynchronous suspension>$',
+  ], stackAfterYield!);
+
+  effectOrder = '';
+
+  emit('1');
+  await for (final value in produceYieldStar()) {
+    emit('5');
+    Expect.equals('|value|', value);
+    break;
+  }
+  emit('6');
+  Expect.equals('123456', effectOrder);
+}
+
+Stream<dynamic> produce() async* {
+  emit('2');
+  await for (String response in produceInner()) {
+    emit('4');
+    yield response;
+  }
+  emit('7');
+}
+
+Stream produceInner() async* {
+  emit('3');
+  yield '|value|';
+  emit('6');
+  stackAfterYield = StackTrace.current;
+}
+
+Stream<dynamic> produceYieldStar() async* {
+  emit('2');
+  await for (String response in produceInner()) {
+    emit('4');
+    yield response;
+  }
+  emit('x');
+}
+
+Stream produceInnerYieldStar() async* {
+  emit('3');
+  yield* Stream.fromIterable(['|value|', '|value2|']);
+  emit('x');
+}
diff --git a/runtime/tests/vm/dart_2/causal_stacks/flutter_regress_100441_test.dart b/runtime/tests/vm/dart_2/causal_stacks/flutter_regress_100441_test.dart
new file mode 100644
index 0000000..2d4d2ef
--- /dev/null
+++ b/runtime/tests/vm/dart_2/causal_stacks/flutter_regress_100441_test.dart
@@ -0,0 +1,78 @@
+// 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.
+//
+// VMOptions=--lazy-async-stacks
+
+// @dart = 2.9
+import 'dart:async';
+
+import 'package:expect/expect.dart';
+
+import 'utils.dart' show assertStack;
+
+String effectOrder = '';
+StackTrace stackAfterYield = null;
+
+void emit(String m) => effectOrder += m;
+
+main() async {
+  emit('1');
+  await for (final value in produce()) {
+    emit('5');
+    Expect.equals('|value|', value);
+  }
+  emit('8');
+  Expect.equals('12345678', effectOrder);
+
+  assertStack(const <String>[
+    r'^#0      produceInner .*$',
+    r'^<asynchronous suspension>$',
+    r'^#1      produce .*$',
+    r'^<asynchronous suspension>$',
+    r'^#2      main .*$',
+    r'^<asynchronous suspension>$',
+  ], stackAfterYield);
+
+  effectOrder = '';
+
+  emit('1');
+  await for (final value in produceYieldStar()) {
+    emit('5');
+    Expect.equals('|value|', value);
+    break;
+  }
+  emit('6');
+  Expect.equals('123456', effectOrder);
+}
+
+Stream<dynamic> produce() async* {
+  emit('2');
+  await for (String response in produceInner()) {
+    emit('4');
+    yield response;
+  }
+  emit('7');
+}
+
+Stream produceInner() async* {
+  emit('3');
+  yield '|value|';
+  emit('6');
+  stackAfterYield = StackTrace.current;
+}
+
+Stream<dynamic> produceYieldStar() async* {
+  emit('2');
+  await for (String response in produceInner()) {
+    emit('4');
+    yield response;
+  }
+  emit('x');
+}
+
+Stream produceInnerYieldStar() async* {
+  emit('3');
+  yield* Stream.fromIterable(['|value|', '|value2|']);
+  emit('x');
+}
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_riscv.cc b/runtime/vm/compiler/backend/flow_graph_compiler_riscv.cc
index b89c53f..30d5374 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_riscv.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_riscv.cc
@@ -1031,7 +1031,24 @@
 void FlowGraphCompiler::LoadBSSEntry(BSS::Relocation relocation,
                                      Register dst,
                                      Register tmp) {
-  UNIMPLEMENTED();
+  compiler::Label skip_reloc;
+  __ j(&skip_reloc, compiler::Assembler::kNearJump);
+  InsertBSSRelocation(relocation);
+  __ Bind(&skip_reloc);
+
+  __ auipc(tmp, 0);
+  __ addi(tmp, tmp, -compiler::target::kWordSize);
+
+  // tmp holds the address of the relocation.
+  __ lx(dst, compiler::Address(tmp));
+
+  // dst holds the relocation itself: tmp - bss_start.
+  // tmp = tmp + (bss_start - tmp) = bss_start
+  __ add(tmp, tmp, dst);
+
+  // tmp holds the start of the BSS section.
+  // Load the "get-thread" routine: *bss_start.
+  __ lx(dst, compiler::Address(tmp));
 }
 
 #undef __
diff --git a/runtime/vm/heap/safepoint_test.cc b/runtime/vm/heap/safepoint_test.cc
index 15eedc5..0693ec1 100644
--- a/runtime/vm/heap/safepoint_test.cc
+++ b/runtime/vm/heap/safepoint_test.cc
@@ -316,27 +316,38 @@
 
     uword last_sync = OS::GetCurrentTimeMillis();
     while (!data()->IsIn(kPleaseExit)) {
+      OS::SleepMicros(100);  // Make test avoid consuming 100% CPU x kTaskCount.
       switch (data()->level) {
         case SafepointLevel::kGC: {
           // This thread should join only GC safepoint operations.
           RuntimeCallDeoptScope no_deopt(
               Thread::Current(), RuntimeCallDeoptAbility::kCannotLazyDeopt);
-          SafepointIfRequested(thread_, data()->gc_only_checkins);
+          if (SafepointIfRequested(thread_, data()->gc_only_checkins)) {
+            last_sync = OS::GetCurrentTimeMillis();
+          }
           break;
         }
         case SafepointLevel::kGCAndDeopt: {
           // This thread should join any safepoint operations.
-          SafepointIfRequested(thread_, data()->deopt_checkin);
+          if (SafepointIfRequested(thread_, data()->deopt_checkin)) {
+            last_sync = OS::GetCurrentTimeMillis();
+          }
           break;
         }
         case SafepointLevel::kNumLevels:
           UNREACHABLE();
       }
 
-      // If we are asked to join a deopt safepoint operation we will comply with
-      // that but only every second.
+      // If the main thread asks us to join a deopt safepoint but we are
+      // instructed to only really collaborate with GC safepoints we won't
+      // participate in the above cases (and therefore not register our
+      // check-in by increasing the checkin counts).
+      //
+      // After being quite sure to not have joined deopt safepoint if we only
+      // support GC safepoints, we will eventually comply here to make main
+      // thread continue.
       const auto now = OS::GetCurrentTimeMillis();
-      if ((now - last_sync) > 200) {
+      if ((now - last_sync) > 1000) {
         thread_->EnterSafepoint();
         thread_->ExitSafepoint();
         last_sync = now;
@@ -344,13 +355,14 @@
     }
   }
 
-  void SafepointIfRequested(Thread* thread, std::atomic<intptr_t>* checkins) {
-    OS::SleepMicros(10);
+  bool SafepointIfRequested(Thread* thread, std::atomic<intptr_t>* checkins) {
     if (thread->IsSafepointRequested()) {
       // Collaborates by checking into the safepoint.
       thread->BlockForSafepoint();
       (*checkins)++;
+      return true;
     }
+    return false;
   }
 };
 
@@ -392,11 +404,11 @@
     }
     {
       { GcSafepointOperationScope safepoint_operation(thread); }
-      OS::SleepMicros(500);
+      OS::SleepMicros(1000);  // Wait for threads to exit safepoint
       { DeoptSafepointOperationScope safepoint_operation(thread); }
-      OS::SleepMicros(500);
+      OS::SleepMicros(1000);  // Wait for threads to exit safepoint
       { GcSafepointOperationScope safepoint_operation(thread); }
-      OS::SleepMicros(500);
+      OS::SleepMicros(1000);  // Wait for threads to exit safepoint
       { DeoptSafepointOperationScope safepoint_operation(thread); }
     }
     for (intptr_t i = 0; i < kTaskCount; i++) {
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 2a287b9..a8eb8b1 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -15757,34 +15757,70 @@
 }
 
 intptr_t ICData::NumberOfChecks() const {
-  const intptr_t length = Length();
-  for (intptr_t i = 0; i < length; i++) {
-    if (IsSentinelAt(i)) {
-      return i;
-    }
-  }
-  UNREACHABLE();
-  return -1;
+  DEBUG_ONLY(AssertInvariantsAreSatisfied());
+  return Length() - 1;
 }
 
 bool ICData::NumberOfChecksIs(intptr_t n) const {
-  const intptr_t length = Length();
-  for (intptr_t i = 0; i < length; i++) {
-    if (i == n) {
-      return IsSentinelAt(i);
-    } else {
-      if (IsSentinelAt(i)) return false;
+  DEBUG_ONLY(AssertInvariantsAreSatisfied());
+  return NumberOfChecks() == n;
+}
+
+#if defined(DEBUG)
+void ICData::AssertInvariantsAreSatisfied() const {
+  // See layout and invariant of [ICData] in class comment in object.h.
+  //
+  // This method can be called without holding any locks, it will grab a
+  // snapshot of `entries()` and do it's verification logic on that.
+  auto zone = Thread::Current()->zone();
+  const auto& array = Array::Handle(zone, entries());
+
+  const intptr_t entry_length = TestEntryLength();
+  const intptr_t num_checks = array.Length() / entry_length - 1;
+  const intptr_t num_args = NumArgsTested();
+
+  /// Backing store must be multiple of entry length.
+  ASSERT((array.Length() % entry_length) == 0);
+
+  /// Entries must be valid.
+  for (intptr_t i = 0; i < num_checks; ++i) {
+    // Should be valid entry.
+    const intptr_t start = entry_length * i;
+    for (intptr_t i = 0; i < num_args; ++i) {
+      ASSERT(!array.At(start + i)->IsHeapObject());
+      ASSERT(array.At(start + i) != smi_illegal_cid().ptr());
+    }
+    ASSERT(array.At(start + TargetIndexFor(num_args))->IsHeapObject());
+    if (is_tracking_exactness()) {
+      ASSERT(!array.At(start + ExactnessIndexFor(num_args))->IsHeapObject());
     }
   }
-  return n == length;
+
+  /// Sentinel at end must be valid.
+  const intptr_t sentinel_start = num_checks * entry_length;
+  for (intptr_t i = 0; i < entry_length - 1; ++i) {
+    ASSERT(array.At(sentinel_start + i) == smi_illegal_cid().ptr());
+  }
+  if (num_checks == 0) {
+    ASSERT(array.At(sentinel_start + entry_length - 1) ==
+           smi_illegal_cid().ptr());
+    ASSERT(ICData::CachedEmptyICDataArray(num_args, is_tracking_exactness()) ==
+           entries());
+  } else {
+    ASSERT(array.At(sentinel_start + entry_length - 1) == ptr());
+  }
+
+  // Invariants for ICData of static calls.
+  if (num_args == 0) {
+    ASSERT(Length() == 2);
+    ASSERT(TestEntryLength() == 2);
+  }
 }
+#endif  // defined(DEBUG)
 
 // Discounts any checks with usage of zero.
 intptr_t ICData::NumberOfUsedChecks() const {
-  intptr_t n = NumberOfChecks();
-  if (n == 0) {
-    return 0;
-  }
+  const intptr_t n = NumberOfChecks();
   intptr_t count = 0;
   for (intptr_t i = 0; i < n; i++) {
     if (GetCountAt(i) > 0) {
@@ -15799,10 +15835,11 @@
                            const Object& back_ref) {
   ASSERT(!data.IsNull());
   RELEASE_ASSERT(smi_illegal_cid().Value() == kIllegalCid);
-  for (intptr_t i = 2; i <= test_entry_length; i++) {
-    data.SetAt(data.Length() - i, smi_illegal_cid());
+  const intptr_t entry_start = data.Length() - test_entry_length;
+  for (intptr_t i = 0; i < test_entry_length - 1; i++) {
+    data.SetAt(entry_start + i, smi_illegal_cid());
   }
-  data.SetAt(data.Length() - 1, back_ref);
+  data.SetAt(entry_start + test_entry_length - 1, back_ref);
 }
 
 #if defined(DEBUG)
@@ -15832,27 +15869,35 @@
   return -1;
 }
 
-void ICData::WriteSentinelAt(intptr_t index,
-                             const CallSiteResetter& proof_of_reload) const {
+void ICData::TruncateTo(intptr_t num_checks,
+                        const CallSiteResetter& proof_of_reload) const {
   USE(proof_of_reload);  // This method can only be called during reload.
 
-  Thread* thread = Thread::Current();
-  const intptr_t len = Length();
-  ASSERT(index >= 0);
-  ASSERT(index < len);
+  DEBUG_ONLY(AssertInvariantsAreSatisfied());
+  ASSERT(num_checks <= NumberOfChecks());
+
+  // Nothing to do.
+  if (NumberOfChecks() == num_checks) return;
+
+  auto thread = Thread::Current();
   REUSABLE_ARRAY_HANDLESCOPE(thread);
-  Array& data = thread->ArrayHandle();
-  data = entries();
-  const intptr_t start = index * TestEntryLength();
-  const intptr_t end = start + TestEntryLength();
-  for (intptr_t i = start; i < end; i++) {
-    data.SetAt(i, smi_illegal_cid());
+  auto& array = thread->ArrayHandle();
+
+  // If we make the ICData empty, use the pre-allocated shared backing stores.
+  const intptr_t num_args = NumArgsTested();
+  if (num_checks == 0) {
+    array = ICData::CachedEmptyICDataArray(num_args, is_tracking_exactness());
+    set_entries(array);
+    return;
   }
-  // The last slot in the last entry of the [ICData::entries_] is a back-ref to
-  // the [ICData] itself.
-  if (index == (len - 1)) {
-    data.SetAt(end - 1, *this);
-  }
+
+  // Otherwise truncate array and initialize sentinel.
+  // Use kSmiCid for all slots in the entry except the last, which is a backref
+  // to ICData.
+  const intptr_t entry_length = TestEntryLength();
+  array = entries();
+  array.Truncate((num_checks + 1) * entry_length);
+  WriteSentinel(array, entry_length, *this);
 }
 
 void ICData::ClearCountAt(intptr_t index,
@@ -15869,50 +15914,27 @@
     const CallSiteResetter& proof_of_reload) const {
   USE(proof_of_reload);  // This method can only be called during reload.
 
-  if (IsImmutable()) {
-    return;
-  }
-  const intptr_t len = Length();
-  if (len == 0) {
-    return;
-  }
-  Thread* thread = Thread::Current();
-
   // The final entry is always the sentinel.
-  ASSERT(IsSentinelAt(len - 1));
-  const intptr_t num_args_tested = NumArgsTested();
-  if (num_args_tested == 0) {
-    // No type feedback is being collected.
-    REUSABLE_ARRAY_HANDLESCOPE(thread);
-    Array& data = thread->ArrayHandle();
-    data = entries();
-    // Static calls with no argument checks hold only one target and the
-    // sentinel value.
-    ASSERT(len == 2);
-    // Static calls with no argument checks only need two words.
-    ASSERT(TestEntryLength() == 2);
-    // Set the target.
-    data.SetAt(TargetIndexFor(num_args_tested), func);
-    // Set count to 0 as this is called during compilation, before the
-    // call has been executed.
-    data.SetAt(CountIndexFor(num_args_tested), Object::smi_zero());
-  } else {
-    // Type feedback on arguments is being collected.
-    // Fill all but the first entry with the sentinel.
-    for (intptr_t i = len - 1; i > 0; i--) {
-      WriteSentinelAt(i, proof_of_reload);
-    }
-    REUSABLE_ARRAY_HANDLESCOPE(thread);
-    Array& data = thread->ArrayHandle();
-    data = entries();
-    // Rewrite the dummy entry.
-    const Smi& object_cid = Smi::Handle(Smi::New(kObjectCid));
-    for (intptr_t i = 0; i < NumArgsTested(); i++) {
-      data.SetAt(i, object_cid);
-    }
-    data.SetAt(TargetIndexFor(num_args_tested), func);
-    data.SetAt(CountIndexFor(num_args_tested), Object::smi_zero());
+  DEBUG_ONLY(AssertInvariantsAreSatisfied());
+
+  if (IsImmutable()) return;
+  if (NumberOfChecks() == 0) return;
+
+  // Leave one entry.
+  TruncateTo(/*num_checks=*/1, proof_of_reload);
+
+  // Reinitialize the one and only entry.
+  const intptr_t num_args = NumArgsTested();
+  Thread* thread = Thread::Current();
+  REUSABLE_ARRAY_HANDLESCOPE(thread);
+  Array& data = thread->ArrayHandle();
+  data = entries();
+  const Smi& object_cid = Smi::Handle(Smi::New(kObjectCid));
+  for (intptr_t i = 0; i < num_args; i++) {
+    data.SetAt(i, object_cid);
   }
+  data.SetAt(TargetIndexFor(num_args), func);
+  data.SetAt(CountIndexFor(num_args), Object::smi_zero());
 }
 
 bool ICData::ValidateInterceptor(const Function& target) const {
@@ -16004,13 +16026,10 @@
 }
 
 ArrayPtr ICData::Grow(intptr_t* index) const {
+  DEBUG_ONLY(AssertInvariantsAreSatisfied());
+
+  *index = NumberOfChecks();
   Array& data = Array::Handle(entries());
-  // Last entry in array should be a sentinel and will be the new entry
-  // that can be updated after growing.
-  *index = Length() - 1;
-  ASSERT(*index >= 0);
-  ASSERT(IsSentinelAt(*index));
-  // Grow the array and write the new final sentinel into place.
   const intptr_t new_len = data.Length() + TestEntryLength();
   data = Array::Grow(data, new_len, Heap::kOld);
   WriteSentinel(data, TestEntryLength(), *this);
@@ -16133,29 +16152,11 @@
   (*target) ^= data.At(data_pos + TargetIndexFor(NumArgsTested()));
 }
 
-bool ICData::IsSentinelAt(intptr_t index) const {
-  ASSERT(index < Length());
-  Thread* thread = Thread::Current();
-  REUSABLE_ARRAY_HANDLESCOPE(thread);
-  Array& data = thread->ArrayHandle();
-  data = entries();
-  const intptr_t entry_length = TestEntryLength();
-  intptr_t data_pos = index * TestEntryLength();
-  const intptr_t kBackRefLen = (index == (Length() - 1)) ? 1 : 0;
-  for (intptr_t i = 0; i < entry_length - kBackRefLen; i++) {
-    if (data.At(data_pos++) != smi_illegal_cid().ptr()) {
-      return false;
-    }
-  }
-  // The entry at |index| was filled with the value kIllegalCid.
-  return true;
-}
-
 void ICData::GetClassIdsAt(intptr_t index,
                            GrowableArray<intptr_t>* class_ids) const {
   ASSERT(index < Length());
   ASSERT(class_ids != NULL);
-  ASSERT(!IsSentinelAt(index));
+  ASSERT(IsValidEntryIndex(index));
   class_ids->Clear();
   Thread* thread = Thread::Current();
   REUSABLE_ARRAY_HANDLESCOPE(thread);
@@ -16200,7 +16201,7 @@
 
 intptr_t ICData::GetReceiverClassIdAt(intptr_t index) const {
   ASSERT(index < Length());
-  ASSERT(!IsSentinelAt(index));
+  ASSERT(IsValidEntryIndex(index));
   const intptr_t data_pos = index * TestEntryLength();
   NoSafepointScope no_safepoint;
   ArrayPtr raw_data = entries();
@@ -16469,6 +16470,13 @@
   }
 }
 
+bool ICData::IsCachedEmptyEntry(const Array& array) {
+  for (int i = 0; i < kCachedICDataArrayCount; ++i) {
+    if (cached_icdata_arrays_[i] == array.ptr()) return true;
+  }
+  return false;
+}
+
 // Does not initialize ICData array.
 ICDataPtr ICData::NewDescriptor(Zone* zone,
                                 const Function& owner,
@@ -16643,14 +16651,21 @@
                 AbstractType::Handle(zone, from.receivers_static_type())));
   // Clone entry array.
   const Array& from_array = Array::Handle(zone, from.entries());
-  const intptr_t len = from_array.Length();
-  const Array& cloned_array = Array::Handle(zone, Array::New(len, Heap::kOld));
-  Object& obj = Object::Handle(zone);
-  for (intptr_t i = 0; i < len; i++) {
-    obj = from_array.At(i);
-    cloned_array.SetAt(i, obj);
+  if (ICData::IsCachedEmptyEntry(from_array)) {
+    result.set_entries(from_array);
+  } else {
+    const intptr_t len = from_array.Length();
+    const Array& cloned_array =
+        Array::Handle(zone, Array::New(len, Heap::kOld));
+    Object& obj = Object::Handle(zone);
+    for (intptr_t i = 0; i < len; i++) {
+      obj = from_array.At(i);
+      cloned_array.SetAt(i, obj);
+    }
+    // Update backref in our clone.
+    cloned_array.SetAt(cloned_array.Length() - 1, result);
+    result.set_entries(cloned_array);
   }
-  result.set_entries(cloned_array);
   // Copy deoptimization reasons.
   result.SetDeoptReasons(from.DeoptReasons());
   result.set_is_megamorphic(is_megamorphic);
@@ -16658,6 +16673,8 @@
   RELEASE_ASSERT(!is_megamorphic ||
                  result.NumberOfChecks() >= FLAG_max_polymorphic_checks);
 
+  DEBUG_ONLY(result.AssertInvariantsAreSatisfied());
+
   return result.ptr();
 }
 #endif
@@ -16665,20 +16682,12 @@
 ICDataPtr ICData::ICDataOfEntriesArray(const Array& array) {
   const auto& back_ref = Object::Handle(array.At(array.Length() - 1));
   if (back_ref.ptr() == smi_illegal_cid().ptr()) {
-    // The ICData must be empty.
-#if defined(DEBUG)
-    const int kMaxTestEntryLen = TestEntryLengthFor(2, true);
-    ASSERT(array.Length() <= kMaxTestEntryLen);
-    for (intptr_t i = 0; i < array.Length(); ++i) {
-      ASSERT(array.At(i) == Object::sentinel().ptr());
-    }
-#endif
+    ASSERT(IsCachedEmptyEntry(array));
     return ICData::null();
   }
+
   const auto& ic_data = ICData::Cast(back_ref);
-#if defined(DEBUG)
-  ic_data.IsSentinelAt(ic_data.Length() - 1);
-#endif
+  DEBUG_ONLY(ic_data.AssertInvariantsAreSatisfied());
   return ic_data.ptr();
 }
 
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index cb2be7d..4d16d9c 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -2131,6 +2131,30 @@
 // or the original ICData object. In case of background compilation we
 // copy the ICData in a child object, thus freezing it during background
 // compilation. Code may contain only original ICData objects.
+//
+// ICData's backing store is an array that logically contains several valid
+// entries followed by a sentinal entry.
+//
+//   [<entry-0>, <...>, <entry-N>, <sentinel>]
+//
+// Each entry has the following form:
+//
+//   [arg0?, arg1?, argN?, count, target-function/code, exactness?]
+//
+// The <entry-X> need to contain valid type feedback.
+// The <sentinel> entry and must have kIllegalCid value for all
+// members of the entry except for the last one (`exactness` if
+// present, otherwise `target-function/code`) - which we use as a backref:
+//
+//   * For empty ICData we use a cached/shared backing store. So there is no
+//     unique backref, we use kIllegalCid instead.
+//   * For non-empty ICData the backref in the backing store array will point to
+//     the ICData object.
+//
+// Updating the ICData happens under a lock to avoid phantom-reads. The backing
+// is treated as an immutable Copy-on-Write data structure: Adding to the ICData
+// makes a copy with length+1 which will be store-release'd so any reader can
+// see it (and doesn't need to hold a lock).
 class ICData : public CallSiteData {
  public:
   FunctionPtr Owner() const;
@@ -2239,16 +2263,18 @@
   // the final one.
   intptr_t Length() const;
 
-  // Takes O(result) time!
   intptr_t NumberOfChecks() const;
 
   // Discounts any checks with usage of zero.
   // Takes O(result)) time!
   intptr_t NumberOfUsedChecks() const;
 
-  // Takes O(n) time!
   bool NumberOfChecksIs(intptr_t n) const;
 
+  bool IsValidEntryIndex(intptr_t index) const {
+    return 0 <= index && index < NumberOfChecks();
+  }
+
   static intptr_t InstanceSize() {
     return RoundedAllocationSize(sizeof(UntaggedICData));
   }
@@ -2275,10 +2301,14 @@
   }
 #endif
 
-  // Replaces entry |index| with the sentinel.
   // NOTE: Can only be called during reload.
-  void WriteSentinelAt(intptr_t index,
-                       const CallSiteResetter& proof_of_reload) const;
+  void Clear(const CallSiteResetter& proof_of_reload) const {
+    TruncateTo(0, proof_of_reload);
+  }
+
+  // NOTE: Can only be called during reload.
+  void TruncateTo(intptr_t num_checks,
+                  const CallSiteResetter& proof_of_reload) const;
 
   // Clears the count for entry |index|.
   // NOTE: Can only be called during reload.
@@ -2483,9 +2513,9 @@
   }
 
   // Does entry |index| contain the sentinel value?
-  bool IsSentinelAt(intptr_t index) const;
   void SetNumArgsTested(intptr_t value) const;
   void SetReceiversStaticType(const AbstractType& type) const;
+  DEBUG_ONLY(void AssertInvariantsAreSatisfied() const;)
 
   static void SetTargetAtPos(const Array& data,
                              intptr_t data_pos,
@@ -2567,6 +2597,7 @@
                                                bool tracking_exactness);
   static ArrayPtr CachedEmptyICDataArray(intptr_t num_args_tested,
                                          bool tracking_exactness);
+  static bool IsCachedEmptyEntry(const Array& array);
   static ICDataPtr NewDescriptor(Zone* zone,
                                  const Function& owner,
                                  const String& target_name,
diff --git a/runtime/vm/object_reload.cc b/runtime/vm/object_reload.cc
index 87874b8..f591661 100644
--- a/runtime/vm/object_reload.cc
+++ b/runtime/vm/object_reload.cc
@@ -858,7 +858,6 @@
   ICData::RebindRule rule = ic.rebind_rule();
   if (rule == ICData::kInstance) {
     const intptr_t num_args = ic.NumArgsTested();
-    const bool tracking_exactness = ic.is_tracking_exactness();
     const intptr_t len = ic.Length();
     // We need at least one non-sentinel entry to require a check
     // for the smi fast path case.
@@ -878,15 +877,12 @@
         // The smi fast path case, preserve the initial entry but reset the
         // count.
         ic.ClearCountAt(0, *this);
-        ic.WriteSentinelAt(1, *this);
-        entries_ = ic.entries();
-        entries_.Truncate(2 * ic.TestEntryLength());
+        ic.TruncateTo(/*num_checks=*/1, *this);
         return;
       }
       // Fall back to the normal behavior with cached empty ICData arrays.
     }
-    entries_ = ICData::CachedEmptyICDataArray(num_args, tracking_exactness);
-    ic.set_entries(entries_);
+    ic.Clear(*this);
     ic.set_is_megamorphic(false);
     return;
   } else if (rule == ICData::kNoRebind || rule == ICData::kNSMDispatch) {
diff --git a/runtime/vm/os_macos.cc b/runtime/vm/os_macos.cc
index eed635d..05f9ee4 100644
--- a/runtime/vm/os_macos.cc
+++ b/runtime/vm/os_macos.cc
@@ -106,6 +106,12 @@
 }
 
 int64_t OS::GetCurrentThreadCPUMicros() {
+  if (__builtin_available(macOS 10.12, iOS 10.0, *)) {
+    // This is more efficient when available.
+    return clock_gettime_nsec_np(CLOCK_THREAD_CPUTIME_ID) /
+           kNanosecondsPerMicrosecond;
+  }
+
   mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
   thread_basic_info_data_t info_data;
   thread_basic_info_t info = &info_data;
diff --git a/runtime/vm/stack_trace.cc b/runtime/vm/stack_trace.cc
index 4c7cbcf..eee21f8 100644
--- a/runtime/vm/stack_trace.cc
+++ b/runtime/vm/stack_trace.cc
@@ -71,7 +71,7 @@
       future_listener_class(Class::Handle(zone)),
       async_start_stream_controller_class(Class::Handle(zone)),
       stream_controller_class(Class::Handle(zone)),
-      async_stream_controller_class(Class::Handle(zone)),
+      sync_stream_controller_class(Class::Handle(zone)),
       controller_subscription_class(Class::Handle(zone)),
       buffering_stream_subscription_class(Class::Handle(zone)),
       stream_iterator_class(Class::Handle(zone)),
@@ -100,9 +100,9 @@
   stream_controller_class =
       async_lib.LookupClassAllowPrivate(Symbols::_StreamController());
   ASSERT(!stream_controller_class.IsNull());
-  async_stream_controller_class =
-      async_lib.LookupClassAllowPrivate(Symbols::_AsyncStreamController());
-  ASSERT(!async_stream_controller_class.IsNull());
+  sync_stream_controller_class =
+      async_lib.LookupClassAllowPrivate(Symbols::_SyncStreamController());
+  ASSERT(!sync_stream_controller_class.IsNull());
   controller_subscription_class =
       async_lib.LookupClassAllowPrivate(Symbols::_ControllerSubscription());
   ASSERT(!controller_subscription_class.IsNull());
@@ -171,7 +171,7 @@
   const Instance& controller = Instance::Cast(context_entry_);
   controller_ = controller.GetField(controller_controller_field);
   ASSERT(!controller_.IsNull());
-  ASSERT(controller_.GetClassId() == async_stream_controller_class.id());
+  ASSERT(controller_.GetClassId() == sync_stream_controller_class.id());
 
   // Get the _StreamController._state field.
   state_ = Instance::Cast(controller_).GetField(state_field);
@@ -209,7 +209,7 @@
     // contains the iterator's value. In that case we cannot unwind anymore.
     //
     // Notice: With correct async* semantics this may never be true: The async*
-    // generator should only be invoked to produce a vaue if there's an
+    // generator should only be invoked to produce a value if there's an
     // in-progress `await streamIterator.moveNext()` call. Once such call has
     // finished the async* generator should be paused/yielded until the next
     // such call - and being paused/yielded means it should not appear in stack
diff --git a/runtime/vm/stack_trace.h b/runtime/vm/stack_trace.h
index 0683cf7..58a4cba 100644
--- a/runtime/vm/stack_trace.h
+++ b/runtime/vm/stack_trace.h
@@ -78,7 +78,7 @@
   Class& future_listener_class;
   Class& async_start_stream_controller_class;
   Class& stream_controller_class;
-  Class& async_stream_controller_class;
+  Class& sync_stream_controller_class;
   Class& controller_subscription_class;
   Class& buffering_stream_subscription_class;
   Class& stream_iterator_class;
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 52ec048..f96d8d3 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -278,7 +278,6 @@
   V(Values, "values")                                                          \
   V(WeakSerializationReference, "WeakSerializationReference")                  \
   V(_AsyncStarStreamController, "_AsyncStarStreamController")                  \
-  V(_AsyncStreamController, "_AsyncStreamController")                          \
   V(_BufferingStreamSubscription, "_BufferingStreamSubscription")              \
   V(_ByteBuffer, "_ByteBuffer")                                                \
   V(_ByteBufferDot_New, "_ByteBuffer._New")                                    \
@@ -381,6 +380,7 @@
   V(_StreamIterator, "_StreamIterator")                                        \
   V(_String, "String")                                                         \
   V(_SyncIterator, "_SyncIterator")                                            \
+  V(_SyncStreamController, "_SyncStreamController")                            \
   V(_TransferableTypedDataImpl, "_TransferableTypedDataImpl")                  \
   V(_Type, "_Type")                                                            \
   V(_FunctionType, "_FunctionType")                                            \
diff --git a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart
index 9452ed3..3c7ccc7 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart
@@ -171,7 +171,7 @@
 /// This function should be used to ensure that a function is a native JS
 /// function before it is passed to native JS code.
 @NoReifyGeneric()
-F assertInterop<F extends Function>(F f) {
+F assertInterop<F extends Function?>(F f) {
   assert(
       _isJsObject(f) ||
           !JS<bool>('bool', '# instanceof #.Function', f, global_),
diff --git a/sdk/lib/_internal/js_runtime/lib/js_helper.dart b/sdk/lib/_internal/js_runtime/lib/js_helper.dart
index 7c43ba8..cd6018f 100644
--- a/sdk/lib/_internal/js_runtime/lib/js_helper.dart
+++ b/sdk/lib/_internal/js_runtime/lib/js_helper.dart
@@ -2451,10 +2451,6 @@
   return JS('var', r'#[#]', jsObject, property);
 }
 
-/// Called at the end of unaborted switch cases to get the singleton
-/// FallThroughError exception that will be thrown.
-getFallThroughError() => new FallThroughErrorImplementation();
-
 /// A metadata annotation describing the types instantiated by a native element.
 ///
 /// The annotation is valid on a native method and a field of a native class.
@@ -2587,14 +2583,6 @@
   if (assertTest(condition)) throw AssertionError();
 }
 
-/// Called by generated code when a method that must be statically
-/// resolved cannot be found.
-void throwNoSuchMethod(obj, name, arguments, expectedArgumentNames) {
-  Symbol memberName = new _symbol_dev.Symbol.unvalidated(name);
-  throw new NoSuchMethodError(
-      obj, memberName, arguments, new Map<Symbol, dynamic>());
-}
-
 /// Called by generated code when a static field's initializer references the
 /// field that is currently being initialized.
 void throwCyclicInit(String staticName) {
diff --git a/sdk/lib/_internal/vm/lib/async_patch.dart b/sdk/lib/_internal/vm/lib/async_patch.dart
index 2e322f1..9fddad2 100644
--- a/sdk/lib/_internal/vm/lib/async_patch.dart
+++ b/sdk/lib/_internal/vm/lib/async_patch.dart
@@ -117,6 +117,13 @@
   bool isSuspendedAtYield = false;
   _Future? cancellationFuture = null;
 
+  /// Argument passed to the generator when it is resumed after an addStream.
+  ///
+  /// `true` if the generator should exit after `yield*` resumes.
+  /// `false` if the generator should continue after `yield*` resumes.
+  /// `null` otherwies.
+  bool? continuationArgument = null;
+
   Stream<T> get stream {
     final Stream<T> local = controller.stream;
     if (local is _StreamImpl<T>) {
@@ -128,7 +135,8 @@
   void runBody() {
     isScheduled = false;
     isSuspendedAtYield = false;
-    asyncStarBody(null, null);
+    asyncStarBody(continuationArgument, null);
+    continuationArgument = null;
   }
 
   void scheduleGenerator() {
@@ -152,11 +160,11 @@
   bool add(T event) {
     if (!onListenReceived) _fatal("yield before stream is listened to");
     if (isSuspendedAtYield) _fatal("unexpected yield");
-    // If stream is cancelled, tell caller to exit the async generator.
+    controller.add(event);
     if (!controller.hasListener) {
       return true;
     }
-    controller.add(event);
+
     scheduleGenerator();
     isSuspendedAtYield = true;
     return false;
@@ -165,22 +173,41 @@
   // Adds the elements of stream into this controller's stream.
   // The generator will be scheduled again when all of the
   // elements of the added stream have been consumed.
-  // Returns true if the caller should terminate
-  // execution of the generator.
-  bool addStream(Stream<T> stream) {
+  void addStream(Stream<T> stream) {
     if (!onListenReceived) _fatal("yield before stream is listened to");
-    // If stream is cancelled, tell caller to exit the async generator.
-    if (!controller.hasListener) return true;
+
+    if (exitAfterYieldStarIfCancelled()) return;
+
     isAdding = true;
-    var whenDoneAdding = controller.addStream(stream, cancelOnError: false);
+    final whenDoneAdding = controller.addStream(stream, cancelOnError: false);
     whenDoneAdding.then((_) {
       isAdding = false;
-      scheduleGenerator();
-      if (!isScheduled) isSuspendedAtYield = true;
+      if (exitAfterYieldStarIfCancelled()) return;
+      resumeNormallyAfterYieldStar();
     });
+  }
+
+  /// Schedules the generator to exit after `yield*` if stream was cancelled.
+  ///
+  /// Returns `true` if generator is told to exit and `false` otherwise.
+  bool exitAfterYieldStarIfCancelled() {
+    // If consumer cancelled subscription we should tell async* generator to
+    // finish (i.e. run finally clauses and return).
+    if (!controller.hasListener) {
+      continuationArgument = true;
+      scheduleGenerator();
+      return true;
+    }
     return false;
   }
 
+  /// Schedules the generator to resume normally after `yield*`.
+  void resumeNormallyAfterYieldStar() {
+    continuationArgument = false;
+    scheduleGenerator();
+    if (!isScheduled) isSuspendedAtYield = true;
+  }
+
   void addError(Object error, StackTrace stackTrace) {
     // TODO(40614): Remove once non-nullability is sound.
     ArgumentError.checkNotNull(error, "error");
@@ -211,7 +238,7 @@
   }
 
   _AsyncStarStreamController(this.asyncStarBody)
-      : controller = new StreamController() {
+      : controller = new StreamController(sync: true) {
     controller.onListen = this.onListen;
     controller.onResume = this.onResume;
     controller.onCancel = this.onCancel;
diff --git a/sdk/lib/vmservice/asset.dart b/sdk/lib/vmservice/asset.dart
index 7e086ff..0b6a249 100644
--- a/sdk/lib/vmservice/asset.dart
+++ b/sdk/lib/vmservice/asset.dart
@@ -55,7 +55,7 @@
 }
 
 @pragma("vm:external-name", "VMService_DecodeAssets")
-external List<Object> _decodeAssets(Uint8List data);
+external List<dynamic> _decodeAssets(Uint8List data);
 
 Map<String, Asset>? _assets;
 Map<String, Asset>? get assets {
diff --git a/tests/co19/co19-dart2js.status b/tests/co19/co19-dart2js.status
index 75ebe8c..ea38443 100644
--- a/tests/co19/co19-dart2js.status
+++ b/tests/co19/co19-dart2js.status
@@ -42,6 +42,7 @@
 LanguageFeatures/Abstract-external-fields/static_analysis_external_A05_t03: SkipByDesign # Non-JS-interop external members are not supported
 LanguageFeatures/Abstract-external-fields/syntax_A01_t03: SkipByDesign # Non-JS-interop external members are not supported
 LanguageFeatures/Abstract-external-fields/syntax_A02_t03: SkipByDesign # Non-JS-interop external members are not supported
+LanguageFeatures/Enhanced-Enum/grammar_A07_t01: SkipByDesign # Non-JS-interop external members are not supported
 LanguageFeatures/FinalizationRegistry/ffi/*: SkipByDesign # dart:ffi is not supported
 LibTest/core/DateTime/DateTime.fromMicrosecondsSinceEpoch_A01_t01: SkipByDesign # microseconds are not supported in JavaScript
 LibTest/core/DateTime/microsecond_A01_t01: SkipByDesign # microseconds are not supported in JavaScript
diff --git a/tests/co19/co19-dartdevc.status b/tests/co19/co19-dartdevc.status
index 94d2a50..8103c34 100644
--- a/tests/co19/co19-dartdevc.status
+++ b/tests/co19/co19-dartdevc.status
@@ -39,6 +39,7 @@
 LanguageFeatures/Abstract-external-fields/static_analysis_external_A05_t03: SkipByDesign # External variables are not supported
 LanguageFeatures/Abstract-external-fields/syntax_A01_t03: SkipByDesign # External variables are not supported
 LanguageFeatures/Abstract-external-fields/syntax_A02_t03: SkipByDesign # External variables are not supported
+LanguageFeatures/Enhanced-Enum/grammar_A07_t01: SkipByDesign # Non-JS-interop external members are not supported
 LanguageFeatures/FinalizationRegistry/ffi/*: SkipByDesign # dart:ffi is not supported
 LibTest/core/DateTime/DateTime.fromMicrosecondsSinceEpoch_A01_t01: SkipByDesign # microseconds are not supported in JavaScript
 LibTest/core/DateTime/microsecond_A01_t01: SkipByDesign # microseconds are not supported in JavaScript
diff --git a/tests/language/async_star/pause2_test.dart b/tests/language/async_star/pause2_test.dart
index a240d3d..3df0223 100644
--- a/tests/language/async_star/pause2_test.dart
+++ b/tests/language/async_star/pause2_test.dart
@@ -72,8 +72,8 @@
     f() async* {
       int i = 0;
       while (true) {
-        yield i;
         list.add(i);
+        yield i;
         i++;
       }
     }
diff --git a/tests/language/async_star/throw_in_catch_test.dart b/tests/language/async_star/throw_in_catch_test.dart
index 88982e5..7509a00 100644
--- a/tests/language/async_star/throw_in_catch_test.dart
+++ b/tests/language/async_star/throw_in_catch_test.dart
@@ -115,7 +115,7 @@
 test() async {
   // TODO(sigurdm): These tests are too dependent on scheduling, and buffering
   // behavior.
-  await runTest(foo1, "abcYdgC", null, true);
+  await runTest(foo1, "abcYgC", null, true);
   await runTest(foo2, "abcX", "Error", false);
   await runTest(foo3, "abcYX", "Error", false);
   await runTest(foo4, "abcYdYefX", "Error2", false);
diff --git a/tests/language_2/async_star/pause2_test.dart b/tests/language_2/async_star/pause2_test.dart
index 11e0429..f6e8d5f 100644
--- a/tests/language_2/async_star/pause2_test.dart
+++ b/tests/language_2/async_star/pause2_test.dart
@@ -74,8 +74,8 @@
     f() async* {
       int i = 0;
       while (true) {
-        yield i;
         list.add(i);
+        yield i;
         i++;
       }
     }
diff --git a/tests/language_2/async_star/throw_in_catch_test.dart b/tests/language_2/async_star/throw_in_catch_test.dart
index 57b6b0e..6a26245 100644
--- a/tests/language_2/async_star/throw_in_catch_test.dart
+++ b/tests/language_2/async_star/throw_in_catch_test.dart
@@ -118,7 +118,7 @@
 test() async {
   // TODO(sigurdm): These tests are too dependent on scheduling, and buffering
   // behavior.
-  await runTest(foo1, "abcYdgC", null, true);
+  await runTest(foo1, "abcYgC", null, true);
   await runTest(foo2, "abcX", "Error", false);
   await runTest(foo3, "abcYX", "Error", false);
   await runTest(foo4, "abcYdYefX", "Error2", false);
diff --git a/tools/VERSION b/tools/VERSION
index 46decc5..eaf4277 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 17
 PATCH 0
-PRERELEASE 274
+PRERELEASE 275
 PRERELEASE_PATCH 0
\ No newline at end of file