Create a macro executor that runs in a separate isolate group with a precompiled dill.

Also gets a start on the serialization protocol and implements a chunk of it.

Change-Id: Ia16136351a868858d679fe4bae96318d9126d3a2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/226966
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Auto-Submit: Jake Macdonald <jakemac@google.com>
Commit-Queue: Jake Macdonald <jakemac@google.com>
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart b/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart
index 89f169f..1486d1c 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart
@@ -7,10 +7,14 @@
 /// The base class representing an arbitrary chunk of Dart code, which may or
 /// may not be syntactically or semantically valid yet.
 class Code {
-  /// All the chunks of [Code] or raw [String]s that comprise this [Code]
-  /// object.
+  /// All the chunks of [Code], raw [String]s, or [TypeAnnotation]s that
+  /// comprise this [Code] object.
   final List<Object> parts;
 
+  /// Can be used to more efficiently detect the kind of code, avoiding is
+  /// checks and enabling switch statements.
+  CodeKind get kind => CodeKind.raw;
+
   Code.fromString(String code) : parts = [code];
 
   Code.fromParts(this.parts);
@@ -18,6 +22,9 @@
 
 /// A piece of code representing a syntactically valid declaration.
 class DeclarationCode extends Code {
+  @override
+  CodeKind get kind => CodeKind.declaration;
+
   DeclarationCode.fromString(String code) : super.fromString(code);
 
   DeclarationCode.fromParts(List<Object> parts) : super.fromParts(parts);
@@ -27,6 +34,9 @@
 ///
 /// Should not include any trailing commas,
 class ElementCode extends Code {
+  @override
+  CodeKind get kind => CodeKind.element;
+
   ElementCode.fromString(String code) : super.fromString(code);
 
   ElementCode.fromParts(List<Object> parts) : super.fromParts(parts);
@@ -34,6 +44,9 @@
 
 /// A piece of code representing a syntactically valid expression.
 class ExpressionCode extends Code {
+  @override
+  CodeKind get kind => CodeKind.expression;
+
   ExpressionCode.fromString(String code) : super.fromString(code);
 
   ExpressionCode.fromParts(List<Object> parts) : super.fromParts(parts);
@@ -46,6 +59,9 @@
 ///
 /// Both arrow and block function bodies are allowed.
 class FunctionBodyCode extends Code {
+  @override
+  CodeKind get kind => CodeKind.functionBody;
+
   FunctionBodyCode.fromString(String code) : super.fromString(code);
 
   FunctionBodyCode.fromParts(List<Object> parts) : super.fromParts(parts);
@@ -53,6 +69,9 @@
 
 /// A piece of code representing a syntactically valid identifier.
 class IdentifierCode extends Code {
+  @override
+  CodeKind get kind => CodeKind.identifier;
+
   IdentifierCode.fromString(String code) : super.fromString(code);
 
   IdentifierCode.fromParts(List<Object> parts) : super.fromParts(parts);
@@ -62,6 +81,9 @@
 ///
 /// This should not include any trailing commas.
 class NamedArgumentCode extends Code {
+  @override
+  CodeKind get kind => CodeKind.namedArgument;
+
   NamedArgumentCode.fromString(String code) : super.fromString(code);
 
   NamedArgumentCode.fromParts(List<Object> parts) : super.fromParts(parts);
@@ -77,6 +99,9 @@
 /// construct and combine these together in a way that creates valid parameter
 /// lists.
 class ParameterCode extends Code {
+  @override
+  CodeKind get kind => CodeKind.parameter;
+
   ParameterCode.fromString(String code) : super.fromString(code);
 
   ParameterCode.fromParts(List<Object> parts) : super.fromParts(parts);
@@ -86,6 +111,9 @@
 ///
 /// Should always end with a semicolon.
 class StatementCode extends Code {
+  @override
+  CodeKind get kind => CodeKind.statement;
+
   StatementCode.fromString(String code) : super.fromString(code);
 
   StatementCode.fromParts(List<Object> parts) : super.fromParts(parts);
@@ -102,3 +130,15 @@
         last,
       ];
 }
+
+enum CodeKind {
+  raw,
+  declaration,
+  element,
+  expression,
+  functionBody,
+  identifier,
+  namedArgument,
+  parameter,
+  statement,
+}
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 e892296..318219b 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart
@@ -14,6 +14,10 @@
 
   /// 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.
@@ -29,6 +33,9 @@
 
   /// The type parameters for this function.
   Iterable<TypeParameterDeclaration> get typeParameters;
+
+  @override
+  TypeAnnotationKind get kind => TypeAnnotationKind.functionType;
 }
 
 /// An unresolved reference to a type.
@@ -41,6 +48,9 @@
 
   /// The type arguments, if applicable.
   Iterable<TypeAnnotation> get typeArguments;
+
+  @override
+  TypeAnnotationKind get kind => TypeAnnotationKind.namedType;
 }
 
 /// The interface representing a resolved type.
@@ -65,9 +75,15 @@
 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.
+///
+/// See subtypes [ClassDeclaration] and [TypeAliasDeclaration].
 abstract class TypeDeclaration implements Declaration {
   /// The type parameters defined for this type declaration.
   Iterable<TypeParameterDeclaration> get typeParameters;
@@ -89,6 +105,9 @@
 /// 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;
 
@@ -108,8 +127,20 @@
   Iterable<TypeParameterDeclaration> get typeParameters;
 }
 
+/// 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;
 
@@ -137,6 +168,9 @@
 
 /// Method introspection information.
 abstract class MethodDeclaration implements FunctionDeclaration {
+  @override
+  DeclarationKind get kind => DeclarationKind.method;
+
   /// The class that defines this method.
   TypeAnnotation get definingClass;
 }
@@ -149,6 +183,9 @@
 
 /// Variable introspection information.
 abstract class VariableDeclaration implements Declaration {
+  @override
+  DeclarationKind get kind => DeclarationKind.variable;
+
   /// Whether this function has an `abstract` modifier.
   bool get isAbstract;
 
@@ -164,12 +201,18 @@
 
 /// 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;
 
@@ -187,6 +230,28 @@
 
 /// 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
new file mode 100644
index 0000000..55ea8bd
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
@@ -0,0 +1,143 @@
+// 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.
+
+/// Generates a Dart program for a given macro, which can be compiled and then
+/// passed as a precompiled kernel file to `MacroExecutor.loadMacro`.
+String bootstrapMacroIsolate(
+    String macroImport, String macroName, List<String> constructorNames) {
+  StringBuffer constructorEntries = new StringBuffer(
+      "MacroClassIdentifierImpl(Uri.parse('$macroImport'), '$macroName'): {");
+  for (String constructor in constructorNames) {
+    constructorEntries.writeln("'$constructor': "
+        "$macroName.${constructor.isEmpty ? 'new' : constructor},");
+  }
+  constructorEntries.writeln('},');
+  return template
+      .replaceFirst(_importMarker, 'import \'$macroImport\';')
+      .replaceFirst(
+          _macroConstructorEntriesMarker, constructorEntries.toString());
+}
+
+const String _importMarker = '{{IMPORT}}';
+const String _macroConstructorEntriesMarker = '{{MACRO_CONSTRUCTOR_ENTRIES}}';
+
+const String template = '''
+import 'dart:async';
+import 'dart:isolate';
+
+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';
+import 'package:_fe_analyzer_shared/src/macros/executor.dart';
+import 'package:_fe_analyzer_shared/src/macros/api.dart';
+
+$_importMarker
+
+/// Entrypoint to be spawned with [Isolate.spawnUri].
+///
+/// Supports the client side of the macro expansion protocol.
+void main(_, SendPort sendPort) {
+  withSerializationMode(SerializationMode.client, () {
+    ReceivePort receivePort = new ReceivePort();
+    sendPort.send(receivePort.sendPort);
+    receivePort.listen((message) async {
+      var deserializer = JsonDeserializer(message as Iterable<Object?>)
+          ..moveNext();
+      var type = MessageType.values[deserializer.expectNum()];
+      var serializer = JsonSerializer();
+      switch (type) {
+        case MessageType.instantiateMacroRequest:
+          var request = InstantiateMacroRequest.deserialize(deserializer);
+          (await _instantiateMacro(request)).serialize(serializer);
+          break;
+        case MessageType.executeDefinitionsPhaseRequest:
+          var request = ExecuteDefinitionsPhaseRequest.deserialize(
+              deserializer,
+              ClientTypeResolver(),
+              ClientClassIntrospector(),
+              ClientTypeDeclarationsResolver());
+          (await _executeDefinitionsPhase(request)).serialize(serializer);
+          break;
+        default:
+          throw new StateError('Unhandled event type \$type');
+      }
+      sendPort.send(serializer.result);
+    });
+  });
+}
+
+/// Maps macro identifiers to constructors.
+final _macroConstructors = <MacroClassIdentifierImpl, Map<String, Macro Function()>>{
+  $_macroConstructorEntriesMarker
+};
+
+/// Maps macro instance identifiers to instances.
+final _macroInstances = <MacroInstanceIdentifierImpl, Macro>{};
+
+/// Handles [InstantiateMacroRequest]s.
+Future<SerializableResponse> _instantiateMacro(
+    InstantiateMacroRequest request) async {
+  try {
+    var constructors = _macroConstructors[request.macroClass];
+    if (constructors == null) {
+      throw new ArgumentError('Unrecognized macro class \${request.macroClass}');
+    }
+    var constructor = constructors[request.constructorName];
+    if (constructor == null) {
+      throw new ArgumentError(
+          'Unrecognized constructor name "\${request.constructorName}" for '
+          'macro class "\${request.macroClass}".');
+    }
+
+    var instance = Function.apply(constructor, request.arguments.positional, {
+      for (MapEntry<String, Object?> entry in request.arguments.named.entries)
+        new Symbol(entry.key): entry.value,
+    }) as Macro;
+    var identifier = new MacroInstanceIdentifierImpl();
+    _macroInstances[identifier] = instance;
+    return new SerializableResponse(
+        responseType: MessageType.macroInstanceIdentifier,
+        response: identifier,
+        requestId: request.id);
+  } catch (e) {
+    return new SerializableResponse(
+      responseType: MessageType.error,
+      error: e.toString(),
+      requestId: request.id);
+  }
+}
+
+Future<SerializableResponse> _executeDefinitionsPhase(
+    ExecuteDefinitionsPhaseRequest request) async {
+  try {
+    Macro? instance = _macroInstances[request.macro];
+    if (instance == null) {
+      throw new StateError('Unrecognized macro instance \${request.macro}\\n'
+          'Known instances: \$_macroInstances)');
+    }
+    Declaration declaration = request.declaration;
+    if (instance is FunctionDefinitionMacro &&
+        declaration is FunctionDeclaration) {
+      FunctionDefinitionBuilderImpl builder = new FunctionDefinitionBuilderImpl(
+          declaration,
+          request.typeResolver,
+          request.typeDeclarationResolver,
+          request.classIntrospector);
+      await instance.buildDefinitionForFunction(declaration, builder);
+      return new SerializableResponse(
+          responseType: MessageType.macroExecutionResult,
+          response: builder.result,
+          requestId: request.id);
+    } else {
+      throw new UnsupportedError(
+          ('Only FunctionDefinitionMacros are supported currently'));
+    }
+  } catch (e) {
+    return new SerializableResponse(
+      responseType: MessageType.error,
+      error: e.toString(),
+      requestId: request.id);
+  }
+}
+''';
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart
index d4d248d..38a9f4b 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart
@@ -3,13 +3,17 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'api.dart';
+import 'bootstrap.dart'; // For doc comments only.
+import 'executor_shared/serialization.dart';
 
 /// Exposes a platform specific [MacroExecutor], through a top level
 /// `Future<MacroExecutor> start()` function.
 ///
 /// TODO: conditionally load isolate_mirrors_executor.dart once conditional
 /// imports of mirrors are supported in AOT (issue #48057).
-import 'fake_executor/fake_executor.dart' as executor_impl show start;
+import 'fake_executor/fake_executor.dart'
+    if (dart.library.isolate) 'isolated_executor/isolated_executor.dart'
+    as executor_impl show start;
 
 /// The interface used by Dart language implementations, in order to load
 /// and execute macros, as well as produce library augmentations from those
@@ -22,6 +26,9 @@
   /// Returns a platform specific [MacroExecutor]. On unsupported platforms this
   /// will be a fake executor object, which will throw an [UnsupportedError] if
   /// used.
+  ///
+  /// Note that some implementations will also require calls to [loadMacro]
+  /// to pass a `precompiledKernelUri`.
   static Future<MacroExecutor> start() => executor_impl.start();
 
   /// Invoked when an implementation discovers a new macro definition in a
@@ -32,8 +39,17 @@
   /// [MacroInstanceIdentifier]s given for this macro will be invalid after
   /// that point and should be discarded.
   ///
+  /// The [precompiledKernelUri] if passed must point to a kernel program for
+  /// the given macro. A bootstrap Dart program can be generated with the
+  /// [bootstrapMacroIsolate] function, and the result should be compiled to
+  /// kernel and passed here.
+  ///
+  /// Some implementations may require [precompiledKernelUri] to be passed, and
+  /// will throw an [UnsupportedError] if it is not.
+  ///
   /// Throws an exception if the macro fails to load.
-  Future<MacroClassIdentifier> loadMacro(Uri library, String name);
+  Future<MacroClassIdentifier> loadMacro(Uri library, String name,
+      {Uri? precompiledKernelUri});
 
   /// Creates an instance of [macroClass] in the executor, and returns an
   /// identifier for that instance.
@@ -81,31 +97,143 @@
 ///
 /// All argument instances must be of type [Code] or a built-in value type that
 /// is serializable (num, bool, String, null, etc).
-class Arguments {
+class Arguments implements Serializable {
   final List<Object?> positional;
 
   final Map<String, Object?> named;
 
   Arguments(this.positional, this.named);
+
+  factory Arguments.deserialize(Deserializer deserializer) {
+    deserializer
+      ..moveNext()
+      ..expectList();
+    List<Object?> positionalArgs = [
+      for (bool hasNext = deserializer.moveNext();
+          hasNext;
+          hasNext = deserializer.moveNext())
+        _deserializeArg(deserializer, alreadyMoved: true),
+    ];
+    deserializer
+      ..moveNext()
+      ..expectList();
+    Map<String, Object?> namedArgs = {
+      for (bool hasNext = deserializer.moveNext();
+          hasNext;
+          hasNext = deserializer.moveNext())
+        deserializer.expectString(): _deserializeArg(deserializer),
+    };
+    return new Arguments(positionalArgs, namedArgs);
+  }
+
+  static Object? _deserializeArg(Deserializer deserializer,
+      {bool alreadyMoved = false}) {
+    if (!alreadyMoved) deserializer.moveNext();
+    _ArgumentKind kind = _ArgumentKind.values[deserializer.expectNum()];
+    switch (kind) {
+      case _ArgumentKind.nil:
+        return null;
+      case _ArgumentKind.string:
+        deserializer.moveNext();
+        return deserializer.expectString();
+      case _ArgumentKind.bool:
+        deserializer.moveNext();
+        return deserializer.expectBool();
+      case _ArgumentKind.num:
+        deserializer.moveNext();
+        return deserializer.expectNum();
+      case _ArgumentKind.list:
+        deserializer.moveNext();
+        deserializer.expectList();
+        return [
+          for (bool hasNext = deserializer.moveNext();
+              hasNext;
+              hasNext = deserializer.moveNext())
+            _deserializeArg(deserializer, alreadyMoved: true),
+        ];
+      case _ArgumentKind.map:
+        deserializer.moveNext();
+        deserializer.expectList();
+        return {
+          for (bool hasNext = deserializer.moveNext();
+              hasNext;
+              hasNext = deserializer.moveNext())
+            _deserializeArg(deserializer, alreadyMoved: true):
+                _deserializeArg(deserializer),
+        };
+    }
+  }
+
+  void serialize(Serializer serializer) {
+    serializer.startList();
+    for (Object? arg in positional) {
+      _serializeArg(arg, serializer);
+    }
+    serializer.endList();
+
+    serializer.startList();
+    for (MapEntry<String, Object?> arg in named.entries) {
+      serializer.addString(arg.key);
+      _serializeArg(arg.value, serializer);
+    }
+    serializer.endList();
+  }
+
+  static void _serializeArg(Object? arg, Serializer serializer) {
+    if (arg == null) {
+      serializer.addNum(_ArgumentKind.nil.index);
+    } else if (arg is String) {
+      serializer
+        ..addNum(_ArgumentKind.string.index)
+        ..addString(arg);
+    } else if (arg is num) {
+      serializer
+        ..addNum(_ArgumentKind.num.index)
+        ..addNum(arg);
+    } else if (arg is bool) {
+      serializer
+        ..addNum(_ArgumentKind.bool.index)
+        ..addBool(arg);
+    } else if (arg is List) {
+      serializer
+        ..addNum(_ArgumentKind.list.index)
+        ..startList();
+      for (Object? item in arg) {
+        _serializeArg(item, serializer);
+      }
+      serializer.endList();
+    } else if (arg is Map) {
+      serializer
+        ..addNum(_ArgumentKind.map.index)
+        ..startList();
+      for (MapEntry<Object?, Object?> entry in arg.entries) {
+        _serializeArg(entry.key, serializer);
+        _serializeArg(entry.value, serializer);
+      }
+      serializer.endList();
+    } else {
+      throw new UnsupportedError('Unsupported argument type $arg');
+    }
+  }
 }
 
 /// An opaque identifier for a macro class, retrieved by
 /// [MacroExecutor.loadMacro].
 ///
 /// Used to execute or reload this macro in the future.
-abstract class MacroClassIdentifier {}
+abstract class MacroClassIdentifier implements Serializable {}
 
 /// An opaque identifier for an instance of a macro class, retrieved by
 /// [MacroExecutor.instantiateMacro].
 ///
 /// Used to execute or reload this macro in the future.
-abstract class MacroInstanceIdentifier {}
+abstract class MacroInstanceIdentifier implements Serializable {}
 
 /// A summary of the results of running a macro in a given phase.
 ///
 /// All modifications are expressed in terms of library augmentation
 /// declarations.
-abstract class MacroExecutionResult {
+abstract class MacroExecutionResult implements Serializable {
   /// Any library imports that should be added to support the code used in
   /// the augmentations.
   Iterable<DeclarationCode> get imports;
@@ -125,3 +253,6 @@
   /// This phase allows augmenting existing declarations.
   definitions,
 }
+
+/// Used for serializing and deserializing arguments.
+enum _ArgumentKind { string, bool, num, list, map, nil }
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
new file mode 100644
index 0000000..e237127
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/introspection_impls.dart
@@ -0,0 +1,181 @@
+// 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 '../api.dart';
+
+abstract class TypeAnnotationImpl implements TypeAnnotation {
+  final bool isNullable;
+
+  TypeAnnotationImpl({required this.isNullable});
+}
+
+class NamedTypeAnnotationImpl extends TypeAnnotationImpl
+    implements NamedTypeAnnotation {
+  @override
+  Code get code => new Code.fromParts([
+        name,
+        if (typeArguments.isNotEmpty) ...[
+          '<',
+          for (TypeAnnotation arg in typeArguments) ...[arg, ','],
+          '>',
+        ],
+        if (isNullable) '?',
+      ]);
+
+  @override
+  final String name;
+
+  @override
+  final List<TypeAnnotation> typeArguments;
+
+  @override
+  TypeAnnotationKind get kind => TypeAnnotationKind.namedType;
+
+  NamedTypeAnnotationImpl({
+    required bool isNullable,
+    required this.name,
+    required this.typeArguments,
+  }) : super(isNullable: isNullable);
+}
+
+class FunctionTypeAnnotationImpl extends TypeAnnotationImpl
+    implements FunctionTypeAnnotation {
+  @override
+  Code get code => new Code.fromParts([
+        returnType,
+        'Function',
+        if (typeParameters.isNotEmpty) ...[
+          '<',
+          for (TypeParameterDeclaration arg in typeParameters) ...[
+            arg.name,
+            if (arg.bounds != null) ...[' extends ', arg.bounds!],
+            ','
+          ],
+          '>',
+        ],
+        '(',
+        for (ParameterDeclaration positional in positionalParameters) ...[
+          positional.type,
+          ' ${positional.name}',
+        ],
+        if (namedParameters.isNotEmpty) ...[
+          '{',
+          for (ParameterDeclaration named in namedParameters) ...[
+            named.type,
+            ' ${named.name}',
+          ],
+          '}',
+        ],
+        ')',
+        if (isNullable) '?',
+      ]);
+
+  @override
+  final List<ParameterDeclaration> namedParameters;
+
+  @override
+  final List<ParameterDeclaration> positionalParameters;
+
+  @override
+  final TypeAnnotation returnType;
+
+  @override
+  final List<TypeParameterDeclaration> typeParameters;
+
+  @override
+  TypeAnnotationKind get kind => TypeAnnotationKind.functionType;
+
+  FunctionTypeAnnotationImpl({
+    required bool isNullable,
+    required this.namedParameters,
+    required this.positionalParameters,
+    required this.returnType,
+    required this.typeParameters,
+  }) : super(isNullable: isNullable);
+}
+
+class ParameterDeclarationImpl implements ParameterDeclaration {
+  @override
+  final String name;
+
+  @override
+  final Code? defaultValue;
+
+  @override
+  final bool isNamed;
+
+  @override
+  final bool isRequired;
+
+  @override
+  final TypeAnnotation type;
+
+  @override
+  DeclarationKind get kind => DeclarationKind.parameter;
+
+  ParameterDeclarationImpl({
+    required this.name,
+    required this.defaultValue,
+    required this.isNamed,
+    required this.isRequired,
+    required this.type,
+  });
+}
+
+class TypeParameterDeclarationImpl implements TypeParameterDeclaration {
+  @override
+  final String name;
+
+  @override
+  final TypeAnnotation? bounds;
+
+  @override
+  DeclarationKind get kind => DeclarationKind.typeParameter;
+
+  TypeParameterDeclarationImpl({required this.name, required this.bounds});
+}
+
+class FunctionDeclarationImpl implements FunctionDeclaration {
+  @override
+  final String name;
+
+  @override
+  final bool isAbstract;
+
+  @override
+  final bool isExternal;
+
+  @override
+  final bool isGetter;
+
+  @override
+  final bool isSetter;
+
+  @override
+  final List<ParameterDeclaration> namedParameters;
+
+  @override
+  final List<ParameterDeclaration> positionalParameters;
+
+  @override
+  final TypeAnnotation returnType;
+
+  @override
+  final List<TypeParameterDeclaration> typeParameters;
+
+  @override
+  DeclarationKind get kind => DeclarationKind.function;
+
+  FunctionDeclarationImpl({
+    required this.name,
+    required this.isAbstract,
+    required this.isExternal,
+    required this.isGetter,
+    required this.isSetter,
+    required this.namedParameters,
+    required this.positionalParameters,
+    required this.returnType,
+    required this.typeParameters,
+  });
+}
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
new file mode 100644
index 0000000..3dcc455
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/protocol.dart
@@ -0,0 +1,263 @@
+// 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.
+
+/// Defines the objects used for communication between the macro executor and
+/// the isolate or process doing the work of macro loading and execution.
+library _fe_analyzer_shared.src.macros.executor_shared.protocol;
+
+import '../executor.dart';
+import '../api.dart';
+import '../executor_shared/response_impls.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++;
+
+  Request.deserialize(Deserializer deserializer)
+      : id = (deserializer..moveNext()).expectNum();
+
+  void serialize(Serializer serializer) => serializer.addNum(id);
+
+  static int _next = 0;
+}
+
+/// A generic response object that contains either a response or an error, and
+/// a unique ID.
+class Response {
+  final Object? response;
+  final Object? error;
+  final int requestId;
+
+  Response({this.response, this.error, required this.requestId})
+      : assert(response != null || error != null),
+        assert(response == null || error == null);
+}
+
+/// A serializable [Response], contains the message type as an enum.
+class SerializableResponse implements Response, Serializable {
+  final Serializable? response;
+  final MessageType responseType;
+  final String? error;
+  final int requestId;
+
+  SerializableResponse({
+    this.error,
+    required this.requestId,
+    this.response,
+    required this.responseType,
+  })  : assert(response != null || error != null),
+        assert(response == null || error == null);
+
+  factory SerializableResponse.deserialize(Deserializer deserializer) {
+    deserializer.moveNext();
+    MessageType responseType = MessageType.values[deserializer.expectNum()];
+    Serializable? response;
+    String? error;
+    switch (responseType) {
+      case MessageType.error:
+        deserializer.moveNext();
+        error = deserializer.expectString();
+        break;
+      case MessageType.macroClassIdentifier:
+        response = new MacroClassIdentifierImpl.deserialize(deserializer);
+        break;
+      case MessageType.macroInstanceIdentifier:
+        response = new MacroInstanceIdentifierImpl.deserialize(deserializer);
+        break;
+      case MessageType.macroExecutionResult:
+        response = new MacroExecutionResultImpl.deserialize(deserializer);
+        break;
+      default:
+        throw new StateError('Unexpected response type $responseType');
+    }
+
+    return new SerializableResponse(
+        responseType: responseType,
+        response: response,
+        error: error,
+        requestId: (deserializer..moveNext()).expectNum());
+  }
+
+  void serialize(Serializer serializer) {
+    serializer.addNum(responseType.index);
+    if (response != null) {
+      response!.serialize(serializer);
+    } else if (error != null) {
+      serializer.addString(error!.toString());
+    }
+    serializer.addNum(requestId);
+  }
+}
+
+/// A request to load a macro in this isolate.
+class LoadMacroRequest extends Request {
+  final Uri library;
+  final String name;
+
+  LoadMacroRequest(this.library, this.name);
+
+  LoadMacroRequest.deserialize(Deserializer deserializer)
+      : library = Uri.parse((deserializer..moveNext()).expectString()),
+        name = (deserializer..moveNext()).expectString(),
+        super.deserialize(deserializer);
+
+  @override
+  void serialize(Serializer serializer) {
+    serializer
+      ..addNum(MessageType.loadMacroRequest.index)
+      ..addString(library.toString())
+      ..addString(name);
+    super.serialize(serializer);
+  }
+}
+
+/// A request to instantiate a macro instance.
+class InstantiateMacroRequest extends Request {
+  final MacroClassIdentifier macroClass;
+  final String constructorName;
+  final Arguments arguments;
+
+  InstantiateMacroRequest(
+      this.macroClass, this.constructorName, this.arguments);
+
+  InstantiateMacroRequest.deserialize(Deserializer deserializer)
+      : macroClass = new MacroClassIdentifierImpl.deserialize(deserializer),
+        constructorName = (deserializer..moveNext()).expectString(),
+        arguments = new Arguments.deserialize(deserializer),
+        super.deserialize(deserializer);
+
+  @override
+  void serialize(Serializer serializer) {
+    serializer.addNum(MessageType.instantiateMacroRequest.index);
+    macroClass.serialize(serializer);
+    serializer.addString(constructorName);
+    arguments.serialize(serializer);
+    super.serialize(serializer);
+  }
+}
+
+/// A request to execute a macro on a particular declaration in the definition
+/// phase.
+class ExecuteDefinitionsPhaseRequest extends Request {
+  final MacroInstanceIdentifier macro;
+  final Declaration declaration;
+
+  /// Client/Server specific implementation, not serialized.
+  final TypeResolver typeResolver;
+
+  /// Client/Server specific implementation, not serialized.
+  final ClassIntrospector classIntrospector;
+
+  /// Client/Server specific implementation, not serialized.
+  final TypeDeclarationResolver typeDeclarationResolver;
+
+  ExecuteDefinitionsPhaseRequest(this.macro, this.declaration,
+      this.typeResolver, this.classIntrospector, this.typeDeclarationResolver);
+
+  /// 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(),
+        super.deserialize(deserializer);
+
+  void serialize(Serializer serializer) {
+    serializer.addNum(MessageType.executeDefinitionsPhaseRequest.index);
+    macro.serialize(serializer);
+    declaration.serialize(serializer);
+    super.serialize(serializer);
+  }
+}
+
+/// A request to reflect on a type annotation
+class ReflectTypeRequest extends Request {
+  final TypeAnnotation typeAnnotation;
+
+  ReflectTypeRequest(this.typeAnnotation);
+
+  /// When deserializing we have already consumed the message type, so we don't
+  /// consume it again.
+  ReflectTypeRequest.deserialize(Deserializer deserializer)
+      : typeAnnotation = (deserializer..moveNext()).expectTypeAnnotation(),
+        super.deserialize(deserializer);
+
+  void serialize(Serializer serializer) {
+    serializer.addNum(MessageType.reflectTypeRequest.index);
+    typeAnnotation.serialize(serializer);
+    super.serialize(serializer);
+  }
+}
+
+/// TODO: Implement this
+class ClientTypeResolver implements TypeResolver {
+  @override
+  Future<StaticType> resolve(TypeAnnotation typeAnnotation) {
+    // TODO: implement resolve
+    throw new UnimplementedError();
+  }
+}
+
+/// TODO: Implement this
+class ClientClassIntrospector implements ClassIntrospector {
+  @override
+  Future<List<ConstructorDeclaration>> constructorsOf(ClassDeclaration clazz) {
+    // TODO: implement constructorsOf
+    throw new UnimplementedError();
+  }
+
+  @override
+  Future<List<FieldDeclaration>> fieldsOf(ClassDeclaration clazz) {
+    // TODO: implement fieldsOf
+    throw new UnimplementedError();
+  }
+
+  @override
+  Future<List<ClassDeclaration>> interfacesOf(ClassDeclaration clazz) {
+    // TODO: implement interfacesOf
+    throw new UnimplementedError();
+  }
+
+  @override
+  Future<List<MethodDeclaration>> methodsOf(ClassDeclaration clazz) {
+    // TODO: implement methodsOf
+    throw new UnimplementedError();
+  }
+
+  @override
+  Future<List<ClassDeclaration>> mixinsOf(ClassDeclaration clazz) {
+    // TODO: implement mixinsOf
+    throw new UnimplementedError();
+  }
+
+  @override
+  Future<ClassDeclaration?> superclassOf(ClassDeclaration clazz) {
+    // TODO: implement superclassOf
+    throw new UnimplementedError();
+  }
+}
+
+/// TODO: Implement this
+class ClientTypeDeclarationsResolver implements TypeDeclarationResolver {
+  @override
+  Future<TypeDeclaration> declarationOf(NamedStaticType annotation) {
+    // TODO: implement declarationOf
+    throw new UnimplementedError();
+  }
+}
+
+enum MessageType {
+  error,
+  executeDefinitionsPhaseRequest,
+  instantiateMacroRequest,
+  loadMacroRequest,
+  reflectTypeRequest,
+  macroClassIdentifier,
+  macroInstanceIdentifier,
+  macroExecutionResult,
+}
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
new file mode 100644
index 0000000..5155f01
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/response_impls.dart
@@ -0,0 +1,222 @@
+// 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 'dart:async';
+
+import '../executor.dart';
+import '../api.dart';
+import 'serialization.dart';
+import 'serialization_extensions.dart';
+
+/// Implementation of [MacroClassIdentifier].
+class MacroClassIdentifierImpl implements MacroClassIdentifier {
+  final String id;
+
+  MacroClassIdentifierImpl(Uri library, String name) : id = '$library#$name';
+
+  MacroClassIdentifierImpl.deserialize(Deserializer deserializer)
+      : id = (deserializer..moveNext()).expectString();
+
+  void serialize(Serializer serializer) => serializer.addString(id);
+
+  operator ==(other) => other is MacroClassIdentifierImpl && id == other.id;
+
+  int get hashCode => id.hashCode;
+}
+
+/// Implementation of [MacroInstanceIdentifier].
+class MacroInstanceIdentifierImpl implements MacroInstanceIdentifier {
+  static int _next = 0;
+
+  final int id;
+
+  MacroInstanceIdentifierImpl() : id = _next++;
+
+  MacroInstanceIdentifierImpl.deserialize(Deserializer deserializer)
+      : id = (deserializer..moveNext()).expectNum();
+
+  void serialize(Serializer serializer) => serializer.addNum(id);
+
+  operator ==(other) => other is MacroInstanceIdentifierImpl && id == other.id;
+
+  int get hashCode => id;
+}
+
+/// Implementation of [MacroExecutionResult].
+class MacroExecutionResultImpl implements MacroExecutionResult {
+  @override
+  final List<DeclarationCode> augmentations;
+
+  @override
+  final List<DeclarationCode> imports;
+
+  MacroExecutionResultImpl({
+    List<DeclarationCode>? augmentations,
+    List<DeclarationCode>? imports,
+  })  : augmentations = augmentations ?? [],
+        imports = imports ?? [];
+
+  factory MacroExecutionResultImpl.deserialize(Deserializer deserializer) {
+    deserializer.moveNext();
+    deserializer.expectList();
+    List<DeclarationCode> augmentations = [
+      for (bool hasNext = deserializer.moveNext();
+          hasNext;
+          hasNext = deserializer.moveNext())
+        deserializer.expectCode()
+    ];
+    deserializer.moveNext();
+    deserializer.expectList();
+    List<DeclarationCode> imports = [
+      for (bool hasNext = deserializer.moveNext();
+          hasNext;
+          hasNext = deserializer.moveNext())
+        deserializer.expectCode()
+    ];
+
+    return new MacroExecutionResultImpl(
+      augmentations: augmentations,
+      imports: imports,
+    );
+  }
+
+  void serialize(Serializer serializer) {
+    serializer.startList();
+    for (DeclarationCode augmentation in augmentations) {
+      augmentation.serialize(serializer);
+    }
+    serializer.endList();
+    serializer.startList();
+    for (DeclarationCode import in imports) {
+      import.serialize(serializer);
+    }
+    serializer.endList();
+  }
+}
+
+/// Implementation of [FunctionDefinitionBuilder].
+class FunctionDefinitionBuilderImpl implements FunctionDefinitionBuilder {
+  final TypeResolver typeResolver;
+  final TypeDeclarationResolver typeDeclarationResolver;
+  final ClassIntrospector classIntrospector;
+
+  /// The declaration this is a builder for.
+  final FunctionDeclaration declaration;
+
+  /// The final result, will be built up over `augment` calls.
+  final MacroExecutionResultImpl result;
+
+  FunctionDefinitionBuilderImpl(this.declaration, this.typeResolver,
+      this.typeDeclarationResolver, this.classIntrospector)
+      : result = new MacroExecutionResultImpl();
+
+  FunctionDefinitionBuilderImpl.deserialize(Deserializer deserializer,
+      this.typeResolver, this.typeDeclarationResolver, this.classIntrospector)
+      : declaration =
+            (deserializer..moveNext()).expectDeclaration<FunctionDeclaration>(),
+        result = new MacroExecutionResultImpl.deserialize(deserializer);
+
+  void serialize(Serializer serializer) {
+    // Note that the `typeResolver`, `typeDeclarationResolver`, and
+    // `classIntrospector` are not serialized. These have custom implementations
+    // on the client/server side.
+    declaration.serialize(serializer);
+    result.serialize(serializer);
+  }
+
+  @override
+  void augment(FunctionBodyCode body) {
+    result.augmentations.add(new DeclarationCode.fromParts([
+      'augment ',
+      declaration.returnType.code,
+      ' ',
+      declaration.name,
+      if (declaration.typeParameters.isNotEmpty) ...[
+        '<',
+        for (TypeParameterDeclaration typeParam
+            in declaration.typeParameters) ...[
+          typeParam.name,
+          if (typeParam.bounds != null) ...['extends ', typeParam.bounds!.code],
+          if (typeParam != declaration.typeParameters.last) ', ',
+        ],
+        '>',
+      ],
+      '(',
+      for (ParameterDeclaration positionalRequired
+          in declaration.positionalParameters.where((p) => p.isRequired)) ...[
+        new ParameterCode.fromParts([
+          positionalRequired.type.code,
+          ' ',
+          positionalRequired.name,
+        ]),
+        ', '
+      ],
+      if (declaration.positionalParameters.any((p) => !p.isRequired)) ...[
+        '[',
+        for (ParameterDeclaration positionalOptional in declaration
+            .positionalParameters
+            .where((p) => !p.isRequired)) ...[
+          new ParameterCode.fromParts([
+            positionalOptional.type.code,
+            ' ',
+            positionalOptional.name,
+          ]),
+          ', ',
+        ],
+        ']',
+      ],
+      if (declaration.namedParameters.isNotEmpty) ...[
+        '{',
+        for (ParameterDeclaration named in declaration.namedParameters) ...[
+          new ParameterCode.fromParts([
+            if (named.isRequired) 'required ',
+            named.type.code,
+            ' ',
+            named.name,
+            if (named.defaultValue != null) ...[
+              ' = ',
+              named.defaultValue!,
+            ],
+          ]),
+          ', ',
+        ],
+        '}',
+      ],
+      ') ',
+      body,
+    ]));
+  }
+
+  @override
+  Future<List<ConstructorDeclaration>> constructorsOf(ClassDeclaration clazz) =>
+      classIntrospector.constructorsOf(clazz);
+
+  @override
+  Future<List<FieldDeclaration>> fieldsOf(ClassDeclaration clazz) =>
+      classIntrospector.fieldsOf(clazz);
+
+  @override
+  Future<List<ClassDeclaration>> interfacesOf(ClassDeclaration clazz) =>
+      classIntrospector.interfacesOf(clazz);
+
+  @override
+  Future<List<MethodDeclaration>> methodsOf(ClassDeclaration clazz) =>
+      classIntrospector.methodsOf(clazz);
+
+  @override
+  Future<List<ClassDeclaration>> mixinsOf(ClassDeclaration clazz) =>
+      classIntrospector.mixinsOf(clazz);
+
+  @override
+  Future<TypeDeclaration> declarationOf(NamedStaticType annotation) =>
+      typeDeclarationResolver.declarationOf(annotation);
+
+  @override
+  Future<ClassDeclaration?> superclassOf(ClassDeclaration clazz) =>
+      classIntrospector.superclassOf(clazz);
+
+  @override
+  Future<StaticType> resolve(TypeAnnotation typeAnnotation) =>
+      typeResolver.resolve(typeAnnotation);
+}
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
new file mode 100644
index 0000000..f5f9343
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization.dart
@@ -0,0 +1,229 @@
+// 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 'dart:async';
+
+/// All serialization must be done in a serialization Zone, which tells it
+/// whether we are the client or server.
+T withSerializationMode<T>(SerializationMode mode, T Function() fn) =>
+    runZoned(fn, zoneValues: {#serializationMode: mode});
+
+/// Serializable interface
+abstract class Serializable {
+  /// Serializes this object using [serializer].
+  void serialize(Serializer serializer);
+}
+
+/// A push based object serialization interface.
+abstract class Serializer {
+  /// Serializes a [String].
+  void addString(String value);
+
+  /// Serializes a nullable [String].
+  void addNullableString(String? value);
+
+  /// Serializes a [num].
+  void addNum(num value);
+
+  /// Serializes a nullable [num].
+  void addNullableNum(num? value);
+
+  /// Serializes a [bool].
+  void addBool(bool value);
+
+  /// Serializes a nullable [bool].
+  void addNullableBool(bool? value);
+
+  /// Serializes a `null` literal.
+  void addNull();
+
+  /// Used to signal the start of an arbitrary length list of items.
+  void startList();
+
+  /// Used to signal the end of an arbitrary length list of items.
+  void endList();
+}
+
+/// A pull based object deserialization interface.
+///
+/// You must call [moveNext] before reading any items, and in order to advance
+/// to the next item.
+abstract class Deserializer {
+  /// Checks if the current value is a null, returns `true` if so and `false`
+  /// otherwise.
+  bool checkNull();
+
+  /// Reads the current value as a non-nullable [String].
+  bool expectBool();
+
+  /// Reads the current value as a nullable [bool].
+  bool? expectNullableBool();
+
+  /// Reads the current value as a non-nullable [String].
+  T expectNum<T extends num>();
+
+  /// Reads the current value as a nullable [num].
+  num? expectNullableNum();
+
+  /// Reads the current value as a non-nullable [String].
+  String expectString();
+
+  /// Reads the current value as a nullable [String].
+  String? expectNullableString();
+
+  /// Asserts that the current item is the start of a list.
+  ///
+  /// An example for how to read from a list is as follows:
+  ///
+  /// var json = JsonReader.fromString(source);
+  /// I know it's a list of strings.
+  ///
+  /// ```
+  ///   var result = <String>[];
+  ///   deserializer.moveNext();
+  ///   deserializer.expectList();
+  ///   while (json.moveNext()) {
+  ///     result.add(json.expectString());
+  ///   }
+  ///   // Can now read later items, but need to call `moveNext` again to move
+  ///   // past the list.
+  ///   deserializer.moveNext();
+  ///   deserializer.expectBool();
+  /// ```
+  void expectList();
+
+  /// Moves to the next item, returns `false` if there are no more items to
+  /// read.
+  ///
+  /// If inside of a list, this returns `false` when the end of the list is
+  /// reached, and moves back to the parent, but does not advance it, so another
+  /// call to `moveNext` is needed. See example in the [expectList] docs.
+  bool moveNext();
+}
+
+class JsonSerializer implements Serializer {
+  /// The full result.
+  final _result = <Object?>[];
+
+  /// A path to the current list we are modifying.
+  late List<List<Object?>> _path = [_result];
+
+  /// Returns the result as an unmodifiable [Iterable].
+  ///
+  /// Asserts that all [List] entries have not been closed with [endList].
+  Iterable<Object?> get result {
+    assert(_path.length == 1);
+    return _result;
+  }
+
+  @override
+  void addBool(bool value) => _path.last.add(value);
+  @override
+  void addNullableBool(bool? value) => _path.last.add(value);
+
+  @override
+  void addNum(num value) => _path.last.add(value);
+  @override
+  void addNullableNum(num? value) => _path.last.add(value);
+
+  @override
+  void addString(String value) => _path.last.add(value);
+  @override
+  void addNullableString(String? value) => _path.last.add(value);
+
+  @override
+  void addNull() => _path.last.add(null);
+
+  @override
+  void startList() {
+    List<Object?> sublist = [];
+    _path.last.add(sublist);
+    _path.add(sublist);
+  }
+
+  @override
+  void endList() {
+    _path.removeLast();
+  }
+}
+
+class JsonDeserializer implements Deserializer {
+  /// The root source list to read from.
+  final Iterable<Object?> _source;
+
+  /// The path to the current iterator we are reading from.
+  late List<Iterator<Object?>> _path = [];
+
+  /// Whether we have received our first [moveNext] call.
+  bool _initialized = false;
+
+  /// Initialize this deserializer from `_source`.
+  JsonDeserializer(this._source);
+
+  @override
+  bool checkNull() => _expectValue<Object?>() == null;
+
+  @override
+  void expectList() => _path.add(_expectValue<Iterable<Object?>>().iterator);
+
+  @override
+  bool expectBool() => _expectValue();
+  @override
+  bool? expectNullableBool() => _expectValue();
+
+  @override
+  T expectNum<T extends num>() => _expectValue();
+  @override
+  num? expectNullableNum() => _expectValue();
+
+  @override
+  String expectString() => _expectValue();
+  @override
+  String? expectNullableString() => _expectValue();
+
+  /// Reads the current value and casts it to [T].
+  T _expectValue<T>() {
+    if (!_initialized) {
+      throw new StateError(
+          'You must call `moveNext()` before reading any values.');
+    }
+    return _path.last.current as T;
+  }
+
+  @override
+  bool moveNext() {
+    if (!_initialized) {
+      _path.add(_source.iterator);
+      _initialized = true;
+    }
+
+    // Move the current iterable, if its at the end of its items remove it from
+    // the current path and return false.
+    if (!_path.last.moveNext()) {
+      _path.removeLast();
+      return false;
+    }
+
+    return true;
+  }
+}
+
+/// Must be set using `withSerializationMode` before doing any serialization or
+/// deserialization.
+SerializationMode get serializationMode {
+  SerializationMode? mode =
+      Zone.current[#serializationMode] as SerializationMode?;
+  if (mode == null) {
+    throw new StateError('No SerializationMode set, you must do all '
+        'serialization inside a call to `withSerializationMode`.');
+  }
+  return mode;
+}
+
+/// Some objects are serialized differently on the client side versus the server
+/// side. This indicates the different modes.
+enum SerializationMode {
+  server,
+  client,
+}
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
new file mode 100644
index 0000000..01cb768
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization_extensions.dart
@@ -0,0 +1,416 @@
+import 'package:_fe_analyzer_shared/src/macros/executor_shared/introspection_impls.dart';
+
+import 'serialization.dart';
+import '../api.dart';
+
+extension DeserializerExtensions on Deserializer {
+  TypeAnnotation expectTypeAnnotation() {
+    int id = expectNum();
+    switch (serializationMode) {
+      case SerializationMode.server:
+        return _typeAnnotationsById[id]!;
+      case SerializationMode.client:
+        TypeAnnotation typeAnnotation;
+        moveNext();
+        TypeAnnotationKind type = TypeAnnotationKind.values[expectNum()];
+        moveNext();
+        switch (type) {
+          case TypeAnnotationKind.namedType:
+            typeAnnotation = _expectNamedTypeAnnotation();
+            break;
+          case TypeAnnotationKind.functionType:
+            typeAnnotation = _expectFunctionTypeAnnotation();
+            break;
+        }
+        _typeAnnotationIds[typeAnnotation] = id;
+        return typeAnnotation;
+    }
+  }
+
+  NamedTypeAnnotation _expectNamedTypeAnnotation() {
+    bool isNullable = expectBool();
+    moveNext();
+    String name = expectString();
+    moveNext();
+    expectList();
+    List<TypeAnnotation> typeArguments = [
+      for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
+        expectTypeAnnotation(),
+    ];
+    return new NamedTypeAnnotationImpl(
+        isNullable: isNullable, name: name, typeArguments: typeArguments);
+  }
+
+  FunctionTypeAnnotation _expectFunctionTypeAnnotation() {
+    bool isNullable = expectBool();
+
+    moveNext();
+    TypeAnnotation returnType = expectTypeAnnotation();
+
+    moveNext();
+    expectList();
+    List<ParameterDeclaration> positionalParameters = [
+      for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
+        expectDeclaration(),
+    ];
+
+    moveNext();
+    expectList();
+    List<ParameterDeclaration> namedParameters = [
+      for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
+        expectDeclaration(),
+    ];
+
+    moveNext();
+    expectList();
+    List<TypeParameterDeclaration> typeParameters = [
+      for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
+        expectDeclaration(),
+    ];
+
+    return new FunctionTypeAnnotationImpl(
+        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() {
+    String name = expectString();
+    moveNext();
+    Code? defaultValue;
+    if (!checkNull()) {
+      defaultValue = expectCode();
+    }
+    bool isNamed = expectBool();
+    moveNext();
+    bool isRequired = expectBool();
+    moveNext();
+    TypeAnnotation type = expectTypeAnnotation();
+
+    return new ParameterDeclarationImpl(
+        defaultValue: defaultValue,
+        isNamed: isNamed,
+        isRequired: isRequired,
+        name: name,
+        type: type);
+  }
+
+  TypeParameterDeclaration _expectTypeParameterDeclaration() {
+    String name = expectString();
+    moveNext();
+    TypeAnnotation? bounds;
+    if (!checkNull()) {
+      bounds = expectTypeAnnotation();
+    }
+    return new TypeParameterDeclarationImpl(name: name, bounds: bounds);
+  }
+
+  FunctionDeclaration _expectFunctionDeclaration() {
+    String name = expectString();
+    moveNext();
+    bool isAbstract = expectBool();
+    moveNext();
+    bool isExternal = expectBool();
+    moveNext();
+    bool isGetter = expectBool();
+    moveNext();
+    bool isSetter = expectBool();
+
+    moveNext();
+    expectList();
+    List<ParameterDeclaration> namedParameters = [
+      for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
+        expectDeclaration()
+    ];
+
+    moveNext();
+    expectList();
+    List<ParameterDeclaration> positionalParameters = [
+      for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
+        expectDeclaration()
+    ];
+
+    moveNext();
+    TypeAnnotation returnType = expectTypeAnnotation();
+
+    moveNext();
+    expectList();
+    List<TypeParameterDeclaration> typeParameters = [
+      for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
+        expectDeclaration()
+    ];
+    return new FunctionDeclarationImpl(
+        name: name,
+        isAbstract: isAbstract,
+        isExternal: isExternal,
+        isGetter: isGetter,
+        isSetter: isSetter,
+        namedParameters: namedParameters,
+        positionalParameters: positionalParameters,
+        returnType: returnType,
+        typeParameters: typeParameters);
+  }
+
+  T expectCode<T extends Code>() {
+    CodeKind kind = CodeKind.values[expectNum()];
+    moveNext();
+    expectList();
+    List<Object> parts = [];
+    while (moveNext()) {
+      CodePartKind partKind = CodePartKind.values[expectNum()];
+      moveNext();
+      switch (partKind) {
+        case CodePartKind.code:
+          parts.add(expectCode());
+          break;
+        case CodePartKind.string:
+          parts.add(expectString());
+          break;
+        case CodePartKind.typeAnnotation:
+          parts.add(expectTypeAnnotation());
+          break;
+      }
+    }
+
+    switch (kind) {
+      case CodeKind.raw:
+        return new Code.fromParts(parts) as T;
+      case CodeKind.declaration:
+        return new DeclarationCode.fromParts(parts) as T;
+      case CodeKind.element:
+        return new ElementCode.fromParts(parts) as T;
+      case CodeKind.expression:
+        return new ExpressionCode.fromParts(parts) as T;
+      case CodeKind.functionBody:
+        return new FunctionBodyCode.fromParts(parts) as T;
+      case CodeKind.identifier:
+        return new IdentifierCode.fromParts(parts) as T;
+      case CodeKind.namedArgument:
+        return new NamedArgumentCode.fromParts(parts) as T;
+      case CodeKind.parameter:
+        return new ParameterCode.fromParts(parts) as T;
+      case CodeKind.statement:
+        return new StatementCode.fromParts(parts) as T;
+    }
+  }
+}
+
+/// 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
+      ..addNum(kind.index)
+      ..startList();
+    for (Object part in parts) {
+      if (part is String) {
+        serializer
+          ..addNum(CodePartKind.string.index)
+          ..addString(part);
+      } else if (part is Code) {
+        serializer.addNum(CodePartKind.code.index);
+        part.serialize(serializer);
+      } else if (part is TypeAnnotation) {
+        serializer.addNum(CodePartKind.typeAnnotation.index);
+        part.serialize(serializer);
+      } else {
+        throw new StateError('Unrecognized code part $part');
+      }
+    }
+    serializer.endList();
+  }
+}
+
+enum CodePartKind {
+  string,
+  code,
+  typeAnnotation,
+}
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 48b0229..136a6d5 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,7 +7,7 @@
 import 'dart:mirrors';
 
 import 'isolate_mirrors_impl.dart';
-import 'protocol.dart';
+import '../executor_shared/protocol.dart';
 import '../executor.dart';
 import '../api.dart';
 
@@ -29,10 +29,10 @@
   final SendPort _sendPort;
 
   /// The stream of responses from the [_macroIsolate].
-  final Stream<GenericResponse> _responseStream;
+  final Stream<Response> _responseStream;
 
   /// A map of response completers by request id.
-  final _responseCompleters = <int, Completer<GenericResponse>>{};
+  final _responseCompleters = <int, Completer<Response>>{};
 
   /// A function that should be invoked when shutting down this executor
   /// to perform any necessary cleanup.
@@ -41,7 +41,7 @@
   _IsolateMirrorMacroExecutor._(
       this._macroIsolate, this._sendPort, this._responseStream, this._onClose) {
     _responseStream.listen((event) {
-      Completer<GenericResponse>? completer =
+      Completer<Response>? completer =
           _responseCompleters.remove(event.requestId);
       if (completer == null) {
         throw new StateError(
@@ -57,13 +57,13 @@
   static Future<MacroExecutor> start() async {
     ReceivePort receivePort = new ReceivePort();
     Completer<SendPort> sendPortCompleter = new Completer<SendPort>();
-    StreamController<GenericResponse> responseStreamController =
-        new StreamController<GenericResponse>(sync: true);
+    StreamController<Response> responseStreamController =
+        new StreamController<Response>(sync: true);
     receivePort.listen((message) {
       if (!sendPortCompleter.isCompleted) {
         sendPortCompleter.complete(message as SendPort);
       } else {
-        responseStreamController.add(message as GenericResponse);
+        responseStreamController.add(message as Response);
       }
     }).onDone(responseStreamController.close);
     Isolate macroIsolate = await Isolate.spawn(spawn, receivePort.sendPort);
@@ -124,18 +124,24 @@
           new InstantiateMacroRequest(macroClass, constructor, arguments));
 
   @override
-  Future<MacroClassIdentifier> loadMacro(Uri library, String name) =>
-      _sendRequest(new LoadMacroRequest(library, name));
+  Future<MacroClassIdentifier> loadMacro(Uri library, String name,
+      {Uri? precompiledKernelUri}) {
+    if (precompiledKernelUri != null) {
+      // TODO: Implement support?
+      throw new UnsupportedError(
+          'The IsolateMirrorsExecutor does not support precompiled dill files');
+    }
+    return _sendRequest(new LoadMacroRequest(library, name));
+  }
 
   /// Sends a request and returns the response, casting it to the expected
   /// type.
   Future<T> _sendRequest<T>(Request request) async {
     _sendPort.send(request);
-    Completer<GenericResponse<T>> completer =
-        new Completer<GenericResponse<T>>();
+    Completer<Response> completer = new Completer<Response>();
     _responseCompleters[request.id] = completer;
-    GenericResponse<T> response = await completer.future;
-    T? result = response.response;
+    Response response = await completer.future;
+    T? result = response.response as T?;
     if (result != null) return result;
     throw response.error!;
   }
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 3cf24a5..e6848f8 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,8 +6,8 @@
 import 'dart:isolate';
 import 'dart:mirrors';
 
-import 'protocol.dart';
-import '../executor.dart';
+import '../executor_shared/response_impls.dart';
+import '../executor_shared/protocol.dart';
 import '../api.dart';
 
 /// Spawns a new isolate for loading and executing macros.
@@ -15,33 +15,28 @@
   ReceivePort receivePort = new ReceivePort();
   sendPort.send(receivePort.sendPort);
   receivePort.listen((message) async {
+    Response response;
     if (message is LoadMacroRequest) {
-      GenericResponse<MacroClassIdentifier> response =
-          await _loadMacro(message);
-      sendPort.send(response);
+      response = await _loadMacro(message);
     } else if (message is InstantiateMacroRequest) {
-      GenericResponse<MacroInstanceIdentifier> response =
-          await _instantiateMacro(message);
-      sendPort.send(response);
+      response = await _instantiateMacro(message);
     } else if (message is ExecuteDefinitionsPhaseRequest) {
-      GenericResponse<MacroExecutionResult> response =
-          await _executeDefinitionsPhase(message);
-      sendPort.send(response);
+      response = await _executeDefinitionsPhase(message);
     } else {
       throw new StateError('Unrecognized event type $message');
     }
+    sendPort.send(response);
   });
 }
 
 /// Maps macro identifiers to class mirrors.
-final _macroClasses = <_MacroClassIdentifier, ClassMirror>{};
+final _macroClasses = <MacroClassIdentifierImpl, ClassMirror>{};
 
 /// Handles [LoadMacroRequest]s.
-Future<GenericResponse<MacroClassIdentifier>> _loadMacro(
-    LoadMacroRequest request) async {
+Future<Response> _loadMacro(LoadMacroRequest request) async {
   try {
-    _MacroClassIdentifier identifier =
-        new _MacroClassIdentifier(request.library, request.name);
+    MacroClassIdentifierImpl identifier =
+        new MacroClassIdentifierImpl(request.library, request.name);
     if (_macroClasses.containsKey(identifier)) {
       throw new UnsupportedError(
           'Reloading macros is not supported by this implementation');
@@ -51,18 +46,17 @@
     ClassMirror macroClass =
         libMirror.declarations[new Symbol(request.name)] as ClassMirror;
     _macroClasses[identifier] = macroClass;
-    return new GenericResponse(response: identifier, requestId: request.id);
+    return new Response(response: identifier, requestId: request.id);
   } catch (e) {
-    return new GenericResponse(error: e, requestId: request.id);
+    return new Response(error: e, requestId: request.id);
   }
 }
 
 /// Maps macro instance identifiers to instances.
-final _macroInstances = <_MacroInstanceIdentifier, Macro>{};
+final _macroInstances = <MacroInstanceIdentifierImpl, Macro>{};
 
 /// Handles [InstantiateMacroRequest]s.
-Future<GenericResponse<MacroInstanceIdentifier>> _instantiateMacro(
-    InstantiateMacroRequest request) async {
+Future<Response> _instantiateMacro(InstantiateMacroRequest request) async {
   try {
     ClassMirror? clazz = _macroClasses[request.macroClass];
     if (clazz == null) {
@@ -73,16 +67,15 @@
       for (MapEntry<String, Object?> entry in request.arguments.named.entries)
         new Symbol(entry.key): entry.value,
     }).reflectee as Macro;
-    _MacroInstanceIdentifier identifier = new _MacroInstanceIdentifier();
+    MacroInstanceIdentifierImpl identifier = new MacroInstanceIdentifierImpl();
     _macroInstances[identifier] = instance;
-    return new GenericResponse<MacroInstanceIdentifier>(
-        response: identifier, requestId: request.id);
+    return new Response(response: identifier, requestId: request.id);
   } catch (e) {
-    return new GenericResponse(error: e, requestId: request.id);
+    return new Response(error: e, requestId: request.id);
   }
 }
 
-Future<GenericResponse<MacroExecutionResult>> _executeDefinitionsPhase(
+Future<Response> _executeDefinitionsPhase(
     ExecuteDefinitionsPhaseRequest request) async {
   try {
     Macro? instance = _macroInstances[request.macro];
@@ -93,163 +86,18 @@
     Declaration declaration = request.declaration;
     if (instance is FunctionDefinitionMacro &&
         declaration is FunctionDeclaration) {
-      _FunctionDefinitionBuilder builder = new _FunctionDefinitionBuilder(
+      FunctionDefinitionBuilderImpl builder = new FunctionDefinitionBuilderImpl(
           declaration,
           request.typeResolver,
           request.typeDeclarationResolver,
           request.classIntrospector);
       await instance.buildDefinitionForFunction(declaration, builder);
-      return new GenericResponse(
-          response: builder.result, requestId: request.id);
+      return new Response(response: builder.result, requestId: request.id);
     } else {
       throw new UnsupportedError(
           ('Only FunctionDefinitionMacros are supported currently'));
     }
   } catch (e) {
-    return new GenericResponse(error: e, requestId: request.id);
+    return new Response(error: e, requestId: request.id);
   }
 }
-
-/// Our implementation of [MacroClassIdentifier].
-class _MacroClassIdentifier implements MacroClassIdentifier {
-  final String id;
-
-  _MacroClassIdentifier(Uri library, String name) : id = '$library#$name';
-
-  operator ==(other) => other is _MacroClassIdentifier && id == other.id;
-
-  int get hashCode => id.hashCode;
-}
-
-/// Our implementation of [MacroInstanceIdentifier].
-class _MacroInstanceIdentifier implements MacroInstanceIdentifier {
-  static int _next = 0;
-
-  final int id;
-
-  _MacroInstanceIdentifier() : id = _next++;
-
-  operator ==(other) => other is _MacroInstanceIdentifier && id == other.id;
-
-  int get hashCode => id;
-}
-
-/// Our implementation of [MacroExecutionResult].
-class _MacroExecutionResult implements MacroExecutionResult {
-  @override
-  final List<DeclarationCode> augmentations = <DeclarationCode>[];
-
-  @override
-  final List<DeclarationCode> imports = <DeclarationCode>[];
-}
-
-/// Custom implementation of [FunctionDefinitionBuilder].
-class _FunctionDefinitionBuilder implements FunctionDefinitionBuilder {
-  final TypeResolver typeResolver;
-  final TypeDeclarationResolver typeDeclarationResolver;
-  final ClassIntrospector classIntrospector;
-
-  /// The declaration this is a builder for.
-  final FunctionDeclaration declaration;
-
-  /// The final result, will be built up over `augment` calls.
-  final _MacroExecutionResult result = new _MacroExecutionResult();
-
-  _FunctionDefinitionBuilder(this.declaration, this.typeResolver,
-      this.typeDeclarationResolver, this.classIntrospector);
-
-  @override
-  void augment(FunctionBodyCode body) {
-    result.augmentations.add(new DeclarationCode.fromParts([
-      'augment ',
-      declaration.returnType.code,
-      ' ',
-      declaration.name,
-      if (declaration.typeParameters.isNotEmpty) ...[
-        '<',
-        for (TypeParameterDeclaration typeParam
-            in declaration.typeParameters) ...[
-          typeParam.name,
-          if (typeParam.bounds != null) ...['extends ', typeParam.bounds!.code],
-          if (typeParam != declaration.typeParameters.last) ', ',
-        ],
-        '>',
-      ],
-      '(',
-      for (ParameterDeclaration positionalRequired
-          in declaration.positionalParameters.where((p) => p.isRequired)) ...[
-        new ParameterCode.fromParts([
-          positionalRequired.type.code,
-          ' ',
-          positionalRequired.name,
-        ]),
-        ', '
-      ],
-      if (declaration.positionalParameters.any((p) => !p.isRequired)) ...[
-        '[',
-        for (ParameterDeclaration positionalOptional in declaration
-            .positionalParameters
-            .where((p) => !p.isRequired)) ...[
-          new ParameterCode.fromParts([
-            positionalOptional.type.code,
-            ' ',
-            positionalOptional.name,
-          ]),
-          ', ',
-        ],
-        ']',
-      ],
-      if (declaration.namedParameters.isNotEmpty) ...[
-        '{',
-        for (ParameterDeclaration named in declaration.namedParameters) ...[
-          new ParameterCode.fromParts([
-            if (named.isRequired) 'required ',
-            named.type.code,
-            ' ',
-            named.name,
-            if (named.defaultValue != null) ...[
-              ' = ',
-              named.defaultValue!,
-            ],
-          ]),
-          ', ',
-        ],
-        '}',
-      ],
-      ') ',
-      body,
-    ]));
-  }
-
-  @override
-  Future<List<ConstructorDeclaration>> constructorsOf(ClassDeclaration clazz) =>
-      classIntrospector.constructorsOf(clazz);
-
-  @override
-  Future<List<FieldDeclaration>> fieldsOf(ClassDeclaration clazz) =>
-      classIntrospector.fieldsOf(clazz);
-
-  @override
-  Future<List<ClassDeclaration>> interfacesOf(ClassDeclaration clazz) =>
-      classIntrospector.interfacesOf(clazz);
-
-  @override
-  Future<List<MethodDeclaration>> methodsOf(ClassDeclaration clazz) =>
-      classIntrospector.methodsOf(clazz);
-
-  @override
-  Future<List<ClassDeclaration>> mixinsOf(ClassDeclaration clazz) =>
-      classIntrospector.mixinsOf(clazz);
-
-  @override
-  Future<TypeDeclaration> declarationOf(NamedStaticType annotation) =>
-      typeDeclarationResolver.declarationOf(annotation);
-
-  @override
-  Future<ClassDeclaration?> superclassOf(ClassDeclaration clazz) =>
-      classIntrospector.superclassOf(clazz);
-
-  @override
-  Future<StaticType> resolve(TypeAnnotation typeAnnotation) =>
-      typeResolver.resolve(typeAnnotation);
-}
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/protocol.dart b/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/protocol.dart
deleted file mode 100644
index 96a2724..0000000
--- a/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/protocol.dart
+++ /dev/null
@@ -1,61 +0,0 @@
-// 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.
-
-/// Defines the objects used for communication between the macro executor and
-/// the isolate doing the work of macro loading and execution.
-library protocol;
-
-import '../executor.dart';
-import '../api.dart';
-
-/// Base class all requests extend, provides a unique id for each request.
-class Request {
-  final int id;
-
-  Request() : id = _next++;
-
-  static int _next = 0;
-}
-
-/// A generic response object that is either an instance of [T] or an error.
-class GenericResponse<T> {
-  final T? response;
-  final Object? error;
-  final int requestId;
-
-  GenericResponse({this.response, this.error, required this.requestId})
-      : assert(response != null || error != null),
-        assert(response == null || error == null);
-}
-
-/// A request to load a macro in this isolate.
-class LoadMacroRequest extends Request {
-  final Uri library;
-  final String name;
-
-  LoadMacroRequest(this.library, this.name);
-}
-
-/// A request to instantiate a macro instance.
-class InstantiateMacroRequest extends Request {
-  final MacroClassIdentifier macroClass;
-  final String constructorName;
-  final Arguments arguments;
-
-  InstantiateMacroRequest(
-      this.macroClass, this.constructorName, this.arguments);
-}
-
-/// A request to execute a macro on a particular declaration in the definition
-/// phase.
-class ExecuteDefinitionsPhaseRequest extends Request {
-  final MacroInstanceIdentifier macro;
-  final Declaration declaration;
-  final TypeResolver typeResolver;
-  final ClassIntrospector classIntrospector;
-  final TypeDeclarationResolver typeDeclarationResolver;
-
-  ExecuteDefinitionsPhaseRequest(this.macro, this.declaration,
-      this.typeResolver, this.classIntrospector, this.typeDeclarationResolver);
-}
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
new file mode 100644
index 0000000..b33dd94
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/isolated_executor/isolated_executor.dart
@@ -0,0 +1,227 @@
+// 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 'dart:async';
+import 'dart:isolate';
+
+import '../api.dart';
+import '../executor_shared/protocol.dart';
+import '../executor_shared/serialization.dart';
+import '../executor_shared/response_impls.dart';
+import '../executor.dart';
+
+/// Returns an instance of [_IsolatedMacroExecutor].
+///
+/// This is the only public api exposed by this library.
+Future<MacroExecutor> start() async => new _IsolatedMacroExecutor();
+
+/// A [MacroExecutor] implementation which spawns a separate isolate for each
+/// macro that is loaded. Each of these is wrapped in its own
+/// [_SingleIsolatedMacroExecutor] which requests are delegated to.
+///
+/// This implementation requires precompiled kernel files when loading macros,
+/// (you must pass a `precompiledKernelUri` to [loadMacro]).
+///
+/// Spawned isolates are not ran in the same isolate group, so objects are
+/// serialized between isolates.
+class _IsolatedMacroExecutor implements MacroExecutor {
+  /// Individual executors indexed by [MacroClassIdentifier] or
+  /// [MacroInstanceIdentifier].
+  final _executors = <Object, _SingleIsolatedMacroExecutor>{};
+
+  @override
+  Future<String> buildAugmentationLibrary(
+      Iterable<MacroExecutionResult> macroResults) {
+    // TODO: implement buildAugmentationLibrary
+    throw new UnimplementedError();
+  }
+
+  @override
+  void close() {
+    for (_SingleIsolatedMacroExecutor executor in _executors.values) {
+      executor.close();
+    }
+  }
+
+  @override
+  Future<MacroExecutionResult> executeDeclarationsPhase(
+          MacroInstanceIdentifier macro,
+          Declaration declaration,
+          TypeResolver typeResolver,
+          ClassIntrospector classIntrospector) =>
+      _executors[macro]!.executeDeclarationsPhase(
+          macro, declaration, typeResolver, classIntrospector);
+
+  @override
+  Future<MacroExecutionResult> executeDefinitionsPhase(
+          MacroInstanceIdentifier macro,
+          Declaration declaration,
+          TypeResolver typeResolver,
+          ClassIntrospector classIntrospector,
+          TypeDeclarationResolver typeDeclarationResolver) =>
+      _executors[macro]!.executeDefinitionsPhase(macro, declaration,
+          typeResolver, classIntrospector, typeDeclarationResolver);
+
+  @override
+  Future<MacroExecutionResult> executeTypesPhase(
+          MacroInstanceIdentifier macro, Declaration declaration) =>
+      _executors[macro]!.executeTypesPhase(macro, declaration);
+
+  @override
+  Future<MacroInstanceIdentifier> instantiateMacro(
+      MacroClassIdentifier macroClass,
+      String constructor,
+      Arguments arguments) async {
+    _SingleIsolatedMacroExecutor executor = _executors[macroClass]!;
+    MacroInstanceIdentifier instance =
+        await executor.instantiateMacro(macroClass, constructor, arguments);
+    _executors[instance] = executor;
+    return instance;
+  }
+
+  @override
+  Future<MacroClassIdentifier> loadMacro(Uri library, String name,
+      {Uri? precompiledKernelUri}) async {
+    if (precompiledKernelUri == null) {
+      throw new UnsupportedError(
+          'This environment requires a non-null `precompiledKernelUri` to be '
+          'passed when loading macros.');
+    }
+    MacroClassIdentifier identifier =
+        new MacroClassIdentifierImpl(library, name);
+    _executors.remove(identifier)?.close();
+
+    _SingleIsolatedMacroExecutor executor =
+        await _SingleIsolatedMacroExecutor.start(
+            library, name, precompiledKernelUri);
+    _executors[identifier] = executor;
+    return identifier;
+  }
+}
+
+class _SingleIsolatedMacroExecutor extends MacroExecutor {
+  /// The stream on which we receive responses.
+  final Stream<Response> responseStream;
+
+  /// The send port where we should send requests.
+  final SendPort sendPort;
+
+  /// A function that should be invoked when shutting down this executor
+  /// to perform any necessary cleanup.
+  final void Function() onClose;
+
+  /// A map of response completers by request id.
+  final responseCompleters = <int, Completer<Response>>{};
+
+  _SingleIsolatedMacroExecutor(
+      {required this.onClose,
+      required this.responseStream,
+      required this.sendPort}) {
+    responseStream.listen((event) {
+      Completer<Response>? completer =
+          responseCompleters.remove(event.requestId);
+      if (completer == null) {
+        throw new StateError(
+            'Got a response for an unrecognized request id ${event.requestId}');
+      }
+      completer.complete(event);
+    });
+  }
+
+  static Future<_SingleIsolatedMacroExecutor> start(
+      Uri library, String name, Uri precompiledKernelUri) async {
+    ReceivePort receivePort = new ReceivePort();
+    Isolate isolate =
+        await Isolate.spawnUri(precompiledKernelUri, [], receivePort.sendPort);
+    Completer<SendPort> sendPortCompleter = new Completer();
+    StreamController<Response> responseStreamController =
+        new StreamController(sync: true);
+    receivePort.listen((message) {
+      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);
+        });
+      }
+    }).onDone(responseStreamController.close);
+
+    return new _SingleIsolatedMacroExecutor(
+        onClose: () {
+          receivePort.close();
+          isolate.kill();
+        },
+        responseStream: responseStreamController.stream,
+        sendPort: await sendPortCompleter.future);
+  }
+
+  @override
+  void close() => onClose();
+
+  /// These calls are handled by the higher level executor.
+  @override
+  Future<String> buildAugmentationLibrary(
+          Iterable<MacroExecutionResult> macroResults) =>
+      throw new StateError('Unreachable');
+
+  @override
+  Future<MacroExecutionResult> executeDeclarationsPhase(
+      MacroInstanceIdentifier macro,
+      Declaration declaration,
+      TypeResolver typeResolver,
+      ClassIntrospector classIntrospector) {
+    // TODO: implement executeDeclarationsPhase
+    throw new UnimplementedError();
+  }
+
+  @override
+  Future<MacroExecutionResult> executeDefinitionsPhase(
+          MacroInstanceIdentifier macro,
+          Declaration declaration,
+          TypeResolver typeResolver,
+          ClassIntrospector classIntrospector,
+          TypeDeclarationResolver typeDeclarationResolver) =>
+      _sendRequest(new ExecuteDefinitionsPhaseRequest(macro, declaration,
+          typeResolver, classIntrospector, typeDeclarationResolver));
+
+  @override
+  Future<MacroExecutionResult> executeTypesPhase(
+      MacroInstanceIdentifier macro, Declaration declaration) {
+    // TODO: implement executeTypesPhase
+    throw new UnimplementedError();
+  }
+
+  @override
+  Future<MacroInstanceIdentifier> instantiateMacro(
+          MacroClassIdentifier macroClass,
+          String constructor,
+          Arguments arguments) =>
+      _sendRequest(
+          new InstantiateMacroRequest(macroClass, constructor, arguments));
+
+  /// These calls are handled by the higher level executor.
+  @override
+  Future<MacroClassIdentifier> loadMacro(Uri library, String name,
+          {Uri? precompiledKernelUri}) =>
+      throw new StateError('Unreachable');
+
+  /// Sends a [request] and handles the response, casting it to the expected
+  /// type or throwing the error provided.
+  Future<T> _sendRequest<T>(Request request) =>
+      withSerializationMode(SerializationMode.server, () async {
+        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!;
+      });
+}
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
new file mode 100644
index 0000000..c069423
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/test/macros/executor_shared/serialization_test.dart
@@ -0,0 +1,78 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:_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', () {
+      var serializer = JsonSerializer();
+      serializer
+        ..addNum(1)
+        ..addNullableNum(null)
+        ..addString('hello')
+        ..addNullableString(null)
+        ..startList()
+        ..addBool(true)
+        ..startList()
+        ..addNull()
+        ..endList()
+        ..addNullableBool(null)
+        ..endList()
+        ..addNum(1.0)
+        ..startList()
+        ..endList();
+      expect(
+          serializer.result,
+          equals([
+            1,
+            null,
+            'hello',
+            null,
+            [
+              true,
+              [null],
+              null
+            ],
+            1.0,
+            [],
+          ]));
+      var deserializer = JsonDeserializer(serializer.result);
+      expect(deserializer.moveNext(), true);
+      expect(deserializer.expectNum(), 1);
+      expect(deserializer.moveNext(), true);
+      expect(deserializer.expectNullableNum(), null);
+      expect(deserializer.moveNext(), true);
+      expect(deserializer.expectString(), 'hello');
+      expect(deserializer.moveNext(), true);
+      expect(deserializer.expectNullableString(), null);
+      expect(deserializer.moveNext(), true);
+
+      deserializer.expectList();
+      expect(deserializer.moveNext(), true);
+      expect(deserializer.expectBool(), true);
+      expect(deserializer.moveNext(), true);
+
+      deserializer.expectList();
+      expect(deserializer.moveNext(), true);
+      expect(deserializer.checkNull(), true);
+      expect(deserializer.moveNext(), false);
+
+      expect(deserializer.moveNext(), true);
+      expect(deserializer.expectNullableBool(), null);
+      expect(deserializer.moveNext(), false);
+
+      // Have to move the parent again to advance it past the list entry.
+      expect(deserializer.moveNext(), true);
+      expect(deserializer.expectNum(), 1.0);
+      expect(deserializer.moveNext(), true);
+
+      deserializer.expectList();
+      expect(deserializer.moveNext(), false);
+
+      expect(deserializer.moveNext(), false);
+    });
+  });
+}
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 ab7b057..7056208 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
@@ -6,6 +6,7 @@
 
 import 'package:_fe_analyzer_shared/src/macros/api.dart';
 import 'package:_fe_analyzer_shared/src/macros/executor.dart';
+import 'package:_fe_analyzer_shared/src/macros/executor_shared/introspection_impls.dart';
 import 'package:_fe_analyzer_shared/src/macros/isolate_mirrors_executor/isolate_mirrors_executor.dart'
     as mirrorExecutor;
 
@@ -26,7 +27,7 @@
   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/isolate_mirror_executor/simple_macro.dart')
+        File('pkg/_fe_analyzer_shared/test/macros/simple_macro.dart')
             .absolute
             .uri,
         'SimpleMacro');
@@ -49,7 +50,7 @@
 
     var definitionResult = await executor.executeDefinitionsPhase(
         instanceId,
-        _FunctionDeclaration(
+        FunctionDeclarationImpl(
           isAbstract: false,
           isExternal: false,
           isGetter: false,
@@ -57,8 +58,8 @@
           name: 'foo',
           namedParameters: [],
           positionalParameters: [],
-          returnType:
-              _TypeAnnotation(Code.fromString('String'), isNullable: false),
+          returnType: NamedTypeAnnotationImpl(
+              name: 'String', isNullable: false, typeArguments: const []),
           typeParameters: [],
         ),
         _FakeTypeResolver(),
@@ -82,63 +83,14 @@
     with Fake
     implements TypeDeclarationResolver {}
 
-class _FunctionDeclaration implements FunctionDeclaration {
-  @override
-  final bool isAbstract;
-
-  @override
-  final bool isExternal;
-
-  @override
-  final bool isGetter;
-
-  @override
-  final bool isSetter;
-
-  @override
-  final String name;
-
-  @override
-  final Iterable<ParameterDeclaration> namedParameters;
-
-  @override
-  final Iterable<ParameterDeclaration> positionalParameters;
-
-  @override
-  final TypeAnnotation returnType;
-
-  @override
-  final Iterable<TypeParameterDeclaration> typeParameters;
-
-  _FunctionDeclaration({
-    required this.isAbstract,
-    required this.isExternal,
-    required this.isGetter,
-    required this.isSetter,
-    required this.name,
-    required this.namedParameters,
-    required this.positionalParameters,
-    required this.returnType,
-    required this.typeParameters,
-  });
-}
-
-class _TypeAnnotation implements TypeAnnotation {
-  @override
-  final Code code;
-
-  @override
-  final bool isNullable;
-
-  _TypeAnnotation(this.code, {required this.isNullable});
-}
-
 extension _ on Code {
   StringBuffer debugString([StringBuffer? buffer]) {
     buffer ??= StringBuffer();
     for (var part in parts) {
       if (part is Code) {
         part.debugString(buffer);
+      } else if (part is TypeAnnotation) {
+        part.code.debugString(buffer);
       } else {
         buffer.write(part.toString());
       }
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
new file mode 100644
index 0000000..4659971
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/test/macros/isolated_executor/isolated_executor_test.dart
@@ -0,0 +1,122 @@
+// 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 'dart:io';
+import 'dart:isolate';
+
+import 'package:_fe_analyzer_shared/src/macros/api.dart';
+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/isolated_executor/isolated_executor.dart'
+    as isolatedExecutor;
+
+import 'package:test/fake.dart';
+import 'package:test/test.dart';
+
+void main() {
+  late MacroExecutor executor;
+  late Directory tmpDir;
+
+  setUp(() async {
+    executor = await isolatedExecutor.start();
+    tmpDir = Directory.systemTemp.createTempSync('isolated_executor_test');
+  });
+
+  tearDown(() {
+    if (tmpDir.existsSync()) tmpDir.deleteSync(recursive: true);
+    executor.close();
+  });
+
+  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 macroName = 'SimpleMacro';
+
+    var bootstrapContent =
+        bootstrapMacroIsolate(macroUri.toString(), macroName, ['', 'named']);
+    var bootstrapFile = File(tmpDir.uri.resolve('main.dart').toFilePath())
+      ..writeAsStringSync(bootstrapContent);
+    var kernelOutputFile =
+        File(tmpDir.uri.resolve('main.dart.dill').toFilePath());
+    var result = await Process.run(Platform.resolvedExecutable, [
+      '--snapshot=${kernelOutputFile.uri.toFilePath()}',
+      '--snapshot-kind=kernel',
+      '--packages=${(await Isolate.packageConfig)!}',
+      bootstrapFile.uri.toFilePath(),
+    ]);
+    expect(result.exitCode, 0,
+        reason: 'stdout: ${result.stdout}\nstderr: ${result.stderr}');
+
+    var clazzId = await executor.loadMacro(macroUri, macroName,
+        precompiledKernelUri: kernelOutputFile.uri);
+    expect(clazzId, isNotNull, reason: 'Can load a macro.');
+
+    var instanceId =
+        await executor.instantiateMacro(clazzId, '', Arguments([], {}));
+    expect(instanceId, isNotNull,
+        reason: 'Can create an instance with no arguments.');
+
+    instanceId =
+        await executor.instantiateMacro(clazzId, '', Arguments([1, 2], {}));
+    expect(instanceId, isNotNull,
+        reason: 'Can create an instance with positional arguments.');
+
+    instanceId = await executor.instantiateMacro(
+        clazzId, 'named', Arguments([], {'x': 1, 'y': 2}));
+    expect(instanceId, isNotNull,
+        reason: 'Can create an instance with named arguments.');
+
+    var definitionResult = await executor.executeDefinitionsPhase(
+        instanceId,
+        FunctionDeclarationImpl(
+          isAbstract: false,
+          isExternal: false,
+          isGetter: false,
+          isSetter: false,
+          name: 'foo',
+          namedParameters: [],
+          positionalParameters: [],
+          returnType: NamedTypeAnnotationImpl(
+              name: 'String', isNullable: false, typeArguments: const []),
+          typeParameters: [],
+        ),
+        _FakeTypeResolver(),
+        _FakeClassIntrospector(),
+        _FakeTypeDeclarationResolver());
+    expect(definitionResult.augmentations, hasLength(1));
+    expect(definitionResult.augmentations.first.debugString().toString(),
+        equalsIgnoringWhitespace('''
+            augment String foo() {
+              print('x: 1, y: 2');
+              return augment super();
+            }'''));
+  });
+}
+
+class _FakeClassIntrospector with Fake implements ClassIntrospector {}
+
+class _FakeTypeResolver with Fake implements TypeResolver {}
+
+class _FakeTypeDeclarationResolver
+    with Fake
+    implements TypeDeclarationResolver {}
+
+extension _ on Code {
+  StringBuffer debugString([StringBuffer? buffer]) {
+    buffer ??= StringBuffer();
+    for (var part in parts) {
+      if (part is Code) {
+        part.debugString(buffer);
+      } else if (part is TypeAnnotation) {
+        part.code.debugString(buffer);
+      } else {
+        buffer.write(part.toString());
+      }
+    }
+    return buffer;
+  }
+}
diff --git a/pkg/_fe_analyzer_shared/test/macros/isolate_mirror_executor/simple_macro.dart b/pkg/_fe_analyzer_shared/test/macros/simple_macro.dart
similarity index 100%
rename from pkg/_fe_analyzer_shared/test/macros/isolate_mirror_executor/simple_macro.dart
rename to pkg/_fe_analyzer_shared/test/macros/simple_macro.dart
diff --git a/pkg/front_end/test/macros/macro_test.dart b/pkg/front_end/test/macros/macro_test.dart
index 7a4dee8..2ceca95 100644
--- a/pkg/front_end/test/macros/macro_test.dart
+++ b/pkg/front_end/test/macros/macro_test.dart
@@ -5,6 +5,7 @@
 import 'dart:io' show Directory, Platform;
 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/serialization.dart';
 import 'package:_fe_analyzer_shared/src/testing/id.dart' show ActualData, Id;
 import 'package:_fe_analyzer_shared/src/testing/id_testing.dart';
 import 'package:_fe_analyzer_shared/src/testing/features.dart';
@@ -292,7 +293,12 @@
   }
 
   @override
-  Future<MacroClassIdentifier> loadMacro(Uri library, String name) async {
+  Future<MacroClassIdentifier> loadMacro(Uri library, String name,
+      {Uri? precompiledKernelUri}) async {
+    if (precompiledKernelUri != null) {
+      throw new UnsupportedError(
+          'Precompiled kernel not supported for this implementation.');
+    }
     _MacroClassIdentifier id = new _MacroClassIdentifier(library, name);
     macroClasses.add(id);
     return id;
@@ -320,6 +326,9 @@
 
   @override
   String toString() => 'MacroClassIdentifier($uri,$className)';
+
+  @override
+  void serialize(Serializer serializer) => throw UnimplementedError();
 }
 
 class _MacroInstanceIdentifier implements MacroInstanceIdentifier {
@@ -330,4 +339,7 @@
   _MacroInstanceIdentifier(this.macroClass, this.constructor, this.arguments);
 
   String toText() => '${macroClass.toText()}/${constructor}()';
+
+  @override
+  void serialize(Serializer serializer) => throw UnimplementedError();
 }
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index 613e0b7..8b9b65b4 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -141,6 +141,7 @@
 bof
 bom
 bones
+bootstrap
 bother
 boundness
 boxed
@@ -326,6 +327,7 @@
 degrades
 degree
 del
+delegated
 delimit
 delimiting
 demands
@@ -420,6 +422,7 @@
 engineered
 enhanced
 enters
+entrypoint
 entrypointish
 enumerates
 env
@@ -436,6 +439,7 @@
 eval
 execute
 executor
+executors
 exhausted
 existence
 existentially
@@ -603,6 +607,7 @@
 impl
 impl+
 implementers
+impls
 imply
 implying
 importantly
@@ -614,6 +619,7 @@
 inconsistency
 increased
 incremented
+incrementing
 independently
 indexer
 indexing
@@ -668,6 +674,7 @@
 is64
 isolate
 isolated
+isolates
 issuecomment
 issuing
 iterables
@@ -840,6 +847,7 @@
 newest
 nextnext
 ni
+nil
 nine
 nj
 nk
@@ -969,6 +977,7 @@
 pre
 prebuild
 prebuilt
+precompiled
 preexisted
 preexisting
 premark
@@ -1226,6 +1235,7 @@
 spaced
 sparse
 spawn
+spawned
 spawns
 spec
 spec'ed
@@ -1483,6 +1493,7 @@
 vegorov
 verbosity
 versa
+versus
 viable
 vice
 video