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; + } +}