Version 2.17.0-25.0.dev

Merge commit '24ee012ce3705795914c64d2dc0be962a08a014a' into 'dev'
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/introspection_impls.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/introspection_impls.dart
index 7706af7..a3f6fb4 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/introspection_impls.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/introspection_impls.dart
@@ -32,7 +32,8 @@
         name,
         if (typeArguments.isNotEmpty) ...[
           '<',
-          for (TypeAnnotation arg in typeArguments) ...[arg, ','],
+          typeArguments.first,
+          for (TypeAnnotation arg in typeArguments.skip(1)) ...[', ', arg],
           '>',
         ],
         if (isNullable) '?',
@@ -79,10 +80,15 @@
         'Function',
         if (typeParameters.isNotEmpty) ...[
           '<',
-          for (TypeParameterDeclaration arg in typeParameters) ...[
+          typeParameters.first.name,
+          if (typeParameters.first.bounds != null) ...[
+            ' extends ',
+            typeParameters.first.bounds!
+          ],
+          for (TypeParameterDeclaration arg in typeParameters.skip(1)) ...[
+            ', ',
             arg.name,
             if (arg.bounds != null) ...[' extends ', arg.bounds!],
-            ','
           ],
           '>',
         ],
@@ -158,7 +164,10 @@
 }
 
 abstract class DeclarationImpl extends RemoteInstance implements Declaration {
-  DeclarationImpl(int id) : super(id);
+  final String name;
+
+  DeclarationImpl({required int id, required this.name}) : super(id);
+
   @override
   void serialize(Serializer serializer) {
     super.serialize(serializer);
@@ -174,9 +183,6 @@
 class ParameterDeclarationImpl extends DeclarationImpl
     implements ParameterDeclaration {
   @override
-  final String name;
-
-  @override
   final Code? defaultValue;
 
   @override
@@ -193,12 +199,12 @@
 
   ParameterDeclarationImpl({
     required int id,
-    required this.name,
+    required String name,
     required this.defaultValue,
     required this.isNamed,
     required this.isRequired,
     required this.type,
-  }) : super(id);
+  }) : super(id: id, name: name);
 
   @override
   void serialize(Serializer serializer) {
@@ -222,17 +228,16 @@
 class TypeParameterDeclarationImpl extends DeclarationImpl
     implements TypeParameterDeclaration {
   @override
-  final String name;
-
-  @override
   final TypeAnnotationImpl? bounds;
 
   @override
   RemoteInstanceKind get kind => RemoteInstanceKind.typeParameterDeclaration;
 
-  TypeParameterDeclarationImpl(
-      {required int id, required this.name, required this.bounds})
-      : super(id);
+  TypeParameterDeclarationImpl({
+    required int id,
+    required String name,
+    required this.bounds,
+  }) : super(id: id, name: name);
 
   @override
   void serialize(Serializer serializer) {
@@ -254,9 +259,6 @@
 class FunctionDeclarationImpl extends DeclarationImpl
     implements FunctionDeclaration {
   @override
-  final String name;
-
-  @override
   final bool isAbstract;
 
   @override
@@ -285,7 +287,7 @@
 
   FunctionDeclarationImpl({
     required int id,
-    required this.name,
+    required String name,
     required this.isAbstract,
     required this.isExternal,
     required this.isGetter,
@@ -294,7 +296,7 @@
     required this.positionalParameters,
     required this.returnType,
     required this.typeParameters,
-  }) : super(id);
+  }) : super(id: id, name: name);
 
   @override
   void serialize(Serializer serializer) {
@@ -328,3 +330,304 @@
     serializer.endList();
   }
 }
+
+class MethodDeclarationImpl extends FunctionDeclarationImpl
+    implements MethodDeclaration {
+  @override
+  final TypeAnnotationImpl definingClass;
+
+  @override
+  RemoteInstanceKind get kind => RemoteInstanceKind.methodDeclaration;
+
+  MethodDeclarationImpl({
+    // Declaration fields
+    required int id,
+    required String name,
+    // Function fields
+    required bool isAbstract,
+    required bool isExternal,
+    required bool isGetter,
+    required bool isSetter,
+    required List<ParameterDeclarationImpl> namedParameters,
+    required List<ParameterDeclarationImpl> positionalParameters,
+    required TypeAnnotationImpl returnType,
+    required List<TypeParameterDeclarationImpl> typeParameters,
+    // Method fields
+    required this.definingClass,
+  }) : super(
+          id: id,
+          name: name,
+          isAbstract: isAbstract,
+          isExternal: isExternal,
+          isGetter: isGetter,
+          isSetter: isSetter,
+          namedParameters: namedParameters,
+          positionalParameters: positionalParameters,
+          returnType: returnType,
+          typeParameters: typeParameters,
+        );
+
+  @override
+  void serialize(Serializer serializer) {
+    super.serialize(serializer);
+    // Client side we don't encode anything but the ID.
+    if (serializationMode == SerializationMode.client) {
+      return;
+    }
+
+    definingClass.serialize(serializer);
+  }
+}
+
+class ConstructorDeclarationImpl extends MethodDeclarationImpl
+    implements ConstructorDeclaration {
+  @override
+  final bool isFactory;
+
+  @override
+  RemoteInstanceKind get kind => RemoteInstanceKind.constructorDeclaration;
+
+  ConstructorDeclarationImpl({
+    // Declaration fields
+    required int id,
+    required String name,
+    // Function fields
+    required bool isAbstract,
+    required bool isExternal,
+    required bool isGetter,
+    required bool isSetter,
+    required List<ParameterDeclarationImpl> namedParameters,
+    required List<ParameterDeclarationImpl> positionalParameters,
+    required TypeAnnotationImpl returnType,
+    required List<TypeParameterDeclarationImpl> typeParameters,
+    // Method fields
+    required TypeAnnotationImpl definingClass,
+    // Constructor fields
+    required this.isFactory,
+  }) : super(
+          id: id,
+          name: name,
+          isAbstract: isAbstract,
+          isExternal: isExternal,
+          isGetter: isGetter,
+          isSetter: isSetter,
+          namedParameters: namedParameters,
+          positionalParameters: positionalParameters,
+          returnType: returnType,
+          typeParameters: typeParameters,
+          definingClass: definingClass,
+        );
+
+  @override
+  void serialize(Serializer serializer) {
+    super.serialize(serializer);
+    // Client side we don't encode anything but the ID.
+    if (serializationMode == SerializationMode.client) {
+      return;
+    }
+
+    serializer.addBool(isFactory);
+  }
+}
+
+class VariableDeclarationImpl extends DeclarationImpl
+    implements VariableDeclaration {
+  @override
+  final Code? initializer;
+
+  @override
+  final bool isAbstract;
+
+  @override
+  final bool isExternal;
+
+  @override
+  final TypeAnnotationImpl type;
+
+  @override
+  RemoteInstanceKind get kind => RemoteInstanceKind.variableDeclaration;
+
+  VariableDeclarationImpl({
+    required int id,
+    required String name,
+    required this.initializer,
+    required this.isAbstract,
+    required this.isExternal,
+    required this.type,
+  }) : super(id: id, name: name);
+
+  @override
+  void serialize(Serializer serializer) {
+    super.serialize(serializer);
+    // Client side we don't encode anything but the ID.
+    if (serializationMode == SerializationMode.client) {
+      return;
+    }
+
+    initializer.serializeNullable(serializer);
+    serializer
+      ..addBool(isAbstract)
+      ..addBool(isExternal);
+    type.serialize(serializer);
+  }
+}
+
+class FieldDeclarationImpl extends VariableDeclarationImpl
+    implements FieldDeclaration {
+  @override
+  final TypeAnnotationImpl definingClass;
+
+  FieldDeclarationImpl({
+    // Declaration fields
+    required int id,
+    required String name,
+    // Variable fields
+    required Code? initializer,
+    required bool isAbstract,
+    required bool isExternal,
+    required TypeAnnotationImpl type,
+    // Field fields
+    required this.definingClass,
+  }) : super(
+            id: id,
+            name: name,
+            initializer: initializer,
+            isAbstract: isAbstract,
+            isExternal: isExternal,
+            type: type);
+
+  @override
+  RemoteInstanceKind get kind => RemoteInstanceKind.fieldDeclaration;
+
+  void serialize(Serializer serializer) {
+    super.serialize(serializer);
+    // Client side we don't encode anything but the ID.
+    if (serializationMode == SerializationMode.client) {
+      return;
+    }
+
+    definingClass.serialize(serializer);
+  }
+}
+
+abstract class TypeDeclarationImpl extends DeclarationImpl
+    implements TypeDeclaration {
+  @override
+  final List<TypeParameterDeclarationImpl> typeParameters;
+
+  TypeDeclarationImpl({
+    required int id,
+    required String name,
+    required this.typeParameters,
+  }) : super(id: id, name: name);
+
+  @override
+  Future<StaticType> instantiate(
+      {required List<StaticType> typeArguments, required bool isNullable}) {
+    // TODO: implement instantiate
+    throw new UnimplementedError('instantiate');
+  }
+
+  void serialize(Serializer serializer) {
+    super.serialize(serializer);
+    // Client side we don't encode anything but the ID.
+    if (serializationMode == SerializationMode.client) {
+      return;
+    }
+
+    serializer..startList();
+    for (TypeParameterDeclarationImpl param in typeParameters) {
+      param.serialize(serializer);
+    }
+    serializer.endList();
+  }
+}
+
+class ClassDeclarationImpl extends TypeDeclarationImpl
+    implements ClassDeclaration {
+  @override
+  final List<TypeAnnotationImpl> interfaces;
+
+  @override
+  final bool isAbstract;
+
+  @override
+  final bool isExternal;
+
+  @override
+  final List<TypeAnnotationImpl> mixins;
+
+  @override
+  final TypeAnnotationImpl? superclass;
+
+  @override
+  RemoteInstanceKind get kind => RemoteInstanceKind.classDeclaration;
+
+  ClassDeclarationImpl({
+    // Declaration fields
+    required int id,
+    required String name,
+    // TypeDeclaration fields
+    required List<TypeParameterDeclarationImpl> typeParameters,
+    // ClassDeclaration fields
+    required this.interfaces,
+    required this.isAbstract,
+    required this.isExternal,
+    required this.mixins,
+    required this.superclass,
+  }) : super(id: id, name: name, typeParameters: typeParameters);
+
+  @override
+  void serialize(Serializer serializer) {
+    super.serialize(serializer);
+    // Client side we don't encode anything but the ID.
+    if (serializationMode == SerializationMode.client) {
+      return;
+    }
+
+    serializer.startList();
+    for (TypeAnnotationImpl interface in interfaces) {
+      interface.serialize(serializer);
+    }
+    serializer
+      ..endList()
+      ..addBool(isAbstract)
+      ..addBool(isExternal)
+      ..startList();
+    for (TypeAnnotationImpl mixin in mixins) {
+      mixin.serialize(serializer);
+    }
+    serializer..endList();
+    superclass.serializeNullable(serializer);
+  }
+}
+
+class TypeAliasDeclarationImpl extends TypeDeclarationImpl
+    implements TypeAliasDeclaration {
+  @override
+  RemoteInstanceKind get kind => RemoteInstanceKind.typeAliasDeclaration;
+
+  @override
+  final TypeAnnotationImpl type;
+
+  TypeAliasDeclarationImpl({
+    // Declaration fields
+    required int id,
+    required String name,
+    // TypeDeclaration fields
+    required List<TypeParameterDeclarationImpl> typeParameters,
+    // TypeAlias fields
+    required this.type,
+  }) : super(id: id, name: name, typeParameters: typeParameters);
+
+  @override
+  void serialize(Serializer serializer) {
+    super.serialize(serializer);
+    // Client side we don't encode anything but the ID.
+    if (serializationMode == SerializationMode.client) {
+      return;
+    }
+
+    type.serialize(serializer);
+  }
+}
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/protocol.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/protocol.dart
index 3f9e55c..820c899 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/protocol.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/protocol.dart
@@ -369,7 +369,7 @@
 
 /// TODO: Implement this
 class ClientClassIntrospector implements ClassIntrospector {
-  /// The actual remote instance of this type resolver.
+  /// The actual remote instance of this class introspector.
   final RemoteInstanceImpl remoteInstance;
 
   /// The ID of the zone in which to find the original type resolver.
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization_extensions.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization_extensions.dart
index c950279..e341b91 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization_extensions.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization_extensions.dart
@@ -12,161 +12,174 @@
         moveNext();
         RemoteInstanceKind kind = RemoteInstanceKind.values[expectNum()];
         switch (kind) {
-          case RemoteInstanceKind.namedTypeAnnotation:
+          case RemoteInstanceKind.classDeclaration:
             moveNext();
-            return _expectNamedTypeAnnotation(id) as T;
-          case RemoteInstanceKind.functionTypeAnnotation:
+            return _expectClassDeclaration(id) as T;
+          case RemoteInstanceKind.constructorDeclaration:
             moveNext();
-            return _expectFunctionTypeAnnotation(id) as T;
+            return _expectConstructorDeclaration(id) as T;
+          case RemoteInstanceKind.fieldDeclaration:
+            moveNext();
+            return _expectFieldDeclaration(id) as T;
           case RemoteInstanceKind.functionDeclaration:
             moveNext();
             return _expectFunctionDeclaration(id) as T;
+          case RemoteInstanceKind.functionTypeAnnotation:
+            moveNext();
+            return _expectFunctionTypeAnnotation(id) as T;
+          case RemoteInstanceKind.instance:
+            return new RemoteInstanceImpl(id: id) as T;
+          case RemoteInstanceKind.methodDeclaration:
+            moveNext();
+            return _expectMethodDeclaration(id) as T;
+          case RemoteInstanceKind.namedTypeAnnotation:
+            moveNext();
+            return _expectNamedTypeAnnotation(id) as T;
           case RemoteInstanceKind.parameterDeclaration:
             moveNext();
             return _expectParameterDeclaration(id) as T;
+          case RemoteInstanceKind.typeAliasDeclaration:
+            moveNext();
+            return _expectTypeAliasDeclaration(id) as T;
           case RemoteInstanceKind.typeParameterDeclaration:
             moveNext();
             return _expectTypeParameterDeclaration(id) as T;
-          case RemoteInstanceKind.instance:
-            return new RemoteInstanceImpl(id: id) as T;
-          default:
-            throw new UnsupportedError('Unsupported remote object kind: $kind');
+          case RemoteInstanceKind.variableDeclaration:
+            moveNext();
+            return _expectVariableDeclaration(id) as T;
         }
       case SerializationMode.server:
         return RemoteInstance.cached(id) as T;
     }
   }
 
-  NamedTypeAnnotation _expectNamedTypeAnnotation(int id) {
-    bool isNullable = expectBool();
-    moveNext();
-    String name = expectString();
-    moveNext();
+  /// Helper method to read a list of [RemoteInstance]s.
+  List<T> _expectRemoteInstanceList<T extends RemoteInstance>() {
     expectList();
-    List<TypeAnnotationImpl> typeArguments = [
+    return [
       for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
         expectRemoteInstance(),
     ];
-    return new NamedTypeAnnotationImpl(
+  }
+
+  NamedTypeAnnotation _expectNamedTypeAnnotation(int id) =>
+      new NamedTypeAnnotationImpl(
         id: id,
-        isNullable: isNullable,
-        name: name,
-        typeArguments: typeArguments);
-  }
+        isNullable: expectBool(),
+        name: (this..moveNext()).expectString(),
+        typeArguments: (this..moveNext())._expectRemoteInstanceList(),
+      );
 
-  FunctionTypeAnnotation _expectFunctionTypeAnnotation(int id) {
-    bool isNullable = expectBool();
-
-    TypeAnnotationImpl returnType = RemoteInstance.deserialize(this);
-
-    moveNext();
-    expectList();
-    List<ParameterDeclarationImpl> positionalParameters = [
-      for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
-        expectRemoteInstance(),
-    ];
-
-    moveNext();
-    expectList();
-    List<ParameterDeclarationImpl> namedParameters = [
-      for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
-        expectRemoteInstance(),
-    ];
-
-    moveNext();
-    expectList();
-    List<TypeParameterDeclarationImpl> typeParameters = [
-      for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
-        expectRemoteInstance(),
-    ];
-
-    return new FunctionTypeAnnotationImpl(
-      id: id,
-      isNullable: isNullable,
-      returnType: returnType,
-      positionalParameters: positionalParameters,
-      namedParameters: namedParameters,
-      typeParameters: typeParameters,
-    );
-  }
-
-  ParameterDeclaration _expectParameterDeclaration(int id) {
-    String name = expectString();
-    moveNext();
-    Code? defaultValue;
-    if (!checkNull()) {
-      defaultValue = expectCode();
-    }
-    bool isNamed = expectBool();
-    moveNext();
-    bool isRequired = expectBool();
-
-    TypeAnnotationImpl type = RemoteInstance.deserialize(this);
-
-    return new ParameterDeclarationImpl(
+  FunctionTypeAnnotation _expectFunctionTypeAnnotation(int id) =>
+      new FunctionTypeAnnotationImpl(
         id: id,
-        defaultValue: defaultValue,
-        isNamed: isNamed,
-        isRequired: isRequired,
-        name: name,
-        type: type);
-  }
+        isNullable: expectBool(),
+        returnType: RemoteInstance.deserialize(this),
+        positionalParameters: (this..moveNext())._expectRemoteInstanceList(),
+        namedParameters: (this..moveNext())._expectRemoteInstanceList(),
+        typeParameters: (this..moveNext())._expectRemoteInstanceList(),
+      );
 
-  TypeParameterDeclaration _expectTypeParameterDeclaration(int id) {
-    String name = expectString();
-    moveNext();
-    TypeAnnotationImpl? bounds;
-    if (!checkNull()) {
-      bounds = expectRemoteInstance();
-    }
-    return new TypeParameterDeclarationImpl(id: id, name: name, bounds: bounds);
-  }
-
-  FunctionDeclaration _expectFunctionDeclaration(int id) {
-    String name = expectString();
-    moveNext();
-    bool isAbstract = expectBool();
-    moveNext();
-    bool isExternal = expectBool();
-    moveNext();
-    bool isGetter = expectBool();
-    moveNext();
-    bool isSetter = expectBool();
-
-    moveNext();
-    expectList();
-    List<ParameterDeclarationImpl> namedParameters = [
-      for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
-        expectRemoteInstance(),
-    ];
-
-    moveNext();
-    expectList();
-    List<ParameterDeclarationImpl> positionalParameters = [
-      for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
-        expectRemoteInstance(),
-    ];
-
-    TypeAnnotationImpl returnType = RemoteInstance.deserialize(this);
-
-    moveNext();
-    expectList();
-    List<TypeParameterDeclarationImpl> typeParameters = [
-      for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
-        expectRemoteInstance(),
-    ];
-    return new FunctionDeclarationImpl(
+  ParameterDeclaration _expectParameterDeclaration(int id) =>
+      new ParameterDeclarationImpl(
         id: id,
-        name: name,
-        isAbstract: isAbstract,
-        isExternal: isExternal,
-        isGetter: isGetter,
-        isSetter: isSetter,
-        namedParameters: namedParameters,
-        positionalParameters: positionalParameters,
-        returnType: returnType,
-        typeParameters: typeParameters);
-  }
+        name: expectString(),
+        defaultValue: (this..moveNext()).checkNull() ? null : expectCode(),
+        isNamed: (this..moveNext()).expectBool(),
+        isRequired: (this..moveNext()).expectBool(),
+        type: RemoteInstance.deserialize(this),
+      );
+
+  TypeParameterDeclaration _expectTypeParameterDeclaration(int id) =>
+      new TypeParameterDeclarationImpl(
+        id: id,
+        name: expectString(),
+        bounds: (this..moveNext()).checkNull() ? null : expectRemoteInstance(),
+      );
+
+  FunctionDeclaration _expectFunctionDeclaration(int id) =>
+      new FunctionDeclarationImpl(
+        id: id,
+        name: expectString(),
+        isAbstract: (this..moveNext()).expectBool(),
+        isExternal: (this..moveNext()).expectBool(),
+        isGetter: (this..moveNext()).expectBool(),
+        isSetter: (this..moveNext()).expectBool(),
+        namedParameters: (this..moveNext())._expectRemoteInstanceList(),
+        positionalParameters: (this..moveNext())._expectRemoteInstanceList(),
+        returnType: RemoteInstance.deserialize(this),
+        typeParameters: (this..moveNext())._expectRemoteInstanceList(),
+      );
+
+  MethodDeclaration _expectMethodDeclaration(int id) =>
+      new MethodDeclarationImpl(
+        id: id,
+        name: expectString(),
+        isAbstract: (this..moveNext()).expectBool(),
+        isExternal: (this..moveNext()).expectBool(),
+        isGetter: (this..moveNext()).expectBool(),
+        isSetter: (this..moveNext()).expectBool(),
+        namedParameters: (this..moveNext())._expectRemoteInstanceList(),
+        positionalParameters: (this..moveNext())._expectRemoteInstanceList(),
+        returnType: RemoteInstance.deserialize(this),
+        typeParameters: (this..moveNext())._expectRemoteInstanceList(),
+        definingClass: RemoteInstance.deserialize(this),
+      );
+
+  ConstructorDeclaration _expectConstructorDeclaration(int id) =>
+      new ConstructorDeclarationImpl(
+        id: id,
+        name: expectString(),
+        isAbstract: (this..moveNext()).expectBool(),
+        isExternal: (this..moveNext()).expectBool(),
+        isGetter: (this..moveNext()).expectBool(),
+        isSetter: (this..moveNext()).expectBool(),
+        namedParameters: (this..moveNext())._expectRemoteInstanceList(),
+        positionalParameters: (this..moveNext())._expectRemoteInstanceList(),
+        returnType: RemoteInstance.deserialize(this),
+        typeParameters: (this..moveNext())._expectRemoteInstanceList(),
+        definingClass: RemoteInstance.deserialize(this),
+        isFactory: (this..moveNext()).expectBool(),
+      );
+
+  VariableDeclaration _expectVariableDeclaration(int id) =>
+      new VariableDeclarationImpl(
+        id: id,
+        name: expectString(),
+        initializer: (this..moveNext()).expectNullableCode(),
+        isAbstract: (this..moveNext()).expectBool(),
+        isExternal: (this..moveNext()).expectBool(),
+        type: RemoteInstance.deserialize(this),
+      );
+
+  FieldDeclaration _expectFieldDeclaration(int id) => new FieldDeclarationImpl(
+        id: id,
+        name: expectString(),
+        initializer: (this..moveNext()).expectNullableCode(),
+        isAbstract: (this..moveNext()).expectBool(),
+        isExternal: (this..moveNext()).expectBool(),
+        type: RemoteInstance.deserialize(this),
+        definingClass: RemoteInstance.deserialize(this),
+      );
+
+  ClassDeclaration _expectClassDeclaration(int id) => new ClassDeclarationImpl(
+        id: id,
+        name: expectString(),
+        typeParameters: (this..moveNext())._expectRemoteInstanceList(),
+        interfaces: (this..moveNext())._expectRemoteInstanceList(),
+        isAbstract: (this..moveNext()).expectBool(),
+        isExternal: (this..moveNext()).expectBool(),
+        mixins: (this..moveNext())._expectRemoteInstanceList(),
+        superclass: RemoteInstance.deserialize(this),
+      );
+
+  TypeAliasDeclaration _expectTypeAliasDeclaration(int id) =>
+      new TypeAliasDeclarationImpl(
+        id: id,
+        name: expectString(),
+        typeParameters: (this..moveNext())._expectRemoteInstanceList(),
+        type: RemoteInstance.deserialize(this),
+      );
 
   T expectCode<T extends Code>() {
     CodeKind kind = CodeKind.values[expectNum()];
@@ -210,6 +223,35 @@
         return new StatementCode.fromParts(parts) as T;
     }
   }
+
+  T? expectNullableCode<T extends Code>() {
+    if (checkNull()) return null;
+    return expectCode();
+  }
+}
+
+extension SerializeNullable on Serializable? {
+  /// Either serializes a `null` literal or the object.
+  void serializeNullable(Serializer serializer) {
+    Serializable? self = this;
+    if (self == null) {
+      serializer.addNull();
+    } else {
+      self.serialize(serializer);
+    }
+  }
+}
+
+extension SerializeNullableCode on Code? {
+  /// Either serializes a `null` literal or the code object.
+  void serializeNullable(Serializer serializer) {
+    Code? self = this;
+    if (self == null) {
+      serializer.addNull();
+    } else {
+      self.serialize(serializer);
+    }
+  }
 }
 
 extension SerializeCode on Code {
diff --git a/pkg/_fe_analyzer_shared/lib/src/testing/id.dart b/pkg/_fe_analyzer_shared/lib/src/testing/id.dart
index fd2ed54..e1596e1 100644
--- a/pkg/_fe_analyzer_shared/lib/src/testing/id.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/testing/id.dart
@@ -358,7 +358,7 @@
       'offset=$offset,object=$objectText)';
 }
 
-abstract class DataRegistry<T> {
+mixin DataRegistry<T> {
   Map<Id, ActualData<T>> get actualMap;
 
   /// Registers [value] with [id] in [actualMap].
diff --git a/pkg/_fe_analyzer_shared/test/macros/executor_shared/serialization_test.dart b/pkg/_fe_analyzer_shared/test/macros/executor_shared/serialization_test.dart
index 9faf079..6a3807b 100644
--- a/pkg/_fe_analyzer_shared/test/macros/executor_shared/serialization_test.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/executor_shared/serialization_test.dart
@@ -2,11 +2,15 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import 'package:_fe_analyzer_shared/src/macros/api.dart';
 import 'package:_fe_analyzer_shared/src/macros/executor_shared/introspection_impls.dart';
 import 'package:_fe_analyzer_shared/src/macros/executor_shared/remote_instance.dart';
 import 'package:_fe_analyzer_shared/src/macros/executor_shared/serialization.dart';
+import 'package:_fe_analyzer_shared/src/macros/executor_shared/serialization_extensions.dart';
 import 'package:test/test.dart';
 
+import '../util.dart';
+
 void main() {
   group('json serializer', () {
     test('can serialize and deserialize basic data', () {
@@ -100,11 +104,196 @@
         expect(instance, foo);
       });
     });
+
+    group('declarations', () {
+      final barType = NamedTypeAnnotationImpl(
+          id: RemoteInstance.uniqueId,
+          isNullable: false,
+          name: 'Bar',
+          typeArguments: []);
+      final fooType = NamedTypeAnnotationImpl(
+          id: RemoteInstance.uniqueId,
+          isNullable: true,
+          name: 'Foo',
+          typeArguments: [barType]);
+
+      test('NamedTypeAnnotation', () {
+        expectSerializationEquality(fooType);
+      });
+
+      final fooNamedParam = ParameterDeclarationImpl(
+          id: RemoteInstance.uniqueId,
+          defaultValue: null,
+          isNamed: true,
+          isRequired: true,
+          name: 'foo',
+          type: fooType);
+
+      final barPositionalParam = ParameterDeclarationImpl(
+          id: RemoteInstance.uniqueId,
+          defaultValue: Code.fromString('const Bar()'),
+          isNamed: false,
+          isRequired: false,
+          name: 'bar',
+          type: barType);
+
+      final zapTypeParam = TypeParameterDeclarationImpl(
+          id: RemoteInstance.uniqueId, name: 'Zap', bounds: barType);
+
+      // Transitively tests `TypeParameterDeclaration` and
+      // `ParameterDeclaration`.
+      test('FunctionTypeAnnotation', () {
+        var functionType = FunctionTypeAnnotationImpl(
+          id: RemoteInstance.uniqueId,
+          isNullable: true,
+          namedParameters: [fooNamedParam],
+          positionalParameters: [barPositionalParam],
+          returnType: fooType,
+          typeParameters: [zapTypeParam],
+        );
+        expectSerializationEquality(functionType);
+      });
+
+      test('FunctionDeclaration', () {
+        var function = FunctionDeclarationImpl(
+            id: RemoteInstance.uniqueId,
+            name: 'name',
+            isAbstract: true,
+            isExternal: false,
+            isGetter: true,
+            isSetter: false,
+            namedParameters: [],
+            positionalParameters: [],
+            returnType: fooType,
+            typeParameters: []);
+        expectSerializationEquality(function);
+      });
+
+      test('MethodDeclaration', () {
+        var method = MethodDeclarationImpl(
+            id: RemoteInstance.uniqueId,
+            name: 'zorp',
+            isAbstract: false,
+            isExternal: false,
+            isGetter: false,
+            isSetter: true,
+            namedParameters: [fooNamedParam],
+            positionalParameters: [barPositionalParam],
+            returnType: fooType,
+            typeParameters: [zapTypeParam],
+            definingClass: fooType);
+        expectSerializationEquality(method);
+      });
+
+      test('ConstructorDeclaration', () {
+        var constructor = ConstructorDeclarationImpl(
+          id: RemoteInstance.uniqueId,
+          name: 'new',
+          isAbstract: false,
+          isExternal: false,
+          isGetter: false,
+          isSetter: false,
+          namedParameters: [fooNamedParam],
+          positionalParameters: [barPositionalParam],
+          returnType: fooType,
+          typeParameters: [zapTypeParam],
+          definingClass: fooType,
+          isFactory: true,
+        );
+        expectSerializationEquality(constructor);
+      });
+
+      test('VariableDeclaration', () {
+        var bar = VariableDeclarationImpl(
+          id: RemoteInstance.uniqueId,
+          name: 'bar',
+          isAbstract: false,
+          isExternal: true,
+          initializer: Code.fromString('Bar()'),
+          type: barType,
+        );
+        expectSerializationEquality(bar);
+      });
+
+      test('FieldDeclaration', () {
+        var bar = FieldDeclarationImpl(
+          id: RemoteInstance.uniqueId,
+          name: 'bar',
+          isAbstract: false,
+          isExternal: false,
+          initializer: null,
+          type: barType,
+          definingClass: fooType,
+        );
+        expectSerializationEquality(bar);
+      });
+
+      var objectType = NamedTypeAnnotationImpl(
+        id: RemoteInstance.uniqueId,
+        name: 'Object',
+        isNullable: false,
+        typeArguments: [],
+      );
+      var serializableType = NamedTypeAnnotationImpl(
+        id: RemoteInstance.uniqueId,
+        name: 'Serializable',
+        isNullable: false,
+        typeArguments: [],
+      );
+
+      test('ClassDeclaration', () {
+        var fooClass = ClassDeclarationImpl(
+          id: RemoteInstance.uniqueId,
+          name: 'Foo',
+          interfaces: [barType],
+          isAbstract: true,
+          isExternal: false,
+          mixins: [serializableType],
+          superclass: objectType,
+          typeParameters: [zapTypeParam],
+        );
+        expectSerializationEquality(fooClass);
+      });
+
+      test('TypeAliasDeclaration', () {
+        var typeAlias = TypeAliasDeclarationImpl(
+          id: RemoteInstance.uniqueId,
+          name: 'FooOfBar',
+          type: NamedTypeAnnotationImpl(
+              id: RemoteInstance.uniqueId,
+              isNullable: false,
+              name: 'Foo',
+              typeArguments: [barType]),
+          typeParameters: [zapTypeParam],
+        );
+        expectSerializationEquality(typeAlias);
+      });
+    });
+  });
+}
+
+/// Serializes [serializable] in server mode, then deserializes it in client
+/// mode, and checks that all the fields are the same.
+void expectSerializationEquality(Serializable serializable) {
+  var serializer = JsonSerializer();
+  withSerializationMode(SerializationMode.server, () {
+    serializable.serialize(serializer);
+  });
+  withSerializationMode(SerializationMode.client, () {
+    var deserializer = JsonDeserializer(serializer.result);
+    var deserialized = (deserializer..moveNext()).expectRemoteInstance();
+    if (deserialized is Declaration) {
+      expect(serializable, deepEqualsDeclaration(deserialized));
+    } else if (deserialized is TypeAnnotation) {
+      expect(serializable, deepEqualsTypeAnnotation(deserialized));
+    } else {
+      throw new UnsupportedError('Unsupported object type $deserialized');
+    }
   });
 }
 
 /// Deserializes [serialized] in client mode and sends it back.
-Object? roundTrip(Object? serialized) {
+Object? roundTrip<Declaration>(Object? serialized) {
   return withSerializationMode(SerializationMode.client, () {
     var deserializer = JsonDeserializer(serialized as List<Object?>);
     var instance =
diff --git a/pkg/_fe_analyzer_shared/test/macros/util.dart b/pkg/_fe_analyzer_shared/test/macros/util.dart
index 179f50b..d92c884 100644
--- a/pkg/_fe_analyzer_shared/test/macros/util.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/util.dart
@@ -2,9 +2,12 @@
 // 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:mirrors';
+
 import 'package:_fe_analyzer_shared/src/macros/api.dart';
 
 import 'package:test/fake.dart';
+import 'package:test/test.dart';
 
 class FakeClassIntrospector with Fake implements ClassIntrospector {}
 
@@ -58,3 +61,96 @@
     return buffer;
   }
 }
+
+/// Checks if two [Code] objectss are of the same type and all their fields are
+/// equal.
+Matcher deepEqualsCode(Code other) => _DeepEqualityMatcher(other);
+
+/// Checks if two [Declaration]s are of the same type and all their fields are
+/// equal.
+Matcher deepEqualsDeclaration(Declaration declaration) =>
+    _DeepEqualityMatcher(declaration);
+
+/// Checks if two [TypeAnnotation]s are of the same type and all their fields are
+/// equal.
+Matcher deepEqualsTypeAnnotation(TypeAnnotation declaration) =>
+    _DeepEqualityMatcher(declaration);
+
+/// Checks if two [Declaration]s, [TypeAnnotation]s, or [Code] objects are of
+/// the same type and all their fields are equal.
+class _DeepEqualityMatcher extends Matcher {
+  final Object? instance;
+
+  _DeepEqualityMatcher(this.instance);
+
+  @override
+  Description describe(Description description) => description;
+
+  @override
+  bool matches(item, Map matchState) {
+    if (item.runtimeType != instance.runtimeType) {
+      return false;
+    }
+
+    if (instance is Declaration || instance is TypeAnnotation) {
+      var instanceReflector = reflect(instance);
+      var itemReflector = reflect(item);
+
+      var type = instanceReflector.type;
+      for (var getter
+          in type.instanceMembers.values.where((member) => member.isGetter)) {
+        // We only care about synthetic field getters
+        if (!getter.isSynthetic) continue;
+
+        var instanceField = instanceReflector.getField(getter.simpleName);
+        var itemField = itemReflector.getField(getter.simpleName);
+        var instanceValue = instanceField.reflectee;
+        var itemValue = itemField.reflectee;
+
+        // Handle lists of things
+        if (instanceValue is List) {
+          if (!_listEquals(instanceValue, itemValue, matchState)) {
+            return false;
+          }
+        } else if (instanceValue is Declaration ||
+            instanceValue is Code ||
+            instanceValue is TypeAnnotation) {
+          // Handle nested declarations and code objects
+          if (!_DeepEqualityMatcher(instanceValue)
+              .matches(itemValue, matchState)) {
+            return false;
+          }
+        } else {
+          // Handles basic values and identity
+          if (instanceValue != itemValue) {
+            return false;
+          }
+        }
+      }
+    } else if (instance is Code) {
+      if (!_listEquals(
+          (instance as Code).parts, (item as Code).parts, matchState)) {
+        return false;
+      }
+    } else {
+      // Handles basic values and identity
+      if (instance != item) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  bool _listEquals(List instanceValue, List itemValue, Map matchState) {
+    if (instanceValue.length != itemValue.length) {
+      return false;
+    }
+    for (var i = 0; i < instanceValue.length; i++) {
+      if (!_DeepEqualityMatcher(instanceValue[i])
+          .matches(itemValue[i], matchState)) {
+        return false;
+      }
+    }
+    return true;
+  }
+}
diff --git a/pkg/compiler/test/rti/rti_need_test_helper.dart b/pkg/compiler/test/rti/rti_need_test_helper.dart
index 02aaf4e..50cb925 100644
--- a/pkg/compiler/test/rti/rti_need_test_helper.dart
+++ b/pkg/compiler/test/rti/rti_need_test_helper.dart
@@ -329,8 +329,12 @@
   }
 }
 
-class RtiClassNeedIrComputer extends DataRegistry<String>
-    with ComputeValueMixin, IrMixin, IrDataRegistryMixin<String> {
+class RtiClassNeedIrComputer
+    with
+        DataRegistry<String>,
+        ComputeValueMixin,
+        IrMixin,
+        IrDataRegistryMixin<String> {
   @override
   final Compiler compiler;
   final JsToElementMap _elementMap;
diff --git a/pkg/front_end/analysis_options_no_lints.yaml b/pkg/front_end/analysis_options_no_lints.yaml
index cc832b8..c1cae63 100644
--- a/pkg/front_end/analysis_options_no_lints.yaml
+++ b/pkg/front_end/analysis_options_no_lints.yaml
@@ -12,6 +12,7 @@
     - test/extensions/data/**
     - test/id_testing/data/**
     - test/language_versioning/data/**
+    - test/macro_application/data/**
     - test/macros/data/**
     - test/patching/data/**
     - test/predicates/data/**
diff --git a/pkg/front_end/lib/src/api_prototype/compiler_options.dart b/pkg/front_end/lib/src/api_prototype/compiler_options.dart
index 39a7a01..a8355e8 100644
--- a/pkg/front_end/lib/src/api_prototype/compiler_options.dart
+++ b/pkg/front_end/lib/src/api_prototype/compiler_options.dart
@@ -18,6 +18,7 @@
 
 import '../base/nnbd_mode.dart';
 
+import '../fasta/kernel/macro.dart';
 import 'experimental_flags.dart'
     show
         AllowedExperimentalFlags,
@@ -116,6 +117,12 @@
   Future<MacroExecutor> Function() macroExecutorProvider =
       () async => throw 'Macro execution is not supported.';
 
+  /// Map from [MacroClass] to [Uri] for the precompiled dill that contains
+  /// the macro code.
+  ///
+  /// This is an experimental feature.
+  Map<MacroClass, Uri>? precompiledMacroUris;
+
   /// Whether to generate code for the SDK.
   ///
   /// By default the front end resolves components using a prebuilt SDK summary.
diff --git a/pkg/front_end/lib/src/base/processed_options.dart b/pkg/front_end/lib/src/base/processed_options.dart
index db25cec..f8a8de9 100644
--- a/pkg/front_end/lib/src/base/processed_options.dart
+++ b/pkg/front_end/lib/src/base/processed_options.dart
@@ -70,6 +70,7 @@
         templateSdkSpecificationNotFound,
         templateSdkSummaryNotFound;
 
+import '../fasta/kernel/macro.dart';
 import '../fasta/messages.dart' show getLocation;
 
 import '../fasta/problems.dart' show DebugAbort, unimplemented;
@@ -857,6 +858,9 @@
   Future<MacroExecutor> Function() get macroExecutorProvider =>
       _raw.macroExecutorProvider;
 
+  Map<MacroClass, Uri> get precompiledMacroUris =>
+      _raw.precompiledMacroUris ?? const {};
+
   CompilerOptions get rawOptionsForTesting => _raw;
 }
 
diff --git a/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart b/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart
index 306c9aa..e66b50a 100644
--- a/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart
@@ -114,7 +114,8 @@
 
   NamedTypeBuilder(this.name, this.nullabilityBuilder, this.arguments,
       this.fileUri, this.charOffset,
-      {required this.instanceTypeVariableAccess});
+      {required this.instanceTypeVariableAccess})
+      : assert(name is String || name is QualifiedName);
 
   NamedTypeBuilder.fromTypeDeclarationBuilder(
       TypeDeclarationBuilder this.declaration, this.nullabilityBuilder,
diff --git a/pkg/front_end/lib/src/fasta/builder/nullability_builder.dart b/pkg/front_end/lib/src/fasta/builder/nullability_builder.dart
index e9920a6..d2f1000 100644
--- a/pkg/front_end/lib/src/fasta/builder/nullability_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/nullability_builder.dart
@@ -19,6 +19,9 @@
 
   /// Used when the type is declared without any nullability suffixes.
   omitted,
+
+  /// Used when the type is nullable without any nullability suffix.
+  inherent,
 }
 
 class NullabilityBuilder {
@@ -30,8 +33,15 @@
   const NullabilityBuilder.omitted()
       : _syntacticNullability = SyntacticNullability.omitted;
 
+  const NullabilityBuilder.inherent()
+      : _syntacticNullability = SyntacticNullability.inherent;
+
   bool get isOmitted => _syntacticNullability == SyntacticNullability.omitted;
 
+  bool get isInherent => _syntacticNullability == SyntacticNullability.inherent;
+
+  bool get isNullable => _syntacticNullability == SyntacticNullability.nullable;
+
   factory NullabilityBuilder.fromNullability(Nullability nullability) {
     switch (nullability) {
       case Nullability.nullable:
@@ -55,6 +65,7 @@
       case SyntacticNullability.legacy:
         return Nullability.legacy;
       case SyntacticNullability.nullable:
+      case SyntacticNullability.inherent:
         return Nullability.nullable;
       case SyntacticNullability.omitted:
         return ifOmitted;
@@ -70,6 +81,7 @@
         sb.write("?");
         return;
       case SyntacticNullability.omitted:
+      case SyntacticNullability.inherent:
         // Do nothing.
         return;
     }
diff --git a/pkg/front_end/lib/src/fasta/incremental_compiler.dart b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
index 8152374..76ab9eb8 100644
--- a/pkg/front_end/lib/src/fasta/incremental_compiler.dart
+++ b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
@@ -126,7 +126,7 @@
 
 import 'kernel/internal_ast.dart' show VariableDeclarationImpl;
 
-import 'kernel/kernel_target.dart' show KernelTarget;
+import 'kernel/kernel_target.dart' show BuildResult, KernelTarget;
 
 import 'library_graph.dart' show LibraryGraph;
 
@@ -351,13 +351,17 @@
       // Technically, it's the combination of
       // `currentKernelTarget.loader.libraries` and
       // `_dillLoadedData.loader.libraries`.
-      Component? componentWithDill = await currentKernelTarget.buildOutlines();
+      BuildResult buildResult = await currentKernelTarget.buildOutlines();
+      Component? componentWithDill = buildResult.component;
 
       if (!outlineOnly) {
         // Checkpoint: Build the actual bodies.
-        componentWithDill =
-            await currentKernelTarget.buildComponent(verify: c.options.verify);
+        buildResult = await currentKernelTarget.buildComponent(
+            macroApplications: buildResult.macroApplications,
+            verify: c.options.verify);
+        componentWithDill = buildResult.component;
       }
+      buildResult.macroApplications?.macroExecutor.close();
       hierarchy ??= currentKernelTarget.loader.hierarchy;
       if (currentKernelTarget.classHierarchyChanges != null) {
         hierarchy.applyTreeChanges(
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index fe74b48..ea31b84 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -4039,7 +4039,7 @@
     push(new NamedTypeBuilder.fromTypeDeclarationBuilder(
         new VoidTypeDeclarationBuilder(
             const VoidType(), libraryBuilder, offset),
-        const NullabilityBuilder.nullable(),
+        const NullabilityBuilder.inherent(),
         fileUri: uri,
         charOffset: offset,
         instanceTypeVariableAccess:
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
index 72fefca..4576ec6 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -81,6 +81,7 @@
         ConstantEvaluationData;
 import 'kernel_constants.dart' show KernelConstantErrorReporter;
 import 'kernel_helper.dart';
+import 'macro.dart';
 import 'verifier.dart' show verifyComponent, verifyGetStaticType;
 
 class KernelTarget extends TargetImplementation {
@@ -102,7 +103,7 @@
   // TODO(johnniwinther): Why isn't this using a FixedTypeBuilder?
   final TypeBuilder dynamicType = new NamedTypeBuilder(
       "dynamic",
-      const NullabilityBuilder.nullable(),
+      const NullabilityBuilder.inherent(),
       /* arguments = */ null,
       /* fileUri = */ null,
       /* charOffset = */ null,
@@ -121,7 +122,7 @@
   //  have NullType?
   final TypeBuilder nullType = new NamedTypeBuilder(
       "Null",
-      const NullabilityBuilder.nullable(),
+      const NullabilityBuilder.inherent(),
       /* arguments = */ null,
       /* fileUri = */ null,
       /* charOffset = */ null,
@@ -373,20 +374,26 @@
     builder.mixedInTypeBuilder = null;
   }
 
-  Future<Component?> buildOutlines({CanonicalName? nameRoot}) async {
-    if (loader.first == null) return null;
-    return withCrashReporting<Component?>(() async {
+  Future<BuildResult> buildOutlines({CanonicalName? nameRoot}) async {
+    if (loader.first == null) return new BuildResult();
+    return withCrashReporting<BuildResult>(() async {
       await loader.buildOutlines();
       loader.coreLibrary.becomeCoreLibrary();
       loader.resolveParts();
       loader.computeMacroDeclarations();
       loader.computeLibraryScopes();
-      await loader.computeMacroApplications();
+      MacroApplications? macroApplications =
+          await loader.computeMacroApplications();
       setupTopAndBottomTypes();
       loader.resolveTypes();
       loader.computeVariances();
       loader.computeDefaultTypes(
           dynamicType, nullType, bottomType, objectClassBuilder);
+      // TODO(johnniwinther): Enable this when supported in the isolate-based
+      //  macro executor.
+      /*if (macroApplications != null) {
+        await macroApplications.applyTypeMacros();
+      }*/
       List<SourceClassBuilder> sourceClassBuilders =
           loader.checkSemantics(objectClassBuilder);
       loader.finishTypeVariables(objectClassBuilder, dynamicType);
@@ -399,6 +406,12 @@
           link(new List<Library>.from(loader.libraries), nameRoot: nameRoot);
       computeCoreTypes();
       loader.buildClassHierarchy(sourceClassBuilders, objectClassBuilder);
+      // TODO(johnniwinther): Enable this when supported in the isolate-based
+      //  macro executor.
+      /*if (macroApplications != null) {
+        await macroApplications.applyDeclarationMacros();
+      }*/
+      loader.buildClassHierarchyMembers(sourceClassBuilders);
       loader.computeHierarchy();
       loader.computeShowHideElements();
       loader.installTypedefTearOffs();
@@ -416,7 +429,8 @@
       loader.checkMainMethods();
       installAllComponentProblems(loader.allComponentProblems);
       loader.allComponentProblems.clear();
-      return component;
+      return new BuildResult(
+          component: component, macroApplications: macroApplications);
     }, () => loader.currentUriForCrashReporting);
   }
 
@@ -428,15 +442,22 @@
   ///
   /// If [verify], run the default kernel verification on the resulting
   /// component.
-  Future<Component?> buildComponent({bool verify: false}) async {
-    if (loader.first == null) return null;
-    return withCrashReporting<Component?>(() async {
+  Future<BuildResult> buildComponent(
+      {required MacroApplications? macroApplications,
+      bool verify: false}) async {
+    if (loader.first == null) {
+      return new BuildResult(macroApplications: macroApplications);
+    }
+    return withCrashReporting<BuildResult>(() async {
       ticker.logMs("Building component");
       await loader.buildBodies();
       finishSynthesizedParameters();
       loader.finishDeferredLoadTearoffs();
       loader.finishNoSuchMethodForwarders();
       List<SourceClassBuilder> sourceClasses = loader.collectSourceClasses();
+      if (macroApplications != null) {
+        await macroApplications.applyDefinitionMacros();
+      }
       loader.finishNativeMethods();
       loader.finishPatchMethods();
       finishAllConstructors(sourceClasses);
@@ -444,7 +465,8 @@
 
       if (verify) this.verify();
       installAllComponentProblems(loader.allComponentProblems);
-      return component;
+      return new BuildResult(
+          component: component, macroApplications: macroApplications);
     }, () => loader.currentUriForCrashReporting);
   }
 
@@ -1529,3 +1551,10 @@
     loader.addProblem(message, charOffset, noLength, fileUri, context: context);
   }
 }
+
+class BuildResult {
+  final Component? component;
+  final MacroApplications? macroApplications;
+
+  BuildResult({this.component, this.macroApplications});
+}
diff --git a/pkg/front_end/lib/src/fasta/kernel/macro.dart b/pkg/front_end/lib/src/fasta/kernel/macro.dart
index 3055eea..6d95bbf 100644
--- a/pkg/front_end/lib/src/fasta/kernel/macro.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/macro.dart
@@ -2,12 +2,19 @@
 // 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/api.dart' hide TypeBuilder;
 import 'package:_fe_analyzer_shared/src/macros/executor.dart';
+import 'package:_fe_analyzer_shared/src/macros/executor_shared/introspection_impls.dart';
 
 import '../builder/class_builder.dart';
+import '../builder/formal_parameter_builder.dart';
 import '../builder/member_builder.dart';
+import '../builder/named_type_builder.dart';
+import '../builder/type_builder.dart';
+import '../identifiers.dart';
 import '../source/source_class_builder.dart';
 import '../source/source_library_builder.dart';
+import '../source/source_procedure_builder.dart';
 
 bool enableMacros = false;
 
@@ -21,30 +28,60 @@
   List<List<Uri>>? compilationSequence;
 }
 
-class MacroApplicationData {
-  Map<SourceLibraryBuilder, LibraryMacroApplicationData> libraryData = {};
+class MacroClass {
+  final Uri importUri;
+  final String className;
 
-  Future<void> loadMacroIds(
-      Future<MacroExecutor> Function() macroExecutorProvider) async {
-    MacroExecutor macroExecutor = await macroExecutorProvider();
+  const MacroClass(this.importUri, this.className);
 
+  @override
+  int get hashCode => importUri.hashCode * 13 + className.hashCode * 17;
+
+  @override
+  bool operator ==(Object other) {
+    if (identical(this, other)) return true;
+    return other is MacroClass &&
+        importUri == other.importUri &&
+        className == other.className;
+  }
+}
+
+class MacroApplications {
+  final MacroExecutor macroExecutor;
+  final Map<SourceLibraryBuilder, LibraryMacroApplicationData> libraryData;
+  final MacroApplicationDataForTesting? dataForTesting;
+
+  MacroApplications(this.macroExecutor, this.libraryData, this.dataForTesting) {
+    dataForTesting?.libraryData.addAll(libraryData);
+  }
+
+  static Future<MacroApplications> loadMacroIds(
+      MacroExecutor macroExecutor,
+      Map<MacroClass, Uri> precompiledMacroUris,
+      Map<SourceLibraryBuilder, LibraryMacroApplicationData> libraryData,
+      MacroApplicationDataForTesting? dataForTesting) async {
     Map<ClassBuilder, MacroClassIdentifier> classIdCache = {};
 
     Map<MacroApplication, MacroInstanceIdentifier> instanceIdCache = {};
 
-    Future<void> ensureMacroClassIds(MacroApplications? applications) async {
+    Future<void> ensureMacroClassIds(
+        List<MacroApplication>? applications) async {
       if (applications != null) {
-        for (MacroApplication application in applications.macros) {
-          MacroClassIdentifier macroClass =
-              classIdCache[application.classBuilder] ??=
-                  await macroExecutor.loadMacro(
-                      application.classBuilder.library.importUri,
-                      application.classBuilder.name);
-          instanceIdCache[application] = await macroExecutor.instantiateMacro(
-              macroClass,
-              application.constructorName,
-              // TODO(johnniwinther): Support macro arguments.
-              new Arguments([], {}));
+        for (MacroApplication application in applications) {
+          MacroClass macroClass = new MacroClass(
+              application.classBuilder.library.importUri,
+              application.classBuilder.name);
+          Uri? precompiledMacroUri = precompiledMacroUris[macroClass];
+          MacroClassIdentifier macroClassIdentifier =
+              classIdCache[application.classBuilder] ??= await macroExecutor
+                  .loadMacro(macroClass.importUri, macroClass.className,
+                      precompiledKernelUri: precompiledMacroUri);
+          application.instanceIdentifier = instanceIdCache[application] =
+              await macroExecutor.instantiateMacro(
+                  macroClassIdentifier,
+                  application.constructorName,
+                  // TODO(johnniwinther): Support macro arguments.
+                  new Arguments([], {}));
         }
       }
     }
@@ -53,23 +90,138 @@
       for (ClassMacroApplicationData classData
           in libraryData.classData.values) {
         await ensureMacroClassIds(classData.classApplications);
-        for (MacroApplications applications
+        for (List<MacroApplication> applications
             in classData.memberApplications.values) {
           await ensureMacroClassIds(applications);
         }
       }
-      for (MacroApplications applications
+      for (List<MacroApplication> applications
           in libraryData.memberApplications.values) {
         await ensureMacroClassIds(applications);
       }
     }
+    return new MacroApplications(macroExecutor, libraryData, dataForTesting);
   }
-}
 
-class MacroApplications {
-  final List<MacroApplication> macros;
+  Map<MemberBuilder, Declaration?> _memberDeclarations = {};
 
-  MacroApplications(this.macros);
+  // TODO(johnniwinther): Support all members.
+  Declaration? _getMemberDeclaration(MemberBuilder memberBuilder) {
+    return _memberDeclarations[memberBuilder] ??=
+        _createMemberDeclaration(memberBuilder);
+  }
+
+  Declaration? _createMemberDeclaration(MemberBuilder memberBuilder) {
+    if (memberBuilder is SourceProcedureBuilder) {
+      return createTopLevelFunctionDeclaration(memberBuilder);
+    } else {
+      // TODO(johnniwinther): Throw when all members are supported.
+      //throw new UnimplementedError('Unsupported member ${memberBuilder}');
+      return null;
+    }
+  }
+
+  Future<void> _applyTypeMacros(
+      Declaration declaration, List<MacroApplication> macroApplications) async {
+    for (MacroApplication macroApplication in macroApplications) {
+      await macroExecutor.executeTypesPhase(
+          macroApplication.instanceIdentifier, declaration);
+    }
+  }
+
+  Future<void> applyTypeMacros() async {
+    for (MapEntry<SourceLibraryBuilder,
+        LibraryMacroApplicationData> libraryEntry in libraryData.entries) {
+      LibraryMacroApplicationData libraryMacroApplicationData =
+          libraryEntry.value;
+      for (MapEntry<MemberBuilder, List<MacroApplication>> memberEntry
+          in libraryMacroApplicationData.memberApplications.entries) {
+        MemberBuilder memberBuilder = memberEntry.key;
+        Declaration? declaration = _getMemberDeclaration(memberBuilder);
+        if (declaration != null) {
+          await _applyTypeMacros(declaration, memberEntry.value);
+        }
+      }
+    }
+  }
+
+  Future<void> _applyDeclarationMacros(
+      Declaration declaration,
+      List<MacroApplication> macroApplications,
+      TypeResolver typeResolver,
+      ClassIntrospector classIntrospector) async {
+    for (MacroApplication macroApplication in macroApplications) {
+      await macroExecutor.executeDeclarationsPhase(
+          macroApplication.instanceIdentifier,
+          declaration,
+          typeResolver,
+          classIntrospector);
+    }
+  }
+
+  Future<void> applyDeclarationMacros() async {
+    TypeResolver typeResolver = new _TypeResolver();
+    ClassIntrospector classIntrospector = new _ClassIntrospector();
+    for (MapEntry<SourceLibraryBuilder,
+        LibraryMacroApplicationData> libraryEntry in libraryData.entries) {
+      LibraryMacroApplicationData libraryMacroApplicationData =
+          libraryEntry.value;
+      for (MapEntry<MemberBuilder, List<MacroApplication>> memberEntry
+          in libraryMacroApplicationData.memberApplications.entries) {
+        MemberBuilder memberBuilder = memberEntry.key;
+        Declaration? declaration = _getMemberDeclaration(memberBuilder);
+        if (declaration != null) {
+          await _applyDeclarationMacros(
+              declaration, memberEntry.value, typeResolver, classIntrospector);
+        }
+      }
+    }
+  }
+
+  Future<List<MacroExecutionResult>> _applyDefinitionMacros(
+      Declaration declaration,
+      List<MacroApplication> macroApplications,
+      TypeResolver typeResolver,
+      ClassIntrospector classIntrospector,
+      TypeDeclarationResolver typeDeclarationResolver) async {
+    List<MacroExecutionResult> results = [];
+    for (MacroApplication macroApplication in macroApplications) {
+      MacroExecutionResult result = await macroExecutor.executeDefinitionsPhase(
+          macroApplication.instanceIdentifier,
+          declaration,
+          typeResolver,
+          classIntrospector,
+          typeDeclarationResolver);
+      results.add(result);
+    }
+    return results;
+  }
+
+  Future<void> applyDefinitionMacros() async {
+    TypeResolver typeResolver = new _TypeResolver();
+    ClassIntrospector classIntrospector = new _ClassIntrospector();
+    TypeDeclarationResolver typeDeclarationResolver =
+        new _TypeDeclarationResolver();
+    for (MapEntry<SourceLibraryBuilder,
+        LibraryMacroApplicationData> libraryEntry in libraryData.entries) {
+      LibraryMacroApplicationData libraryMacroApplicationData =
+          libraryEntry.value;
+      for (MapEntry<MemberBuilder, List<MacroApplication>> memberEntry
+          in libraryMacroApplicationData.memberApplications.entries) {
+        MemberBuilder memberBuilder = memberEntry.key;
+        Declaration? declaration = _getMemberDeclaration(memberBuilder);
+        if (declaration != null) {
+          List<MacroExecutionResult> results = await _applyDefinitionMacros(
+              declaration,
+              memberEntry.value,
+              typeResolver,
+              classIntrospector,
+              typeDeclarationResolver);
+          dataForTesting?.memberDefinitionsResults[memberBuilder] = results;
+        }
+      }
+    }
+  }
 }
 
 class MacroApplication {
@@ -78,14 +230,163 @@
   // TODO(johnniwinther): Add support for arguments.
 
   MacroApplication(this.classBuilder, this.constructorName);
+
+  late MacroInstanceIdentifier instanceIdentifier;
+}
+
+class MacroApplicationDataForTesting {
+  Map<SourceLibraryBuilder, LibraryMacroApplicationData> libraryData = {};
+  Map<MemberBuilder, List<MacroExecutionResult>> memberDefinitionsResults = {};
 }
 
 class LibraryMacroApplicationData {
   Map<SourceClassBuilder, ClassMacroApplicationData> classData = {};
-  Map<MemberBuilder, MacroApplications> memberApplications = {};
+  Map<MemberBuilder, List<MacroApplication>> memberApplications = {};
 }
 
 class ClassMacroApplicationData {
-  MacroApplications? classApplications;
-  Map<MemberBuilder, MacroApplications> memberApplications = {};
+  List<MacroApplication>? classApplications;
+  Map<MemberBuilder, List<MacroApplication>> memberApplications = {};
+}
+
+FunctionDeclaration createTopLevelFunctionDeclaration(
+    SourceProcedureBuilder builder) {
+  List<ParameterDeclarationImpl>? positionalParameters;
+  List<ParameterDeclarationImpl>? namedParameters;
+
+  List<FormalParameterBuilder>? formals = builder.formals;
+  if (formals == null) {
+    positionalParameters = namedParameters = const [];
+  } else {
+    positionalParameters = [];
+    namedParameters = [];
+    for (FormalParameterBuilder formal in formals) {
+      TypeAnnotationImpl type = computeTypeAnnotation(formal.type);
+      // TODO(johnniwinther): Support default values.
+      if (formal.isNamed) {
+        namedParameters.add(new ParameterDeclarationImpl(
+            id: _removeInstanceId++,
+            name: formal.name,
+            isRequired: formal.isNamedRequired,
+            isNamed: true,
+            type: type,
+            defaultValue: null));
+      } else {
+        positionalParameters.add(new ParameterDeclarationImpl(
+            id: _removeInstanceId++,
+            name: formal.name,
+            isRequired: formal.isRequired,
+            isNamed: false,
+            type: type,
+            defaultValue: null));
+      }
+    }
+  }
+
+  return new FunctionDeclarationImpl(
+      id: _removeInstanceId++,
+      name: builder.name,
+      isAbstract: builder.isAbstract,
+      isExternal: builder.isExternal,
+      isGetter: builder.isGetter,
+      isSetter: builder.isSetter,
+      positionalParameters: positionalParameters,
+      namedParameters: namedParameters,
+      returnType: computeTypeAnnotation(builder.returnType),
+      // TODO(johnniwinther): Support typeParameters
+      typeParameters: const []);
+}
+
+// TODO(johnniwinther): Cache remote instances when needed.
+int _removeInstanceId = 0;
+
+List<TypeAnnotationImpl> computeTypeAnnotations(
+    List<TypeBuilder>? typeBuilders) {
+  if (typeBuilders == null) return const [];
+  return new List.generate(typeBuilders.length,
+      (int index) => computeTypeAnnotation(typeBuilders[index]));
+}
+
+TypeAnnotationImpl computeTypeAnnotation(TypeBuilder? typeBuilder) {
+  if (typeBuilder != null) {
+    if (typeBuilder is NamedTypeBuilder) {
+      Object name = typeBuilder.name;
+      List<TypeAnnotationImpl> typeArguments =
+          computeTypeAnnotations(typeBuilder.arguments);
+      bool isNullable = typeBuilder.nullabilityBuilder.isNullable;
+      if (name is String) {
+        return new NamedTypeAnnotationImpl(
+            id: _removeInstanceId++,
+            name: name,
+            typeArguments: typeArguments,
+            isNullable: isNullable);
+      } else if (name is QualifiedName) {
+        assert(name.qualifier is String);
+        return new NamedTypeAnnotationImpl(
+            id: _removeInstanceId++,
+            name: '${name.qualifier}.${name.name}',
+            typeArguments: typeArguments,
+            isNullable: isNullable);
+      }
+    }
+  }
+  return new NamedTypeAnnotationImpl(
+      id: _removeInstanceId++,
+      name: 'dynamic',
+      isNullable: false,
+      typeArguments: const []);
+}
+
+class _TypeResolver implements TypeResolver {
+  @override
+  Future<StaticType> resolve(TypeAnnotation typeAnnotation) {
+    // TODO: implement resolve
+    throw new UnimplementedError('_TypeResolver.resolve');
+  }
+}
+
+class _ClassIntrospector implements ClassIntrospector {
+  @override
+  Future<List<ConstructorDeclaration>> constructorsOf(ClassDeclaration clazz) {
+    // TODO: implement constructorsOf
+    throw new UnimplementedError('_ClassIntrospector.constructorsOf');
+  }
+
+  @override
+  Future<List<FieldDeclaration>> fieldsOf(ClassDeclaration clazz) {
+    // TODO: implement fieldsOf
+    throw new UnimplementedError('_ClassIntrospector.fieldsOf');
+  }
+
+  @override
+  Future<List<ClassDeclaration>> interfacesOf(ClassDeclaration clazz) {
+    // TODO: implement interfacesOf
+    throw new UnimplementedError('_ClassIntrospector.interfacesOf');
+  }
+
+  @override
+  Future<List<MethodDeclaration>> methodsOf(ClassDeclaration clazz) {
+    // TODO: implement methodsOf
+    throw new UnimplementedError('_ClassIntrospector.methodsOf');
+  }
+
+  @override
+  Future<List<ClassDeclaration>> mixinsOf(ClassDeclaration clazz) {
+    // TODO: implement mixinsOf
+    throw new UnimplementedError('_ClassIntrospector.mixinsOf');
+  }
+
+  @override
+  Future<ClassDeclaration?> superclassOf(ClassDeclaration clazz) {
+    // TODO: implement superclassOf
+    throw new UnimplementedError('_ClassIntrospector.superclassOf');
+  }
+}
+
+class _TypeDeclarationResolver implements TypeDeclarationResolver {
+  @override
+  Future<TypeDeclaration> declarationOf(NamedStaticType annotation) {
+    // TODO: implement declarationOf
+    throw new UnimplementedError('_TypeDeclarationResolver.declarationOf');
+  }
 }
diff --git a/pkg/front_end/lib/src/fasta/kernel/type_algorithms.dart b/pkg/front_end/lib/src/fasta/kernel/type_algorithms.dart
index a0fef49..55c1e0b 100644
--- a/pkg/front_end/lib/src/fasta/kernel/type_algorithms.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/type_algorithms.dart
@@ -158,14 +158,16 @@
 NullabilityBuilder combineNullabilityBuildersForSubstitution(
     NullabilityBuilder a, NullabilityBuilder b) {
   assert(
-      (identical(a, const NullabilityBuilder.nullable()) ||
-              identical(a, const NullabilityBuilder.omitted())) &&
-          (identical(b, const NullabilityBuilder.nullable()) ||
-              identical(b, const NullabilityBuilder.omitted())),
+      // ignore: unnecessary_null_comparison
+      a != null && b != null,
       "Both arguments to combineNullabilityBuildersForSubstitution "
       "should be identical to either 'const NullabilityBuilder.nullable()' or "
       "'const NullabilityBuilder.omitted()'.");
 
+  if (identical(a, const NullabilityBuilder.inherent()) &&
+      identical(b, const NullabilityBuilder.inherent())) {
+    return const NullabilityBuilder.inherent();
+  }
   if (identical(a, const NullabilityBuilder.nullable()) ||
       identical(b, const NullabilityBuilder.nullable())) {
     return const NullabilityBuilder.nullable();
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index 67f880b..0b7498f 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -1630,7 +1630,7 @@
   TypeBuilder addVoidType(int charOffset) {
     // 'void' is always nullable.
     return addNamedType(
-        "void", const NullabilityBuilder.nullable(), null, charOffset,
+        "void", const NullabilityBuilder.inherent(), null, charOffset,
         instanceTypeVariableAccess: InstanceTypeVariableAccessState.Unexpected)
       ..bind(
           new VoidTypeDeclarationBuilder(const VoidType(), this, charOffset));
diff --git a/pkg/front_end/lib/src/fasta/source/source_loader.dart b/pkg/front_end/lib/src/fasta/source/source_loader.dart
index a731d66..d764604 100644
--- a/pkg/front_end/lib/src/fasta/source/source_loader.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart
@@ -8,6 +8,8 @@
 import 'dart:convert' show utf8;
 import 'dart:typed_data' show Uint8List;
 
+import 'package:_fe_analyzer_shared/src/macros/executor.dart'
+    show MacroExecutor;
 import 'package:_fe_analyzer_shared/src/parser/class_member_parser.dart'
     show ClassMemberParser;
 import 'package:_fe_analyzer_shared/src/parser/parser.dart'
@@ -44,7 +46,6 @@
 import '../builder/invalid_type_declaration_builder.dart';
 import '../builder/library_builder.dart';
 import '../builder/member_builder.dart';
-import '../builder/metadata_builder.dart';
 import '../builder/modifier_builder.dart';
 import '../builder/named_type_builder.dart';
 import '../builder/procedure_builder.dart';
@@ -1426,23 +1427,10 @@
     }
   }
 
-  Future<void> computeMacroApplications() async {
-    if (!enableMacros || _macroClassBuilder == null) return;
+  Future<MacroApplications?> computeMacroApplications() async {
+    if (!enableMacros || _macroClassBuilder == null) return null;
 
-    MacroApplications? computeApplications(
-        SourceLibraryBuilder enclosingLibrary,
-        Scope scope,
-        Uri fileUri,
-        List<MetadataBuilder>? annotations) {
-      List<MacroApplication>? result = prebuildAnnotations(
-          enclosingLibrary: enclosingLibrary,
-          metadataBuilders: annotations,
-          fileUri: fileUri,
-          scope: scope);
-      return result != null ? new MacroApplications(result) : null;
-    }
-
-    MacroApplicationData macroApplicationData = new MacroApplicationData();
+    Map<SourceLibraryBuilder, LibraryMacroApplicationData> libraryData = {};
     for (SourceLibraryBuilder libraryBuilder in sourceLibraryBuilders) {
       // TODO(johnniwinther): Handle patch libraries.
       LibraryMacroApplicationData libraryMacroApplicationData =
@@ -1454,28 +1442,28 @@
           SourceClassBuilder classBuilder = builder;
           ClassMacroApplicationData classMacroApplicationData =
               new ClassMacroApplicationData();
-          classMacroApplicationData.classApplications = computeApplications(
-              libraryBuilder,
-              classBuilder.scope,
-              classBuilder.fileUri,
-              classBuilder.metadata);
+          classMacroApplicationData.classApplications = prebuildAnnotations(
+              enclosingLibrary: libraryBuilder,
+              scope: classBuilder.scope,
+              fileUri: classBuilder.fileUri,
+              metadataBuilders: classBuilder.metadata);
           builder.forEach((String name, Builder memberBuilder) {
             if (memberBuilder is SourceProcedureBuilder) {
-              MacroApplications? macroApplications = computeApplications(
-                  libraryBuilder,
-                  classBuilder.scope,
-                  memberBuilder.fileUri,
-                  memberBuilder.metadata);
+              List<MacroApplication>? macroApplications = prebuildAnnotations(
+                  enclosingLibrary: libraryBuilder,
+                  scope: classBuilder.scope,
+                  fileUri: memberBuilder.fileUri,
+                  metadataBuilders: memberBuilder.metadata);
               if (macroApplications != null) {
                 classMacroApplicationData.memberApplications[memberBuilder] =
                     macroApplications;
               }
             } else if (memberBuilder is SourceFieldBuilder) {
-              MacroApplications? macroApplications = computeApplications(
-                  libraryBuilder,
-                  classBuilder.scope,
-                  memberBuilder.fileUri,
-                  memberBuilder.metadata);
+              List<MacroApplication>? macroApplications = prebuildAnnotations(
+                  enclosingLibrary: libraryBuilder,
+                  scope: classBuilder.scope,
+                  fileUri: memberBuilder.fileUri,
+                  metadataBuilders: memberBuilder.metadata);
               if (macroApplications != null) {
                 classMacroApplicationData.memberApplications[memberBuilder] =
                     macroApplications;
@@ -1484,11 +1472,11 @@
           });
           classBuilder.forEachConstructor((String name, Builder memberBuilder) {
             if (memberBuilder is DeclaredSourceConstructorBuilder) {
-              MacroApplications? macroApplications = computeApplications(
-                  libraryBuilder,
-                  classBuilder.scope,
-                  memberBuilder.fileUri,
-                  memberBuilder.metadata);
+              List<MacroApplication>? macroApplications = prebuildAnnotations(
+                  enclosingLibrary: libraryBuilder,
+                  scope: classBuilder.scope,
+                  fileUri: memberBuilder.fileUri,
+                  metadataBuilders: memberBuilder.metadata);
               if (macroApplications != null) {
                 classMacroApplicationData.memberApplications[memberBuilder] =
                     macroApplications;
@@ -1502,21 +1490,21 @@
                 classMacroApplicationData;
           }
         } else if (builder is SourceProcedureBuilder) {
-          MacroApplications? macroApplications = computeApplications(
-              libraryBuilder,
-              libraryBuilder.scope,
-              builder.fileUri,
-              builder.metadata);
+          List<MacroApplication>? macroApplications = prebuildAnnotations(
+              enclosingLibrary: libraryBuilder,
+              scope: libraryBuilder.scope,
+              fileUri: builder.fileUri,
+              metadataBuilders: builder.metadata);
           if (macroApplications != null) {
             libraryMacroApplicationData.memberApplications[builder] =
                 macroApplications;
           }
         } else if (builder is SourceFieldBuilder) {
-          MacroApplications? macroApplications = computeApplications(
-              libraryBuilder,
-              libraryBuilder.scope,
-              builder.fileUri,
-              builder.metadata);
+          List<MacroApplication>? macroApplications = prebuildAnnotations(
+              enclosingLibrary: libraryBuilder,
+              scope: libraryBuilder.scope,
+              fileUri: builder.fileUri,
+              metadataBuilders: builder.metadata);
           if (macroApplications != null) {
             libraryMacroApplicationData.memberApplications[builder] =
                 macroApplications;
@@ -1525,18 +1513,21 @@
       }
       if (libraryMacroApplicationData.classData.isNotEmpty ||
           libraryMacroApplicationData.memberApplications.isNotEmpty) {
-        macroApplicationData.libraryData[libraryBuilder] =
-            libraryMacroApplicationData;
-        if (retainDataForTesting) {
-          dataForTesting!.macroApplicationData.libraryData[libraryBuilder] =
-              libraryMacroApplicationData;
-        }
+        libraryData[libraryBuilder] = libraryMacroApplicationData;
       }
     }
-    if (macroApplicationData.libraryData.isNotEmpty) {
-      await macroApplicationData
-          .loadMacroIds(target.context.options.macroExecutorProvider);
+    if (libraryData.isNotEmpty) {
+      MacroExecutor macroExecutor =
+          await target.context.options.macroExecutorProvider();
+      Map<MacroClass, Uri> precompiledMacroUris =
+          target.context.options.precompiledMacroUris;
+      return await MacroApplications.loadMacroIds(
+          macroExecutor,
+          precompiledMacroUris,
+          libraryData,
+          dataForTesting?.macroApplicationData);
     }
+    return null;
   }
 
   void finishDeferredLoadTearoffs() {
@@ -2012,11 +2003,15 @@
     ClassHierarchyBuilder hierarchyBuilder = _hierarchyBuilder =
         ClassHierarchyBuilder.build(
             objectClass, sourceClasses, this, coreTypes);
+    typeInferenceEngine.hierarchyBuilder = hierarchyBuilder;
+    ticker.logMs("Built class hierarchy");
+  }
+
+  void buildClassHierarchyMembers(List<SourceClassBuilder> sourceClasses) {
     ClassMembersBuilder membersBuilder = _membersBuilder =
         ClassMembersBuilder.build(hierarchyBuilder, sourceClasses);
-    typeInferenceEngine.hierarchyBuilder = hierarchyBuilder;
     typeInferenceEngine.membersBuilder = membersBuilder;
-    ticker.logMs("Built class hierarchy");
+    ticker.logMs("Built class hierarchy members");
   }
 
   void createTypeInferenceEngine() {
@@ -2558,7 +2553,8 @@
 
   final MacroDeclarationData macroDeclarationData = new MacroDeclarationData();
 
-  final MacroApplicationData macroApplicationData = new MacroApplicationData();
+  final MacroApplicationDataForTesting macroApplicationData =
+      new MacroApplicationDataForTesting();
 }
 
 class _SourceClassGraph implements Graph<SourceClassBuilder> {
diff --git a/pkg/front_end/lib/src/kernel_generator_impl.dart b/pkg/front_end/lib/src/kernel_generator_impl.dart
index 4d54b4b..0deb368 100644
--- a/pkg/front_end/lib/src/kernel_generator_impl.dart
+++ b/pkg/front_end/lib/src/kernel_generator_impl.dart
@@ -23,7 +23,7 @@
 
 import 'fasta/fasta_codes.dart' show LocatedMessage;
 
-import 'fasta/kernel/kernel_target.dart' show KernelTarget;
+import 'fasta/kernel/kernel_target.dart' show BuildResult, KernelTarget;
 
 import 'fasta/kernel/utils.dart' show printComponentText, serializeComponent;
 
@@ -96,8 +96,9 @@
         new KernelTarget(fs, false, dillTarget, uriTranslator);
     sourceLoader = kernelTarget.loader;
     kernelTarget.setEntryPoints(options.inputs);
-    Component summaryComponent =
-        (await kernelTarget.buildOutlines(nameRoot: nameRoot))!;
+    BuildResult buildResult =
+        await kernelTarget.buildOutlines(nameRoot: nameRoot);
+    Component summaryComponent = buildResult.component!;
     List<int>? summary = null;
     if (buildSummary) {
       if (options.verify) {
@@ -159,13 +160,19 @@
 
     Component? component;
     if (buildComponent) {
-      component = await kernelTarget.buildComponent(verify: options.verify);
+      buildResult = await kernelTarget.buildComponent(
+          macroApplications: buildResult.macroApplications,
+          verify: options.verify);
+      component = buildResult.component;
       if (options.debugDump) {
         printComponentText(component,
             libraryFilter: kernelTarget.isSourceLibraryForDebugging);
       }
       options.ticker.logMs("Generated component");
     }
+    // TODO(johnniwinther): Should we reuse the macro executor on subsequent
+    // compilations where possible?
+    buildResult.macroApplications?.macroExecutor.close();
 
     return new InternalCompilerResult(
         summary: summary,
diff --git a/pkg/front_end/lib/src/testing/id_testing_helper.dart b/pkg/front_end/lib/src/testing/id_testing_helper.dart
index 13b47b5..b9a1a86 100644
--- a/pkg/front_end/lib/src/testing/id_testing_helper.dart
+++ b/pkg/front_end/lib/src/testing/id_testing_helper.dart
@@ -4,11 +4,20 @@
 
 import 'package:_fe_analyzer_shared/src/messages/severity.dart' show Severity;
 import 'package:_fe_analyzer_shared/src/testing/id.dart'
-    show ActualData, ClassId, Id, IdKind, IdValue, MemberId, NodeId;
+    show
+        ActualData,
+        ClassId,
+        DataRegistry,
+        Id,
+        IdKind,
+        IdValue,
+        MemberId,
+        NodeId;
 import 'package:_fe_analyzer_shared/src/testing/id_testing.dart';
 import 'package:front_end/src/base/nnbd_mode.dart';
 import 'package:kernel/ast.dart';
 import 'package:kernel/target/targets.dart';
+
 import '../api_prototype/compiler_options.dart'
     show CompilerOptions, DiagnosticMessage;
 import '../api_prototype/experimental_flags.dart'
@@ -23,8 +32,8 @@
 import 'id_testing_utils.dart';
 
 export '../fasta/compiler_context.dart' show CompilerContext;
-export '../kernel_generator_impl.dart' show InternalCompilerResult;
 export '../fasta/messages.dart' show FormattedMessage;
+export '../kernel_generator_impl.dart' show InternalCompilerResult;
 
 /// Test configuration used for testing CFE in its default state.
 const TestConfig defaultCfeConfig = const TestConfig(cfeMarker, 'cfe');
@@ -213,11 +222,8 @@
   }
 }
 
-abstract class CfeDataExtractor<T> extends DataExtractor<T> {
-  final InternalCompilerResult compilerResult;
-
-  CfeDataExtractor(this.compilerResult, Map<Id, ActualData<T>> actualMap)
-      : super(actualMap);
+mixin CfeDataRegistryMixin<T> implements DataRegistry<T> {
+  InternalCompilerResult get compilerResult;
 
   @override
   void report(Uri uri, int offset, String message) {
@@ -231,6 +237,25 @@
   }
 }
 
+class CfeDataRegistry<T> with DataRegistry<T>, CfeDataRegistryMixin<T> {
+  @override
+  final InternalCompilerResult compilerResult;
+
+  @override
+  final Map<Id, ActualData<T>> actualMap;
+
+  CfeDataRegistry(this.compilerResult, this.actualMap);
+}
+
+abstract class CfeDataExtractor<T> extends DataExtractor<T>
+    with CfeDataRegistryMixin<T> {
+  @override
+  final InternalCompilerResult compilerResult;
+
+  CfeDataExtractor(this.compilerResult, Map<Id, ActualData<T>> actualMap)
+      : super(actualMap);
+}
+
 /// Create the testing URI used for [fileName] in annotated tests.
 Uri createUriForFileName(String fileName) => toTestUri(fileName);
 
diff --git a/pkg/front_end/test/explicit_creation_git_test.dart b/pkg/front_end/test/explicit_creation_git_test.dart
index 726ffae..0f00042 100644
--- a/pkg/front_end/test/explicit_creation_git_test.dart
+++ b/pkg/front_end/test/explicit_creation_git_test.dart
@@ -101,8 +101,10 @@
 
     kernelTarget.setEntryPoints(c.options.inputs);
     dillTarget.buildOutlines();
-    await kernelTarget.buildOutlines();
-    await kernelTarget.buildComponent();
+    BuildResult buildResult = await kernelTarget.buildOutlines();
+    buildResult = await kernelTarget.buildComponent(
+        macroApplications: buildResult.macroApplications);
+    buildResult.macroApplications?.macroExecutor.close();
   });
 
   print("Done in ${stopwatch.elapsedMilliseconds} ms. "
diff --git a/pkg/front_end/test/fasta/testing/suite.dart b/pkg/front_end/test/fasta/testing/suite.dart
index 2c4da28..d4e1b8c 100644
--- a/pkg/front_end/test/fasta/testing/suite.dart
+++ b/pkg/front_end/test/fasta/testing/suite.dart
@@ -53,7 +53,7 @@
 import 'package:front_end/src/fasta/kernel/hierarchy/hierarchy_node.dart'
     show ClassHierarchyNode;
 import 'package:front_end/src/fasta/kernel/kernel_target.dart'
-    show KernelTarget;
+    show BuildResult, KernelTarget;
 import 'package:front_end/src/fasta/kernel/utils.dart' show ByteSink;
 import 'package:front_end/src/fasta/kernel/verifier.dart' show verifyComponent;
 import 'package:front_end/src/fasta/messages.dart' show LocatedMessage;
@@ -1846,13 +1846,17 @@
         if (compilationSetup.testOptions.errors != null) {
           compilationSetup.errors.addAll(compilationSetup.testOptions.errors!);
         }
-        Component p = (await sourceTarget.buildOutlines())!;
+        BuildResult buildResult = await sourceTarget.buildOutlines();
+        Component p = buildResult.component!;
         if (compileMode == CompileMode.full) {
-          p = (await sourceTarget.buildComponent(
+          buildResult = await sourceTarget.buildComponent(
+              macroApplications: buildResult.macroApplications,
               verify: compilationSetup.folderOptions.noVerify
                   ? false
-                  : context.verify))!;
+                  : context.verify);
+          p = buildResult.component!;
         }
+        buildResult.macroApplications?.macroExecutor.close();
 
         // To avoid possible crash in mixin transformation in the transformation
         // of the user of this linked dependency we have to transform this too.
@@ -1907,20 +1911,24 @@
           new ValidatingInstrumentation();
       await instrumentation.loadExpectations(description.uri);
       sourceTarget.loader.instrumentation = instrumentation;
-      Component p = (await sourceTarget.buildOutlines())!;
+      BuildResult buildResult = await sourceTarget.buildOutlines();
+      Component p = buildResult.component!;
       Set<Uri> userLibraries = createUserLibrariesImportUriSet(
           p, sourceTarget.uriTranslator,
           excludedLibraries: excludedLibraries);
       if (compileMode != CompileMode.outline) {
-        p = (await sourceTarget.buildComponent(
+        buildResult = await sourceTarget.buildComponent(
+            macroApplications: buildResult.macroApplications,
             verify: compilationSetup.folderOptions.noVerify
                 ? false
-                : context.verify))!;
+                : context.verify);
+        p = buildResult.component!;
         instrumentation.finish();
         if (instrumentation.hasProblems) {
           if (updateComments) {
             await instrumentation.fixSource(description.uri, false);
           } else {
+            buildResult.macroApplications?.macroExecutor.close();
             return new Result<ComponentResult>(
                 new ComponentResult(description, p, userLibraries,
                     compilationSetup, sourceTarget),
@@ -1931,6 +1939,7 @@
           }
         }
       }
+      buildResult.macroApplications?.macroExecutor.close();
       return pass(new ComponentResult(
           description, p, userLibraries, compilationSetup, sourceTarget));
     });
diff --git a/pkg/front_end/test/macro_application/data/package_config.json b/pkg/front_end/test/macro_application/data/package_config.json
new file mode 100644
index 0000000..fbfd870
--- /dev/null
+++ b/pkg/front_end/test/macro_application/data/package_config.json
@@ -0,0 +1,18 @@
+{
+  "configVersion": 2,
+  "packages": [
+    {
+      "name": "macro",
+      "rootUri": "pkgs/macro/lib/"
+    },
+    {
+      "name": "meta",
+      "rootUri": "../../../../meta/",
+      "packageUri": "lib/"
+    },
+    {
+      "name": "_fe_analyzer_shared",
+      "rootUri": "../../../../_fe_analyzer_shared/lib/"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/pkg/front_end/test/macro_application/data/pkgs/macro/lib/macro.dart b/pkg/front_end/test/macro_application/data/pkgs/macro/lib/macro.dart
new file mode 100644
index 0000000..a5469f1
--- /dev/null
+++ b/pkg/front_end/test/macro_application/data/pkgs/macro/lib/macro.dart
@@ -0,0 +1,17 @@
+// 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 'package:_fe_analyzer_shared/src/macros/api.dart';
+
+macro class FunctionDefinitionMacro1 implements FunctionDefinitionMacro {
+  const FunctionDefinitionMacro1();
+
+  FutureOr<void> buildDefinitionForFunction(
+      FunctionDeclaration function, FunctionDefinitionBuilder builder) {
+      builder.augment(new FunctionBodyCode.fromString('''{
+  return 42;
+}'''));
+  }
+}
diff --git a/pkg/front_end/test/macro_application/data/tests/marker.options b/pkg/front_end/test/macro_application/data/tests/marker.options
new file mode 100644
index 0000000..578e904
--- /dev/null
+++ b/pkg/front_end/test/macro_application/data/tests/marker.options
@@ -0,0 +1 @@
+cfe=pkg/front_end/test/macro_application/macro_application_test.dart
diff --git a/pkg/front_end/test/macro_application/data/tests/parameters.dart b/pkg/front_end/test/macro_application/data/tests/parameters.dart
new file mode 100644
index 0000000..41fcf36
--- /dev/null
+++ b/pkg/front_end/test/macro_application/data/tests/parameters.dart
@@ -0,0 +1,33 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:macro/macro.dart';
+
+@FunctionDefinitionMacro1()
+/*member: topLevelFunction1:
+augment void topLevelFunction1(int a, ) {
+  return 42;
+}*/
+external void topLevelFunction1(int a);
+
+@FunctionDefinitionMacro1()
+/*member: topLevelFunction2:
+augment void topLevelFunction2(int a, int b, ) {
+  return 42;
+}*/
+external void topLevelFunction2(int a, int b);
+
+@FunctionDefinitionMacro1()
+/*member: topLevelFunction3:
+augment void topLevelFunction3(int a, [int? b, ]) {
+  return 42;
+}*/
+external void topLevelFunction3(int a, [int? b]);
+
+@FunctionDefinitionMacro1()
+/*member: topLevelFunction4:
+augment void topLevelFunction4(int a, {int? b, int? c, }) {
+  return 42;
+}*/
+external void topLevelFunction4(int a, {int? b, int? c});
diff --git a/pkg/front_end/test/macro_application/data/tests/type_annotations.dart b/pkg/front_end/test/macro_application/data/tests/type_annotations.dart
new file mode 100644
index 0000000..b6a4cd8
--- /dev/null
+++ b/pkg/front_end/test/macro_application/data/tests/type_annotations.dart
@@ -0,0 +1,63 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:math' as math;
+
+import 'package:macro/macro.dart';
+
+/*member: topLevelFunction1:
+augment void topLevelFunction1() {
+  return 42;
+}*/
+@FunctionDefinitionMacro1()
+external void topLevelFunction1();
+
+/*member: topLevelFunction2:
+augment dynamic topLevelFunction2() {
+  return 42;
+}*/
+@FunctionDefinitionMacro1()
+external dynamic topLevelFunction2();
+
+/*member: topLevelFunction3:
+augment int topLevelFunction3() {
+  return 42;
+}*/
+@FunctionDefinitionMacro1()
+external int topLevelFunction3();
+
+/*member: topLevelFunction4:
+augment dynamic topLevelFunction4() {
+  return 42;
+}*/
+@FunctionDefinitionMacro1()
+external topLevelFunction4();
+
+/*member: topLevelFunction5:
+augment math.Random topLevelFunction5() {
+  return 42;
+}*/
+@FunctionDefinitionMacro1()
+external math.Random topLevelFunction5();
+
+/*member: topLevelFunction6:
+augment List<int> topLevelFunction6() {
+  return 42;
+}*/
+@FunctionDefinitionMacro1()
+external List<int> topLevelFunction6();
+
+/*member: topLevelFunction7:
+augment Map<math.Random, List<int>> topLevelFunction7() {
+  return 42;
+}*/
+@FunctionDefinitionMacro1()
+external Map<math.Random, List<int>> topLevelFunction7();
+
+/*member: topLevelFunction8:
+augment Map<int?, String>? topLevelFunction8() {
+  return 42;
+}*/
+@FunctionDefinitionMacro1()
+external Map<int?, String>? topLevelFunction8();
diff --git a/pkg/front_end/test/macro_application/macro_application_test.dart b/pkg/front_end/test/macro_application/macro_application_test.dart
new file mode 100644
index 0000000..db171e8
--- /dev/null
+++ b/pkg/front_end/test/macro_application/macro_application_test.dart
@@ -0,0 +1,135 @@
+// 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' show Directory, Platform;
+
+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/isolated_executor/isolated_executor.dart'
+    as isolatedExecutor;
+import 'package:_fe_analyzer_shared/src/testing/id.dart' show ActualData, Id;
+import 'package:_fe_analyzer_shared/src/testing/id_testing.dart';
+import 'package:front_end/src/api_prototype/compiler_options.dart';
+import 'package:front_end/src/api_prototype/experimental_flags.dart';
+import 'package:front_end/src/api_prototype/kernel_generator.dart';
+import 'package:front_end/src/fasta/builder/member_builder.dart';
+import 'package:front_end/src/fasta/kernel/macro.dart';
+import 'package:front_end/src/fasta/kernel/utils.dart';
+import 'package:front_end/src/testing/compiler_common.dart';
+import 'package:front_end/src/testing/id_extractor.dart';
+import 'package:front_end/src/testing/id_testing_helper.dart';
+import 'package:kernel/ast.dart' hide Arguments;
+import 'package:kernel/kernel.dart';
+import 'package:kernel/target/targets.dart';
+import 'package:vm/target/vm.dart';
+
+Future<Uri> compileMacros(Directory directory) async {
+  CompilerOptions options = new CompilerOptions();
+  options.target = new VmTarget(new TargetFlags());
+  options.explicitExperimentalFlags[ExperimentalFlag.macros] = true;
+  options.environmentDefines = {};
+  options.packagesFileUri = Platform.script.resolve('data/package_config.json');
+
+  CompilerResult? compilerResult = await compileScript({
+    'main.dart': bootstrapMacroIsolate(
+        'package:macro/macro.dart', 'FunctionDefinitionMacro1', [''])
+  }, options: options, requireMain: false);
+  Uri uri = directory.absolute.uri.resolve('macros.dill');
+  await writeComponentToFile(compilerResult!.component!, uri);
+  return uri;
+}
+
+Future<void> main(List<String> args) async {
+  enableMacros = true;
+
+  Directory tempDirectory =
+      await Directory.systemTemp.createTemp('macro_application');
+
+  Uri macrosUri = await compileMacros(tempDirectory);
+  Map<MacroClass, Uri> precompiledMacroUris = {
+    new MacroClass(
+            Uri.parse('package:macro/macro.dart'), 'FunctionDefinitionMacro1'):
+        macrosUri
+  };
+
+  Directory dataDir =
+      new Directory.fromUri(Platform.script.resolve('data/tests'));
+  await runTests<String>(dataDir,
+      args: args,
+      createUriForFileName: createUriForFileName,
+      onFailure: onFailure,
+      runTest: runTestFor(const MacroDataComputer(),
+          [new MacroTestConfig(precompiledMacroUris)]),
+      preserveWhitespaceInAnnotations: true);
+}
+
+class MacroTestConfig extends TestConfig {
+  final Map<MacroClass, Uri> precompiledMacroUris;
+
+  MacroTestConfig(this.precompiledMacroUris)
+      : super(cfeMarker, 'cfe',
+            explicitExperimentalFlags: {ExperimentalFlag.macros: true},
+            packageConfigUri:
+                Platform.script.resolve('data/package_config.json'));
+
+  @override
+  void customizeCompilerOptions(CompilerOptions options, TestData testData) {
+    options.macroExecutorProvider = () async {
+      return await isolatedExecutor.start();
+    };
+    options.precompiledMacroUris = precompiledMacroUris;
+  }
+}
+
+class MacroDataComputer extends DataComputer<String> {
+  const MacroDataComputer();
+
+  @override
+  DataInterpreter<String> get dataValidator => const StringDataInterpreter();
+
+  @override
+  void computeMemberData(TestResultData testResultData, Member member,
+      Map<Id, ActualData<String>> actualMap,
+      {bool? verbose}) {
+    CfeDataRegistry<String> registry =
+        new CfeDataRegistry(testResultData.compilerResult, actualMap);
+    MacroApplicationDataForTesting macroApplicationData = testResultData
+        .compilerResult
+        .kernelTargetForTesting!
+        .loader
+        .dataForTesting!
+        .macroApplicationData;
+    for (MapEntry<MemberBuilder, List<MacroExecutionResult>> entry
+        in macroApplicationData.memberDefinitionsResults.entries) {
+      if (entry.key.member == member) {
+        StringBuffer sb = new StringBuffer();
+        for (MacroExecutionResult result in entry.value) {
+          sb.write('\n${codeToString(result.augmentations.first)}');
+        }
+        Id id = computeMemberId(member);
+        registry.registerValue(
+            member.fileUri, member.fileOffset, id, sb.toString(), member);
+      }
+    }
+  }
+}
+
+void _codeToString(StringBuffer sb, Code code) {
+  for (Object part in code.parts) {
+    if (part is Code) {
+      _codeToString(sb, part);
+    } else if (part is TypeAnnotation) {
+      _codeToString(sb, part.code);
+    } else {
+      sb.write(part);
+    }
+  }
+}
+
+String codeToString(Code code) {
+  StringBuffer sb = new StringBuffer();
+  _codeToString(sb, code);
+  return sb.toString();
+}
diff --git a/pkg/front_end/test/macros/macro_test.dart b/pkg/front_end/test/macros/macro_test.dart
index 2ceca95..756e587 100644
--- a/pkg/front_end/test/macros/macro_test.dart
+++ b/pkg/front_end/test/macros/macro_test.dart
@@ -3,12 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 
 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/features.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';
 import 'package:front_end/src/api_prototype/compiler_options.dart';
 import 'package:front_end/src/api_prototype/experimental_flags.dart';
 import 'package:front_end/src/fasta/builder/class_builder.dart';
@@ -106,20 +107,22 @@
 
 class MacroDataExtractor extends CfeDataExtractor<Features> {
   final TestResultData testResultData;
-  late final MacroDeclarationData macroDeclarationData;
-  late final MacroApplicationData macroApplicationData;
 
   MacroDataExtractor(
       this.testResultData, Map<Id, ActualData<Features>> actualMap)
-      : super(testResultData.compilerResult, actualMap) {
-    macroDeclarationData = testResultData.compilerResult.kernelTargetForTesting!
-        .loader.dataForTesting!.macroDeclarationData;
-    macroApplicationData = testResultData.compilerResult.kernelTargetForTesting!
-        .loader.dataForTesting!.macroApplicationData;
-  }
+      : super(testResultData.compilerResult, actualMap);
 
   TestMacroExecutor get macroExecutor => testResultData.customData;
 
+  MacroDeclarationData get macroDeclarationData => testResultData.compilerResult
+      .kernelTargetForTesting!.loader.dataForTesting!.macroDeclarationData;
+  MacroApplicationDataForTesting get macroApplicationData => testResultData
+      .compilerResult
+      .kernelTargetForTesting!
+      .loader
+      .dataForTesting!
+      .macroApplicationData;
+
   LibraryMacroApplicationData? getLibraryMacroApplicationData(Library library) {
     for (MapEntry<LibraryBuilder, LibraryMacroApplicationData> entry
         in macroApplicationData.libraryData.entries) {
@@ -144,13 +147,13 @@
     return null;
   }
 
-  MacroApplications? getClassMacroApplications(Class cls) {
+  List<MacroApplication>? getClassMacroApplications(Class cls) {
     return getClassMacroApplicationData(cls)?.classApplications;
   }
 
-  MacroApplications? getMemberMacroApplications(Member member) {
+  List<MacroApplication>? getMemberMacroApplications(Member member) {
     Class? enclosingClass = member.enclosingClass;
-    Map<MemberBuilder, MacroApplications>? memberApplications;
+    Map<MemberBuilder, List<MacroApplication>>? memberApplications;
     if (enclosingClass != null) {
       memberApplications =
           getClassMacroApplicationData(enclosingClass)?.memberApplications;
@@ -160,7 +163,7 @@
               ?.memberApplications;
     }
     if (memberApplications != null) {
-      for (MapEntry<MemberBuilder, MacroApplications> entry
+      for (MapEntry<MemberBuilder, List<MacroApplication>> entry
           in memberApplications.entries) {
         if (entry.key.member == member) {
           return entry.value;
@@ -171,9 +174,9 @@
   }
 
   void registerMacroApplications(
-      Features features, MacroApplications? macroApplications) {
+      Features features, List<MacroApplication>? macroApplications) {
     if (macroApplications != null) {
-      for (MacroApplication application in macroApplications.macros) {
+      for (MacroApplication application in macroApplications) {
         String className = application.classBuilder.name;
         String constructorName = application.constructorName == ''
             ? 'new'
@@ -258,9 +261,8 @@
       MacroInstanceIdentifier macro,
       Declaration declaration,
       TypeResolver typeResolver,
-      ClassIntrospector classIntrospector) {
-    // TODO: implement executeDeclarationsPhase
-    throw UnimplementedError();
+      ClassIntrospector classIntrospector) async {
+    return new _MacroExecutionResult();
   }
 
   @override
@@ -269,16 +271,14 @@
       Declaration declaration,
       TypeResolver typeResolver,
       ClassIntrospector classIntrospector,
-      TypeDeclarationResolver typeDeclarationResolver) {
-    // TODO: implement executeDefinitionsPhase
-    throw UnimplementedError();
+      TypeDeclarationResolver typeDeclarationResolver) async {
+    return new _MacroExecutionResult();
   }
 
   @override
   Future<MacroExecutionResult> executeTypesPhase(
-      MacroInstanceIdentifier macro, Declaration declaration) {
-    // TODO: implement executeTypesPhase
-    throw UnimplementedError();
+      MacroInstanceIdentifier macro, Declaration declaration) async {
+    return new _MacroExecutionResult();
   }
 
   @override
@@ -343,3 +343,14 @@
   @override
   void serialize(Serializer serializer) => throw UnimplementedError();
 }
+
+class _MacroExecutionResult implements MacroExecutionResult {
+  @override
+  Iterable<DeclarationCode> augmentations = const [];
+
+  @override
+  Iterable<DeclarationCode> imports = const [];
+
+  @override
+  void serialize(Serializer serializer) {}
+}
diff --git a/pkg/front_end/test/spell_checking_list_common.txt b/pkg/front_end/test/spell_checking_list_common.txt
index 09b0c93..d69db07 100644
--- a/pkg/front_end/test/spell_checking_list_common.txt
+++ b/pkg/front_end/test/spell_checking_list_common.txt
@@ -1518,6 +1518,7 @@
 information
 informational
 informs
+inherent
 inherently
 inherit
 inheritable
diff --git a/pkg/front_end/testing.json b/pkg/front_end/testing.json
index d0f87bb..9b60daf 100644
--- a/pkg/front_end/testing.json
+++ b/pkg/front_end/testing.json
@@ -494,6 +494,7 @@
       "test/extensions/data/",
       "test/id_testing/data/",
       "test/language_versioning/data/",
+      "test/macro_application/data/",
       "test/macros/data/",
       "test/patching/data",
       "test/predicates/data",
diff --git a/pkg/front_end/tool/_fasta/entry_points.dart b/pkg/front_end/tool/_fasta/entry_points.dart
index 70b5606..f99d05f 100644
--- a/pkg/front_end/tool/_fasta/entry_points.dart
+++ b/pkg/front_end/tool/_fasta/entry_points.dart
@@ -37,7 +37,7 @@
     show IncrementalCompiler;
 
 import 'package:front_end/src/fasta/kernel/kernel_target.dart'
-    show KernelTarget;
+    show BuildResult, KernelTarget;
 
 import 'package:front_end/src/fasta/kernel/utils.dart'
     show printComponentText, writeComponentToFile;
@@ -304,10 +304,27 @@
       {Uri? output,
       bool omitPlatform: false,
       bool supportAdditionalDills: true}) async {
+    KernelTarget kernelTarget = await _createKernelTarget();
+    BuildResult buildResult = await _buildOutline(kernelTarget,
+        output: output,
+        omitPlatform: omitPlatform,
+        supportAdditionalDills: supportAdditionalDills);
+    buildResult.macroApplications?.macroExecutor.close();
+    return kernelTarget;
+  }
+
+  Future<KernelTarget> _createKernelTarget() async {
     UriTranslator uriTranslator = await c.options.getUriTranslator();
     ticker.logMs("Read packages file");
     DillTarget dillTarget = createDillTarget(uriTranslator);
-    KernelTarget kernelTarget = createKernelTarget(dillTarget, uriTranslator);
+    return createKernelTarget(dillTarget, uriTranslator);
+  }
+
+  Future<BuildResult> _buildOutline(KernelTarget kernelTarget,
+      {Uri? output,
+      bool omitPlatform: false,
+      bool supportAdditionalDills: true}) async {
+    DillTarget dillTarget = kernelTarget.dillTarget;
 
     if (supportAdditionalDills) {
       Component? sdkSummary = await c.options.loadSdkSummary(null);
@@ -329,7 +346,8 @@
 
     kernelTarget.setEntryPoints(c.options.inputs);
     dillTarget.buildOutlines();
-    var outline = await kernelTarget.buildOutlines();
+    BuildResult buildResult = await kernelTarget.buildOutlines();
+    Component? outline = buildResult.component;
     if (c.options.debugDump && output != null) {
       printComponentText(outline,
           libraryFilter: kernelTarget.isSourceLibraryForDebugging);
@@ -353,17 +371,21 @@
       await writeComponentToFile(outline!, output);
       ticker.logMs("Wrote outline to ${output.toFilePath()}");
     }
-    return kernelTarget;
+    return buildResult;
   }
 
   Future<Uri> compile(
       {bool omitPlatform: false, bool supportAdditionalDills: true}) async {
     c.options.reportNullSafetyCompilationModeInfo();
-    KernelTarget kernelTarget =
-        await buildOutline(supportAdditionalDills: supportAdditionalDills);
+    KernelTarget kernelTarget = await _createKernelTarget();
+    BuildResult buildResult = await _buildOutline(kernelTarget,
+        supportAdditionalDills: supportAdditionalDills);
     Uri uri = c.options.output!;
-    Component component =
-        (await kernelTarget.buildComponent(verify: c.options.verify))!;
+    buildResult = await kernelTarget.buildComponent(
+        macroApplications: buildResult.macroApplications,
+        verify: c.options.verify);
+    buildResult.macroApplications?.macroExecutor.close();
+    Component component = buildResult.component!;
     if (c.options.debugDump) {
       printComponentText(component,
           libraryFilter: kernelTarget.isSourceLibraryForDebugging);
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 360c4a4..097f562 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -5513,8 +5513,11 @@
     return memcmp(a->untag()->data(), b->untag()->data(), Size(a)) == 0;
   }
 
-  uint32_t Hash() const {
-    return HashBytes(reinterpret_cast<const uint8_t*>(PayloadStart()), Size());
+  uint32_t Hash() const { return Hash(ptr()); }
+
+  static uint32_t Hash(const InstructionsPtr instr) {
+    return HashBytes(reinterpret_cast<const uint8_t*>(PayloadStart(instr)),
+                     Size(instr));
   }
 
   CodeStatistics* stats() const;
diff --git a/runtime/vm/program_visitor.cc b/runtime/vm/program_visitor.cc
index 9d57230..b7f31f3 100644
--- a/runtime/vm/program_visitor.cc
+++ b/runtime/vm/program_visitor.cc
@@ -1178,7 +1178,9 @@
 
   static Value ValueOf(Pair kv) { return kv; }
 
-  static inline uword Hash(Key key) { return Utils::WordHash(key->Size()); }
+  static inline uword Hash(Key key) {
+    return Utils::WordHash(Instructions::Hash(key->instructions()));
+  }
 
   static inline bool IsKeyEqual(Pair pair, Key key) {
     // In AOT, disabled code objects should not be considered for deduplication.
diff --git a/sdk/lib/core/pattern.dart b/sdk/lib/core/pattern.dart
index dbf7a23..fbf70ea 100644
--- a/sdk/lib/core/pattern.dart
+++ b/sdk/lib/core/pattern.dart
@@ -95,7 +95,7 @@
   ///
   /// The result may be `null` if the pattern didn't assign a value to it
   /// as part of this match.
-  /// ```dart import dart:convert
+  /// ```dart import:convert
   ///
   /// final string = '[00:13.37] This is a chat message.';
   /// final regExp = RegExp(r'^\[\s*(\d+):(\d+)\.(\d+)\]\s*(.*)$');
@@ -119,7 +119,7 @@
   ///
   /// The list contains the strings returned by [group] for each index in
   /// [groupIndices].
-  /// ```dart import dart:convert
+  /// ```dart import:convert
   ///
   /// final string = '[00:13.37] This is a chat message.';
   /// final regExp = RegExp(r'^\[\s*(\d+):(\d+)\.(\d+)\]\s*(.*)$');
diff --git a/tools/VERSION b/tools/VERSION
index f958102..78a60d7 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 17
 PATCH 0
-PRERELEASE 24
+PRERELEASE 25
 PRERELEASE_PATCH 0
\ No newline at end of file