Version 2.17.0-13.0.dev

Merge commit 'a32e171a6fb1da8bdf3212433b389976dc079461' into 'dev'
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart b/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart
index 318219b..09c2de9 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart
@@ -14,10 +14,6 @@
 
   /// A [Code] object representation of this type annotation.
   Code get code;
-
-  /// Allows you to check the kind of a [TypeAnnotation] in a switch statement,
-  /// and without `is` checks.
-  TypeAnnotationKind get kind;
 }
 
 /// The base class for function type declarations.
@@ -33,9 +29,6 @@
 
   /// The type parameters for this function.
   Iterable<TypeParameterDeclaration> get typeParameters;
-
-  @override
-  TypeAnnotationKind get kind => TypeAnnotationKind.functionType;
 }
 
 /// An unresolved reference to a type.
@@ -48,9 +41,6 @@
 
   /// The type arguments, if applicable.
   Iterable<TypeAnnotation> get typeArguments;
-
-  @override
-  TypeAnnotationKind get kind => TypeAnnotationKind.namedType;
 }
 
 /// The interface representing a resolved type.
@@ -75,10 +65,6 @@
 abstract class Declaration {
   /// The name of this declaration.
   String get name;
-
-  /// Allows you to check the kind of a [Declaration] in a switch statement,
-  /// and without `is` checks.
-  DeclarationKind get kind;
 }
 
 /// A declaration that defines a new type in the program.
@@ -105,9 +91,6 @@
 /// Information about fields, methods, and constructors must be retrieved from
 /// the `builder` objects.
 abstract class ClassDeclaration implements TypeDeclaration {
-  @override
-  DeclarationKind get kind => DeclarationKind.clazz;
-
   /// Whether this class has an `abstract` modifier.
   bool get isAbstract;
 
@@ -129,18 +112,12 @@
 
 /// Type alias introspection information.
 abstract class TypeAliasDeclaration extends TypeDeclaration {
-  @override
-  DeclarationKind get kind => DeclarationKind.typeAlias;
-
   /// The type annotation this is an alias for.
   TypeAnnotation get type;
 }
 
 /// Function introspection information.
 abstract class FunctionDeclaration implements Declaration {
-  @override
-  DeclarationKind get kind => DeclarationKind.function;
-
   /// Whether this function has an `abstract` modifier.
   bool get isAbstract;
 
@@ -168,9 +145,6 @@
 
 /// Method introspection information.
 abstract class MethodDeclaration implements FunctionDeclaration {
-  @override
-  DeclarationKind get kind => DeclarationKind.method;
-
   /// The class that defines this method.
   TypeAnnotation get definingClass;
 }
@@ -183,9 +157,6 @@
 
 /// Variable introspection information.
 abstract class VariableDeclaration implements Declaration {
-  @override
-  DeclarationKind get kind => DeclarationKind.variable;
-
   /// Whether this function has an `abstract` modifier.
   bool get isAbstract;
 
@@ -201,18 +172,12 @@
 
 /// Field introspection information ..
 abstract class FieldDeclaration implements VariableDeclaration {
-  @override
-  DeclarationKind get kind => DeclarationKind.field;
-
   /// The class that defines this method.
   TypeAnnotation get definingClass;
 }
 
 /// Parameter introspection information.
 abstract class ParameterDeclaration implements Declaration {
-  @override
-  DeclarationKind get kind => DeclarationKind.parameter;
-
   /// The type of this parameter.
   TypeAnnotation get type;
 
@@ -230,28 +195,6 @@
 
 /// Type parameter introspection information.
 abstract class TypeParameterDeclaration implements Declaration {
-  @override
-  DeclarationKind get kind => DeclarationKind.typeParameter;
-
   /// The bounds for this type parameter, if it has any.
   TypeAnnotation? get bounds;
 }
-
-// The kinds of type declarations.
-enum DeclarationKind {
-  clazz,
-  constructor,
-  field,
-  function,
-  method,
-  parameter,
-  typeAlias,
-  typeParameter,
-  variable,
-}
-
-// The kinds of type annotations.
-enum TypeAnnotationKind {
-  namedType,
-  functionType,
-}
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart b/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
index 55ea8bd..332144f 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
@@ -26,6 +26,7 @@
 import 'dart:async';
 import 'dart:isolate';
 
+import 'package:_fe_analyzer_shared/src/macros/executor_shared/introspection_impls.dart';
 import 'package:_fe_analyzer_shared/src/macros/executor_shared/response_impls.dart';
 import 'package:_fe_analyzer_shared/src/macros/executor_shared/serialization.dart';
 import 'package:_fe_analyzer_shared/src/macros/executor_shared/protocol.dart';
@@ -99,12 +100,14 @@
     return new SerializableResponse(
         responseType: MessageType.macroInstanceIdentifier,
         response: identifier,
-        requestId: request.id);
+        requestId: request.id,
+        serializationZoneId: request.serializationZoneId);
   } catch (e) {
     return new SerializableResponse(
       responseType: MessageType.error,
       error: e.toString(),
-      requestId: request.id);
+      requestId: request.id,
+      serializationZoneId: request.serializationZoneId);
   }
 }
 
@@ -118,7 +121,7 @@
     }
     Declaration declaration = request.declaration;
     if (instance is FunctionDefinitionMacro &&
-        declaration is FunctionDeclaration) {
+        declaration is FunctionDeclarationImpl) {
       FunctionDefinitionBuilderImpl builder = new FunctionDefinitionBuilderImpl(
           declaration,
           request.typeResolver,
@@ -128,7 +131,8 @@
       return new SerializableResponse(
           responseType: MessageType.macroExecutionResult,
           response: builder.result,
-          requestId: request.id);
+          requestId: request.id,
+          serializationZoneId: request.serializationZoneId);
     } else {
       throw new UnsupportedError(
           ('Only FunctionDefinitionMacros are supported currently'));
@@ -137,7 +141,8 @@
     return new SerializableResponse(
       responseType: MessageType.error,
       error: e.toString(),
-      requestId: request.id);
+      requestId: request.id,
+      serializationZoneId: request.serializationZoneId);
   }
 }
 ''';
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart
index 38a9f4b..9778950 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart
@@ -62,14 +62,14 @@
   ///
   /// Throws an exception if there is an error executing the macro.
   Future<MacroExecutionResult> executeTypesPhase(
-      MacroInstanceIdentifier macro, Declaration declaration);
+      MacroInstanceIdentifier macro, covariant Declaration declaration);
 
   /// Runs the declarations phase for [macro] on a given [declaration].
   ///
   /// Throws an exception if there is an error executing the macro.
   Future<MacroExecutionResult> executeDeclarationsPhase(
       MacroInstanceIdentifier macro,
-      Declaration declaration,
+      covariant Declaration declaration,
       TypeResolver typeResolver,
       ClassIntrospector classIntrospector);
 
@@ -78,7 +78,7 @@
   /// Throws an exception if there is an error executing the macro.
   Future<MacroExecutionResult> executeDefinitionsPhase(
       MacroInstanceIdentifier macro,
-      Declaration declaration,
+      covariant Declaration declaration,
       TypeResolver typeResolver,
       ClassIntrospector classIntrospector,
       TypeDeclarationResolver typeDeclarationResolver);
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/introspection_impls.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/introspection_impls.dart
index e237127..7706af7 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/introspection_impls.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/introspection_impls.dart
@@ -2,12 +2,27 @@
 // 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 'remote_instance.dart';
+import 'serialization.dart';
+import 'serialization_extensions.dart';
 import '../api.dart';
 
-abstract class TypeAnnotationImpl implements TypeAnnotation {
+abstract class TypeAnnotationImpl extends RemoteInstance
+    implements TypeAnnotation {
   final bool isNullable;
 
-  TypeAnnotationImpl({required this.isNullable});
+  TypeAnnotationImpl({required int id, required this.isNullable}) : super(id);
+
+  @override
+  void serialize(Serializer serializer) {
+    super.serialize(serializer);
+    // Client side we don't encode anything but the ID.
+    if (serializationMode == SerializationMode.client) {
+      return;
+    }
+
+    serializer.addBool(isNullable);
+  }
 }
 
 class NamedTypeAnnotationImpl extends TypeAnnotationImpl
@@ -27,16 +42,33 @@
   final String name;
 
   @override
-  final List<TypeAnnotation> typeArguments;
+  final List<TypeAnnotationImpl> typeArguments;
 
   @override
-  TypeAnnotationKind get kind => TypeAnnotationKind.namedType;
+  RemoteInstanceKind get kind => RemoteInstanceKind.namedTypeAnnotation;
 
   NamedTypeAnnotationImpl({
+    required int id,
     required bool isNullable,
     required this.name,
     required this.typeArguments,
-  }) : super(isNullable: isNullable);
+  }) : super(id: id, isNullable: isNullable);
+
+  @override
+  void serialize(Serializer serializer) {
+    super.serialize(serializer);
+    // Client side we don't encode anything but the ID.
+    if (serializationMode == SerializationMode.client) {
+      return;
+    }
+
+    serializer.addString(name);
+    serializer.startList();
+    for (TypeAnnotationImpl typeArg in typeArguments) {
+      typeArg.serialize(serializer);
+    }
+    serializer.endList();
+  }
 }
 
 class FunctionTypeAnnotationImpl extends TypeAnnotationImpl
@@ -72,30 +104,75 @@
       ]);
 
   @override
-  final List<ParameterDeclaration> namedParameters;
+  final List<ParameterDeclarationImpl> namedParameters;
 
   @override
-  final List<ParameterDeclaration> positionalParameters;
+  final List<ParameterDeclarationImpl> positionalParameters;
 
   @override
-  final TypeAnnotation returnType;
+  final TypeAnnotationImpl returnType;
 
   @override
-  final List<TypeParameterDeclaration> typeParameters;
+  final List<TypeParameterDeclarationImpl> typeParameters;
 
   @override
-  TypeAnnotationKind get kind => TypeAnnotationKind.functionType;
+  RemoteInstanceKind get kind => RemoteInstanceKind.functionTypeAnnotation;
 
   FunctionTypeAnnotationImpl({
+    required int id,
     required bool isNullable,
     required this.namedParameters,
     required this.positionalParameters,
     required this.returnType,
     required this.typeParameters,
-  }) : super(isNullable: isNullable);
+  }) : super(id: id, isNullable: isNullable);
+
+  @override
+  void serialize(Serializer serializer) {
+    super.serialize(serializer);
+    // Client side we don't encode anything but the ID.
+    if (serializationMode == SerializationMode.client) {
+      return;
+    }
+
+    returnType.serialize(serializer);
+
+    serializer.startList();
+    for (ParameterDeclarationImpl param in positionalParameters) {
+      param.serialize(serializer);
+    }
+    serializer.endList();
+
+    serializer.startList();
+    for (ParameterDeclarationImpl param in namedParameters) {
+      param.serialize(serializer);
+    }
+    serializer.endList();
+
+    serializer.startList();
+    for (TypeParameterDeclarationImpl typeParam in typeParameters) {
+      typeParam.serialize(serializer);
+    }
+    serializer.endList();
+  }
 }
 
-class ParameterDeclarationImpl implements ParameterDeclaration {
+abstract class DeclarationImpl extends RemoteInstance implements Declaration {
+  DeclarationImpl(int id) : super(id);
+  @override
+  void serialize(Serializer serializer) {
+    super.serialize(serializer);
+    // Client side we don't encode anything but the ID.
+    if (serializationMode == SerializationMode.client) {
+      return;
+    }
+
+    serializer.addString(name);
+  }
+}
+
+class ParameterDeclarationImpl extends DeclarationImpl
+    implements ParameterDeclaration {
   @override
   final String name;
 
@@ -109,34 +186,73 @@
   final bool isRequired;
 
   @override
-  final TypeAnnotation type;
+  final TypeAnnotationImpl type;
 
   @override
-  DeclarationKind get kind => DeclarationKind.parameter;
+  RemoteInstanceKind get kind => RemoteInstanceKind.parameterDeclaration;
 
   ParameterDeclarationImpl({
+    required int id,
     required this.name,
     required this.defaultValue,
     required this.isNamed,
     required this.isRequired,
     required this.type,
-  });
+  }) : super(id);
+
+  @override
+  void serialize(Serializer serializer) {
+    super.serialize(serializer);
+    // Client side we don't encode anything but the ID.
+    if (serializationMode == SerializationMode.client) {
+      return;
+    }
+
+    if (defaultValue == null) {
+      serializer.addNull();
+    } else {
+      defaultValue!.serialize(serializer);
+    }
+    serializer.addBool(isNamed);
+    serializer.addBool(isRequired);
+    type.serialize(serializer);
+  }
 }
 
-class TypeParameterDeclarationImpl implements TypeParameterDeclaration {
+class TypeParameterDeclarationImpl extends DeclarationImpl
+    implements TypeParameterDeclaration {
   @override
   final String name;
 
   @override
-  final TypeAnnotation? bounds;
+  final TypeAnnotationImpl? bounds;
 
   @override
-  DeclarationKind get kind => DeclarationKind.typeParameter;
+  RemoteInstanceKind get kind => RemoteInstanceKind.typeParameterDeclaration;
 
-  TypeParameterDeclarationImpl({required this.name, required this.bounds});
+  TypeParameterDeclarationImpl(
+      {required int id, required this.name, required this.bounds})
+      : super(id);
+
+  @override
+  void serialize(Serializer serializer) {
+    super.serialize(serializer);
+    // Client side we don't encode anything but the ID.
+    if (serializationMode == SerializationMode.client) {
+      return;
+    }
+
+    TypeAnnotationImpl? bounds = this.bounds;
+    if (bounds == null) {
+      serializer.addNull();
+    } else {
+      bounds.serialize(serializer);
+    }
+  }
 }
 
-class FunctionDeclarationImpl implements FunctionDeclaration {
+class FunctionDeclarationImpl extends DeclarationImpl
+    implements FunctionDeclaration {
   @override
   final String name;
 
@@ -153,21 +269,22 @@
   final bool isSetter;
 
   @override
-  final List<ParameterDeclaration> namedParameters;
+  final List<ParameterDeclarationImpl> namedParameters;
 
   @override
-  final List<ParameterDeclaration> positionalParameters;
+  final List<ParameterDeclarationImpl> positionalParameters;
 
   @override
-  final TypeAnnotation returnType;
+  final TypeAnnotationImpl returnType;
 
   @override
-  final List<TypeParameterDeclaration> typeParameters;
+  final List<TypeParameterDeclarationImpl> typeParameters;
 
   @override
-  DeclarationKind get kind => DeclarationKind.function;
+  RemoteInstanceKind get kind => RemoteInstanceKind.functionDeclaration;
 
   FunctionDeclarationImpl({
+    required int id,
     required this.name,
     required this.isAbstract,
     required this.isExternal,
@@ -177,5 +294,37 @@
     required this.positionalParameters,
     required this.returnType,
     required this.typeParameters,
-  });
+  }) : super(id);
+
+  @override
+  void serialize(Serializer serializer) {
+    super.serialize(serializer);
+    // Client side we don't encode anything but the ID.
+    if (serializationMode == SerializationMode.client) {
+      return;
+    }
+
+    serializer
+      ..addBool(isAbstract)
+      ..addBool(isExternal)
+      ..addBool(isGetter)
+      ..addBool(isSetter)
+      ..startList();
+    for (ParameterDeclarationImpl named in namedParameters) {
+      named.serialize(serializer);
+    }
+    serializer
+      ..endList()
+      ..startList();
+    for (ParameterDeclarationImpl positional in positionalParameters) {
+      positional.serialize(serializer);
+    }
+    serializer.endList();
+    returnType.serialize(serializer);
+    serializer.startList();
+    for (TypeParameterDeclarationImpl param in typeParameters) {
+      param.serialize(serializer);
+    }
+    serializer.endList();
+  }
 }
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/protocol.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/protocol.dart
index 3dcc455..68d0f94 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/protocol.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/protocol.dart
@@ -9,19 +9,26 @@
 import '../executor.dart';
 import '../api.dart';
 import '../executor_shared/response_impls.dart';
+import 'introspection_impls.dart';
+import 'remote_instance.dart';
 import 'serialization.dart';
-import 'serialization_extensions.dart';
 
 /// Base class all requests extend, provides a unique id for each request.
 abstract class Request implements Serializable {
   final int id;
 
-  Request({int? id}) : this.id = id ?? _next++;
+  final int serializationZoneId;
+
+  Request({int? id, required this.serializationZoneId})
+      : this.id = id ?? _next++;
 
   Request.deserialize(Deserializer deserializer)
-      : id = (deserializer..moveNext()).expectNum();
+      : serializationZoneId = (deserializer..moveNext()).expectNum(),
+        id = (deserializer..moveNext()).expectNum();
 
-  void serialize(Serializer serializer) => serializer.addNum(id);
+  void serialize(Serializer serializer) => serializer
+    ..addNum(serializationZoneId)
+    ..addNum(id);
 
   static int _next = 0;
 }
@@ -44,16 +51,21 @@
   final MessageType responseType;
   final String? error;
   final int requestId;
+  final int serializationZoneId;
 
   SerializableResponse({
     this.error,
     required this.requestId,
     this.response,
     required this.responseType,
+    required this.serializationZoneId,
   })  : assert(response != null || error != null),
         assert(response == null || error == null);
 
-  factory SerializableResponse.deserialize(Deserializer deserializer) {
+  /// You must first parse the [serializationZoneId] yourself, and then
+  /// call this function in that zone, and pass the ID.
+  factory SerializableResponse.deserialize(
+      Deserializer deserializer, int serializationZoneId) {
     deserializer.moveNext();
     MessageType responseType = MessageType.values[deserializer.expectNum()];
     Serializable? response;
@@ -80,11 +92,14 @@
         responseType: responseType,
         response: response,
         error: error,
-        requestId: (deserializer..moveNext()).expectNum());
+        requestId: (deserializer..moveNext()).expectNum(),
+        serializationZoneId: serializationZoneId);
   }
 
   void serialize(Serializer serializer) {
-    serializer.addNum(responseType.index);
+    serializer
+      ..addNum(serializationZoneId)
+      ..addNum(responseType.index);
     if (response != null) {
       response!.serialize(serializer);
     } else if (error != null) {
@@ -99,7 +114,8 @@
   final Uri library;
   final String name;
 
-  LoadMacroRequest(this.library, this.name);
+  LoadMacroRequest(this.library, this.name, {required int serializationZoneId})
+      : super(serializationZoneId: serializationZoneId);
 
   LoadMacroRequest.deserialize(Deserializer deserializer)
       : library = Uri.parse((deserializer..moveNext()).expectString()),
@@ -122,8 +138,9 @@
   final String constructorName;
   final Arguments arguments;
 
-  InstantiateMacroRequest(
-      this.macroClass, this.constructorName, this.arguments);
+  InstantiateMacroRequest(this.macroClass, this.constructorName, this.arguments,
+      {required int serializationZoneId})
+      : super(serializationZoneId: serializationZoneId);
 
   InstantiateMacroRequest.deserialize(Deserializer deserializer)
       : macroClass = new MacroClassIdentifierImpl.deserialize(deserializer),
@@ -145,7 +162,7 @@
 /// phase.
 class ExecuteDefinitionsPhaseRequest extends Request {
   final MacroInstanceIdentifier macro;
-  final Declaration declaration;
+  final DeclarationImpl declaration;
 
   /// Client/Server specific implementation, not serialized.
   final TypeResolver typeResolver;
@@ -157,14 +174,16 @@
   final TypeDeclarationResolver typeDeclarationResolver;
 
   ExecuteDefinitionsPhaseRequest(this.macro, this.declaration,
-      this.typeResolver, this.classIntrospector, this.typeDeclarationResolver);
+      this.typeResolver, this.classIntrospector, this.typeDeclarationResolver,
+      {required int serializationZoneId})
+      : super(serializationZoneId: serializationZoneId);
 
   /// When deserializing we have already consumed the message type, so we don't
   /// consume it again.
   ExecuteDefinitionsPhaseRequest.deserialize(Deserializer deserializer,
       this.typeResolver, this.classIntrospector, this.typeDeclarationResolver)
       : macro = new MacroInstanceIdentifierImpl.deserialize(deserializer),
-        declaration = (deserializer..moveNext()).expectDeclaration(),
+        declaration = RemoteInstance.deserialize(deserializer),
         super.deserialize(deserializer);
 
   void serialize(Serializer serializer) {
@@ -177,14 +196,15 @@
 
 /// A request to reflect on a type annotation
 class ReflectTypeRequest extends Request {
-  final TypeAnnotation typeAnnotation;
+  final TypeAnnotationImpl typeAnnotation;
 
-  ReflectTypeRequest(this.typeAnnotation);
+  ReflectTypeRequest(this.typeAnnotation, {required int serializationZoneId})
+      : super(serializationZoneId: serializationZoneId);
 
   /// When deserializing we have already consumed the message type, so we don't
   /// consume it again.
   ReflectTypeRequest.deserialize(Deserializer deserializer)
-      : typeAnnotation = (deserializer..moveNext()).expectTypeAnnotation(),
+      : typeAnnotation = RemoteInstance.deserialize(deserializer),
         super.deserialize(deserializer);
 
   void serialize(Serializer serializer) {
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/remote_instance.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/remote_instance.dart
new file mode 100644
index 0000000..5dcd1f8
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/remote_instance.dart
@@ -0,0 +1,86 @@
+// 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';
+
+import 'package:meta/meta.dart';
+
+import 'serialization.dart';
+import 'serialization_extensions.dart';
+
+/// The key used to store the remote instance cache in the current serialization
+/// zone (in server mode only).
+const Symbol remoteInstanceZoneKey = #remoteInstanceCache;
+
+/// On the server side we keep track of remote instances by their ID.
+///
+/// These are a part of the current serialization zone, which all serialization
+/// and deserialization must be done in.
+///
+/// This means the cache lifetime is that of the serialization zone it is run
+/// in.
+Map<int, RemoteInstance> get _remoteInstanceCache =>
+    Zone.current[remoteInstanceZoneKey];
+
+/// Base class for types that need to be able to be traced back to a specific
+/// instance on the server side.
+abstract class RemoteInstance implements Serializable {
+  /// The unique ID for this instance.
+  final int id;
+
+  /// The type of instance being encoded.
+  RemoteInstanceKind get kind;
+
+  /// Static, incrementing ids.
+  static int _nextId = 0;
+
+  /// Gets the next unique identifier.
+  static int get uniqueId => _nextId++;
+
+  /// On the client side [id]s are given and you should reconstruct objects with
+  /// the given ID. On the server side ids should be created using
+  /// [RemoteInstance.uniqueId].
+  RemoteInstance(this.id);
+
+  /// Retrieves a cached instance by ID.
+  static T cached<T>(int id) => _remoteInstanceCache[id] as T;
+
+  /// Deserializes an instance based on the current [serializationMode].
+  static T deserialize<T>(Deserializer deserializer) =>
+      (deserializer..moveNext()).expectRemoteInstance();
+
+  /// This method should be overridden by all subclasses, which should on their
+  /// first line call this super function.
+  ///
+  /// They should then return immediately if [serializationMode] is
+  /// [SerializationMode.client], so that only an ID is sent.
+  @mustCallSuper
+  void serialize(Serializer serializer) {
+    serializer.addNum(id);
+    switch (serializationMode) {
+      case SerializationMode.client:
+        // We only send the ID from the client side.
+        return;
+      case SerializationMode.server:
+        serializer.addNum(kind.index);
+        _remoteInstanceCache[id] = this;
+        return;
+    }
+  }
+}
+
+// The kinds of instances.
+enum RemoteInstanceKind {
+  classDeclaration,
+  constructorDeclaration,
+  fieldDeclaration,
+  functionDeclaration,
+  functionTypeAnnotation,
+  methodDeclaration,
+  namedTypeAnnotation,
+  parameterDeclaration,
+  typeAliasDeclaration,
+  typeParameterDeclaration,
+  variableDeclaration,
+}
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/response_impls.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/response_impls.dart
index 5155f01..77556ad 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/response_impls.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/response_impls.dart
@@ -4,8 +4,11 @@
 
 import 'dart:async';
 
+import 'package:_fe_analyzer_shared/src/macros/executor_shared/remote_instance.dart';
+
 import '../executor.dart';
 import '../api.dart';
+import 'introspection_impls.dart';
 import 'serialization.dart';
 import 'serialization_extensions.dart';
 
@@ -102,7 +105,7 @@
   final ClassIntrospector classIntrospector;
 
   /// The declaration this is a builder for.
-  final FunctionDeclaration declaration;
+  final FunctionDeclarationImpl declaration;
 
   /// The final result, will be built up over `augment` calls.
   final MacroExecutionResultImpl result;
@@ -113,8 +116,7 @@
 
   FunctionDefinitionBuilderImpl.deserialize(Deserializer deserializer,
       this.typeResolver, this.typeDeclarationResolver, this.classIntrospector)
-      : declaration =
-            (deserializer..moveNext()).expectDeclaration<FunctionDeclaration>(),
+      : declaration = RemoteInstance.deserialize(deserializer),
         result = new MacroExecutionResultImpl.deserialize(deserializer);
 
   void serialize(Serializer serializer) {
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization.dart
index f5f9343..89bbf59 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization.dart
@@ -4,10 +4,19 @@
 
 import 'dart:async';
 
+import 'remote_instance.dart';
+
 /// All serialization must be done in a serialization Zone, which tells it
 /// whether we are the client or server.
+///
+/// In [SerializationMode.server], sets up a remote instance cache to use when
+/// deserializing remote instances back to their original instance.
 T withSerializationMode<T>(SerializationMode mode, T Function() fn) =>
-    runZoned(fn, zoneValues: {#serializationMode: mode});
+    runZoned(fn, zoneValues: {
+      #serializationMode: mode,
+      if (mode == SerializationMode.server)
+        remoteInstanceZoneKey: <int, RemoteInstance>{}
+    });
 
 /// Serializable interface
 abstract class Serializable {
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization_extensions.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization_extensions.dart
index 01cb768..1fafb40 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization_extensions.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization_extensions.dart
@@ -1,97 +1,90 @@
 import 'package:_fe_analyzer_shared/src/macros/executor_shared/introspection_impls.dart';
 
+import 'remote_instance.dart';
 import 'serialization.dart';
 import '../api.dart';
 
 extension DeserializerExtensions on Deserializer {
-  TypeAnnotation expectTypeAnnotation() {
+  T expectRemoteInstance<T>() {
     int id = expectNum();
     switch (serializationMode) {
-      case SerializationMode.server:
-        return _typeAnnotationsById[id]!;
       case SerializationMode.client:
-        TypeAnnotation typeAnnotation;
         moveNext();
-        TypeAnnotationKind type = TypeAnnotationKind.values[expectNum()];
+        RemoteInstanceKind kind = RemoteInstanceKind.values[expectNum()];
         moveNext();
-        switch (type) {
-          case TypeAnnotationKind.namedType:
-            typeAnnotation = _expectNamedTypeAnnotation();
-            break;
-          case TypeAnnotationKind.functionType:
-            typeAnnotation = _expectFunctionTypeAnnotation();
-            break;
+        switch (kind) {
+          case RemoteInstanceKind.namedTypeAnnotation:
+            return _expectNamedTypeAnnotation(id) as T;
+          case RemoteInstanceKind.functionTypeAnnotation:
+            return _expectFunctionTypeAnnotation(id) as T;
+          case RemoteInstanceKind.functionDeclaration:
+            return _expectFunctionDeclaration(id) as T;
+          case RemoteInstanceKind.parameterDeclaration:
+            return _expectParameterDeclaration(id) as T;
+          case RemoteInstanceKind.typeParameterDeclaration:
+            return _expectTypeParameterDeclaration(id) as T;
+          default:
+            throw new UnsupportedError('Unsupported remote object kind: $kind');
         }
-        _typeAnnotationIds[typeAnnotation] = id;
-        return typeAnnotation;
+      case SerializationMode.server:
+        return RemoteInstance.cached(id);
     }
   }
 
-  NamedTypeAnnotation _expectNamedTypeAnnotation() {
+  NamedTypeAnnotation _expectNamedTypeAnnotation(int id) {
     bool isNullable = expectBool();
     moveNext();
     String name = expectString();
     moveNext();
     expectList();
-    List<TypeAnnotation> typeArguments = [
+    List<TypeAnnotationImpl> typeArguments = [
       for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
-        expectTypeAnnotation(),
+        expectRemoteInstance(),
     ];
     return new NamedTypeAnnotationImpl(
-        isNullable: isNullable, name: name, typeArguments: typeArguments);
+        id: id,
+        isNullable: isNullable,
+        name: name,
+        typeArguments: typeArguments);
   }
 
-  FunctionTypeAnnotation _expectFunctionTypeAnnotation() {
+  FunctionTypeAnnotation _expectFunctionTypeAnnotation(int id) {
     bool isNullable = expectBool();
 
-    moveNext();
-    TypeAnnotation returnType = expectTypeAnnotation();
+    TypeAnnotationImpl returnType = RemoteInstance.deserialize(this);
 
     moveNext();
     expectList();
-    List<ParameterDeclaration> positionalParameters = [
+    List<ParameterDeclarationImpl> positionalParameters = [
       for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
-        expectDeclaration(),
+        expectRemoteInstance(),
     ];
 
     moveNext();
     expectList();
-    List<ParameterDeclaration> namedParameters = [
+    List<ParameterDeclarationImpl> namedParameters = [
       for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
-        expectDeclaration(),
+        expectRemoteInstance(),
     ];
 
     moveNext();
     expectList();
-    List<TypeParameterDeclaration> typeParameters = [
+    List<TypeParameterDeclarationImpl> typeParameters = [
       for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
-        expectDeclaration(),
+        expectRemoteInstance(),
     ];
 
     return new FunctionTypeAnnotationImpl(
-        isNullable: isNullable,
-        returnType: returnType,
-        positionalParameters: positionalParameters,
-        namedParameters: namedParameters,
-        typeParameters: typeParameters);
+      id: id,
+      isNullable: isNullable,
+      returnType: returnType,
+      positionalParameters: positionalParameters,
+      namedParameters: namedParameters,
+      typeParameters: typeParameters,
+    );
   }
 
-  T expectDeclaration<T extends Declaration>() {
-    DeclarationKind kind = DeclarationKind.values[expectNum()];
-    moveNext();
-    switch (kind) {
-      case DeclarationKind.parameter:
-        return _expectParameterDeclaration() as T;
-      case DeclarationKind.typeParameter:
-        return _expectTypeParameterDeclaration() as T;
-      case DeclarationKind.function:
-        return _expectFunctionDeclaration() as T;
-      default:
-        throw new UnimplementedError('Cant deserialize $kind yet');
-    }
-  }
-
-  ParameterDeclaration _expectParameterDeclaration() {
+  ParameterDeclaration _expectParameterDeclaration(int id) {
     String name = expectString();
     moveNext();
     Code? defaultValue;
@@ -101,10 +94,11 @@
     bool isNamed = expectBool();
     moveNext();
     bool isRequired = expectBool();
-    moveNext();
-    TypeAnnotation type = expectTypeAnnotation();
+
+    TypeAnnotationImpl type = RemoteInstance.deserialize(this);
 
     return new ParameterDeclarationImpl(
+        id: id,
         defaultValue: defaultValue,
         isNamed: isNamed,
         isRequired: isRequired,
@@ -112,17 +106,17 @@
         type: type);
   }
 
-  TypeParameterDeclaration _expectTypeParameterDeclaration() {
+  TypeParameterDeclaration _expectTypeParameterDeclaration(int id) {
     String name = expectString();
     moveNext();
-    TypeAnnotation? bounds;
+    TypeAnnotationImpl? bounds;
     if (!checkNull()) {
-      bounds = expectTypeAnnotation();
+      bounds = expectRemoteInstance();
     }
-    return new TypeParameterDeclarationImpl(name: name, bounds: bounds);
+    return new TypeParameterDeclarationImpl(id: id, name: name, bounds: bounds);
   }
 
-  FunctionDeclaration _expectFunctionDeclaration() {
+  FunctionDeclaration _expectFunctionDeclaration(int id) {
     String name = expectString();
     moveNext();
     bool isAbstract = expectBool();
@@ -135,28 +129,28 @@
 
     moveNext();
     expectList();
-    List<ParameterDeclaration> namedParameters = [
+    List<ParameterDeclarationImpl> namedParameters = [
       for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
-        expectDeclaration()
+        expectRemoteInstance(),
     ];
 
     moveNext();
     expectList();
-    List<ParameterDeclaration> positionalParameters = [
+    List<ParameterDeclarationImpl> positionalParameters = [
       for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
-        expectDeclaration()
+        expectRemoteInstance(),
     ];
 
-    moveNext();
-    TypeAnnotation returnType = expectTypeAnnotation();
+    TypeAnnotationImpl returnType = RemoteInstance.deserialize(this);
 
     moveNext();
     expectList();
-    List<TypeParameterDeclaration> typeParameters = [
+    List<TypeParameterDeclarationImpl> typeParameters = [
       for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
-        expectDeclaration()
+        expectRemoteInstance(),
     ];
     return new FunctionDeclarationImpl(
+        id: id,
         name: name,
         isAbstract: isAbstract,
         isExternal: isExternal,
@@ -184,7 +178,7 @@
           parts.add(expectString());
           break;
         case CodePartKind.typeAnnotation:
-          parts.add(expectTypeAnnotation());
+          parts.add(expectRemoteInstance());
           break;
       }
     }
@@ -212,179 +206,6 @@
   }
 }
 
-/// On the server side we keep track of type annotations by their ID.
-final _typeAnnotationsById = <int, TypeAnnotation>{};
-
-/// On the client side we keep an expando of ids on [TypeAnnotation]s.
-final _typeAnnotationIds = new Expando<int>();
-
-/// Incrementing ids for [TypeAnnotationImpl]s
-int _nextTypeAnnotationId = 0;
-
-extension SerializeTypeAnnotation on TypeAnnotation {
-  void serialize(Serializer serializer) {
-    TypeAnnotation self = this;
-    if (self is NamedTypeAnnotationImpl) {
-      self.serialize(serializer);
-    } else if (self is FunctionTypeAnnotationImpl) {
-      self.serialize(serializer);
-    } else {
-      throw new UnsupportedError(
-          'Type ${this.runtimeType} is not supported for serialization.');
-    }
-  }
-}
-
-/// Does the parts of serialization for type annotations that is shared between
-/// implementations.
-///
-/// Returns `false` if we should continue serializing the rest of the object, or
-/// `true` if the object is fully serialized (just an ID).
-bool _doSharedTypeAnnotationSerialization(Serializer serializer,
-    TypeAnnotation typeAnnotation, TypeAnnotationKind kind) {
-  switch (serializationMode) {
-    case SerializationMode.client:
-      // Only send the ID from the client side, and assume we have one.
-      int id = _typeAnnotationIds[typeAnnotation]!;
-      serializer.addNum(id);
-      return true;
-    case SerializationMode.server:
-      // Server side we may create new ids for unseen annotations,
-      // and continue to serialize the rest of the annotation.
-      int id = _typeAnnotationIds[typeAnnotation] ?? _nextTypeAnnotationId++;
-      // TODO: We should clean these up at some point.
-      _typeAnnotationsById[id] = typeAnnotation;
-      serializer.addNum(id);
-      break;
-  }
-  serializer.addNum(kind.index);
-  serializer.addBool(typeAnnotation.isNullable);
-  return false;
-}
-
-extension SerializeNamedTypeAnnotation on NamedTypeAnnotation {
-  void serialize(Serializer serializer) {
-    if (_doSharedTypeAnnotationSerialization(
-        serializer, this, TypeAnnotationKind.namedType)) {
-      return;
-    }
-    serializer.addString(name);
-    serializer.startList();
-    for (TypeAnnotation typeArg in typeArguments) {
-      typeArg.serialize(serializer);
-    }
-    serializer.endList();
-  }
-}
-
-extension SerializeFunctionTypeAnnotation on FunctionTypeAnnotation {
-  void serialize(Serializer serializer) {
-    if (_doSharedTypeAnnotationSerialization(
-        serializer, this, TypeAnnotationKind.functionType)) {
-      return;
-    }
-
-    returnType.serialize(serializer);
-
-    serializer.startList();
-    for (ParameterDeclaration param in positionalParameters) {
-      param.serialize(serializer);
-    }
-    serializer.endList();
-
-    serializer.startList();
-    for (ParameterDeclaration param in namedParameters) {
-      param.serialize(serializer);
-    }
-    serializer.endList();
-
-    serializer.startList();
-    for (TypeParameterDeclaration typeParam in typeParameters) {
-      typeParam.serialize(serializer);
-    }
-    serializer.endList();
-  }
-}
-
-/// Does the shared parts of [Declaration] serialization.
-void _serializeDeclaration(Serializer serializer, Declaration declaration) {
-  serializer.addNum(declaration.kind.index);
-  serializer.addString(declaration.name);
-}
-
-/// Checks the type and deserializes it appropriately.
-extension SerializeDeclaration on Declaration {
-  void serialize(Serializer serializer) {
-    switch (kind) {
-      case DeclarationKind.parameter:
-        (this as ParameterDeclaration).serialize(serializer);
-        break;
-      case DeclarationKind.typeParameter:
-        (this as TypeParameterDeclaration).serialize(serializer);
-        break;
-      case DeclarationKind.function:
-        (this as FunctionDeclaration).serialize(serializer);
-        break;
-      default:
-        throw new UnimplementedError('Cant serialize $kind yet');
-    }
-  }
-}
-
-extension SerializeParameterDeclaration on ParameterDeclaration {
-  void serialize(Serializer serializer) {
-    _serializeDeclaration(serializer, this);
-    if (defaultValue == null) {
-      serializer.addNull();
-    } else {
-      defaultValue!.serialize(serializer);
-    }
-    serializer.addBool(isNamed);
-    serializer.addBool(isRequired);
-    type.serialize(serializer);
-  }
-}
-
-extension SerializeTypeParameterDeclaration on TypeParameterDeclaration {
-  void serialize(Serializer serializer) {
-    _serializeDeclaration(serializer, this);
-    TypeAnnotation? bounds = this.bounds;
-    if (bounds == null) {
-      serializer.addNull();
-    } else {
-      bounds.serialize(serializer);
-    }
-  }
-}
-
-extension SerializeFunctionDeclaration on FunctionDeclaration {
-  void serialize(Serializer serializer) {
-    _serializeDeclaration(serializer, this);
-    serializer
-      ..addBool(isAbstract)
-      ..addBool(isExternal)
-      ..addBool(isGetter)
-      ..addBool(isSetter)
-      ..startList();
-    for (ParameterDeclaration named in namedParameters) {
-      named.serialize(serializer);
-    }
-    serializer
-      ..endList()
-      ..startList();
-    for (ParameterDeclaration positional in positionalParameters) {
-      positional.serialize(serializer);
-    }
-    serializer.endList();
-    returnType.serialize(serializer);
-    serializer.startList();
-    for (TypeParameterDeclaration param in typeParameters) {
-      param.serialize(serializer);
-    }
-    serializer.endList();
-  }
-}
-
 extension SerializeCode on Code {
   void serialize(Serializer serializer) {
     serializer
@@ -398,7 +219,7 @@
       } else if (part is Code) {
         serializer.addNum(CodePartKind.code.index);
         part.serialize(serializer);
-      } else if (part is TypeAnnotation) {
+      } else if (part is TypeAnnotationImpl) {
         serializer.addNum(CodePartKind.typeAnnotation.index);
         part.serialize(serializer);
       } else {
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/isolate_mirrors_executor.dart b/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/isolate_mirrors_executor.dart
index 136a6d5..24c7d80 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/isolate_mirrors_executor.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/isolate_mirrors_executor.dart
@@ -7,6 +7,7 @@
 import 'dart:mirrors';
 
 import 'isolate_mirrors_impl.dart';
+import '../executor_shared/introspection_impls.dart';
 import '../executor_shared/protocol.dart';
 import '../executor.dart';
 import '../api.dart';
@@ -101,12 +102,14 @@
   @override
   Future<MacroExecutionResult> executeDefinitionsPhase(
           MacroInstanceIdentifier macro,
-          Declaration declaration,
+          DeclarationImpl declaration,
           TypeResolver typeResolver,
           ClassIntrospector classIntrospector,
           TypeDeclarationResolver typeDeclarationResolver) =>
       _sendRequest(new ExecuteDefinitionsPhaseRequest(macro, declaration,
-          typeResolver, classIntrospector, typeDeclarationResolver));
+          typeResolver, classIntrospector, typeDeclarationResolver,
+          // Serialization zones are not necessary in this executor.
+          serializationZoneId: -1));
 
   @override
   Future<MacroExecutionResult> executeTypesPhase(
@@ -121,7 +124,9 @@
           String constructor,
           Arguments arguments) =>
       _sendRequest(
-          new InstantiateMacroRequest(macroClass, constructor, arguments));
+          new InstantiateMacroRequest(macroClass, constructor, arguments,
+              // Serialization zones are not necessary in this executor.
+              serializationZoneId: -1));
 
   @override
   Future<MacroClassIdentifier> loadMacro(Uri library, String name,
@@ -131,7 +136,9 @@
       throw new UnsupportedError(
           'The IsolateMirrorsExecutor does not support precompiled dill files');
     }
-    return _sendRequest(new LoadMacroRequest(library, name));
+    return _sendRequest(new LoadMacroRequest(library, name,
+        // Serialization zones are not necessary in this executor.
+        serializationZoneId: -1));
   }
 
   /// Sends a request and returns the response, casting it to the expected
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/isolate_mirrors_impl.dart b/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/isolate_mirrors_impl.dart
index e6848f8..fe7e32e 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/isolate_mirrors_impl.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/isolate_mirrors_impl.dart
@@ -6,6 +6,7 @@
 import 'dart:isolate';
 import 'dart:mirrors';
 
+import '../executor_shared/introspection_impls.dart';
 import '../executor_shared/response_impls.dart';
 import '../executor_shared/protocol.dart';
 import '../api.dart';
@@ -83,9 +84,9 @@
       throw new StateError('Unrecognized macro instance ${request.macro}\n'
           'Known instances: $_macroInstances)');
     }
-    Declaration declaration = request.declaration;
+    DeclarationImpl declaration = request.declaration;
     if (instance is FunctionDefinitionMacro &&
-        declaration is FunctionDeclaration) {
+        declaration is FunctionDeclarationImpl) {
       FunctionDefinitionBuilderImpl builder = new FunctionDefinitionBuilderImpl(
           declaration,
           request.typeResolver,
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/isolated_executor/isolated_executor.dart b/pkg/_fe_analyzer_shared/lib/src/macros/isolated_executor/isolated_executor.dart
index b33dd94..4b564b6 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/isolated_executor/isolated_executor.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/isolated_executor/isolated_executor.dart
@@ -6,9 +6,10 @@
 import 'dart:isolate';
 
 import '../api.dart';
+import '../executor_shared/introspection_impls.dart';
 import '../executor_shared/protocol.dart';
-import '../executor_shared/serialization.dart';
 import '../executor_shared/response_impls.dart';
+import '../executor_shared/serialization.dart';
 import '../executor.dart';
 
 /// Returns an instance of [_IsolatedMacroExecutor].
@@ -47,7 +48,7 @@
   @override
   Future<MacroExecutionResult> executeDeclarationsPhase(
           MacroInstanceIdentifier macro,
-          Declaration declaration,
+          DeclarationImpl declaration,
           TypeResolver typeResolver,
           ClassIntrospector classIntrospector) =>
       _executors[macro]!.executeDeclarationsPhase(
@@ -56,7 +57,7 @@
   @override
   Future<MacroExecutionResult> executeDefinitionsPhase(
           MacroInstanceIdentifier macro,
-          Declaration declaration,
+          DeclarationImpl declaration,
           TypeResolver typeResolver,
           ClassIntrospector classIntrospector,
           TypeDeclarationResolver typeDeclarationResolver) =>
@@ -65,7 +66,7 @@
 
   @override
   Future<MacroExecutionResult> executeTypesPhase(
-          MacroInstanceIdentifier macro, Declaration declaration) =>
+          MacroInstanceIdentifier macro, DeclarationImpl declaration) =>
       _executors[macro]!.executeTypesPhase(macro, declaration);
 
   @override
@@ -114,6 +115,15 @@
   /// A map of response completers by request id.
   final responseCompleters = <int, Completer<Response>>{};
 
+  /// We need to know which serialization zone to deserialize objects in, so
+  /// that we read them from the correct cache. Each request creates its own
+  /// zone which it stores here by ID and then responses are deserialized in
+  /// the same zone.
+  static final serializationZones = <int, Zone>{};
+
+  /// Incrementing identifier for the serialization zone ids.
+  static int _nextSerializationZoneId = 0;
+
   _SingleIsolatedMacroExecutor(
       {required this.onClose,
       required this.responseStream,
@@ -141,13 +151,16 @@
       if (!sendPortCompleter.isCompleted) {
         sendPortCompleter.complete(message as SendPort);
       } else {
-        withSerializationMode(SerializationMode.server, () {
-          JsonDeserializer deserializer =
-              new JsonDeserializer(message as List<Object?>);
-          SerializableResponse response =
-              new SerializableResponse.deserialize(deserializer);
-          responseStreamController.add(response);
-        });
+        JsonDeserializer deserializer =
+            new JsonDeserializer(message as List<Object?>);
+        // Ever object starts with a zone ID which dictates the zone in which we
+        // should deserialize the message.
+        deserializer.moveNext();
+        int zoneId = deserializer.expectNum();
+        Zone zone = serializationZones[zoneId]!;
+        SerializableResponse response = zone.run(
+            () => new SerializableResponse.deserialize(deserializer, zoneId));
+        responseStreamController.add(response);
       }
     }).onDone(responseStreamController.close);
 
@@ -172,7 +185,7 @@
   @override
   Future<MacroExecutionResult> executeDeclarationsPhase(
       MacroInstanceIdentifier macro,
-      Declaration declaration,
+      DeclarationImpl declaration,
       TypeResolver typeResolver,
       ClassIntrospector classIntrospector) {
     // TODO: implement executeDeclarationsPhase
@@ -182,12 +195,13 @@
   @override
   Future<MacroExecutionResult> executeDefinitionsPhase(
           MacroInstanceIdentifier macro,
-          Declaration declaration,
+          DeclarationImpl declaration,
           TypeResolver typeResolver,
           ClassIntrospector classIntrospector,
           TypeDeclarationResolver typeDeclarationResolver) =>
-      _sendRequest(new ExecuteDefinitionsPhaseRequest(macro, declaration,
-          typeResolver, classIntrospector, typeDeclarationResolver));
+      _sendRequest((zoneId) => new ExecuteDefinitionsPhaseRequest(macro,
+          declaration, typeResolver, classIntrospector, typeDeclarationResolver,
+          serializationZoneId: zoneId));
 
   @override
   Future<MacroExecutionResult> executeTypesPhase(
@@ -201,8 +215,9 @@
           MacroClassIdentifier macroClass,
           String constructor,
           Arguments arguments) =>
-      _sendRequest(
-          new InstantiateMacroRequest(macroClass, constructor, arguments));
+      _sendRequest((zoneId) => new InstantiateMacroRequest(
+          macroClass, constructor, arguments,
+          serializationZoneId: zoneId));
 
   /// These calls are handled by the higher level executor.
   @override
@@ -212,16 +227,24 @@
 
   /// Sends a [request] and handles the response, casting it to the expected
   /// type or throwing the error provided.
-  Future<T> _sendRequest<T>(Request request) =>
+  Future<T> _sendRequest<T>(Request Function(int) requestFactory) =>
       withSerializationMode(SerializationMode.server, () async {
+        int zoneId = _nextSerializationZoneId++;
+        serializationZones[zoneId] = Zone.current;
+        Request request = requestFactory(zoneId);
         JsonSerializer serializer = new JsonSerializer();
         request.serialize(serializer);
         sendPort.send(serializer.result);
         Completer<Response> completer = new Completer<Response>();
         responseCompleters[request.id] = completer;
-        Response response = await completer.future;
-        T? result = response.response as T?;
-        if (result != null) return result;
-        throw response.error!;
+        try {
+          Response response = await completer.future;
+          T? result = response.response as T?;
+          if (result != null) return result;
+          throw response.error!;
+        } finally {
+          // Clean up the zone after the request is done.
+          serializationZones.remove(zoneId);
+        }
       });
 }
diff --git a/pkg/_fe_analyzer_shared/test/macros/executor_shared/serialization_test.dart b/pkg/_fe_analyzer_shared/test/macros/executor_shared/serialization_test.dart
index c069423..9faf079 100644
--- a/pkg/_fe_analyzer_shared/test/macros/executor_shared/serialization_test.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/executor_shared/serialization_test.dart
@@ -2,12 +2,14 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import 'package:_fe_analyzer_shared/src/macros/executor_shared/introspection_impls.dart';
+import 'package:_fe_analyzer_shared/src/macros/executor_shared/remote_instance.dart';
 import 'package:_fe_analyzer_shared/src/macros/executor_shared/serialization.dart';
 import 'package:test/test.dart';
 
 void main() {
   group('json serializer', () {
-    test('can serialize and deserialize data', () {
+    test('can serialize and deserialize basic data', () {
       var serializer = JsonSerializer();
       serializer
         ..addNum(1)
@@ -74,5 +76,41 @@
 
       expect(deserializer.moveNext(), false);
     });
+
+    test('remote instances', () async {
+      var string = NamedTypeAnnotationImpl(
+          id: RemoteInstance.uniqueId,
+          isNullable: false,
+          name: 'String',
+          typeArguments: const []);
+      var foo = NamedTypeAnnotationImpl(
+          id: RemoteInstance.uniqueId,
+          isNullable: false,
+          name: 'Foo',
+          typeArguments: [string]);
+      Object? serializedFoo;
+      var serializer = JsonSerializer();
+
+      withSerializationMode(SerializationMode.server, () {
+        foo.serialize(serializer);
+        serializedFoo = serializer.result;
+        var response = roundTrip(serializedFoo);
+        var deserializer = JsonDeserializer(response as List<Object?>);
+        var instance = RemoteInstance.deserialize(deserializer);
+        expect(instance, foo);
+      });
+    });
+  });
+}
+
+/// Deserializes [serialized] in client mode and sends it back.
+Object? roundTrip(Object? serialized) {
+  return withSerializationMode(SerializationMode.client, () {
+    var deserializer = JsonDeserializer(serialized as List<Object?>);
+    var instance =
+        RemoteInstance.deserialize<NamedTypeAnnotationImpl>(deserializer);
+    var serializer = JsonSerializer();
+    instance.serialize(serializer);
+    return serializer.result;
   });
 }
diff --git a/pkg/_fe_analyzer_shared/test/macros/isolate_mirror_executor/isolate_mirror_executor_test.dart b/pkg/_fe_analyzer_shared/test/macros/isolate_mirror_executor/isolate_mirror_executor_test.dart
index 7056208..aae2ad0 100644
--- a/pkg/_fe_analyzer_shared/test/macros/isolate_mirror_executor/isolate_mirror_executor_test.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/isolate_mirror_executor/isolate_mirror_executor_test.dart
@@ -7,6 +7,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_shared/introspection_impls.dart';
+import 'package:_fe_analyzer_shared/src/macros/executor_shared/remote_instance.dart';
 import 'package:_fe_analyzer_shared/src/macros/isolate_mirrors_executor/isolate_mirrors_executor.dart'
     as mirrorExecutor;
 
@@ -15,6 +16,16 @@
 
 void main() {
   late MacroExecutor executor;
+  late File simpleMacroFile;
+
+  setUpAll(() {
+    // We support running from either the root of the SDK or the package root.
+    simpleMacroFile =
+        File('pkg/_fe_analyzer_shared/test/macros/simple_macro.dart');
+    if (!simpleMacroFile.existsSync()) {
+      simpleMacroFile = File('test/macros/simple_macro.dart');
+    }
+  });
 
   setUp(() async {
     executor = await mirrorExecutor.start();
@@ -25,12 +36,8 @@
   });
 
   test('can load macros and create instances', () async {
-    var clazzId = await executor.loadMacro(
-        // Tests run from the root of the repo.
-        File('pkg/_fe_analyzer_shared/test/macros/simple_macro.dart')
-            .absolute
-            .uri,
-        'SimpleMacro');
+    var clazzId =
+        await executor.loadMacro(simpleMacroFile.absolute.uri, 'SimpleMacro');
     expect(clazzId, isNotNull, reason: 'Can load a macro.');
 
     var instanceId =
@@ -51,6 +58,7 @@
     var definitionResult = await executor.executeDefinitionsPhase(
         instanceId,
         FunctionDeclarationImpl(
+          id: RemoteInstance.uniqueId,
           isAbstract: false,
           isExternal: false,
           isGetter: false,
@@ -59,7 +67,10 @@
           namedParameters: [],
           positionalParameters: [],
           returnType: NamedTypeAnnotationImpl(
-              name: 'String', isNullable: false, typeArguments: const []),
+              id: RemoteInstance.uniqueId,
+              name: 'String',
+              isNullable: false,
+              typeArguments: const []),
           typeParameters: [],
         ),
         _FakeTypeResolver(),
diff --git a/pkg/_fe_analyzer_shared/test/macros/isolated_executor/isolated_executor_test.dart b/pkg/_fe_analyzer_shared/test/macros/isolated_executor/isolated_executor_test.dart
index 4659971..c4cac8b 100644
--- a/pkg/_fe_analyzer_shared/test/macros/isolated_executor/isolated_executor_test.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/isolated_executor/isolated_executor_test.dart
@@ -9,6 +9,7 @@
 import 'package:_fe_analyzer_shared/src/macros/bootstrap.dart';
 import 'package:_fe_analyzer_shared/src/macros/executor.dart';
 import 'package:_fe_analyzer_shared/src/macros/executor_shared/introspection_impls.dart';
+import 'package:_fe_analyzer_shared/src/macros/executor_shared/remote_instance.dart';
 import 'package:_fe_analyzer_shared/src/macros/isolated_executor/isolated_executor.dart'
     as isolatedExecutor;
 
@@ -18,6 +19,16 @@
 void main() {
   late MacroExecutor executor;
   late Directory tmpDir;
+  late File simpleMacroFile;
+
+  setUpAll(() {
+    // We support running from either the root of the SDK or the package root.
+    simpleMacroFile =
+        File('pkg/_fe_analyzer_shared/test/macros/simple_macro.dart');
+    if (!simpleMacroFile.existsSync()) {
+      simpleMacroFile = File('test/macros/simple_macro.dart');
+    }
+  });
 
   setUp(() async {
     executor = await isolatedExecutor.start();
@@ -30,10 +41,7 @@
   });
 
   test('can load macros and create instances', () async {
-    // Tests run from the root of the repo.
-    var macroUri = File('pkg/_fe_analyzer_shared/test/macros/simple_macro.dart')
-        .absolute
-        .uri;
+    var macroUri = simpleMacroFile.absolute.uri;
     var macroName = 'SimpleMacro';
 
     var bootstrapContent =
@@ -73,6 +81,7 @@
     var definitionResult = await executor.executeDefinitionsPhase(
         instanceId,
         FunctionDeclarationImpl(
+          id: RemoteInstance.uniqueId,
           isAbstract: false,
           isExternal: false,
           isGetter: false,
@@ -81,7 +90,10 @@
           namedParameters: [],
           positionalParameters: [],
           returnType: NamedTypeAnnotationImpl(
-              name: 'String', isNullable: false, typeArguments: const []),
+              id: RemoteInstance.uniqueId,
+              name: 'String',
+              isNullable: false,
+              typeArguments: const []),
           typeParameters: [],
         ),
         _FakeTypeResolver(),
diff --git a/pkg/front_end/lib/src/fasta/source/diet_listener.dart b/pkg/front_end/lib/src/fasta/source/diet_listener.dart
index 77c43b7..99fa6f1 100644
--- a/pkg/front_end/lib/src/fasta/source/diet_listener.dart
+++ b/pkg/front_end/lib/src/fasta/source/diet_listener.dart
@@ -50,6 +50,8 @@
     show InferenceDataForTesting, TypeInferenceEngine;
 import '../type_inference/type_inferrer.dart' show TypeInferrer;
 import 'diet_parser.dart';
+import 'source_constructor_builder.dart';
+import 'source_enum_builder.dart';
 import 'source_field_builder.dart';
 import 'source_function_builder.dart';
 import 'source_library_builder.dart' show SourceLibraryBuilder;
@@ -962,6 +964,19 @@
   void endEnum(Token enumKeyword, Token leftBrace, int memberCount) {
     debugEvent("Enum");
     checkEmpty(enumKeyword.charOffset);
+
+    SourceEnumBuilder? enumBuilder = currentClass as SourceEnumBuilder?;
+    if (enumBuilder != null) {
+      DeclaredSourceConstructorBuilder? defaultConstructorBuilder =
+          enumBuilder.synthesizedDefaultConstructorBuilder;
+      if (defaultConstructorBuilder != null) {
+        BodyBuilder bodyBuilder =
+            createFunctionListener(defaultConstructorBuilder);
+        bodyBuilder.finishConstructor(
+            defaultConstructorBuilder, AsyncMarker.Sync, new EmptyStatement());
+      }
+    }
+
     currentDeclaration = null;
     memberScope = libraryBuilder.scope;
   }
diff --git a/pkg/front_end/lib/src/fasta/source/outline_builder.dart b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
index 820c2d5..6fe3c15 100644
--- a/pkg/front_end/lib/src/fasta/source/outline_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
@@ -2329,7 +2329,7 @@
 
     // We pop more values than needed to reach typeVariables, offset and name.
     List<TypeBuilder>? interfaces = pop() as List<TypeBuilder>?;
-    List<TypeBuilder>? mixins = pop() as List<TypeBuilder>?;
+    Object? mixins = pop();
     List<TypeVariableBuilder>? typeVariables =
         pop() as List<TypeVariableBuilder>?;
     int charOffset = popCharOffset(); // identifier char offset.
@@ -2341,7 +2341,7 @@
     push(name ?? NullValue.Name);
     push(charOffset);
     push(typeVariables ?? NullValue.TypeVariables);
-    push(mixins ?? NullValue.TypeBuilderList);
+    push(mixins ?? NullValue.TypeBuilder);
     push(interfaces ?? NullValue.TypeBuilderList);
 
     push(enumKeyword.charOffset); // start char offset.
@@ -2364,7 +2364,7 @@
     int endCharOffset = popCharOffset();
     int startCharOffset = popCharOffset();
     pop() as List<TypeBuilder>?; // interfaces.
-    pop() as List<TypeBuilder>?; // mixins.
+    MixinApplicationBuilder? mixinBuilder = pop() as MixinApplicationBuilder?;
     List<TypeVariableBuilder>? typeVariables =
         pop() as List<TypeVariableBuilder>?;
     int charOffset = popCharOffset(); // identifier char offset.
@@ -2373,8 +2373,15 @@
     checkEmpty(startCharOffset);
 
     if (name is! ParserRecovery) {
-      libraryBuilder.addEnum(metadata, name as String, typeVariables,
-          enumConstantInfos, startCharOffset, charOffset, endCharOffset);
+      libraryBuilder.addEnum(
+          metadata,
+          name as String,
+          typeVariables,
+          mixinBuilder,
+          enumConstantInfos,
+          startCharOffset,
+          charOffset,
+          endCharOffset);
     } else {
       libraryBuilder
           .endNestedDeclaration(
@@ -3199,15 +3206,23 @@
     if (mixins is ParserRecovery) {
       push(new ParserRecovery(withKeyword.charOffset));
     } else {
-      // TODO(cstefantsova): Handle enum mixins here.
-      push(mixins);
+      NamedTypeBuilder enumType = new NamedTypeBuilder(
+          "_Enum",
+          const NullabilityBuilder.omitted(),
+          /* arguments = */ null,
+          /* fileUri = */ null,
+          /* charOffset = */ null,
+          instanceTypeVariableAccess:
+              InstanceTypeVariableAccessState.Unexpected);
+      push(libraryBuilder.addMixinApplication(
+          enumType, mixins as List<TypeBuilder>, withKeyword.charOffset));
     }
   }
 
   @override
   void handleEnumNoWithClause() {
     debugEvent("EnumNoWithClause");
-    push(NullValue.TypeBuilderList);
+    push(NullValue.TypeBuilder);
   }
 
   @override
diff --git a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
index 3faf679..c95900c 100644
--- a/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_class_builder.dart
@@ -105,6 +105,8 @@
 
   SourceClassBuilder? _patchBuilder;
 
+  final bool isEnumMixin;
+
   SourceClassBuilder(
       List<MetadataBuilder>? metadata,
       int modifiers,
@@ -124,7 +126,8 @@
       {Class? cls,
       this.mixedInTypeBuilder,
       this.isMixinDeclaration = false,
-      this.isMacro: false})
+      this.isMacro: false,
+      this.isEnumMixin: false})
       : actualCls = initializeClass(cls, typeVariables, name, parent,
             startCharOffset, nameOffset, charEndOffset, referencesFromIndexed),
         super(metadata, modifiers, name, typeVariables, supertype, interfaces,
@@ -195,6 +198,11 @@
     if (supertypeBuilder != null) {
       supertypeBuilder = _checkSupertype(supertypeBuilder!);
     }
+    if (isEnumMixin) {
+      assert(supertypeBuilder?.name == "_Enum");
+      supertypeBuilder?.resolveIn(coreLibrary.scope,
+          supertypeBuilder?.charOffset ?? charOffset, fileUri, library);
+    }
     Supertype? supertype =
         supertypeBuilder?.buildSupertype(library, charOffset, fileUri);
     if (supertype != null) {
diff --git a/pkg/front_end/lib/src/fasta/source/source_enum_builder.dart b/pkg/front_end/lib/src/fasta/source/source_enum_builder.dart
index 2f84424..9a0fe61 100644
--- a/pkg/front_end/lib/src/fasta/source/source_enum_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_enum_builder.dart
@@ -46,6 +46,7 @@
 import '../builder/nullability_builder.dart';
 import '../builder/procedure_builder.dart';
 import '../builder/type_builder.dart';
+import '../builder/type_declaration_builder.dart';
 import '../builder/type_variable_builder.dart';
 import '../fasta_codes.dart'
     show
@@ -86,7 +87,7 @@
 
   final NamedTypeBuilder listType;
 
-  DeclaredSourceConstructorBuilder? _synthesizedDefaultConstructorBuilder;
+  DeclaredSourceConstructorBuilder? synthesizedDefaultConstructorBuilder;
 
   SourceEnumBuilder.internal(
       List<MetadataBuilder>? metadata,
@@ -99,7 +100,7 @@
       this.intType,
       this.listType,
       this.objectType,
-      TypeBuilder enumType,
+      TypeBuilder supertypeBuilder,
       this.stringType,
       SourceLibraryBuilder parent,
       int startCharOffset,
@@ -111,7 +112,7 @@
             0,
             name,
             typeVariables,
-            enumType,
+            supertypeBuilder,
             /* interfaces = */ null,
             /* onTypes = */ null,
             scope,
@@ -128,6 +129,7 @@
       List<MetadataBuilder>? metadata,
       String name,
       List<TypeVariableBuilder>? typeVariables,
+      TypeBuilder? supertypeBuilder,
       List<EnumConstantInfo?>? enumConstantInfos,
       SourceLibraryBuilder parent,
       int startCharOffset,
@@ -173,7 +175,7 @@
         /* fileUri = */ null,
         /* charOffset = */ null,
         instanceTypeVariableAccess: InstanceTypeVariableAccessState.Unexpected);
-    NamedTypeBuilder enumType = new NamedTypeBuilder(
+    supertypeBuilder ??= new NamedTypeBuilder(
         "_Enum",
         const NullabilityBuilder.omitted(),
         /* arguments = */ null,
@@ -418,14 +420,14 @@
         intType,
         listType,
         objectType,
-        enumType,
+        supertypeBuilder,
         stringType,
         parent,
         startCharOffsetComputed,
         charOffset,
         charEndOffset,
         referencesFromIndexed)
-      .._synthesizedDefaultConstructorBuilder =
+      ..synthesizedDefaultConstructorBuilder =
           synthesizedDefaultConstructorBuilder;
 
     void setParent(String name, MemberBuilder? builder) {
@@ -458,9 +460,21 @@
         coreLibrary.scope, charOffset, fileUri, libraryBuilder);
     objectType.resolveIn(
         coreLibrary.scope, charOffset, fileUri, libraryBuilder);
-    TypeBuilder supertypeBuilder = this.supertypeBuilder!;
-    supertypeBuilder.resolveIn(
-        coreLibrary.scope, charOffset, fileUri, libraryBuilder);
+    TypeBuilder? supertypeBuilder = this.supertypeBuilder;
+    NamedTypeBuilder? enumType;
+
+    while (enumType == null && supertypeBuilder is NamedTypeBuilder) {
+      TypeDeclarationBuilder? superclassBuilder = supertypeBuilder.declaration;
+      if (superclassBuilder is ClassBuilder &&
+          superclassBuilder.isMixinApplication) {
+        supertypeBuilder = superclassBuilder.supertypeBuilder;
+      } else {
+        enumType = supertypeBuilder;
+      }
+    }
+    assert(enumType is NamedTypeBuilder && enumType.name == "_Enum");
+    enumType!.resolveIn(coreLibrary.scope, charOffset, fileUri, libraryBuilder);
+
     listType.resolveIn(coreLibrary.scope, charOffset, fileUri, libraryBuilder);
 
     List<Expression> values = <Expression>[];
@@ -481,34 +495,39 @@
     valuesBuilder.build(libraryBuilder);
 
     // The super initializer for the synthesized default constructor is
-    // inserted here. Other constructors are handled in
-    // [BodyBuilder.finishConstructor], as they are processed via the pipeline
-    // for constructor parsing and building.
-    if (_synthesizedDefaultConstructorBuilder != null) {
-      Constructor constructor =
-          _synthesizedDefaultConstructorBuilder!.build(libraryBuilder);
-      ClassBuilder objectClass = objectType.declaration as ClassBuilder;
-      ClassBuilder enumClass = supertypeBuilder.declaration as ClassBuilder;
-      MemberBuilder? superConstructor = enumClass.findConstructorOrFactory(
-          "", charOffset, fileUri, libraryBuilder);
-      if (superConstructor == null || !superConstructor.isConstructor) {
-        // TODO(ahe): Ideally, we would also want to check that [Object]'s
-        // unnamed constructor requires no arguments. But that information
-        // isn't always available at this point, and it's not really a
-        // situation that can happen unless you start modifying the SDK
-        // sources. (We should add a correct message. We no longer depend on
-        // Object here.)
-        library.addProblem(
-            messageNoUnnamedConstructorInObject,
-            objectClass.charOffset,
-            objectClass.name.length,
-            objectClass.fileUri);
-      } else {
-        constructor.initializers.add(new SuperInitializer(
-            superConstructor.member as Constructor,
-            new Arguments.forwarded(
-                constructor.function, libraryBuilder.library))
-          ..parent = constructor);
+    // inserted here if the enum's supertype is _Enum to preserve the legacy
+    // behavior or having the old-style enum constants built in the outlines.
+    // Other constructors are handled in [BodyBuilder.finishConstructor] as
+    // they are processed via the pipeline for constructor parsing and
+    // building.
+    if (identical(this.supertypeBuilder, enumType)) {
+      if (synthesizedDefaultConstructorBuilder != null) {
+        Constructor constructor =
+            synthesizedDefaultConstructorBuilder!.build(libraryBuilder);
+        ClassBuilder objectClass = objectType.declaration as ClassBuilder;
+        ClassBuilder enumClass = enumType.declaration as ClassBuilder;
+        MemberBuilder? superConstructor = enumClass.findConstructorOrFactory(
+            "", charOffset, fileUri, libraryBuilder);
+        if (superConstructor == null || !superConstructor.isConstructor) {
+          // TODO(ahe): Ideally, we would also want to check that [Object]'s
+          // unnamed constructor requires no arguments. But that information
+          // isn't always available at this point, and it's not really a
+          // situation that can happen unless you start modifying the SDK
+          // sources. (We should add a correct message. We no longer depend on
+          // Object here.)
+          library.addProblem(
+              messageNoUnnamedConstructorInObject,
+              objectClass.charOffset,
+              objectClass.name.length,
+              objectClass.fileUri);
+        } else {
+          constructor.initializers.add(new SuperInitializer(
+              superConstructor.member as Constructor,
+              new Arguments.forwarded(
+                  constructor.function, libraryBuilder.library))
+            ..parent = constructor);
+        }
+        synthesizedDefaultConstructorBuilder = null;
       }
     }
 
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index 28267ee..0ef86e7 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -2103,7 +2103,8 @@
       List<TypeVariableBuilder>? typeVariables,
       int modifiers: 0,
       List<TypeBuilder>? interfaces,
-      required bool isMacro}) {
+      required bool isMacro,
+      bool isEnumMixin: false}) {
     if (name == null) {
       // The following parameters should only be used when building a named
       // mixin application.
@@ -2322,7 +2323,8 @@
             charEndOffset,
             referencesFromIndexedClass,
             mixedInTypeBuilder: isMixinDeclaration ? null : mixin,
-            isMacro: isNamedMixinApplication && isMacro);
+            isMacro: isNamedMixinApplication && isMacro,
+            isEnumMixin: isEnumMixin && i == 0);
         // TODO(ahe, kmillikin): Should always be true?
         // pkg/analyzer/test/src/summary/resynthesize_kernel_test.dart can't
         // handle that :(
@@ -2791,6 +2793,7 @@
       List<MetadataBuilder>? metadata,
       String name,
       List<TypeVariableBuilder>? typeVariables,
+      TypeBuilder? supertypeBuilder,
       List<EnumConstantInfo?>? enumConstantInfos,
       int startCharOffset,
       int charOffset,
@@ -2812,6 +2815,9 @@
         metadata,
         name,
         typeVariables,
+        applyMixins(supertypeBuilder, startCharOffset, charOffset,
+            charEndOffset, name, /* isMixinDeclaration = */ false,
+            typeVariables: typeVariables, isMacro: false, isEnumMixin: true),
         enumConstantInfos,
         this,
         startCharOffset,
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index 7cfd23a..e105550 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -353,6 +353,7 @@
 deterministic
 dev
 device
+dictates
 diff
 differs
 diffs
@@ -735,6 +736,7 @@
 lhs
 lib
 libs
+lifetime
 lifted
 lifter
 limiting
@@ -1074,6 +1076,7 @@
 recompiling
 recompute
 recomputed
+reconstruct
 recorder
 recoveries
 recreate
@@ -1375,6 +1378,7 @@
 topological
 tops
 tput
+traced
 tracker
 traditional
 transformers
@@ -1602,6 +1606,7 @@
 yields
 yn
 ynon
+yourself
 ypotentially
 ys
 yss
@@ -1613,3 +1618,4 @@
 zn
 zone
 zoned
+zones
diff --git a/pkg/front_end/test/spell_checking_list_common.txt b/pkg/front_end/test/spell_checking_list_common.txt
index 3f775f2..09b0c93 100644
--- a/pkg/front_end/test/spell_checking_list_common.txt
+++ b/pkg/front_end/test/spell_checking_list_common.txt
@@ -1029,6 +1029,7 @@
 entries
 entry
 enum
+enum's
 enumerated
 enumeration
 enumerations
diff --git a/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart
new file mode 100644
index 0000000..81e592e
--- /dev/null
+++ b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart
@@ -0,0 +1,51 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+class A {
+  String get foo => "foo";
+}
+
+class B {
+  int bar() => 42;
+}
+
+mixin M {
+  void set callOnAssignment(void Function() f) {
+    f();
+  }
+}
+
+enum E1 with A { one, two }
+
+enum E2 with A, B { one, two }
+
+enum E3 with M { one, two }
+
+expectEquals(x, y) {
+  if (x != y) {
+    throw "Expected '$x' and '$y' to be equal.";
+  }
+}
+
+expectThrows(void Function() f) {
+  try {
+    f();
+    throw "Expected function to throw.";
+  } catch (e) {}
+}
+
+void throwOnCall() {
+  throw 42;
+}
+
+main() {
+  expectEquals(E1.one.foo, "foo");
+  expectEquals(E1.two.foo, "foo");
+  expectEquals(E2.one.foo, "foo");
+  expectEquals(E2.two.foo, "foo");
+  expectEquals(E2.one.bar(), "bar");
+  expectEquals(E2.two.bar(), "bar");
+  expectThrows(E3.one.callOnAssignment = throwOnCall);
+  expectThrows(E3.two.callOnAssignment = throwOnCall);
+}
diff --git a/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.strong.expect b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.strong.expect
new file mode 100644
index 0000000..6e10e5c
--- /dev/null
+++ b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.strong.expect
@@ -0,0 +1,137 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+  get foo() → core::String
+    return "foo";
+}
+class B extends core::Object {
+  synthetic constructor •() → self::B
+    : super core::Object::•()
+    ;
+  method bar() → core::int
+    return 42;
+}
+abstract class M extends core::Object /*isMixinDeclaration*/  {
+  set callOnAssignment(() → void f) → void {
+    f(){() → void};
+  }
+}
+abstract class _E1&_Enum&A = core::_Enum with self::A /*isAnonymousMixin,hasConstConstructor*/  {
+  const synthetic constructor •(core::int index, core::String _name) → self::_E1&_Enum&A
+    : super core::_Enum::•(index, _name)
+    ;
+  mixin-super-stub get foo() → core::String
+    return super.{self::A::foo};
+}
+class E1 extends self::_E1&_Enum&A /*isEnum*/  {
+  static const field core::List<self::E1> values = #C7;
+  static const field self::E1 one = #C3;
+  static const field self::E1 two = #C6;
+  const constructor •(core::int index, core::String name) → self::E1
+    : super self::_E1&_Enum&A::•(index, name)
+    ;
+  method toString() → core::String
+    ;
+}
+abstract class _E2&_Enum&A = core::_Enum with self::A /*isAnonymousMixin,hasConstConstructor*/  {
+  const synthetic constructor •(core::int index, core::String _name) → self::_E2&_Enum&A
+    : super core::_Enum::•(index, _name)
+    ;
+  mixin-super-stub get foo() → core::String
+    return super.{self::A::foo};
+}
+abstract class _E2&_Enum&A&B = self::_E2&_Enum&A with self::B /*isAnonymousMixin,hasConstConstructor*/  {
+  const synthetic constructor •(core::int index, core::String _name) → self::_E2&_Enum&A&B
+    : super self::_E2&_Enum&A::•(index, _name)
+    ;
+  mixin-super-stub method bar() → core::int
+    return super.{self::B::bar}();
+}
+class E2 extends self::_E2&_Enum&A&B /*isEnum*/  {
+  static const field core::List<self::E2> values = #C10;
+  static const field self::E2 one = #C8;
+  static const field self::E2 two = #C9;
+  const constructor •(core::int index, core::String name) → self::E2
+    : super self::_E2&_Enum&A&B::•(index, name)
+    ;
+  method toString() → core::String
+    ;
+}
+abstract class _E3&_Enum&M = core::_Enum with self::M /*isAnonymousMixin,hasConstConstructor*/  {
+  const synthetic constructor •(core::int index, core::String _name) → self::_E3&_Enum&M
+    : super core::_Enum::•(index, _name)
+    ;
+  mixin-super-stub set callOnAssignment(() → void f) → void
+    return super.{self::M::callOnAssignment} = f;
+}
+class E3 extends self::_E3&_Enum&M /*isEnum*/  {
+  static const field core::List<self::E3> values = #C13;
+  static const field self::E3 one = #C11;
+  static const field self::E3 two = #C12;
+  const constructor •(core::int index, core::String name) → self::E3
+    : super self::_E3&_Enum&M::•(index, name)
+    ;
+  method toString() → core::String
+    ;
+}
+static method expectEquals(dynamic x, dynamic y) → dynamic {
+  if(!(x =={core::Object::==}{(core::Object) → core::bool} y)) {
+    throw "Expected '${x}' and '${y}' to be equal.";
+  }
+}
+static method expectThrows(() → void f) → dynamic {
+  try {
+    f(){() → void};
+    throw "Expected function to throw.";
+  }
+  on core::Object catch(final core::Object e) {
+  }
+}
+static method throwOnCall() → void {
+  throw 42;
+}
+static method main() → dynamic {
+  self::expectEquals(#C3.{self::_E1&_Enum&A::foo}{core::String}, "foo");
+  self::expectEquals(#C6.{self::_E1&_Enum&A::foo}{core::String}, "foo");
+  self::expectEquals(#C8.{self::_E2&_Enum&A::foo}{core::String}, "foo");
+  self::expectEquals(#C9.{self::_E2&_Enum&A::foo}{core::String}, "foo");
+  self::expectEquals(#C8.{self::_E2&_Enum&A&B::bar}(){() → core::int}, "bar");
+  self::expectEquals(#C9.{self::_E2&_Enum&A&B::bar}(){() → core::int}, "bar");
+  self::expectThrows(#C11.{self::_E3&_Enum&M::callOnAssignment} = #C14);
+  self::expectThrows(#C12.{self::_E3&_Enum&M::callOnAssignment} = #C14);
+}
+
+constants  {
+  #C1 = 0
+  #C2 = "one"
+  #C3 = self::E1 {index:#C1, _name:#C2}
+  #C4 = 1
+  #C5 = "two"
+  #C6 = self::E1 {index:#C4, _name:#C5}
+  #C7 = <self::E1>[#C3, #C6]
+  #C8 = self::E2 {index:#C1, _name:#C2}
+  #C9 = self::E2 {index:#C4, _name:#C5}
+  #C10 = <self::E2>[#C8, #C9]
+  #C11 = self::E3 {index:#C1, _name:#C2}
+  #C12 = self::E3 {index:#C4, _name:#C5}
+  #C13 = <self::E3>[#C11, #C12]
+  #C14 = static-tearoff self::throwOnCall
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///simple_mixins.dart:
+- E1. (from org-dartlang-testcase:///simple_mixins.dart:19:6)
+- _E1&_Enum&A. (from org-dartlang-testcase:///simple_mixins.dart:19:6)
+- _Enum. (from org-dartlang-sdk:///sdk/lib/core/enum.dart:76:9)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
+- E2. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- _E2&_Enum&A&B. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- _E2&_Enum&A. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- E3. (from org-dartlang-testcase:///simple_mixins.dart:23:6)
+- _E3&_Enum&M. (from org-dartlang-testcase:///simple_mixins.dart:23:6)
diff --git a/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.strong.transformed.expect b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.strong.transformed.expect
new file mode 100644
index 0000000..4a5994b
--- /dev/null
+++ b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.strong.transformed.expect
@@ -0,0 +1,138 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+  get foo() → core::String
+    return "foo";
+}
+class B extends core::Object {
+  synthetic constructor •() → self::B
+    : super core::Object::•()
+    ;
+  method bar() → core::int
+    return 42;
+}
+abstract class M extends core::Object /*isMixinDeclaration*/  {
+  set callOnAssignment(() → void f) → void {
+    f(){() → void};
+  }
+}
+abstract class _E1&_Enum&A extends core::_Enum implements self::A /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/  {
+  const synthetic constructor •(core::int index, core::String _name) → self::_E1&_Enum&A
+    : super core::_Enum::•(index, _name)
+    ;
+  get foo() → core::String
+    return "foo";
+}
+class E1 extends self::_E1&_Enum&A /*isEnum*/  {
+  static const field core::List<self::E1> values = #C7;
+  static const field self::E1 one = #C3;
+  static const field self::E1 two = #C6;
+  const constructor •(core::int index, core::String name) → self::E1
+    : super self::_E1&_Enum&A::•(index, name)
+    ;
+  method toString() → core::String
+    ;
+}
+abstract class _E2&_Enum&A extends core::_Enum implements self::A /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/  {
+  const synthetic constructor •(core::int index, core::String _name) → self::_E2&_Enum&A
+    : super core::_Enum::•(index, _name)
+    ;
+  get foo() → core::String
+    return "foo";
+}
+abstract class _E2&_Enum&A&B extends self::_E2&_Enum&A implements self::B /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/  {
+  const synthetic constructor •(core::int index, core::String _name) → self::_E2&_Enum&A&B
+    : super self::_E2&_Enum&A::•(index, _name)
+    ;
+  method bar() → core::int
+    return 42;
+}
+class E2 extends self::_E2&_Enum&A&B /*isEnum*/  {
+  static const field core::List<self::E2> values = #C10;
+  static const field self::E2 one = #C8;
+  static const field self::E2 two = #C9;
+  const constructor •(core::int index, core::String name) → self::E2
+    : super self::_E2&_Enum&A&B::•(index, name)
+    ;
+  method toString() → core::String
+    ;
+}
+abstract class _E3&_Enum&M extends core::_Enum implements self::M /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/  {
+  const synthetic constructor •(core::int index, core::String _name) → self::_E3&_Enum&M
+    : super core::_Enum::•(index, _name)
+    ;
+  set callOnAssignment(() → void f) → void {
+    f(){() → void};
+  }
+}
+class E3 extends self::_E3&_Enum&M /*isEnum*/  {
+  static const field core::List<self::E3> values = #C13;
+  static const field self::E3 one = #C11;
+  static const field self::E3 two = #C12;
+  const constructor •(core::int index, core::String name) → self::E3
+    : super self::_E3&_Enum&M::•(index, name)
+    ;
+  method toString() → core::String
+    ;
+}
+static method expectEquals(dynamic x, dynamic y) → dynamic {
+  if(!(x =={core::Object::==}{(core::Object) → core::bool} y)) {
+    throw "Expected '${x}' and '${y}' to be equal.";
+  }
+}
+static method expectThrows(() → void f) → dynamic {
+  try {
+    f(){() → void};
+    throw "Expected function to throw.";
+  }
+  on core::Object catch(final core::Object e) {
+  }
+}
+static method throwOnCall() → void {
+  throw 42;
+}
+static method main() → dynamic {
+  self::expectEquals(#C3.{self::_E1&_Enum&A::foo}{core::String}, "foo");
+  self::expectEquals(#C6.{self::_E1&_Enum&A::foo}{core::String}, "foo");
+  self::expectEquals(#C8.{self::_E2&_Enum&A::foo}{core::String}, "foo");
+  self::expectEquals(#C9.{self::_E2&_Enum&A::foo}{core::String}, "foo");
+  self::expectEquals(#C8.{self::_E2&_Enum&A&B::bar}(){() → core::int}, "bar");
+  self::expectEquals(#C9.{self::_E2&_Enum&A&B::bar}(){() → core::int}, "bar");
+  self::expectThrows(#C11.{self::_E3&_Enum&M::callOnAssignment} = #C14);
+  self::expectThrows(#C12.{self::_E3&_Enum&M::callOnAssignment} = #C14);
+}
+
+constants  {
+  #C1 = 0
+  #C2 = "one"
+  #C3 = self::E1 {index:#C1, _name:#C2}
+  #C4 = 1
+  #C5 = "two"
+  #C6 = self::E1 {index:#C4, _name:#C5}
+  #C7 = <self::E1>[#C3, #C6]
+  #C8 = self::E2 {index:#C1, _name:#C2}
+  #C9 = self::E2 {index:#C4, _name:#C5}
+  #C10 = <self::E2>[#C8, #C9]
+  #C11 = self::E3 {index:#C1, _name:#C2}
+  #C12 = self::E3 {index:#C4, _name:#C5}
+  #C13 = <self::E3>[#C11, #C12]
+  #C14 = static-tearoff self::throwOnCall
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///simple_mixins.dart:
+- E1. (from org-dartlang-testcase:///simple_mixins.dart:19:6)
+- _E1&_Enum&A. (from org-dartlang-testcase:///simple_mixins.dart:19:6)
+- _Enum. (from org-dartlang-sdk:///sdk/lib/core/enum.dart:76:9)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
+- E2. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- _E2&_Enum&A&B. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- _E2&_Enum&A. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- E3. (from org-dartlang-testcase:///simple_mixins.dart:23:6)
+- _E3&_Enum&M. (from org-dartlang-testcase:///simple_mixins.dart:23:6)
diff --git a/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.textual_outline.expect b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.textual_outline.expect
new file mode 100644
index 0000000..8e3abd8
--- /dev/null
+++ b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.textual_outline.expect
@@ -0,0 +1,16 @@
+class A {
+  String get foo => "foo";
+}
+class B {
+  int bar() => 42;
+}
+mixin M {
+  void set callOnAssignment(void Function() f) {}
+}
+enum E1 with A { one, two }
+enum E2 with A, B { one, two }
+enum E3 with M { one, two }
+expectEquals(x, y) {}
+expectThrows(void Function() f) {}
+void throwOnCall() {}
+main() {}
diff --git a/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.weak.expect b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.weak.expect
new file mode 100644
index 0000000..82c66e8
--- /dev/null
+++ b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.weak.expect
@@ -0,0 +1,137 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+  get foo() → core::String
+    return "foo";
+}
+class B extends core::Object {
+  synthetic constructor •() → self::B
+    : super core::Object::•()
+    ;
+  method bar() → core::int
+    return 42;
+}
+abstract class M extends core::Object /*isMixinDeclaration*/  {
+  set callOnAssignment(() → void f) → void {
+    f(){() → void};
+  }
+}
+abstract class _E1&_Enum&A = core::_Enum with self::A /*isAnonymousMixin,hasConstConstructor*/  {
+  const synthetic constructor •(core::int index, core::String _name) → self::_E1&_Enum&A
+    : super core::_Enum::•(index, _name)
+    ;
+  mixin-super-stub get foo() → core::String
+    return super.{self::A::foo};
+}
+class E1 extends self::_E1&_Enum&A /*isEnum*/  {
+  static const field core::List<self::E1> values = #C7;
+  static const field self::E1 one = #C3;
+  static const field self::E1 two = #C6;
+  const constructor •(core::int index, core::String name) → self::E1
+    : super self::_E1&_Enum&A::•(index, name)
+    ;
+  method toString() → core::String
+    ;
+}
+abstract class _E2&_Enum&A = core::_Enum with self::A /*isAnonymousMixin,hasConstConstructor*/  {
+  const synthetic constructor •(core::int index, core::String _name) → self::_E2&_Enum&A
+    : super core::_Enum::•(index, _name)
+    ;
+  mixin-super-stub get foo() → core::String
+    return super.{self::A::foo};
+}
+abstract class _E2&_Enum&A&B = self::_E2&_Enum&A with self::B /*isAnonymousMixin,hasConstConstructor*/  {
+  const synthetic constructor •(core::int index, core::String _name) → self::_E2&_Enum&A&B
+    : super self::_E2&_Enum&A::•(index, _name)
+    ;
+  mixin-super-stub method bar() → core::int
+    return super.{self::B::bar}();
+}
+class E2 extends self::_E2&_Enum&A&B /*isEnum*/  {
+  static const field core::List<self::E2> values = #C10;
+  static const field self::E2 one = #C8;
+  static const field self::E2 two = #C9;
+  const constructor •(core::int index, core::String name) → self::E2
+    : super self::_E2&_Enum&A&B::•(index, name)
+    ;
+  method toString() → core::String
+    ;
+}
+abstract class _E3&_Enum&M = core::_Enum with self::M /*isAnonymousMixin,hasConstConstructor*/  {
+  const synthetic constructor •(core::int index, core::String _name) → self::_E3&_Enum&M
+    : super core::_Enum::•(index, _name)
+    ;
+  mixin-super-stub set callOnAssignment(() → void f) → void
+    return super.{self::M::callOnAssignment} = f;
+}
+class E3 extends self::_E3&_Enum&M /*isEnum*/  {
+  static const field core::List<self::E3> values = #C13;
+  static const field self::E3 one = #C11;
+  static const field self::E3 two = #C12;
+  const constructor •(core::int index, core::String name) → self::E3
+    : super self::_E3&_Enum&M::•(index, name)
+    ;
+  method toString() → core::String
+    ;
+}
+static method expectEquals(dynamic x, dynamic y) → dynamic {
+  if(!(x =={core::Object::==}{(core::Object) → core::bool} y)) {
+    throw "Expected '${x}' and '${y}' to be equal.";
+  }
+}
+static method expectThrows(() → void f) → dynamic {
+  try {
+    f(){() → void};
+    throw "Expected function to throw.";
+  }
+  on core::Object catch(final core::Object e) {
+  }
+}
+static method throwOnCall() → void {
+  throw 42;
+}
+static method main() → dynamic {
+  self::expectEquals(#C3.{self::_E1&_Enum&A::foo}{core::String}, "foo");
+  self::expectEquals(#C6.{self::_E1&_Enum&A::foo}{core::String}, "foo");
+  self::expectEquals(#C8.{self::_E2&_Enum&A::foo}{core::String}, "foo");
+  self::expectEquals(#C9.{self::_E2&_Enum&A::foo}{core::String}, "foo");
+  self::expectEquals(#C8.{self::_E2&_Enum&A&B::bar}(){() → core::int}, "bar");
+  self::expectEquals(#C9.{self::_E2&_Enum&A&B::bar}(){() → core::int}, "bar");
+  self::expectThrows(#C11.{self::_E3&_Enum&M::callOnAssignment} = #C14);
+  self::expectThrows(#C12.{self::_E3&_Enum&M::callOnAssignment} = #C14);
+}
+
+constants  {
+  #C1 = 0
+  #C2 = "one"
+  #C3 = self::E1 {index:#C1, _name:#C2}
+  #C4 = 1
+  #C5 = "two"
+  #C6 = self::E1 {index:#C4, _name:#C5}
+  #C7 = <self::E1*>[#C3, #C6]
+  #C8 = self::E2 {index:#C1, _name:#C2}
+  #C9 = self::E2 {index:#C4, _name:#C5}
+  #C10 = <self::E2*>[#C8, #C9]
+  #C11 = self::E3 {index:#C1, _name:#C2}
+  #C12 = self::E3 {index:#C4, _name:#C5}
+  #C13 = <self::E3*>[#C11, #C12]
+  #C14 = static-tearoff self::throwOnCall
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///simple_mixins.dart:
+- E1. (from org-dartlang-testcase:///simple_mixins.dart:19:6)
+- _E1&_Enum&A. (from org-dartlang-testcase:///simple_mixins.dart:19:6)
+- _Enum. (from org-dartlang-sdk:///sdk/lib/core/enum.dart:76:9)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
+- E2. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- _E2&_Enum&A&B. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- _E2&_Enum&A. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- E3. (from org-dartlang-testcase:///simple_mixins.dart:23:6)
+- _E3&_Enum&M. (from org-dartlang-testcase:///simple_mixins.dart:23:6)
diff --git a/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.weak.modular.expect b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.weak.modular.expect
new file mode 100644
index 0000000..82c66e8
--- /dev/null
+++ b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.weak.modular.expect
@@ -0,0 +1,137 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+  get foo() → core::String
+    return "foo";
+}
+class B extends core::Object {
+  synthetic constructor •() → self::B
+    : super core::Object::•()
+    ;
+  method bar() → core::int
+    return 42;
+}
+abstract class M extends core::Object /*isMixinDeclaration*/  {
+  set callOnAssignment(() → void f) → void {
+    f(){() → void};
+  }
+}
+abstract class _E1&_Enum&A = core::_Enum with self::A /*isAnonymousMixin,hasConstConstructor*/  {
+  const synthetic constructor •(core::int index, core::String _name) → self::_E1&_Enum&A
+    : super core::_Enum::•(index, _name)
+    ;
+  mixin-super-stub get foo() → core::String
+    return super.{self::A::foo};
+}
+class E1 extends self::_E1&_Enum&A /*isEnum*/  {
+  static const field core::List<self::E1> values = #C7;
+  static const field self::E1 one = #C3;
+  static const field self::E1 two = #C6;
+  const constructor •(core::int index, core::String name) → self::E1
+    : super self::_E1&_Enum&A::•(index, name)
+    ;
+  method toString() → core::String
+    ;
+}
+abstract class _E2&_Enum&A = core::_Enum with self::A /*isAnonymousMixin,hasConstConstructor*/  {
+  const synthetic constructor •(core::int index, core::String _name) → self::_E2&_Enum&A
+    : super core::_Enum::•(index, _name)
+    ;
+  mixin-super-stub get foo() → core::String
+    return super.{self::A::foo};
+}
+abstract class _E2&_Enum&A&B = self::_E2&_Enum&A with self::B /*isAnonymousMixin,hasConstConstructor*/  {
+  const synthetic constructor •(core::int index, core::String _name) → self::_E2&_Enum&A&B
+    : super self::_E2&_Enum&A::•(index, _name)
+    ;
+  mixin-super-stub method bar() → core::int
+    return super.{self::B::bar}();
+}
+class E2 extends self::_E2&_Enum&A&B /*isEnum*/  {
+  static const field core::List<self::E2> values = #C10;
+  static const field self::E2 one = #C8;
+  static const field self::E2 two = #C9;
+  const constructor •(core::int index, core::String name) → self::E2
+    : super self::_E2&_Enum&A&B::•(index, name)
+    ;
+  method toString() → core::String
+    ;
+}
+abstract class _E3&_Enum&M = core::_Enum with self::M /*isAnonymousMixin,hasConstConstructor*/  {
+  const synthetic constructor •(core::int index, core::String _name) → self::_E3&_Enum&M
+    : super core::_Enum::•(index, _name)
+    ;
+  mixin-super-stub set callOnAssignment(() → void f) → void
+    return super.{self::M::callOnAssignment} = f;
+}
+class E3 extends self::_E3&_Enum&M /*isEnum*/  {
+  static const field core::List<self::E3> values = #C13;
+  static const field self::E3 one = #C11;
+  static const field self::E3 two = #C12;
+  const constructor •(core::int index, core::String name) → self::E3
+    : super self::_E3&_Enum&M::•(index, name)
+    ;
+  method toString() → core::String
+    ;
+}
+static method expectEquals(dynamic x, dynamic y) → dynamic {
+  if(!(x =={core::Object::==}{(core::Object) → core::bool} y)) {
+    throw "Expected '${x}' and '${y}' to be equal.";
+  }
+}
+static method expectThrows(() → void f) → dynamic {
+  try {
+    f(){() → void};
+    throw "Expected function to throw.";
+  }
+  on core::Object catch(final core::Object e) {
+  }
+}
+static method throwOnCall() → void {
+  throw 42;
+}
+static method main() → dynamic {
+  self::expectEquals(#C3.{self::_E1&_Enum&A::foo}{core::String}, "foo");
+  self::expectEquals(#C6.{self::_E1&_Enum&A::foo}{core::String}, "foo");
+  self::expectEquals(#C8.{self::_E2&_Enum&A::foo}{core::String}, "foo");
+  self::expectEquals(#C9.{self::_E2&_Enum&A::foo}{core::String}, "foo");
+  self::expectEquals(#C8.{self::_E2&_Enum&A&B::bar}(){() → core::int}, "bar");
+  self::expectEquals(#C9.{self::_E2&_Enum&A&B::bar}(){() → core::int}, "bar");
+  self::expectThrows(#C11.{self::_E3&_Enum&M::callOnAssignment} = #C14);
+  self::expectThrows(#C12.{self::_E3&_Enum&M::callOnAssignment} = #C14);
+}
+
+constants  {
+  #C1 = 0
+  #C2 = "one"
+  #C3 = self::E1 {index:#C1, _name:#C2}
+  #C4 = 1
+  #C5 = "two"
+  #C6 = self::E1 {index:#C4, _name:#C5}
+  #C7 = <self::E1*>[#C3, #C6]
+  #C8 = self::E2 {index:#C1, _name:#C2}
+  #C9 = self::E2 {index:#C4, _name:#C5}
+  #C10 = <self::E2*>[#C8, #C9]
+  #C11 = self::E3 {index:#C1, _name:#C2}
+  #C12 = self::E3 {index:#C4, _name:#C5}
+  #C13 = <self::E3*>[#C11, #C12]
+  #C14 = static-tearoff self::throwOnCall
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///simple_mixins.dart:
+- E1. (from org-dartlang-testcase:///simple_mixins.dart:19:6)
+- _E1&_Enum&A. (from org-dartlang-testcase:///simple_mixins.dart:19:6)
+- _Enum. (from org-dartlang-sdk:///sdk/lib/core/enum.dart:76:9)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
+- E2. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- _E2&_Enum&A&B. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- _E2&_Enum&A. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- E3. (from org-dartlang-testcase:///simple_mixins.dart:23:6)
+- _E3&_Enum&M. (from org-dartlang-testcase:///simple_mixins.dart:23:6)
diff --git a/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.weak.outline.expect b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.weak.outline.expect
new file mode 100644
index 0000000..abd958f
--- /dev/null
+++ b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.weak.outline.expect
@@ -0,0 +1,96 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    ;
+  get foo() → core::String
+    ;
+}
+class B extends core::Object {
+  synthetic constructor •() → self::B
+    ;
+  method bar() → core::int
+    ;
+}
+abstract class M extends core::Object /*isMixinDeclaration*/  {
+  set callOnAssignment(() → void f) → void
+    ;
+}
+abstract class _E1&_Enum&A = core::_Enum with self::A /*isAnonymousMixin,hasConstConstructor*/  {
+  const synthetic constructor •(core::int index, core::String _name) → self::_E1&_Enum&A
+    : super core::_Enum::•(index, _name)
+    ;
+  mixin-super-stub get foo() → core::String
+    return super.{self::A::foo};
+}
+class E1 extends self::_E1&_Enum&A /*isEnum*/  {
+  static const field core::List<self::E1> values = const <self::E1>[self::E1::one, self::E1::two];
+  static const field self::E1 one = const self::E1::•(0, "one");
+  static const field self::E1 two = const self::E1::•(1, "two");
+  const constructor •(core::int index, core::String name) → self::E1
+    ;
+  method toString() → core::String
+    ;
+}
+abstract class _E2&_Enum&A = core::_Enum with self::A /*isAnonymousMixin,hasConstConstructor*/  {
+  const synthetic constructor •(core::int index, core::String _name) → self::_E2&_Enum&A
+    : super core::_Enum::•(index, _name)
+    ;
+  mixin-super-stub get foo() → core::String
+    return super.{self::A::foo};
+}
+abstract class _E2&_Enum&A&B = self::_E2&_Enum&A with self::B /*isAnonymousMixin,hasConstConstructor*/  {
+  const synthetic constructor •(core::int index, core::String _name) → self::_E2&_Enum&A&B
+    : super self::_E2&_Enum&A::•(index, _name)
+    ;
+  mixin-super-stub method bar() → core::int
+    return super.{self::B::bar}();
+}
+class E2 extends self::_E2&_Enum&A&B /*isEnum*/  {
+  static const field core::List<self::E2> values = const <self::E2>[self::E2::one, self::E2::two];
+  static const field self::E2 one = const self::E2::•(0, "one");
+  static const field self::E2 two = const self::E2::•(1, "two");
+  const constructor •(core::int index, core::String name) → self::E2
+    ;
+  method toString() → core::String
+    ;
+}
+abstract class _E3&_Enum&M = core::_Enum with self::M /*isAnonymousMixin,hasConstConstructor*/  {
+  const synthetic constructor •(core::int index, core::String _name) → self::_E3&_Enum&M
+    : super core::_Enum::•(index, _name)
+    ;
+  mixin-super-stub set callOnAssignment(() → void f) → void
+    return super.{self::M::callOnAssignment} = f;
+}
+class E3 extends self::_E3&_Enum&M /*isEnum*/  {
+  static const field core::List<self::E3> values = const <self::E3>[self::E3::one, self::E3::two];
+  static const field self::E3 one = const self::E3::•(0, "one");
+  static const field self::E3 two = const self::E3::•(1, "two");
+  const constructor •(core::int index, core::String name) → self::E3
+    ;
+  method toString() → core::String
+    ;
+}
+static method expectEquals(dynamic x, dynamic y) → dynamic
+  ;
+static method expectThrows(() → void f) → dynamic
+  ;
+static method throwOnCall() → void
+  ;
+static method main() → dynamic
+  ;
+
+
+Extra constant evaluation status:
+Evaluated: ListLiteral @ org-dartlang-testcase:///simple_mixins.dart:19:6 -> ListConstant(const <E1*>[const E1{}, const E1{}])
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///simple_mixins.dart:19:18 -> InstanceConstant(const E1{})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///simple_mixins.dart:19:23 -> InstanceConstant(const E1{})
+Evaluated: ListLiteral @ org-dartlang-testcase:///simple_mixins.dart:21:6 -> ListConstant(const <E2*>[const E2{}, const E2{}])
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///simple_mixins.dart:21:21 -> InstanceConstant(const E2{})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///simple_mixins.dart:21:26 -> InstanceConstant(const E2{})
+Evaluated: ListLiteral @ org-dartlang-testcase:///simple_mixins.dart:23:6 -> ListConstant(const <E3*>[const E3{}, const E3{}])
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///simple_mixins.dart:23:18 -> InstanceConstant(const E3{})
+Evaluated: ConstructorInvocation @ org-dartlang-testcase:///simple_mixins.dart:23:23 -> InstanceConstant(const E3{})
+Extra constant evaluation: evaluated: 22, effectively constant: 9
diff --git a/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.weak.transformed.expect b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.weak.transformed.expect
new file mode 100644
index 0000000..af40a65
--- /dev/null
+++ b/pkg/front_end/testcases/enhanced_enums/simple_mixins.dart.weak.transformed.expect
@@ -0,0 +1,138 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+class A extends core::Object {
+  synthetic constructor •() → self::A
+    : super core::Object::•()
+    ;
+  get foo() → core::String
+    return "foo";
+}
+class B extends core::Object {
+  synthetic constructor •() → self::B
+    : super core::Object::•()
+    ;
+  method bar() → core::int
+    return 42;
+}
+abstract class M extends core::Object /*isMixinDeclaration*/  {
+  set callOnAssignment(() → void f) → void {
+    f(){() → void};
+  }
+}
+abstract class _E1&_Enum&A extends core::_Enum implements self::A /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/  {
+  const synthetic constructor •(core::int index, core::String _name) → self::_E1&_Enum&A
+    : super core::_Enum::•(index, _name)
+    ;
+  get foo() → core::String
+    return "foo";
+}
+class E1 extends self::_E1&_Enum&A /*isEnum*/  {
+  static const field core::List<self::E1> values = #C7;
+  static const field self::E1 one = #C3;
+  static const field self::E1 two = #C6;
+  const constructor •(core::int index, core::String name) → self::E1
+    : super self::_E1&_Enum&A::•(index, name)
+    ;
+  method toString() → core::String
+    ;
+}
+abstract class _E2&_Enum&A extends core::_Enum implements self::A /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/  {
+  const synthetic constructor •(core::int index, core::String _name) → self::_E2&_Enum&A
+    : super core::_Enum::•(index, _name)
+    ;
+  get foo() → core::String
+    return "foo";
+}
+abstract class _E2&_Enum&A&B extends self::_E2&_Enum&A implements self::B /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/  {
+  const synthetic constructor •(core::int index, core::String _name) → self::_E2&_Enum&A&B
+    : super self::_E2&_Enum&A::•(index, _name)
+    ;
+  method bar() → core::int
+    return 42;
+}
+class E2 extends self::_E2&_Enum&A&B /*isEnum*/  {
+  static const field core::List<self::E2> values = #C10;
+  static const field self::E2 one = #C8;
+  static const field self::E2 two = #C9;
+  const constructor •(core::int index, core::String name) → self::E2
+    : super self::_E2&_Enum&A&B::•(index, name)
+    ;
+  method toString() → core::String
+    ;
+}
+abstract class _E3&_Enum&M extends core::_Enum implements self::M /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/  {
+  const synthetic constructor •(core::int index, core::String _name) → self::_E3&_Enum&M
+    : super core::_Enum::•(index, _name)
+    ;
+  set callOnAssignment(() → void f) → void {
+    f(){() → void};
+  }
+}
+class E3 extends self::_E3&_Enum&M /*isEnum*/  {
+  static const field core::List<self::E3> values = #C13;
+  static const field self::E3 one = #C11;
+  static const field self::E3 two = #C12;
+  const constructor •(core::int index, core::String name) → self::E3
+    : super self::_E3&_Enum&M::•(index, name)
+    ;
+  method toString() → core::String
+    ;
+}
+static method expectEquals(dynamic x, dynamic y) → dynamic {
+  if(!(x =={core::Object::==}{(core::Object) → core::bool} y)) {
+    throw "Expected '${x}' and '${y}' to be equal.";
+  }
+}
+static method expectThrows(() → void f) → dynamic {
+  try {
+    f(){() → void};
+    throw "Expected function to throw.";
+  }
+  on core::Object catch(final core::Object e) {
+  }
+}
+static method throwOnCall() → void {
+  throw 42;
+}
+static method main() → dynamic {
+  self::expectEquals(#C3.{self::_E1&_Enum&A::foo}{core::String}, "foo");
+  self::expectEquals(#C6.{self::_E1&_Enum&A::foo}{core::String}, "foo");
+  self::expectEquals(#C8.{self::_E2&_Enum&A::foo}{core::String}, "foo");
+  self::expectEquals(#C9.{self::_E2&_Enum&A::foo}{core::String}, "foo");
+  self::expectEquals(#C8.{self::_E2&_Enum&A&B::bar}(){() → core::int}, "bar");
+  self::expectEquals(#C9.{self::_E2&_Enum&A&B::bar}(){() → core::int}, "bar");
+  self::expectThrows(#C11.{self::_E3&_Enum&M::callOnAssignment} = #C14);
+  self::expectThrows(#C12.{self::_E3&_Enum&M::callOnAssignment} = #C14);
+}
+
+constants  {
+  #C1 = 0
+  #C2 = "one"
+  #C3 = self::E1 {index:#C1, _name:#C2}
+  #C4 = 1
+  #C5 = "two"
+  #C6 = self::E1 {index:#C4, _name:#C5}
+  #C7 = <self::E1*>[#C3, #C6]
+  #C8 = self::E2 {index:#C1, _name:#C2}
+  #C9 = self::E2 {index:#C4, _name:#C5}
+  #C10 = <self::E2*>[#C8, #C9]
+  #C11 = self::E3 {index:#C1, _name:#C2}
+  #C12 = self::E3 {index:#C4, _name:#C5}
+  #C13 = <self::E3*>[#C11, #C12]
+  #C14 = static-tearoff self::throwOnCall
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///simple_mixins.dart:
+- E1. (from org-dartlang-testcase:///simple_mixins.dart:19:6)
+- _E1&_Enum&A. (from org-dartlang-testcase:///simple_mixins.dart:19:6)
+- _Enum. (from org-dartlang-sdk:///sdk/lib/core/enum.dart:76:9)
+- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)
+- E2. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- _E2&_Enum&A&B. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- _E2&_Enum&A. (from org-dartlang-testcase:///simple_mixins.dart:21:6)
+- E3. (from org-dartlang-testcase:///simple_mixins.dart:23:6)
+- _E3&_Enum&M. (from org-dartlang-testcase:///simple_mixins.dart:23:6)
diff --git a/pkg/front_end/testcases/strong.status b/pkg/front_end/testcases/strong.status
index 2aef19d..f57bbdf 100644
--- a/pkg/front_end/testcases/strong.status
+++ b/pkg/front_end/testcases/strong.status
@@ -15,6 +15,7 @@
 
 constructor_tearoffs/call_instantiation: TypeCheckError
 constructor_tearoffs/lowering/invalid_redirect: VerificationError
+enhanced_enums/simple_mixins: RuntimeError
 extension_types/access_setter_as_getter: ExpectationFileMismatchSerialized # Expected.
 extension_types/call_not_get: ExpectationFileMismatchSerialized # Expected.
 extension_types/extension_on_nullable: ExpectationFileMismatchSerialized # Expected.
diff --git a/pkg/front_end/testcases/text_serialization.status b/pkg/front_end/testcases/text_serialization.status
index b66d7f8..5a838fa 100644
--- a/pkg/front_end/testcases/text_serialization.status
+++ b/pkg/front_end/testcases/text_serialization.status
@@ -8,6 +8,7 @@
 
 constructor_tearoffs/call_instantiation: TypeCheckError
 constructor_tearoffs/lowering/invalid_redirect: VerificationError
+enhanced_enums/simple_mixins: RuntimeError
 extension_types/access_setter_as_getter: ExpectationFileMismatchSerialized # Expected.
 extension_types/call_not_get: ExpectationFileMismatchSerialized # Expected.
 extension_types/extension_on_nullable: ExpectationFileMismatchSerialized # Expected.
diff --git a/pkg/front_end/testcases/textual_outline.status b/pkg/front_end/testcases/textual_outline.status
index 6736b08..0228fca 100644
--- a/pkg/front_end/testcases/textual_outline.status
+++ b/pkg/front_end/testcases/textual_outline.status
@@ -35,6 +35,7 @@
 enhanced_enums/qualified_names_with_no_type_arguments: FormatterCrash
 enhanced_enums/redirecting_initializers: FormatterCrash
 enhanced_enums/simple_fields: FormatterCrash
+enhanced_enums/simple_mixins: FormatterCrash
 extension_types/basic_show: FormatterCrash
 extension_types/call_not_get: FormatterCrash
 extension_types/keyword_in_show_hide_element: FormatterCrash
diff --git a/pkg/front_end/testcases/weak.status b/pkg/front_end/testcases/weak.status
index 8898c1a..f75594a 100644
--- a/pkg/front_end/testcases/weak.status
+++ b/pkg/front_end/testcases/weak.status
@@ -22,6 +22,7 @@
 
 constructor_tearoffs/call_instantiation: TypeCheckError
 constructor_tearoffs/lowering/invalid_redirect: VerificationError
+enhanced_enums/simple_mixins: RuntimeError
 extension_types/access_setter_as_getter: ExpectationFileMismatchSerialized # Expected.
 extension_types/call_not_get: ExpectationFileMismatchSerialized # Expected.
 extension_types/extension_on_nullable: ExpectationFileMismatchSerialized # Expected.
diff --git a/runtime/vm/compiler/asm_intrinsifier_arm64.cc b/runtime/vm/compiler/asm_intrinsifier_arm64.cc
index f9c178a..4056cbf 100644
--- a/runtime/vm/compiler/asm_intrinsifier_arm64.cc
+++ b/runtime/vm/compiler/asm_intrinsifier_arm64.cc
@@ -51,9 +51,8 @@
       R1);
 
   // Set the length field in the growable array object to 0.
-  __ LoadImmediate(R1, 0);
   __ StoreCompressedIntoObjectNoBarrier(
-      R0, FieldAddress(R0, target::GrowableObjectArray::length_offset()), R1);
+      R0, FieldAddress(R0, target::GrowableObjectArray::length_offset()), ZR);
   __ ret();  // Returns the newly allocated object in R0.
 
   __ Bind(normal_ir_body);
diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc
index 1c5673c..979c8ba 100644
--- a/runtime/vm/compiler/backend/il_arm64.cc
+++ b/runtime/vm/compiler/backend/il_arm64.cc
@@ -5286,10 +5286,16 @@
   __ CompareObjectRegisters(result_mod, ZR);
   __ b(&done, GE);
   // Result is negative, adjust it.
-  __ CompareObjectRegisters(right, ZR);
-  __ sub(TMP2, result_mod, compiler::Operand(right), compiler::kObjectBytes);
-  __ add(TMP, result_mod, compiler::Operand(right), compiler::kObjectBytes);
-  __ csel(result_mod, TMP, TMP2, GE);
+  if (RangeUtils::IsNegative(divisor_range())) {
+    __ sub(result_mod, result_mod, compiler::Operand(right));
+  } else if (RangeUtils::IsPositive(divisor_range())) {
+    __ add(result_mod, result_mod, compiler::Operand(right));
+  } else {
+    __ CompareObjectRegisters(right, ZR);
+    __ sub(TMP2, result_mod, compiler::Operand(right), compiler::kObjectBytes);
+    __ add(TMP, result_mod, compiler::Operand(right), compiler::kObjectBytes);
+    __ csel(result_mod, TMP, TMP2, GE);
+  }
   __ Bind(&done);
 }
 
@@ -5634,8 +5640,7 @@
 
   // Handle modulo/division by zero exception on slow path.
   if (slow_path->has_divide_by_zero()) {
-    __ CompareRegisters(right, ZR);
-    __ b(slow_path->entry_label(), EQ);
+    __ cbz(slow_path->entry_label(), right);
   }
 
   // Perform actual operation
diff --git a/runtime/vm/compiler/backend/range_analysis.cc b/runtime/vm/compiler/backend/range_analysis.cc
index 7d02f87..20c4ba0 100644
--- a/runtime/vm/compiler/backend/range_analysis.cc
+++ b/runtime/vm/compiler/backend/range_analysis.cc
@@ -2082,6 +2082,10 @@
   return OnlyGreaterThanOrEqualTo(0);
 }
 
+bool Range::IsNegative() const {
+  return OnlyLessThanOrEqualTo(-1);
+}
+
 bool Range::OnlyLessThanOrEqualTo(int64_t val) const {
   const RangeBoundary upper_bound = max().UpperBound();
   return !upper_bound.IsPositiveInfinity() &&
diff --git a/runtime/vm/compiler/backend/range_analysis.h b/runtime/vm/compiler/backend/range_analysis.h
index b0037f4..aa247c7 100644
--- a/runtime/vm/compiler/backend/range_analysis.h
+++ b/runtime/vm/compiler/backend/range_analysis.h
@@ -413,6 +413,9 @@
   // [0, +inf]
   bool IsPositive() const;
 
+  // [-inf, -1]
+  bool IsNegative() const;
+
   // [-inf, val].
   bool OnlyLessThanOrEqualTo(int64_t val) const;
 
@@ -560,6 +563,9 @@
   static bool IsPositive(Range* range) {
     return !Range::IsUnknown(range) && range->IsPositive();
   }
+  static bool IsNegative(Range* range) {
+    return !Range::IsUnknown(range) && range->IsNegative();
+  }
 
   static bool Overlaps(Range* range, intptr_t min, intptr_t max) {
     return Range::IsUnknown(range) || range->Overlaps(min, max);
diff --git a/runtime/vm/compiler/stub_code_compiler_arm64.cc b/runtime/vm/compiler/stub_code_compiler_arm64.cc
index bcdc1f4..4d22204 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm64.cc
@@ -1961,15 +1961,15 @@
       Label not_parameterized_case;
 
       const Register kClsIdReg = R4;
-      const Register kTypeOffestReg = R5;
+      const Register kTypeOffsetReg = R5;
 
       __ ExtractClassIdFromTags(kClsIdReg, kTagsReg);
 
       // Load class' type_arguments_field offset in words.
-      __ LoadClassById(kTypeOffestReg, kClsIdReg);
+      __ LoadClassById(kTypeOffsetReg, kClsIdReg);
       __ ldr(
-          kTypeOffestReg,
-          FieldAddress(kTypeOffestReg,
+          kTypeOffsetReg,
+          FieldAddress(kTypeOffsetReg,
                        target::Class::
                            host_type_arguments_field_offset_in_words_offset()),
           kFourBytes);
@@ -1977,7 +1977,7 @@
       // Set the type arguments in the new object.
       __ StoreCompressedIntoObjectNoBarrier(
           AllocateObjectABI::kResultReg,
-          Address(AllocateObjectABI::kResultReg, kTypeOffestReg, UXTX,
+          Address(AllocateObjectABI::kResultReg, kTypeOffsetReg, UXTX,
                   Address::Scaled),
           AllocateObjectABI::kTypeArgumentsReg);
 
@@ -3723,9 +3723,7 @@
     /* R0: new object start as a tagged pointer. */
     /* R1: new object end address. */
     /* R2: iterator which initially points to the start of the variable */
-    /* R3: scratch register. */
     /* data area to be initialized. */
-    __ mov(R3, ZR);
     __ AddImmediate(R2, R0, target::TypedData::HeaderSize() - 1);
     __ StoreInternalPointer(
         R0, FieldAddress(R0, target::TypedDataBase::data_field_offset()), R2);
@@ -3733,7 +3731,7 @@
     __ Bind(&init_loop);
     __ cmp(R2, Operand(R1));
     __ b(&done, CS);
-    __ str(R3, Address(R2, 0));
+    __ str(ZR, Address(R2, 0));
     __ add(R2, R2, Operand(target::kWordSize));
     __ b(&init_loop);
     __ Bind(&done);
diff --git a/sdk/lib/_internal/js_dev_runtime/private/string_helper.dart b/sdk/lib/_internal/js_dev_runtime/private/string_helper.dart
index 7a0de57..474042a 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/string_helper.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/string_helper.dart
@@ -169,8 +169,15 @@
     var re = regExpGetGlobalNative(pattern);
     return stringReplaceJS(receiver, re, replacement);
   } else {
-    // TODO(floitsch): implement generic String.replace (with patterns).
-    throw "String.replaceAll(Pattern) UNIMPLEMENTED";
+    int startIndex = 0;
+    StringBuffer result = StringBuffer();
+    for (Match match in pattern.allMatches(receiver)) {
+      result.write(substring2Unchecked(receiver, startIndex, match.start));
+      result.write(replacement);
+      startIndex = match.end;
+    }
+    result.write(substring1Unchecked(receiver, startIndex));
+    return result.toString();
   }
 }
 
diff --git a/sdk/lib/_internal/js_runtime/lib/string_helper.dart b/sdk/lib/_internal/js_runtime/lib/string_helper.dart
index 5e6c50d..9305f55 100644
--- a/sdk/lib/_internal/js_runtime/lib/string_helper.dart
+++ b/sdk/lib/_internal/js_runtime/lib/string_helper.dart
@@ -173,9 +173,21 @@
     return stringReplaceJS(receiver, re, replacement);
   }
 
+  return stringReplaceAllGeneral(receiver, pattern, replacement);
+}
+
+String stringReplaceAllGeneral(
+    String receiver, Pattern pattern, String replacement) {
   checkNull(pattern);
-  // TODO(floitsch): implement generic String.replace (with patterns).
-  throw "String.replaceAll(Pattern) UNIMPLEMENTED";
+  int startIndex = 0;
+  StringBuffer result = StringBuffer();
+  for (Match match in pattern.allMatches(receiver)) {
+    result.write(substring2Unchecked(receiver, startIndex, match.start));
+    result.write(replacement);
+    startIndex = match.end;
+  }
+  result.write(substring1Unchecked(receiver, startIndex));
+  return result.toString();
 }
 
 /// Replaces all non-overlapping occurences of [pattern] in [receiver] with
diff --git a/tests/corelib/string_replace_all_common.dart b/tests/corelib/string_replace_all_common.dart
new file mode 100644
index 0000000..4bdbff0
--- /dev/null
+++ b/tests/corelib/string_replace_all_common.dart
@@ -0,0 +1,144 @@
+// 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.
+
+import "package:expect/expect.dart";
+
+testAll(Pattern Function(Pattern) wrap) {
+  testReplaceAll(wrap);
+  testReplaceAllMapped(wrap);
+  testSplitMapJoin(wrap);
+}
+
+testReplaceAll(Pattern Function(Pattern) wrap) {
+  Expect.equals("aXXcaXXdae", "abcabdae".replaceAll(wrap("b"), "XX"));
+
+  // Test with the replaced string at the beginning.
+  Expect.equals("XXbcXXbdXXe", "abcabdae".replaceAll(wrap("a"), "XX"));
+
+  // Test with the replaced string at the end.
+  Expect.equals("abcabdaXX", "abcabdae".replaceAll(wrap("e"), "XX"));
+
+  // Test when there are no occurence of the string to replace.
+  Expect.equals("abcabdae", "abcabdae".replaceAll(wrap("f"), "XX"));
+
+  // Test when the string to change is the empty string.
+  Expect.equals("", "".replaceAll(wrap("from"), "to"));
+
+  // Test when the string to change is a substring of the string to
+  // replace.
+  Expect.equals("fro", "fro".replaceAll(wrap("from"), "to"));
+
+  // Test when the string to change is the replaced string.
+  Expect.equals("to", "from".replaceAll(wrap("from"), "to"));
+
+  // Test when matches are adjacent
+  Expect.equals("toto", "fromfrom".replaceAll(wrap("from"), "to"));
+
+  // Test when the string to change is the replacement string.
+  Expect.equals("to", "to".replaceAll(wrap("from"), "to"));
+
+  // Test replacing by the empty string.
+  Expect.equals("bcbde", "abcabdae".replaceAll(wrap("a"), ""));
+  Expect.equals("AB", "AfromB".replaceAll(wrap("from"), ""));
+
+  // Test changing the empty string.
+  Expect.equals("to", "".replaceAll(wrap(""), "to"));
+
+  // Test replacing the empty string.
+  Expect.equals("toAtoBtoCto", "ABC".replaceAll(wrap(""), "to"));
+
+  // Pattern strings containing RegExp metacharacters - these are not
+  // interpreted as RegExps.
+  Expect.equals(r"$$", "||".replaceAll(wrap("|"), r"$"));
+  Expect.equals(r"$$$$", "||".replaceAll(wrap("|"), r"$$"));
+  Expect.equals(r"x$|x", "x|.|x".replaceAll(wrap("|."), r"$"));
+  Expect.equals(r"$$", "..".replaceAll(wrap("."), r"$"));
+  Expect.equals(r"[$$$$]", "[..]".replaceAll(wrap("."), r"$$"));
+  Expect.equals(r"[$]", "[..]".replaceAll(wrap(".."), r"$"));
+  Expect.equals(r"$$", r"\\".replaceAll(wrap(r"\"), r"$"));
+}
+
+testReplaceAllMapped(Pattern Function(Pattern) wrap) {
+  String mark(Match m) => "[${m[0]}]";
+  Expect.equals("a[b]ca[b]dae", "abcabdae".replaceAllMapped(wrap("b"), mark));
+
+  // Test with the replaced string at the beginning.
+  Expect.equals("[a]bc[a]bd[a]e", "abcabdae".replaceAllMapped(wrap("a"), mark));
+
+  // Test with the replaced string at the end.
+  Expect.equals("abcabda[e]", "abcabdae".replaceAllMapped(wrap("e"), mark));
+
+  // Test when there are no occurence of the string to replace.
+  Expect.equals("abcabdae", "abcabdae".replaceAllMapped(wrap("f"), mark));
+
+  // Test when the string to change is the empty string.
+  Expect.equals("", "".replaceAllMapped(wrap("from"), mark));
+
+  // Test when the string to change is a substring of the string to
+  // replace.
+  Expect.equals("fro", "fro".replaceAllMapped(wrap("from"), mark));
+
+  // Test when matches are adjacent
+  Expect.equals(
+      "[from][from]", "fromfrom".replaceAllMapped(wrap("from"), mark));
+
+  // Test replacing by the empty string.
+  Expect.equals("bcbde", "abcabdae".replaceAllMapped(wrap("a"), (m) => ""));
+  Expect.equals("AB", "AfromB".replaceAllMapped(wrap("from"), (m) => ""));
+
+  // Test changing the empty string.
+  Expect.equals("[]", "".replaceAllMapped(wrap(""), mark));
+
+  // Test replacing the empty string.
+  Expect.equals("[]A[]B[]C[]", "ABC".replaceAllMapped(wrap(""), mark));
+}
+
+testSplitMapJoin(Pattern Function(Pattern) wrap) {
+  String mark(Match m) => "[${m[0]}]";
+  String rest(String s) => "<${s}>";
+
+  Expect.equals("<a>[b]<ca>[b]<dae>",
+      "abcabdae".splitMapJoin(wrap("b"), onMatch: mark, onNonMatch: rest));
+
+  // Test with the replaced string at the beginning.
+  Expect.equals("<>[a]<bc>[a]<bd>[a]<e>",
+      "abcabdae".splitMapJoin(wrap("a"), onMatch: mark, onNonMatch: rest));
+
+  // Test with the replaced string at the end.
+  Expect.equals("<abcabda>[e]<>",
+      "abcabdae".splitMapJoin(wrap("e"), onMatch: mark, onNonMatch: rest));
+
+  // Test when there are no occurence of the string to replace.
+  Expect.equals("<abcabdae>",
+      "abcabdae".splitMapJoin(wrap("f"), onMatch: mark, onNonMatch: rest));
+
+  // Test when the string to change is the empty string.
+  Expect.equals(
+      "<>", "".splitMapJoin(wrap("from"), onMatch: mark, onNonMatch: rest));
+
+  // Test when the string to change is a substring of the string to
+  // replace.
+  Expect.equals("<fro>",
+      "fro".splitMapJoin(wrap("from"), onMatch: mark, onNonMatch: rest));
+
+  // Test when matches are adjacent
+  Expect.equals("<>[from]<>[from]<>",
+      "fromfrom".splitMapJoin(wrap("from"), onMatch: mark, onNonMatch: rest));
+
+  // Test changing the empty string.
+  Expect.equals(
+      "<>[]<>", "".splitMapJoin(wrap(""), onMatch: mark, onNonMatch: rest));
+
+  // Test replacing the empty string.
+  Expect.equals("<>[]<A>[]<B>[]<C>[]<>",
+      "ABC".splitMapJoin(wrap(""), onMatch: mark, onNonMatch: rest));
+
+  // Test with only onMatch.
+  Expect.equals(
+      "[a]bc[a]bd[a]e", "abcabdae".splitMapJoin(wrap("a"), onMatch: mark));
+
+  // Test with only onNonMatch
+  Expect.equals(
+      "<>a<bc>a<bd>a<e>", "abcabdae".splitMapJoin(wrap("a"), onNonMatch: rest));
+}
diff --git a/tests/corelib/string_replace_all_pattern_test.dart b/tests/corelib/string_replace_all_pattern_test.dart
new file mode 100644
index 0000000..0ca86e7
--- /dev/null
+++ b/tests/corelib/string_replace_all_pattern_test.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "string_replace_all_common.dart";
+
+main() {
+  testAll(Wrapper.wrap);
+}
+
+/// A wrapper that is not recognizable as a String or RegExp.
+class Wrapper implements Pattern {
+  final Pattern _pattern;
+  Wrapper(this._pattern);
+
+  static Pattern wrap(Pattern p) => Wrapper(p);
+
+  Iterable<Match> allMatches(String string, [int start = 0]) =>
+      _pattern.allMatches(string, start);
+
+  Match? matchAsPrefix(String string, [int start = 0]) =>
+      _pattern.matchAsPrefix(string, start);
+
+  String toString() => "Wrap($_pattern)";
+}
diff --git a/tests/corelib/string_replace_all_test.dart b/tests/corelib/string_replace_all_test.dart
index 548ad62..b6eb43b 100644
--- a/tests/corelib/string_replace_all_test.dart
+++ b/tests/corelib/string_replace_all_test.dart
@@ -1,140 +1,9 @@
-// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
+// 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 "package:expect/expect.dart";
-
-testReplaceAll() {
-  Expect.equals("aXXcaXXdae", "abcabdae".replaceAll("b", "XX"));
-
-  // Test with the replaced string at the beginning.
-  Expect.equals("XXbcXXbdXXe", "abcabdae".replaceAll("a", "XX"));
-
-  // Test with the replaced string at the end.
-  Expect.equals("abcabdaXX", "abcabdae".replaceAll("e", "XX"));
-
-  // Test when there are no occurence of the string to replace.
-  Expect.equals("abcabdae", "abcabdae".replaceAll("f", "XX"));
-
-  // Test when the string to change is the empty string.
-  Expect.equals("", "".replaceAll("from", "to"));
-
-  // Test when the string to change is a substring of the string to
-  // replace.
-  Expect.equals("fro", "fro".replaceAll("from", "to"));
-
-  // Test when the string to change is the replaced string.
-  Expect.equals("to", "from".replaceAll("from", "to"));
-
-  // Test when matches are adjacent
-  Expect.equals("toto", "fromfrom".replaceAll("from", "to"));
-
-  // Test when the string to change is the replacement string.
-  Expect.equals("to", "to".replaceAll("from", "to"));
-
-  // Test replacing by the empty string.
-  Expect.equals("bcbde", "abcabdae".replaceAll("a", ""));
-  Expect.equals("AB", "AfromB".replaceAll("from", ""));
-
-  // Test changing the empty string.
-  Expect.equals("to", "".replaceAll("", "to"));
-
-  // Test replacing the empty string.
-  Expect.equals("toAtoBtoCto", "ABC".replaceAll("", "to"));
-
-  // Pattern strings containing RegExp metacharacters - these are not
-  // interpreted as RegExps.
-  Expect.equals(r"$$", "||".replaceAll("|", r"$"));
-  Expect.equals(r"$$$$", "||".replaceAll("|", r"$$"));
-  Expect.equals(r"x$|x", "x|.|x".replaceAll("|.", r"$"));
-  Expect.equals(r"$$", "..".replaceAll(".", r"$"));
-  Expect.equals(r"[$$$$]", "[..]".replaceAll(".", r"$$"));
-  Expect.equals(r"[$]", "[..]".replaceAll("..", r"$"));
-  Expect.equals(r"$$", r"\\".replaceAll(r"\", r"$"));
-}
-
-testReplaceAllMapped() {
-  String mark(Match m) => "[${m[0]}]";
-  Expect.equals("a[b]ca[b]dae", "abcabdae".replaceAllMapped("b", mark));
-
-  // Test with the replaced string at the beginning.
-  Expect.equals("[a]bc[a]bd[a]e", "abcabdae".replaceAllMapped("a", mark));
-
-  // Test with the replaced string at the end.
-  Expect.equals("abcabda[e]", "abcabdae".replaceAllMapped("e", mark));
-
-  // Test when there are no occurence of the string to replace.
-  Expect.equals("abcabdae", "abcabdae".replaceAllMapped("f", mark));
-
-  // Test when the string to change is the empty string.
-  Expect.equals("", "".replaceAllMapped("from", mark));
-
-  // Test when the string to change is a substring of the string to
-  // replace.
-  Expect.equals("fro", "fro".replaceAllMapped("from", mark));
-
-  // Test when matches are adjacent
-  Expect.equals("[from][from]", "fromfrom".replaceAllMapped("from", mark));
-
-  // Test replacing by the empty string.
-  Expect.equals("bcbde", "abcabdae".replaceAllMapped("a", (m) => ""));
-  Expect.equals("AB", "AfromB".replaceAllMapped("from", (m) => ""));
-
-  // Test changing the empty string.
-  Expect.equals("[]", "".replaceAllMapped("", mark));
-
-  // Test replacing the empty string.
-  Expect.equals("[]A[]B[]C[]", "ABC".replaceAllMapped("", mark));
-}
-
-testSplitMapJoin() {
-  String mark(Match m) => "[${m[0]}]";
-  String wrap(String s) => "<${s}>";
-
-  Expect.equals("<a>[b]<ca>[b]<dae>",
-      "abcabdae".splitMapJoin("b", onMatch: mark, onNonMatch: wrap));
-
-  // Test with the replaced string at the beginning.
-  Expect.equals("<>[a]<bc>[a]<bd>[a]<e>",
-      "abcabdae".splitMapJoin("a", onMatch: mark, onNonMatch: wrap));
-
-  // Test with the replaced string at the end.
-  Expect.equals("<abcabda>[e]<>",
-      "abcabdae".splitMapJoin("e", onMatch: mark, onNonMatch: wrap));
-
-  // Test when there are no occurence of the string to replace.
-  Expect.equals("<abcabdae>",
-      "abcabdae".splitMapJoin("f", onMatch: mark, onNonMatch: wrap));
-
-  // Test when the string to change is the empty string.
-  Expect.equals("<>", "".splitMapJoin("from", onMatch: mark, onNonMatch: wrap));
-
-  // Test when the string to change is a substring of the string to
-  // replace.
-  Expect.equals(
-      "<fro>", "fro".splitMapJoin("from", onMatch: mark, onNonMatch: wrap));
-
-  // Test when matches are adjacent
-  Expect.equals("<>[from]<>[from]<>",
-      "fromfrom".splitMapJoin("from", onMatch: mark, onNonMatch: wrap));
-
-  // Test changing the empty string.
-  Expect.equals("<>[]<>", "".splitMapJoin("", onMatch: mark, onNonMatch: wrap));
-
-  // Test replacing the empty string.
-  Expect.equals("<>[]<A>[]<B>[]<C>[]<>",
-      "ABC".splitMapJoin("", onMatch: mark, onNonMatch: wrap));
-
-  // Test with only onMatch.
-  Expect.equals("[a]bc[a]bd[a]e", "abcabdae".splitMapJoin("a", onMatch: mark));
-
-  // Test with only onNonMatch
-  Expect.equals(
-      "<>a<bc>a<bd>a<e>", "abcabdae".splitMapJoin("a", onNonMatch: wrap));
-}
+import "string_replace_all_common.dart";
 
 main() {
-  testReplaceAll();
-  testReplaceAllMapped();
-  testSplitMapJoin();
+  testAll((pattern) => pattern); // unwrapped
 }
diff --git a/tests/corelib_2/string_replace_all_common.dart b/tests/corelib_2/string_replace_all_common.dart
new file mode 100644
index 0000000..780761c
--- /dev/null
+++ b/tests/corelib_2/string_replace_all_common.dart
@@ -0,0 +1,146 @@
+// 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.
+
+// @dart = 2.9
+
+import "package:expect/expect.dart";
+
+testAll(Pattern Function(Pattern) wrap) {
+  testReplaceAll(wrap);
+  testReplaceAllMapped(wrap);
+  testSplitMapJoin(wrap);
+}
+
+testReplaceAll(Pattern Function(Pattern) wrap) {
+  Expect.equals("aXXcaXXdae", "abcabdae".replaceAll(wrap("b"), "XX"));
+
+  // Test with the replaced string at the beginning.
+  Expect.equals("XXbcXXbdXXe", "abcabdae".replaceAll(wrap("a"), "XX"));
+
+  // Test with the replaced string at the end.
+  Expect.equals("abcabdaXX", "abcabdae".replaceAll(wrap("e"), "XX"));
+
+  // Test when there are no occurence of the string to replace.
+  Expect.equals("abcabdae", "abcabdae".replaceAll(wrap("f"), "XX"));
+
+  // Test when the string to change is the empty string.
+  Expect.equals("", "".replaceAll(wrap("from"), "to"));
+
+  // Test when the string to change is a substring of the string to
+  // replace.
+  Expect.equals("fro", "fro".replaceAll(wrap("from"), "to"));
+
+  // Test when the string to change is the replaced string.
+  Expect.equals("to", "from".replaceAll(wrap("from"), "to"));
+
+  // Test when matches are adjacent
+  Expect.equals("toto", "fromfrom".replaceAll(wrap("from"), "to"));
+
+  // Test when the string to change is the replacement string.
+  Expect.equals("to", "to".replaceAll(wrap("from"), "to"));
+
+  // Test replacing by the empty string.
+  Expect.equals("bcbde", "abcabdae".replaceAll(wrap("a"), ""));
+  Expect.equals("AB", "AfromB".replaceAll(wrap("from"), ""));
+
+  // Test changing the empty string.
+  Expect.equals("to", "".replaceAll(wrap(""), "to"));
+
+  // Test replacing the empty string.
+  Expect.equals("toAtoBtoCto", "ABC".replaceAll(wrap(""), "to"));
+
+  // Pattern strings containing RegExp metacharacters - these are not
+  // interpreted as RegExps.
+  Expect.equals(r"$$", "||".replaceAll(wrap("|"), r"$"));
+  Expect.equals(r"$$$$", "||".replaceAll(wrap("|"), r"$$"));
+  Expect.equals(r"x$|x", "x|.|x".replaceAll(wrap("|."), r"$"));
+  Expect.equals(r"$$", "..".replaceAll(wrap("."), r"$"));
+  Expect.equals(r"[$$$$]", "[..]".replaceAll(wrap("."), r"$$"));
+  Expect.equals(r"[$]", "[..]".replaceAll(wrap(".."), r"$"));
+  Expect.equals(r"$$", r"\\".replaceAll(wrap(r"\"), r"$"));
+}
+
+testReplaceAllMapped(Pattern Function(Pattern) wrap) {
+  String mark(Match m) => "[${m[0]}]";
+  Expect.equals("a[b]ca[b]dae", "abcabdae".replaceAllMapped(wrap("b"), mark));
+
+  // Test with the replaced string at the beginning.
+  Expect.equals("[a]bc[a]bd[a]e", "abcabdae".replaceAllMapped(wrap("a"), mark));
+
+  // Test with the replaced string at the end.
+  Expect.equals("abcabda[e]", "abcabdae".replaceAllMapped(wrap("e"), mark));
+
+  // Test when there are no occurence of the string to replace.
+  Expect.equals("abcabdae", "abcabdae".replaceAllMapped(wrap("f"), mark));
+
+  // Test when the string to change is the empty string.
+  Expect.equals("", "".replaceAllMapped(wrap("from"), mark));
+
+  // Test when the string to change is a substring of the string to
+  // replace.
+  Expect.equals("fro", "fro".replaceAllMapped(wrap("from"), mark));
+
+  // Test when matches are adjacent
+  Expect.equals(
+      "[from][from]", "fromfrom".replaceAllMapped(wrap("from"), mark));
+
+  // Test replacing by the empty string.
+  Expect.equals("bcbde", "abcabdae".replaceAllMapped(wrap("a"), (m) => ""));
+  Expect.equals("AB", "AfromB".replaceAllMapped(wrap("from"), (m) => ""));
+
+  // Test changing the empty string.
+  Expect.equals("[]", "".replaceAllMapped(wrap(""), mark));
+
+  // Test replacing the empty string.
+  Expect.equals("[]A[]B[]C[]", "ABC".replaceAllMapped(wrap(""), mark));
+}
+
+testSplitMapJoin(Pattern Function(Pattern) wrap) {
+  String mark(Match m) => "[${m[0]}]";
+  String rest(String s) => "<${s}>";
+
+  Expect.equals("<a>[b]<ca>[b]<dae>",
+      "abcabdae".splitMapJoin(wrap("b"), onMatch: mark, onNonMatch: rest));
+
+  // Test with the replaced string at the beginning.
+  Expect.equals("<>[a]<bc>[a]<bd>[a]<e>",
+      "abcabdae".splitMapJoin(wrap("a"), onMatch: mark, onNonMatch: rest));
+
+  // Test with the replaced string at the end.
+  Expect.equals("<abcabda>[e]<>",
+      "abcabdae".splitMapJoin(wrap("e"), onMatch: mark, onNonMatch: rest));
+
+  // Test when there are no occurence of the string to replace.
+  Expect.equals("<abcabdae>",
+      "abcabdae".splitMapJoin(wrap("f"), onMatch: mark, onNonMatch: rest));
+
+  // Test when the string to change is the empty string.
+  Expect.equals(
+      "<>", "".splitMapJoin(wrap("from"), onMatch: mark, onNonMatch: rest));
+
+  // Test when the string to change is a substring of the string to
+  // replace.
+  Expect.equals("<fro>",
+      "fro".splitMapJoin(wrap("from"), onMatch: mark, onNonMatch: rest));
+
+  // Test when matches are adjacent
+  Expect.equals("<>[from]<>[from]<>",
+      "fromfrom".splitMapJoin(wrap("from"), onMatch: mark, onNonMatch: rest));
+
+  // Test changing the empty string.
+  Expect.equals(
+      "<>[]<>", "".splitMapJoin(wrap(""), onMatch: mark, onNonMatch: rest));
+
+  // Test replacing the empty string.
+  Expect.equals("<>[]<A>[]<B>[]<C>[]<>",
+      "ABC".splitMapJoin(wrap(""), onMatch: mark, onNonMatch: rest));
+
+  // Test with only onMatch.
+  Expect.equals(
+      "[a]bc[a]bd[a]e", "abcabdae".splitMapJoin(wrap("a"), onMatch: mark));
+
+  // Test with only onNonMatch
+  Expect.equals(
+      "<>a<bc>a<bd>a<e>", "abcabdae".splitMapJoin(wrap("a"), onNonMatch: rest));
+}
diff --git a/tests/corelib_2/string_replace_all_pattern_test.dart b/tests/corelib_2/string_replace_all_pattern_test.dart
new file mode 100644
index 0000000..10827c9
--- /dev/null
+++ b/tests/corelib_2/string_replace_all_pattern_test.dart
@@ -0,0 +1,27 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart = 2.9
+
+import "string_replace_all_common.dart";
+
+main() {
+  testAll(Wrapper.wrap);
+}
+
+/// A wrapper that is not recognizable as a String or RegExp.
+class Wrapper implements Pattern {
+  final Pattern _pattern;
+  Wrapper(this._pattern);
+
+  static Pattern wrap(Pattern p) => Wrapper(p);
+
+  Iterable<Match> allMatches(String string, [int start = 0]) =>
+      _pattern.allMatches(string, start);
+
+  Match matchAsPrefix(String string, [int start = 0]) =>
+      _pattern.matchAsPrefix(string, start);
+
+  String toString() => "Wrap($_pattern)";
+}
diff --git a/tests/corelib_2/string_replace_all_test.dart b/tests/corelib_2/string_replace_all_test.dart
index e4e340b..9d43fb7 100644
--- a/tests/corelib_2/string_replace_all_test.dart
+++ b/tests/corelib_2/string_replace_all_test.dart
@@ -1,142 +1,11 @@
-// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
+// 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.
 
 // @dart = 2.9
 
-import "package:expect/expect.dart";
-
-testReplaceAll() {
-  Expect.equals("aXXcaXXdae", "abcabdae".replaceAll("b", "XX"));
-
-  // Test with the replaced string at the beginning.
-  Expect.equals("XXbcXXbdXXe", "abcabdae".replaceAll("a", "XX"));
-
-  // Test with the replaced string at the end.
-  Expect.equals("abcabdaXX", "abcabdae".replaceAll("e", "XX"));
-
-  // Test when there are no occurence of the string to replace.
-  Expect.equals("abcabdae", "abcabdae".replaceAll("f", "XX"));
-
-  // Test when the string to change is the empty string.
-  Expect.equals("", "".replaceAll("from", "to"));
-
-  // Test when the string to change is a substring of the string to
-  // replace.
-  Expect.equals("fro", "fro".replaceAll("from", "to"));
-
-  // Test when the string to change is the replaced string.
-  Expect.equals("to", "from".replaceAll("from", "to"));
-
-  // Test when matches are adjacent
-  Expect.equals("toto", "fromfrom".replaceAll("from", "to"));
-
-  // Test when the string to change is the replacement string.
-  Expect.equals("to", "to".replaceAll("from", "to"));
-
-  // Test replacing by the empty string.
-  Expect.equals("bcbde", "abcabdae".replaceAll("a", ""));
-  Expect.equals("AB", "AfromB".replaceAll("from", ""));
-
-  // Test changing the empty string.
-  Expect.equals("to", "".replaceAll("", "to"));
-
-  // Test replacing the empty string.
-  Expect.equals("toAtoBtoCto", "ABC".replaceAll("", "to"));
-
-  // Pattern strings containing RegExp metacharacters - these are not
-  // interpreted as RegExps.
-  Expect.equals(r"$$", "||".replaceAll("|", r"$"));
-  Expect.equals(r"$$$$", "||".replaceAll("|", r"$$"));
-  Expect.equals(r"x$|x", "x|.|x".replaceAll("|.", r"$"));
-  Expect.equals(r"$$", "..".replaceAll(".", r"$"));
-  Expect.equals(r"[$$$$]", "[..]".replaceAll(".", r"$$"));
-  Expect.equals(r"[$]", "[..]".replaceAll("..", r"$"));
-  Expect.equals(r"$$", r"\\".replaceAll(r"\", r"$"));
-}
-
-testReplaceAllMapped() {
-  String mark(Match m) => "[${m[0]}]";
-  Expect.equals("a[b]ca[b]dae", "abcabdae".replaceAllMapped("b", mark));
-
-  // Test with the replaced string at the beginning.
-  Expect.equals("[a]bc[a]bd[a]e", "abcabdae".replaceAllMapped("a", mark));
-
-  // Test with the replaced string at the end.
-  Expect.equals("abcabda[e]", "abcabdae".replaceAllMapped("e", mark));
-
-  // Test when there are no occurence of the string to replace.
-  Expect.equals("abcabdae", "abcabdae".replaceAllMapped("f", mark));
-
-  // Test when the string to change is the empty string.
-  Expect.equals("", "".replaceAllMapped("from", mark));
-
-  // Test when the string to change is a substring of the string to
-  // replace.
-  Expect.equals("fro", "fro".replaceAllMapped("from", mark));
-
-  // Test when matches are adjacent
-  Expect.equals("[from][from]", "fromfrom".replaceAllMapped("from", mark));
-
-  // Test replacing by the empty string.
-  Expect.equals("bcbde", "abcabdae".replaceAllMapped("a", (m) => ""));
-  Expect.equals("AB", "AfromB".replaceAllMapped("from", (m) => ""));
-
-  // Test changing the empty string.
-  Expect.equals("[]", "".replaceAllMapped("", mark));
-
-  // Test replacing the empty string.
-  Expect.equals("[]A[]B[]C[]", "ABC".replaceAllMapped("", mark));
-}
-
-testSplitMapJoin() {
-  String mark(Match m) => "[${m[0]}]";
-  String wrap(String s) => "<${s}>";
-
-  Expect.equals("<a>[b]<ca>[b]<dae>",
-      "abcabdae".splitMapJoin("b", onMatch: mark, onNonMatch: wrap));
-
-  // Test with the replaced string at the beginning.
-  Expect.equals("<>[a]<bc>[a]<bd>[a]<e>",
-      "abcabdae".splitMapJoin("a", onMatch: mark, onNonMatch: wrap));
-
-  // Test with the replaced string at the end.
-  Expect.equals("<abcabda>[e]<>",
-      "abcabdae".splitMapJoin("e", onMatch: mark, onNonMatch: wrap));
-
-  // Test when there are no occurence of the string to replace.
-  Expect.equals("<abcabdae>",
-      "abcabdae".splitMapJoin("f", onMatch: mark, onNonMatch: wrap));
-
-  // Test when the string to change is the empty string.
-  Expect.equals("<>", "".splitMapJoin("from", onMatch: mark, onNonMatch: wrap));
-
-  // Test when the string to change is a substring of the string to
-  // replace.
-  Expect.equals(
-      "<fro>", "fro".splitMapJoin("from", onMatch: mark, onNonMatch: wrap));
-
-  // Test when matches are adjacent
-  Expect.equals("<>[from]<>[from]<>",
-      "fromfrom".splitMapJoin("from", onMatch: mark, onNonMatch: wrap));
-
-  // Test changing the empty string.
-  Expect.equals("<>[]<>", "".splitMapJoin("", onMatch: mark, onNonMatch: wrap));
-
-  // Test replacing the empty string.
-  Expect.equals("<>[]<A>[]<B>[]<C>[]<>",
-      "ABC".splitMapJoin("", onMatch: mark, onNonMatch: wrap));
-
-  // Test with only onMatch.
-  Expect.equals("[a]bc[a]bd[a]e", "abcabdae".splitMapJoin("a", onMatch: mark));
-
-  // Test with only onNonMatch
-  Expect.equals(
-      "<>a<bc>a<bd>a<e>", "abcabdae".splitMapJoin("a", onNonMatch: wrap));
-}
+import "string_replace_all_common.dart";
 
 main() {
-  testReplaceAll();
-  testReplaceAllMapped();
-  testSplitMapJoin();
+  testAll((pattern) => pattern); // unwrapped
 }
diff --git a/tools/VERSION b/tools/VERSION
index 3bb13e6..dc4983b 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 17
 PATCH 0
-PRERELEASE 12
+PRERELEASE 13
 PRERELEASE_PATCH 0
\ No newline at end of file