[cfe] Support TypeResolver and StaticType in macro implementation

This adds initial support for TypeResolver.resolve and
StaticType.isExactly and StaticType.isSubtypeOf.

Change-Id: Iaa147a6fc3c8c9eee8940aa9bcf4a01b51819ddc
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/228860
Reviewed-by: Jake Macdonald <jakemac@google.com>
Reviewed-by: Jens Johansen <jensj@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart b/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
index 514fcd9..4d2f6a1 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
@@ -2,21 +2,32 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-/// Generates a Dart program for a given macro, which can be compiled and then
-/// passed as a precompiled kernel file to `MacroExecutor.loadMacro`.
+/// Generates a Dart program for a given set of macros, which can be compiled
+/// and then passed as a precompiled kernel file to `MacroExecutor.loadMacro`.
+///
+/// The [macroDeclarations] is a map from library URIs to macro classes for the
+/// macros supported. The macro classes are provided as a map from macro class
+/// names to the names of the macro class constructors.
 String bootstrapMacroIsolate(
-    String macroImport, String macroName, List<String> constructorNames) {
-  StringBuffer constructorEntries = new StringBuffer(
-      "MacroClassIdentifierImpl(Uri.parse('$macroImport'), '$macroName'): {");
-  for (String constructor in constructorNames) {
-    constructorEntries.writeln("'$constructor': "
-        "$macroName.${constructor.isEmpty ? 'new' : constructor},");
-  }
-  constructorEntries.writeln('},');
-  return template
-      .replaceFirst(_importMarker, 'import \'$macroImport\';')
-      .replaceFirst(
-          _macroConstructorEntriesMarker, constructorEntries.toString());
+    Map<String, Map<String, List<String>>> macroDeclarations) {
+  StringBuffer imports = new StringBuffer();
+  StringBuffer constructorEntries = new StringBuffer();
+  macroDeclarations
+      .forEach((String macroImport, Map<String, List<String>> macroClasses) {
+    imports.writeln('import \'$macroImport\';');
+    macroClasses.forEach((String macroName, List<String> constructorNames) {
+      constructorEntries
+          .writeln("MacroClassIdentifierImpl(Uri.parse('$macroImport'), "
+              "'$macroName'): {");
+      for (String constructor in constructorNames) {
+        constructorEntries.writeln("'$constructor': "
+            "$macroName.${constructor.isEmpty ? 'new' : constructor},");
+      }
+      constructorEntries.writeln('},');
+    });
+  });
+  return template.replaceFirst(_importMarker, imports.toString()).replaceFirst(
+      _macroConstructorEntriesMarker, constructorEntries.toString());
 }
 
 const String _importMarker = '{{IMPORT}}';
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/isolated_executor/isolated_executor.dart b/pkg/_fe_analyzer_shared/lib/src/macros/isolated_executor/isolated_executor.dart
index 849c662..57f0ec8 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/isolated_executor/isolated_executor.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/isolated_executor/isolated_executor.dart
@@ -182,7 +182,7 @@
               IsExactlyTypeRequest request =
                   new IsExactlyTypeRequest.deserialize(deserializer, zoneId);
               StaticType leftType = request.leftType.instance as StaticType;
-              StaticType rightType = request.leftType.instance as StaticType;
+              StaticType rightType = request.rightType.instance as StaticType;
               SerializableResponse response = new SerializableResponse(
                   response:
                       new BooleanValue(await leftType.isExactly(rightType)),
@@ -197,7 +197,7 @@
               IsSubtypeOfRequest request =
                   new IsSubtypeOfRequest.deserialize(deserializer, zoneId);
               StaticType leftType = request.leftType.instance as StaticType;
-              StaticType rightType = request.leftType.instance as StaticType;
+              StaticType rightType = request.rightType.instance as StaticType;
               SerializableResponse response = new SerializableResponse(
                   response:
                       new BooleanValue(await leftType.isSubtypeOf(rightType)),
diff --git a/pkg/_fe_analyzer_shared/test/macros/isolated_executor/isolated_executor_test.dart b/pkg/_fe_analyzer_shared/test/macros/isolated_executor/isolated_executor_test.dart
index a7f5491..7c242a4 100644
--- a/pkg/_fe_analyzer_shared/test/macros/isolated_executor/isolated_executor_test.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/isolated_executor/isolated_executor_test.dart
@@ -44,8 +44,11 @@
     var macroUri = simpleMacroFile.absolute.uri;
     var macroName = 'SimpleMacro';
 
-    var bootstrapContent =
-        bootstrapMacroIsolate(macroUri.toString(), macroName, ['', 'named']);
+    var bootstrapContent = bootstrapMacroIsolate({
+      macroUri.toString(): {
+        macroName: ['', 'named']
+      }
+    });
     var bootstrapFile = File(tmpDir.uri.resolve('main.dart').toFilePath())
       ..writeAsStringSync(bootstrapContent);
     var kernelOutputFile =
diff --git a/pkg/_fe_analyzer_shared/test/macros/isolated_executor/simple_macro.dart b/pkg/_fe_analyzer_shared/test/macros/isolated_executor/simple_macro.dart
index 035d96f..84ddb7a 100644
--- a/pkg/_fe_analyzer_shared/test/macros/isolated_executor/simple_macro.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/isolated_executor/simple_macro.dart
@@ -36,13 +36,13 @@
     }
     var classType =
         await builder.resolve(method.definingClass) as NamedStaticType;
-    if (!(await staticReturnType.isExactly(classType))) {
+    if (await staticReturnType.isExactly(classType)) {
       throw StateError(
           'The return type should not be exactly equal to the class type');
     }
-    if (!(await staticReturnType.isSubtypeOf(classType))) {
+    if (await staticReturnType.isSubtypeOf(classType)) {
       throw StateError(
-          'The return type should be a subtype of the class type!');
+          'The return type should not be a subtype of the class type!');
     }
 
     // Test the type declaration resolver
diff --git a/pkg/front_end/lib/src/fasta/incremental_compiler.dart b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
index 76ab9eb8..2a1e8b7 100644
--- a/pkg/front_end/lib/src/fasta/incremental_compiler.dart
+++ b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
@@ -361,7 +361,7 @@
             verify: c.options.verify);
         componentWithDill = buildResult.component;
       }
-      buildResult.macroApplications?.macroExecutor.close();
+      buildResult.macroApplications?.close();
       hierarchy ??= currentKernelTarget.loader.hierarchy;
       if (currentKernelTarget.classHierarchyChanges != null) {
         hierarchy.applyTreeChanges(
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 814b523..86144de 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -284,6 +284,7 @@
   }
 
   Version? _currentSdkVersion;
+
   Version get currentSdkVersion {
     if (_currentSdkVersion == null) {
       _parseCurrentSdkVersion();
@@ -464,7 +465,8 @@
       loader.finishNoSuchMethodForwarders();
       List<SourceClassBuilder> sourceClasses = loader.collectSourceClasses();
       if (macroApplications != null) {
-        await macroApplications.applyDefinitionMacros();
+        await macroApplications.applyDefinitionMacros(
+            loader.coreTypes, loader.hierarchy);
       }
       loader.finishNativeMethods();
       loader.finishPatchMethods();
diff --git a/pkg/front_end/lib/src/fasta/kernel/macro.dart b/pkg/front_end/lib/src/fasta/kernel/macro.dart
index 6d95bbf..3c8f11a 100644
--- a/pkg/front_end/lib/src/fasta/kernel/macro.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/macro.dart
@@ -5,9 +5,15 @@
 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 'package:_fe_analyzer_shared/src/macros/executor_shared/remote_instance.dart';
+import 'package:kernel/ast.dart' show DartType, DynamicType;
+import 'package:kernel/class_hierarchy.dart';
+import 'package:kernel/core_types.dart';
+import 'package:kernel/type_environment.dart';
 
 import '../builder/class_builder.dart';
 import '../builder/formal_parameter_builder.dart';
+import '../builder/library_builder.dart';
 import '../builder/member_builder.dart';
 import '../builder/named_type_builder.dart';
 import '../builder/type_builder.dart';
@@ -46,12 +52,39 @@
   }
 }
 
+class MacroApplication {
+  final ClassBuilder classBuilder;
+  final String constructorName;
+
+  // 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, List<MacroApplication>> memberApplications = {};
+}
+
+class ClassMacroApplicationData {
+  List<MacroApplication>? classApplications;
+  Map<MemberBuilder, List<MacroApplication>> memberApplications = {};
+}
+
 class MacroApplications {
-  final MacroExecutor macroExecutor;
+  final MacroExecutor _macroExecutor;
   final Map<SourceLibraryBuilder, LibraryMacroApplicationData> libraryData;
   final MacroApplicationDataForTesting? dataForTesting;
 
-  MacroApplications(this.macroExecutor, this.libraryData, this.dataForTesting) {
+  MacroApplications(
+      this._macroExecutor, this.libraryData, this.dataForTesting) {
     dataForTesting?.libraryData.addAll(libraryData);
   }
 
@@ -124,7 +157,7 @@
   Future<void> _applyTypeMacros(
       Declaration declaration, List<MacroApplication> macroApplications) async {
     for (MacroApplication macroApplication in macroApplications) {
-      await macroExecutor.executeTypesPhase(
+      await _macroExecutor.executeTypesPhase(
           macroApplication.instanceIdentifier, declaration);
     }
   }
@@ -151,7 +184,7 @@
       TypeResolver typeResolver,
       ClassIntrospector classIntrospector) async {
     for (MacroApplication macroApplication in macroApplications) {
-      await macroExecutor.executeDeclarationsPhase(
+      await _macroExecutor.executeDeclarationsPhase(
           macroApplication.instanceIdentifier,
           declaration,
           typeResolver,
@@ -160,7 +193,7 @@
   }
 
   Future<void> applyDeclarationMacros() async {
-    TypeResolver typeResolver = new _TypeResolver();
+    TypeResolver typeResolver = new _TypeResolver(this);
     ClassIntrospector classIntrospector = new _ClassIntrospector();
     for (MapEntry<SourceLibraryBuilder,
         LibraryMacroApplicationData> libraryEntry in libraryData.entries) {
@@ -186,19 +219,24 @@
       TypeDeclarationResolver typeDeclarationResolver) async {
     List<MacroExecutionResult> results = [];
     for (MacroApplication macroApplication in macroApplications) {
-      MacroExecutionResult result = await macroExecutor.executeDefinitionsPhase(
-          macroApplication.instanceIdentifier,
-          declaration,
-          typeResolver,
-          classIntrospector,
-          typeDeclarationResolver);
+      MacroExecutionResult result =
+          await _macroExecutor.executeDefinitionsPhase(
+              macroApplication.instanceIdentifier,
+              declaration,
+              typeResolver,
+              classIntrospector,
+              typeDeclarationResolver);
       results.add(result);
     }
     return results;
   }
 
-  Future<void> applyDefinitionMacros() async {
-    TypeResolver typeResolver = new _TypeResolver();
+  late TypeEnvironment typeEnvironment;
+
+  Future<void> applyDefinitionMacros(
+      CoreTypes coreTypes, ClassHierarchy classHierarchy) async {
+    typeEnvironment = new TypeEnvironment(coreTypes, classHierarchy);
+    TypeResolver typeResolver = new _TypeResolver(this);
     ClassIntrospector classIntrospector = new _ClassIntrospector();
     TypeDeclarationResolver typeDeclarationResolver =
         new _TypeDeclarationResolver();
@@ -222,126 +260,179 @@
       }
     }
   }
-}
 
-class MacroApplication {
-  final ClassBuilder classBuilder;
-  final String constructorName;
-  // 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, List<MacroApplication>> memberApplications = {};
-}
-
-class ClassMacroApplicationData {
-  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));
-      }
-    }
+  void close() {
+    _macroExecutor.close();
+    _staticTypeCache.clear();
+    _typeAnnotationCache.clear();
   }
 
-  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 []);
+  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(builder.library, formal.type);
+        // TODO(johnniwinther): Support default values.
+        if (formal.isNamed) {
+          namedParameters.add(new ParameterDeclarationImpl(
+              id: RemoteInstance.uniqueId,
+              name: formal.name,
+              isRequired: formal.isNamedRequired,
+              isNamed: true,
+              type: type,
+              defaultValue: null));
+        } else {
+          positionalParameters.add(new ParameterDeclarationImpl(
+              id: RemoteInstance.uniqueId,
+              name: formal.name,
+              isRequired: formal.isRequired,
+              isNamed: false,
+              type: type,
+              defaultValue: null));
+        }
+      }
+    }
+
+    return new FunctionDeclarationImpl(
+        id: RemoteInstance.uniqueId,
+        name: builder.name,
+        isAbstract: builder.isAbstract,
+        isExternal: builder.isExternal,
+        isGetter: builder.isGetter,
+        isSetter: builder.isSetter,
+        positionalParameters: positionalParameters,
+        namedParameters: namedParameters,
+        returnType: computeTypeAnnotation(builder.library, builder.returnType),
+        // TODO(johnniwinther): Support typeParameters
+        typeParameters: const []);
+  }
+
+  Map<TypeBuilder?, _NamedTypeAnnotationImpl> _typeAnnotationCache = {};
+
+  List<TypeAnnotationImpl> computeTypeAnnotations(
+      LibraryBuilder library, List<TypeBuilder>? typeBuilders) {
+    if (typeBuilders == null) return const [];
+    return new List.generate(typeBuilders.length,
+        (int index) => computeTypeAnnotation(library, typeBuilders[index]));
+  }
+
+  _NamedTypeAnnotationImpl _computeTypeAnnotation(
+      LibraryBuilder libraryBuilder, TypeBuilder? typeBuilder) {
+    if (typeBuilder != null) {
+      if (typeBuilder is NamedTypeBuilder) {
+        Object name = typeBuilder.name;
+        List<TypeAnnotationImpl> typeArguments =
+            computeTypeAnnotations(libraryBuilder, typeBuilder.arguments);
+        bool isNullable = typeBuilder.nullabilityBuilder.isNullable;
+        if (name is String) {
+          return new _NamedTypeAnnotationImpl(
+              typeBuilder: typeBuilder,
+              libraryBuilder: libraryBuilder,
+              id: RemoteInstance.uniqueId,
+              name: name,
+              typeArguments: typeArguments,
+              isNullable: isNullable);
+        } else if (name is QualifiedName) {
+          assert(name.qualifier is String);
+          return new _NamedTypeAnnotationImpl(
+              typeBuilder: typeBuilder,
+              libraryBuilder: libraryBuilder,
+              id: RemoteInstance.uniqueId,
+              name: '${name.qualifier}.${name.name}',
+              typeArguments: typeArguments,
+              isNullable: isNullable);
+        }
+      }
+    }
+    return new _NamedTypeAnnotationImpl(
+        typeBuilder: typeBuilder,
+        libraryBuilder: libraryBuilder,
+        id: RemoteInstance.uniqueId,
+        name: 'dynamic',
+        isNullable: false,
+        typeArguments: const []);
+  }
+
+  TypeAnnotationImpl computeTypeAnnotation(
+      LibraryBuilder libraryBuilder, TypeBuilder? typeBuilder) {
+    return _typeAnnotationCache[typeBuilder] ??=
+        _computeTypeAnnotation(libraryBuilder, typeBuilder);
+  }
+
+  StaticType resolveTypeAnnotation(_NamedTypeAnnotationImpl typeAnnotation) {
+    TypeBuilder? typeBuilder = typeAnnotation.typeBuilder;
+    LibraryBuilder libraryBuilder = typeAnnotation.libraryBuilder;
+    DartType dartType;
+    if (typeBuilder != null) {
+      dartType = typeBuilder.build(libraryBuilder);
+    } else {
+      dartType = const DynamicType();
+    }
+    return createStaticType(dartType);
+  }
+
+  Map<DartType, _StaticTypeImpl> _staticTypeCache = {};
+
+  StaticType createStaticType(DartType dartType) {
+    return _staticTypeCache[dartType] ??= new _StaticTypeImpl(this, dartType);
+  }
 }
 
-// TODO(johnniwinther): Cache remote instances when needed.
-int _removeInstanceId = 0;
+class _NamedTypeAnnotationImpl extends NamedTypeAnnotationImpl {
+  final TypeBuilder? typeBuilder;
+  final LibraryBuilder libraryBuilder;
 
-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++,
+  _NamedTypeAnnotationImpl({
+    required this.typeBuilder,
+    required this.libraryBuilder,
+    required int id,
+    required bool isNullable,
+    required String name,
+    required List<TypeAnnotationImpl> typeArguments,
+  }) : super(
+            id: id,
+            isNullable: isNullable,
             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);
-      }
-    }
+            typeArguments: typeArguments);
+}
+
+class _StaticTypeImpl extends StaticType {
+  final MacroApplications macroApplications;
+  final DartType type;
+
+  _StaticTypeImpl(this.macroApplications, this.type);
+
+  @override
+  Future<bool> isExactly(covariant _StaticTypeImpl other) {
+    return new Future.value(type == other.type);
   }
-  return new NamedTypeAnnotationImpl(
-      id: _removeInstanceId++,
-      name: 'dynamic',
-      isNullable: false,
-      typeArguments: const []);
+
+  @override
+  Future<bool> isSubtypeOf(covariant _StaticTypeImpl other) {
+    return new Future.value(macroApplications.typeEnvironment
+        .isSubtypeOf(type, other.type, SubtypeCheckMode.withNullabilities));
+  }
 }
 
 class _TypeResolver implements TypeResolver {
+  final MacroApplications macroApplications;
+
+  _TypeResolver(this.macroApplications);
+
   @override
-  Future<StaticType> resolve(TypeAnnotation typeAnnotation) {
-    // TODO: implement resolve
-    throw new UnimplementedError('_TypeResolver.resolve');
+  Future<StaticType> resolve(
+      covariant _NamedTypeAnnotationImpl typeAnnotation) {
+    return new Future.value(
+        macroApplications.resolveTypeAnnotation(typeAnnotation));
   }
 }
 
diff --git a/pkg/front_end/lib/src/kernel_generator_impl.dart b/pkg/front_end/lib/src/kernel_generator_impl.dart
index 0deb368..2470a1a 100644
--- a/pkg/front_end/lib/src/kernel_generator_impl.dart
+++ b/pkg/front_end/lib/src/kernel_generator_impl.dart
@@ -172,7 +172,7 @@
     }
     // TODO(johnniwinther): Should we reuse the macro executor on subsequent
     // compilations where possible?
-    buildResult.macroApplications?.macroExecutor.close();
+    buildResult.macroApplications?.close();
 
     return new InternalCompilerResult(
         summary: summary,
diff --git a/pkg/front_end/test/explicit_creation_git_test.dart b/pkg/front_end/test/explicit_creation_git_test.dart
index 0f00042..3200635 100644
--- a/pkg/front_end/test/explicit_creation_git_test.dart
+++ b/pkg/front_end/test/explicit_creation_git_test.dart
@@ -104,7 +104,7 @@
     BuildResult buildResult = await kernelTarget.buildOutlines();
     buildResult = await kernelTarget.buildComponent(
         macroApplications: buildResult.macroApplications);
-    buildResult.macroApplications?.macroExecutor.close();
+    buildResult.macroApplications?.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 d4e1b8c..02231dd 100644
--- a/pkg/front_end/test/fasta/testing/suite.dart
+++ b/pkg/front_end/test/fasta/testing/suite.dart
@@ -1856,7 +1856,7 @@
                   : context.verify);
           p = buildResult.component!;
         }
-        buildResult.macroApplications?.macroExecutor.close();
+        buildResult.macroApplications?.close();
 
         // To avoid possible crash in mixin transformation in the transformation
         // of the user of this linked dependency we have to transform this too.
@@ -1928,7 +1928,7 @@
           if (updateComments) {
             await instrumentation.fixSource(description.uri, false);
           } else {
-            buildResult.macroApplications?.macroExecutor.close();
+            buildResult.macroApplications?.close();
             return new Result<ComponentResult>(
                 new ComponentResult(description, p, userLibraries,
                     compilationSetup, sourceTarget),
@@ -1939,7 +1939,7 @@
           }
         }
       }
-      buildResult.macroApplications?.macroExecutor.close();
+      buildResult.macroApplications?.close();
       return pass(new ComponentResult(
           description, p, userLibraries, compilationSetup, sourceTarget));
     });
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
index a5469f1..b686cc0 100644
--- 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
@@ -15,3 +15,21 @@
 }'''));
   }
 }
+
+macro class FunctionDefinitionMacro2 implements FunctionDefinitionMacro {
+  const FunctionDefinitionMacro2();
+
+  FutureOr<void> buildDefinitionForFunction(
+      FunctionDeclaration function, FunctionDefinitionBuilder builder) async {
+    if (function.positionalParameters.isEmpty) {
+      return;
+    }
+    StaticType returnType = await builder.resolve(function.returnType);
+    StaticType parameterType =
+        await builder.resolve(function.positionalParameters.first.type);
+    builder.augment(new FunctionBodyCode.fromString('''{
+  print('isExactly=${await returnType.isExactly(parameterType)}');
+  print('isSubtype=${await returnType.isSubtypeOf(parameterType)}');
+}'''));
+  }
+}
diff --git a/pkg/front_end/test/macro_application/data/tests/parameters.dart b/pkg/front_end/test/macro_application/data/tests/parameters.dart
index 41fcf36..bb606bb 100644
--- a/pkg/front_end/test/macro_application/data/tests/parameters.dart
+++ b/pkg/front_end/test/macro_application/data/tests/parameters.dart
@@ -4,30 +4,30 @@
 
 import 'package:macro/macro.dart';
 
-@FunctionDefinitionMacro1()
 /*member: topLevelFunction1:
 augment void topLevelFunction1(int a, ) {
   return 42;
 }*/
+@FunctionDefinitionMacro1()
 external void topLevelFunction1(int a);
 
-@FunctionDefinitionMacro1()
 /*member: topLevelFunction2:
 augment void topLevelFunction2(int a, int b, ) {
   return 42;
 }*/
+@FunctionDefinitionMacro1()
 external void topLevelFunction2(int a, int b);
 
-@FunctionDefinitionMacro1()
 /*member: topLevelFunction3:
 augment void topLevelFunction3(int a, [int? b, ]) {
   return 42;
 }*/
+@FunctionDefinitionMacro1()
 external void topLevelFunction3(int a, [int? b]);
 
-@FunctionDefinitionMacro1()
 /*member: topLevelFunction4:
 augment void topLevelFunction4(int a, {int? b, int? c, }) {
   return 42;
 }*/
+@FunctionDefinitionMacro1()
 external void topLevelFunction4(int a, {int? b, int? c});
diff --git a/pkg/front_end/test/macro_application/data/tests/subtypes.dart b/pkg/front_end/test/macro_application/data/tests/subtypes.dart
new file mode 100644
index 0000000..245908a
--- /dev/null
+++ b/pkg/front_end/test/macro_application/data/tests/subtypes.dart
@@ -0,0 +1,51 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:macro/macro.dart';
+
+class A {}
+
+class B1 {}
+
+class B2 extends B1 {}
+
+class C1 extends C2 {}
+
+class C2 {}
+
+class D1 {}
+
+class D2 {}
+
+/*member: topLevelFunction1:
+augment A topLevelFunction1(A a, ) {
+  print('isExactly=true');
+  print('isSubtype=true');
+}*/
+@FunctionDefinitionMacro2()
+external A topLevelFunction1(A a);
+
+/*member: topLevelFunction2:
+augment B2 topLevelFunction2(B1 a, ) {
+  print('isExactly=false');
+  print('isSubtype=true');
+}*/
+@FunctionDefinitionMacro2()
+external B2 topLevelFunction2(B1 a);
+
+/*member: topLevelFunction3:
+augment C2 topLevelFunction3(C1 a, ) {
+  print('isExactly=false');
+  print('isSubtype=false');
+}*/
+@FunctionDefinitionMacro2()
+external C2 topLevelFunction3(C1 a);
+
+/*member: topLevelFunction4:
+augment D2 topLevelFunction4(D1 a, ) {
+  print('isExactly=false');
+  print('isSubtype=false');
+}*/
+@FunctionDefinitionMacro2()
+external D2 topLevelFunction4(D1 a);
diff --git a/pkg/front_end/test/macro_application/macro_application_test.dart b/pkg/front_end/test/macro_application/macro_application_test.dart
index db171e8..d24b4ae 100644
--- a/pkg/front_end/test/macro_application/macro_application_test.dart
+++ b/pkg/front_end/test/macro_application/macro_application_test.dart
@@ -25,6 +25,13 @@
 import 'package:kernel/target/targets.dart';
 import 'package:vm/target/vm.dart';
 
+const Map<String, Map<String, List<String>>> macroDeclarations = {
+  'package:macro/macro.dart': {
+    'FunctionDefinitionMacro1': [''],
+    'FunctionDefinitionMacro2': [''],
+  }
+};
+
 Future<Uri> compileMacros(Directory directory) async {
   CompilerOptions options = new CompilerOptions();
   options.target = new VmTarget(new TargetFlags());
@@ -32,10 +39,9 @@
   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);
+  CompilerResult? compilerResult = await compileScript(
+      {'main.dart': bootstrapMacroIsolate(macroDeclarations)},
+      options: options, requireMain: false);
   Uri uri = directory.absolute.uri.resolve('macros.dill');
   await writeComponentToFile(compilerResult!.component!, uri);
   return uri;
@@ -48,11 +54,14 @@
       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
-  };
+  Map<MacroClass, Uri> precompiledMacroUris = {};
+  macroDeclarations
+      .forEach((String macroUri, Map<String, List<String>> macroClasses) {
+    macroClasses.forEach((String macroClass, List<String> constructorNames) {
+      precompiledMacroUris[new MacroClass(Uri.parse(macroUri), macroClass)] =
+          macrosUri;
+    });
+  });
 
   Directory dataDir =
       new Directory.fromUri(Platform.script.resolve('data/tests'));
diff --git a/pkg/front_end/tool/_fasta/entry_points.dart b/pkg/front_end/tool/_fasta/entry_points.dart
index f99d05f..24fad8f 100644
--- a/pkg/front_end/tool/_fasta/entry_points.dart
+++ b/pkg/front_end/tool/_fasta/entry_points.dart
@@ -309,7 +309,7 @@
         output: output,
         omitPlatform: omitPlatform,
         supportAdditionalDills: supportAdditionalDills);
-    buildResult.macroApplications?.macroExecutor.close();
+    buildResult.macroApplications?.close();
     return kernelTarget;
   }
 
@@ -384,7 +384,7 @@
     buildResult = await kernelTarget.buildComponent(
         macroApplications: buildResult.macroApplications,
         verify: c.options.verify);
-    buildResult.macroApplications?.macroExecutor.close();
+    buildResult.macroApplications?.close();
     Component component = buildResult.component!;
     if (c.options.debugDump) {
       printComponentText(component,