Macro. Support for running pre-compiled native executables.

Change-Id: I79c8c6bb02d683c3ec5d384323a77622f2b3d4dd
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/330996
Reviewed-by: Jake Macdonald <jakemac@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/src/dart/analysis/analysis_context_collection.dart b/pkg/analyzer/lib/src/dart/analysis/analysis_context_collection.dart
index f24ba3a..bb55e52 100644
--- a/pkg/analyzer/lib/src/dart/analysis/analysis_context_collection.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/analysis_context_collection.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart'
-    as macro;
 import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
 import 'package:analyzer/dart/analysis/context_locator.dart';
 import 'package:analyzer/dart/analysis/context_root.dart';
@@ -29,11 +27,8 @@
   /// The resource provider used to access the file system.
   final ResourceProvider resourceProvider;
 
-  /// The instance of macro executor that is used for all macros.
-  final macro.MultiMacroExecutor macroExecutor = macro.MultiMacroExecutor();
-
-  /// The instance of the macro kernel builder.
-  final MacroKernelBuilder macroKernelBuilder = MacroKernelBuilder();
+  /// The support for executing macros.
+  late final MacroSupport macroSupport;
 
   /// The shared container into which drivers record files ownership.
   final OwnedFiles ownedFiles = OwnedFiles();
@@ -70,6 +65,7 @@
       required ContextRoot contextRoot,
       required DartSdk sdk,
     })? updateAnalysisOptions2,
+    MacroSupport? macroSupport,
   }) : resourceProvider =
             resourceProvider ?? PhysicalResourceProvider.INSTANCE {
     sdkPath ??= getSdkPath();
@@ -83,6 +79,8 @@
           'given, but not both.');
     }
 
+    this.macroSupport = macroSupport ??= KernelMacroSupport();
+
     var contextLocator = ContextLocator(
       resourceProvider: this.resourceProvider,
     );
@@ -114,8 +112,7 @@
         fileContentCache: fileContentCache,
         unlinkedUnitStore: unlinkedUnitStore ?? UnlinkedUnitStoreImpl(),
         infoDeclarationStore: infoDeclarationStore,
-        macroKernelBuilder: macroKernelBuilder,
-        macroExecutor: macroExecutor,
+        macroSupport: macroSupport,
         ownedFiles: ownedFiles,
       );
       contexts.add(context);
@@ -155,7 +152,7 @@
     for (final analysisContext in contexts) {
       await analysisContext.driver.dispose2();
     }
-    await macroExecutor.close();
+    await macroSupport.dispose();
     // If there are other collections, they will have to start it again.
     if (!forTesting) {
       await KernelCompilationService.dispose();
diff --git a/pkg/analyzer/lib/src/dart/analysis/context_builder.dart b/pkg/analyzer/lib/src/dart/analysis/context_builder.dart
index 3ec3c2f..c8ab3e0 100644
--- a/pkg/analyzer/lib/src/dart/analysis/context_builder.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/context_builder.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart'
-    as macro;
 import 'package:analyzer/dart/analysis/context_builder.dart';
 import 'package:analyzer/dart/analysis/context_root.dart';
 import 'package:analyzer/dart/analysis/declared_variables.dart';
@@ -75,8 +73,7 @@
     FileContentCache? fileContentCache,
     UnlinkedUnitStore? unlinkedUnitStore,
     InfoDeclarationStore? infoDeclarationStore,
-    MacroKernelBuilder? macroKernelBuilder,
-    macro.MultiMacroExecutor? macroExecutor,
+    MacroSupport? macroSupport,
     OwnedFiles? ownedFiles,
   }) {
     // TODO(scheglov) Remove this, and make `sdkPath` required.
@@ -152,8 +149,7 @@
       fileContentCache: fileContentCache,
       unlinkedUnitStore: unlinkedUnitStore,
       infoDeclarationStore: infoDeclarationStore,
-      macroKernelBuilder: macroKernelBuilder,
-      macroExecutor: macroExecutor,
+      macroSupport: macroSupport,
       declaredVariables: declaredVariables,
       testView: retainDataForTesting ? AnalysisDriverTestView() : null,
       ownedFiles: ownedFiles,
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 5793439..6a1b0bb 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -5,8 +5,6 @@
 import 'dart:async';
 import 'dart:typed_data';
 
-import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart'
-    as macro;
 import 'package:analyzer/dart/analysis/declared_variables.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/ast/ast.dart';
@@ -139,10 +137,8 @@
   /// from file paths.
   final SourceFactory _sourceFactory;
 
-  final MacroKernelBuilder? macroKernelBuilder;
-
-  /// The instance of macro executor that is used for all macros.
-  final macro.MultiMacroExecutor? macroExecutor;
+  /// The support for executing macros.
+  final MacroSupport? macroSupport;
 
   /// The container, shared with other drivers within the same collection,
   /// into which all drivers record files ownership.
@@ -281,8 +277,7 @@
     required SourceFactory sourceFactory,
     required AnalysisOptionsImpl analysisOptions,
     required Packages packages,
-    this.macroKernelBuilder,
-    this.macroExecutor,
+    this.macroSupport,
     this.ownedFiles,
     this.analysisContext,
     FileContentCache? fileContentCache,
@@ -362,8 +357,7 @@
       analysisOptions: _analysisOptions,
       declaredVariables: declaredVariables,
       sourceFactory: _sourceFactory,
-      macroKernelBuilder: macroKernelBuilder,
-      macroExecutor: macroExecutor,
+      macroSupport: macroSupport,
       externalSummaries: _externalSummaries,
       fileSystemState: _fsState,
     );
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_context.dart b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
index 4ddf7bc..6acb303 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_context.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
@@ -5,8 +5,6 @@
 import 'dart:collection';
 import 'dart:typed_data';
 
-import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart'
-    as macro;
 import 'package:analyzer/dart/analysis/declared_variables.dart';
 import 'package:analyzer/dart/element/element.dart'
     show CompilationUnitElement, LibraryElement;
@@ -43,8 +41,7 @@
   final ByteStore byteStore;
   final InfoDeclarationStore infoDeclarationStore;
   final FileSystemState fileSystemState;
-  final MacroKernelBuilder? macroKernelBuilder;
-  final macro.MultiMacroExecutor? macroExecutor;
+  final MacroSupport? macroSupport;
   final SummaryDataStore store = SummaryDataStore();
 
   late final AnalysisContextImpl analysisContext;
@@ -62,8 +59,7 @@
     required AnalysisOptionsImpl analysisOptions,
     required DeclaredVariables declaredVariables,
     required SourceFactory sourceFactory,
-    required this.macroKernelBuilder,
-    required this.macroExecutor,
+    required this.macroSupport,
     required SummaryDataStore? externalSummaries,
   }) {
     analysisContext = AnalysisContextImpl(
@@ -76,6 +72,7 @@
       analysisContext,
       analysisSession,
       Reference.root(),
+      macroSupport,
     );
     if (externalSummaries != null) {
       for (var bundle in externalSummaries.bundles) {
@@ -185,7 +182,7 @@
             elementFactory: elementFactory,
             performance: OperationPerformanceImpl('link'),
             inputLibraries: cycle.libraries,
-            macroExecutor: macroExecutor,
+            macroExecutor: this.macroSupport?.executor,
           );
           librariesLinked += cycle.libraries.length;
         } catch (exception, stackTrace) {
@@ -216,33 +213,26 @@
         _addMacroAugmentations(cycle, bundleReader);
       }
 
-      final macroKernelBuilder = this.macroKernelBuilder;
-      if (macroKernelBuilder != null && macroLibraries.isNotEmpty) {
-        var macroKernelBytes = byteStore.get(cycle.macroKey);
-        if (macroKernelBytes == null) {
-          macroKernelBytes = await macroKernelBuilder.build(
+      // If we can compile to kernel, check if there are macros.
+      final macroSupport = this.macroSupport;
+      if (macroSupport is KernelMacroSupport && macroLibraries.isNotEmpty) {
+        var kernelBytes = byteStore.get(cycle.macroKey);
+        if (kernelBytes == null) {
+          kernelBytes = await macroSupport.builder.build(
             fileSystem: _MacroFileSystem(fileSystemState),
             libraries: macroLibraries,
           );
-          byteStore.putGet(cycle.macroKey, macroKernelBytes);
-          bytesPut += macroKernelBytes.length;
+          byteStore.putGet(cycle.macroKey, kernelBytes);
+          bytesPut += kernelBytes.length;
         } else {
-          bytesGet += macroKernelBytes.length;
+          bytesGet += kernelBytes.length;
         }
 
-        final macroExecutor = this.macroExecutor;
-        if (macroExecutor != null) {
-          var bundleMacroExecutor = BundleMacroExecutor(
-            macroExecutor: macroExecutor,
-            kernelBytes: macroKernelBytes,
-            libraries: cycle.libraries.map((e) => e.file.uri).toSet(),
-          );
-          for (var library in cycle.libraries) {
-            var libraryUri = library.file.uri;
-            var libraryElement = elementFactory.libraryOfUri2(libraryUri);
-            libraryElement.bundleMacroExecutor = bundleMacroExecutor;
-          }
-        }
+        elementFactory.addKernelMacroBundle(
+          macroSupport: macroSupport,
+          kernelBytes: kernelBytes,
+          libraries: cycle.libraries.map((e) => e.file.uri).toSet(),
+        );
       }
     }
 
diff --git a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
index b4bf694..0351e43 100644
--- a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
+++ b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
@@ -773,8 +773,7 @@
         fileSystemState: fsState!,
         sourceFactory: sourceFactory,
         externalSummaries: SummaryDataStore(),
-        macroExecutor: null,
-        macroKernelBuilder: null,
+        macroSupport: null,
         testData: testData?.libraryContext,
       );
 
diff --git a/pkg/analyzer/lib/src/summary2/linked_element_factory.dart b/pkg/analyzer/lib/src/summary2/linked_element_factory.dart
index c4ba9c2..7b9d796 100644
--- a/pkg/analyzer/lib/src/summary2/linked_element_factory.dart
+++ b/pkg/analyzer/lib/src/summary2/linked_element_factory.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:collection';
+import 'dart:typed_data';
 
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/src/context/context.dart';
@@ -12,6 +13,7 @@
 import 'package:analyzer/src/dart/resolver/scope.dart';
 import 'package:analyzer/src/summary2/bundle_reader.dart';
 import 'package:analyzer/src/summary2/export.dart';
+import 'package:analyzer/src/summary2/macro.dart';
 import 'package:analyzer/src/summary2/reference.dart';
 import 'package:analyzer/src/utilities/uri_cache.dart';
 import 'package:meta/meta.dart';
@@ -33,13 +35,14 @@
   AnalysisSessionImpl analysisSession;
   final Reference rootReference;
   final Map<Uri, LibraryReader> _libraryReaders = {};
-
+  final MacroSupport? macroSupport;
   bool isApplyingInformativeData = false;
 
   LinkedElementFactory(
     this.analysisContext,
     this.analysisSession,
     this.rootReference,
+    this.macroSupport,
   ) {
     ArgumentError.checkNotNull(analysisContext, 'analysisContext');
     ArgumentError.checkNotNull(analysisSession, 'analysisSession');
@@ -77,6 +80,26 @@
     addLibraries(bundle.libraryMap);
   }
 
+  /// Adds newly compiled kernel for a macro bundle.
+  void addKernelMacroBundle({
+    required KernelMacroSupport macroSupport,
+    required Uint8List kernelBytes,
+    required Set<Uri> libraries,
+  }) {
+    macroSupport.add(
+      kernelBytes: kernelBytes,
+      libraries: libraries,
+    );
+    // Check if elements of libraries are ready.
+    // This is the case when we have just linked them.
+    for (final uri in libraries) {
+      final element = rootReference['$uri']?.element;
+      if (element is LibraryElementImpl) {
+        _setMacroExecutorForLibrary(element);
+      }
+    }
+  }
+
   void addLibraries(Map<Uri, LibraryReader> libraries) {
     _libraryReaders.addAll(libraries);
   }
@@ -140,6 +163,7 @@
       librarySource: librarySource,
     );
     setLibraryTypeSystem(libraryElement);
+    _setMacroExecutorForLibrary(libraryElement);
     return libraryElement;
   }
 
@@ -244,6 +268,7 @@
     addToLogRing('[removeLibraries][uriSet: $uriSet][${StackTrace.current}]');
     for (final uri in uriSet) {
       _libraryReaders.remove(uri);
+      macroSupport?.removeLibrary(uri);
       final libraryReference = rootReference.removeChild('$uri');
       _disposeLibrary(libraryReference?.element);
     }
@@ -305,4 +330,10 @@
       libraryElement.bundleMacroExecutor?.dispose();
     }
   }
+
+  void _setMacroExecutorForLibrary(LibraryElementImpl element) {
+    final uri = element.source.uri;
+    final macroExecutor = macroSupport?.forLibrary(uri);
+    element.bundleMacroExecutor = macroExecutor;
+  }
 }
diff --git a/pkg/analyzer/lib/src/summary2/macro.dart b/pkg/analyzer/lib/src/summary2/macro.dart
index be975d5..e04c34e 100644
--- a/pkg/analyzer/lib/src/summary2/macro.dart
+++ b/pkg/analyzer/lib/src/summary2/macro.dart
@@ -2,6 +2,7 @@
 // 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' as io;
 import 'dart:isolate';
 import 'dart:typed_data';
 
@@ -11,23 +12,98 @@
     as isolated_executor;
 import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart'
     as macro;
+import 'package:_fe_analyzer_shared/src/macros/executor/process_executor.dart'
+    as process_executor;
 import 'package:_fe_analyzer_shared/src/macros/executor/serialization.dart'
     as macro;
 import 'package:analyzer/src/summary2/kernel_compilation_service.dart';
+import 'package:meta/meta.dart';
 import 'package:path/path.dart' as package_path;
 
-class BundleMacroExecutor {
-  final macro.MultiMacroExecutor macroExecutor;
+/// The interface for a bundle of macros.
+abstract class BundleMacroExecutor {
+  void dispose();
+
+  Future<macro.MacroInstanceIdentifier> instantiate({
+    required Uri libraryUri,
+    required String className,
+    required String constructorName,
+    required macro.Arguments arguments,
+  });
+}
+
+/// [BundleMacroExecutor] that runs pre-compiled native executables.
+class ExecutableBundleMacroExecutor extends BundleMacroExecutor {
+  final ExecutableMacroSupport support;
+  late final macro.ExecutorFactoryToken _executorFactoryToken;
+
+  ExecutableBundleMacroExecutor({
+    required this.support,
+    required io.File executable,
+    required Set<Uri> libraries,
+  }) {
+    _executorFactoryToken = support.executor.registerExecutorFactory(
+      () => process_executor.start(
+        macro.SerializationMode.byteData,
+        process_executor.CommunicationChannel.socket,
+        executable.path,
+      ),
+      libraries,
+    );
+  }
+
+  @override
+  void dispose() {
+    support.executor.unregisterExecutorFactory(_executorFactoryToken);
+  }
+
+  @override
+  Future<macro.MacroInstanceIdentifier> instantiate({
+    required Uri libraryUri,
+    required String className,
+    required String constructorName,
+    required macro.Arguments arguments,
+  }) async {
+    // TODO: Dispose of these instances using `macroExecutor.disposeMacro` once
+    // we are done with them.
+    return await support.executor
+        .instantiateMacro(libraryUri, className, constructorName, arguments);
+  }
+}
+
+/// [MacroSupport] that can runs macros from native executables.
+///
+/// It does not support compilation, because it happens outside.
+class ExecutableMacroSupport extends MacroSupport {
+  void add({
+    required io.File executable,
+    required Set<Uri> libraries,
+  }) {
+    final bundleExecutor = ExecutableBundleMacroExecutor(
+      support: this,
+      executable: executable,
+      libraries: libraries,
+    );
+
+    for (final libraryUri in libraries) {
+      _bundleExecutors[libraryUri] = bundleExecutor;
+    }
+  }
+}
+
+/// [BundleMacroExecutor] that runs macros from kernels.
+class KernelBundleMacroExecutor extends BundleMacroExecutor {
+  final KernelMacroSupport support;
   late final macro.ExecutorFactoryToken _executorFactoryToken;
   final Uint8List kernelBytes;
   Uri? _kernelUriCached;
 
-  BundleMacroExecutor({
-    required this.macroExecutor,
+  KernelBundleMacroExecutor({
+    required this.support,
     required Uint8List kernelBytes,
     required Set<Uri> libraries,
   }) : kernelBytes = Uint8List.fromList(kernelBytes) {
-    _executorFactoryToken = macroExecutor.registerExecutorFactory(
+    _executorFactoryToken = support.executor.registerExecutorFactory(
       () => isolated_executor.start(
         macro.SerializationMode.byteData,
         _kernelUri,
@@ -42,8 +118,9 @@
         (Isolate.current as dynamic).createUriForKernelBlob(kernelBytes) as Uri;
   }
 
+  @override
   void dispose() {
-    macroExecutor.unregisterExecutorFactory(_executorFactoryToken);
+    support.executor.unregisterExecutorFactory(_executorFactoryToken);
     final kernelUriCached = _kernelUriCached;
     if (kernelUriCached != null) {
       // ignore: avoid_dynamic_calls
@@ -52,9 +129,7 @@
     }
   }
 
-  /// Any macro must be instantiated using this method to guarantee that
-  /// the corresponding kernel was registered first. Although as it is now,
-  /// it is still possible to request an unrelated [libraryUri].
+  @override
   Future<macro.MacroInstanceIdentifier> instantiate({
     required Uri libraryUri,
     required String className,
@@ -63,8 +138,28 @@
   }) async {
     // TODO: Dispose of these instances using `macroExecutor.disposeMacro` once
     // we are done with them.
-    return await macroExecutor.instantiateMacro(
-        libraryUri, className, constructorName, arguments);
+    return await support.executor
+        .instantiateMacro(libraryUri, className, constructorName, arguments);
+  }
+}
+
+/// [MacroSupport] that can compile and run macros from kernels.
+class KernelMacroSupport extends MacroSupport {
+  final MacroKernelBuilder builder = MacroKernelBuilder();
+
+  void add({
+    required Uint8List kernelBytes,
+    required Set<Uri> libraries,
+  }) {
+    final bundleExecutor = KernelBundleMacroExecutor(
+      support: this,
+      kernelBytes: kernelBytes,
+      libraries: libraries,
+    );
+
+    for (final libraryUri in libraries) {
+      _bundleExecutors[libraryUri] = bundleExecutor;
+    }
   }
 }
 
@@ -134,6 +229,39 @@
   String get uriStr => uri.toString();
 }
 
+/// The interface for tracking macro executors for libraries.
+class MacroSupport {
+  /// The instance of macro executor that is used for all macros.
+  final macro.MultiMacroExecutor executor = macro.MultiMacroExecutor();
+
+  final Map<Uri, BundleMacroExecutor> _bundleExecutors = {};
+
+  /// Disposes the whole macro support, with all its executors.
+  @mustCallSuper
+  Future<void> dispose() async {
+    await executor.close();
+  }
+
+  /// Returns the executor registered for this library.
+  BundleMacroExecutor? forLibrary(Uri uri) {
+    return _bundleExecutors[uri];
+  }
+
+  /// Removes and disposes executors for all libraries.
+  void removeLibraries() {
+    for (final bundleExecutor in _bundleExecutors.values) {
+      bundleExecutor.dispose();
+    }
+    _bundleExecutors.clear();
+  }
+
+  /// Removes and disposes the executor for the library.
+  void removeLibrary(Uri uri) {
+    final bundleExecutor = _bundleExecutors.remove(uri);
+    bundleExecutor?.dispose();
+  }
+}
+
 /// [MacroFileEntry] for a file with overridden content.
 class _OverlayMacroFileEntry implements MacroFileEntry {
   @override
diff --git a/pkg/analyzer/lib/src/test_utilities/mock_packages.dart b/pkg/analyzer/lib/src/test_utilities/mock_packages.dart
index d08a7bb..6b7ae00 100644
--- a/pkg/analyzer/lib/src/test_utilities/mock_packages.dart
+++ b/pkg/analyzer/lib/src/test_utilities/mock_packages.dart
@@ -80,6 +80,7 @@
   /// Create a fake 'meta' package that can be used by tests.
   static void addMetaPackageFiles(Folder rootFolder) {
     var libFolder = rootFolder.getChildAssumingFolder('lib');
+    libFolder.create();
     libFolder.getChildAssumingFile('meta.dart').writeAsStringSync(r'''
 library meta;
 
@@ -268,6 +269,10 @@
   const _Sealed();
 }
 
+class _Virtual {
+  const _Virtual();
+}
+
 class _VisibleForOverriding {
   const _VisibleForOverriding();
 }
diff --git a/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart b/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
index e6b0ff8..703dbd1 100644
--- a/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
+++ b/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
@@ -17,6 +17,7 @@
 import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
 import 'package:analyzer/src/generated/sdk.dart';
 import 'package:analyzer/src/summary2/kernel_compilation_service.dart';
+import 'package:analyzer/src/summary2/macro.dart';
 import 'package:analyzer/src/test_utilities/mock_packages.dart';
 import 'package:analyzer/src/test_utilities/mock_sdk.dart';
 import 'package:analyzer/src/test_utilities/package_config_file_builder.dart';
@@ -136,6 +137,9 @@
   /// Optional summaries to provide for the collection.
   List<File>? librarySummaryFiles;
 
+  /// By default the kernel implementation is used, this can override it.
+  MacroSupport? macroSupport;
+
   AnalyzerStatePrinterConfiguration analyzerStatePrinterConfiguration =
       AnalyzerStatePrinterConfiguration();
 
@@ -166,6 +170,7 @@
       sdkSummaryPath: sdkSummaryFile?.path,
       librarySummaryPaths: librarySummaryFiles?.map((e) => e.path).toList(),
       updateAnalysisOptions2: updateAnalysisOptions,
+      macroSupport: macroSupport,
     );
 
     _analysisContextCollection = collection;
diff --git a/pkg/analyzer/test/src/summary/macro_test.dart b/pkg/analyzer/test/src/summary/macro_test.dart
index e087839..6deb4b1 100644
--- a/pkg/analyzer/test/src/summary/macro_test.dart
+++ b/pkg/analyzer/test/src/summary/macro_test.dart
@@ -2,11 +2,22 @@
 // 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' as io;
+import 'dart:typed_data';
+
+import 'package:_fe_analyzer_shared/src/macros/bootstrap.dart' as macro;
+import 'package:_fe_analyzer_shared/src/macros/executor/serialization.dart'
+    as macro;
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/file_system/physical_file_system.dart';
 import 'package:analyzer/src/dart/element/element.dart';
+import 'package:analyzer/src/summary2/macro.dart';
 import 'package:analyzer/src/summary2/macro_application_error.dart';
+import 'package:analyzer/src/test_utilities/mock_packages.dart';
 import 'package:analyzer/src/test_utilities/package_config_file_builder.dart';
+import 'package:path/path.dart' as package_path;
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -1437,9 +1448,24 @@
 }
 
 abstract class MacroTypesTest extends MacroElementsBaseTest {
+  final List<io.Directory> _ioDirectoriesToDelete = [];
+
   @override
   bool get retainDataForTesting => true;
 
+  @override
+  Future<void> tearDown() async {
+    for (final directory in _ioDirectoriesToDelete) {
+      try {
+        directory.deleteSync(
+          recursive: true,
+        );
+      } catch (_) {}
+    }
+
+    return super.tearDown();
+  }
+
   test_application_newInstance_withoutPrefix() async {
     newFile('$testPackageLibPath/a.dart', r'''
 import 'dart:async';
@@ -1643,6 +1669,186 @@
 ''');
   }
 
+  test_executable() async {
+    // We use AOT executables only on Linux.
+    if (resourceProvider.pathContext.style != package_path.Style.posix) {
+      return;
+    }
+
+    // No need to verify reading elements for this test.
+    if (!keepLinkingLibraries) {
+      return;
+    }
+
+    const macroCode = r'''
+import 'dart:async';
+import 'package:_fe_analyzer_shared/src/macros/api.dart';
+
+macro class MyMacro implements ClassTypesMacro {
+  const MyMacro();
+
+  FutureOr<void> buildTypesForClass(clazz, builder) {
+    builder.declareType(
+      'MyClass',
+      DeclarationCode.fromString('class MyClass {}'),
+    );
+  }
+}
+''';
+
+    // Compile the macro to executable.
+    io.File macroExecutable;
+    {
+      final macroMainContent = macro.bootstrapMacroIsolate(
+        {
+          'package:test/a.dart': {
+            'MyMacro': ['']
+          },
+        },
+        macro.SerializationMode.byteData,
+      );
+
+      final tempCompileDirectory =
+          io.Directory.systemTemp.createTempSync('dartAnalyzerMacro');
+      _ioDirectoriesToDelete.add(tempCompileDirectory);
+
+      final fileSystem = PhysicalResourceProvider.INSTANCE;
+      final compileRoot = fileSystem.getFolder(tempCompileDirectory.path);
+
+      final testRoot = compileRoot.getChildAssumingFolder('test');
+      testRoot.newFile('lib/a.dart').writeAsStringSync(macroCode);
+
+      final testBin = testRoot.getChildAssumingFolder('bin');
+      final testMain = testBin.newFile('main.dart');
+      testMain.writeAsStringSync(macroMainContent);
+
+      final metaDir = compileRoot.getChildAssumingFolder('meta');
+      MockPackages.addMetaPackageFiles(metaDir);
+
+      MacrosEnvironment.instance.packageSharedFolder.copyTo(compileRoot);
+
+      compileRoot
+          .newFile('.dart_tool/package_config.json')
+          .writeAsStringSync(r'''
+{
+  "configVersion": 2,
+  "packages": [
+    {
+      "name": "test",
+      "rootUri": "../test",
+      "packageUri": "lib/"
+    },
+    {
+      "name": "_fe_analyzer_shared",
+      "rootUri": "../_fe_analyzer_shared",
+      "packageUri": "lib/"
+    },
+    {
+      "name": "meta",
+      "rootUri": "../meta",
+      "packageUri": "lib/"
+    }
+  ]
+}
+''');
+
+      final process = await io.Process.start(
+        io.Platform.executable,
+        ['compile', 'exe', '--enable-experiment=macros', testMain.path],
+      );
+
+      final exitCode = await process.exitCode;
+      expect(exitCode, isZero);
+
+      final executable = testBin.getChildAssumingFile('main.exe');
+      expect(executable.exists, isTrue);
+
+      // Convert to io.File
+      macroExecutable = io.File(executable.path);
+    }
+
+    // Build the summary for `a.dart`, with the macro.
+    // We always have summaries for libraries with macro executable.
+    final Uint8List aBundleBytes;
+    {
+      final a = newFile('$testPackageLibPath/a.dart', macroCode);
+
+      // Disable compilation to kernel.
+      macroSupport = ExecutableMacroSupport();
+
+      final analysisDriver = driverFor(a);
+      aBundleBytes = await analysisDriver.buildPackageBundle(
+        uriList: [
+          Uri.parse('package:_fe_analyzer_shared/src/macros/api.dart'),
+          Uri.parse('package:test/a.dart'),
+        ],
+      );
+
+      // We should not read the file anyway, but we make it explicit.
+      a.delete();
+    }
+
+    await disposeAnalysisContextCollection();
+    useEmptyByteStore();
+
+    // Configure summaries.
+    {
+      sdkSummaryFile = await writeSdkSummary();
+
+      final aBundleFile = getFile('/home/summaries/a.sum');
+      aBundleFile.writeAsBytesSync(aBundleBytes);
+      librarySummaryFiles = [aBundleFile];
+    }
+
+    // Configure the macro executor.
+    macroSupport = ExecutableMacroSupport()
+      ..add(
+        executable: macroExecutable,
+        libraries: {
+          Uri.parse('package:test/a.dart'),
+        },
+      );
+
+    // Verify that we can use the executable to run the macro.
+    {
+      var library = await buildLibrary(r'''
+import 'a.dart';
+
+@MyMacro()
+class A {}
+''');
+      configuration
+        ..withConstructors = false
+        ..withMetadata = false
+        ..withReferences = true;
+      checkElementText(library, r'''
+library
+  reference: self
+  imports
+    package:test/a.dart
+  definingUnit
+    reference: self
+    classes
+      class A @35
+        reference: self::@class::A
+  augmentationImports
+    package:test/test.macro.dart
+      reference: self::@augmentation::package:test/test.macro.dart
+      macroGeneratedCode
+---
+library augment 'test.dart';
+
+class MyClass {}
+---
+      definingUnit
+        reference: self::@augmentation::package:test/test.macro.dart
+        classes
+          class MyClass @36
+            reference: self::@augmentation::package:test/test.macro.dart::@class::MyClass
+''');
+    }
+  }
+
   test_imports_class() async {
     useEmptyByteStore();
 
@@ -2240,3 +2446,11 @@
     expect(actual, isEmpty);
   }
 }
+
+extension on Folder {
+  File newFile(String relPath) {
+    final file = getChildAssumingFile(relPath);
+    file.parent.create();
+    return file;
+  }
+}