blob: 27130c3e3953e5e984a07e4af93d2cb67b9dd11a [file] [log] [blame]
// 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:isolate';
import 'dart:typed_data';
import 'package:_fe_analyzer_shared/src/macros/api.dart' as macro;
import 'package:_fe_analyzer_shared/src/macros/bootstrap.dart' as macro;
import 'package:_fe_analyzer_shared/src/macros/executor.dart' as macro;
import 'package:_fe_analyzer_shared/src/macros/executor/isolated_executor.dart'
as isolated_executor;
import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart'
as macro;
import 'package:_fe_analyzer_shared/src/macros/executor/serialization.dart'
as macro;
import 'package:analyzer/src/summary2/kernel_compilation_service.dart';
import 'package:path/path.dart' as package_path;
class BundleMacroExecutor {
final macro.MultiMacroExecutor macroExecutor;
late final macro.ExecutorFactoryToken _executorFactoryToken;
final Uint8List kernelBytes;
Uri? _kernelUriCached;
BundleMacroExecutor({
required this.macroExecutor,
required Uint8List kernelBytes,
required Set<Uri> libraries,
}) : kernelBytes = Uint8List.fromList(kernelBytes) {
_executorFactoryToken = macroExecutor.registerExecutorFactory(
() => isolated_executor.start(
macro.SerializationMode.byteDataServer,
_kernelUri,
),
libraries,
);
}
Uri get _kernelUri {
return _kernelUriCached ??=
// ignore: avoid_dynamic_calls
(Isolate.current as dynamic).createUriForKernelBlob(kernelBytes);
}
void dispose() {
macroExecutor.unregisterExecutorFactory(_executorFactoryToken);
final kernelUriCached = _kernelUriCached;
if (kernelUriCached != null) {
// ignore: avoid_dynamic_calls
(Isolate.current as dynamic).unregisterKernelBlobUri(kernelUriCached);
_kernelUriCached = null;
}
}
Future<MacroClassInstance> instantiate({
required Uri libraryUri,
required String className,
required String constructorName,
required macro.Arguments arguments,
required macro.Declaration declaration,
required macro.IdentifierResolver identifierResolver,
}) async {
var instanceIdentifier = await macroExecutor.instantiateMacro(
libraryUri, className, constructorName, arguments);
return MacroClassInstance._(
this, identifierResolver, declaration, instanceIdentifier);
}
}
/// Implementation of [MacroKernelBuilder] using `frontend_server`.
class FrontEndServerMacroKernelBuilder implements MacroKernelBuilder {
@override
Future<Uint8List> build({
required MacroFileSystem fileSystem,
required List<MacroLibrary> libraries,
}) async {
final macroMainContent = macro.bootstrapMacroIsolate(
{
for (final library in libraries)
library.uri.toString(): {
for (final c in library.classes) c.name: c.constructors
},
},
macro.SerializationMode.byteDataClient,
);
final macroMainPath = '${libraries.first.path}.macro';
final overlayFileSystem = _OverlayMacroFileSystem(fileSystem);
overlayFileSystem.overlays[macroMainPath] = macroMainContent;
return KernelCompilationService.compile(
fileSystem: overlayFileSystem,
path: macroMainPath,
);
}
}
class MacroClass {
final String name;
final List<String> constructors;
MacroClass({
required this.name,
required this.constructors,
});
}
class MacroClassInstance {
final BundleMacroExecutor _bundleExecutor;
final macro.IdentifierResolver _identifierResolver;
final macro.Declaration _declaration;
final macro.MacroInstanceIdentifier _instanceIdentifier;
MacroClassInstance._(
this._bundleExecutor,
this._identifierResolver,
this._declaration,
this._instanceIdentifier,
);
Future<macro.MacroExecutionResult> executeTypesPhase() async {
macro.MacroExecutor executor = _bundleExecutor.macroExecutor;
return await executor.executeTypesPhase(
_instanceIdentifier, _declaration, _identifierResolver);
}
}
abstract class MacroFileEntry {
String get content;
/// When CFE searches for `package_config.json` we need to check this.
bool get exists;
}
abstract class MacroFileSystem {
/// Used to convert `file:` URIs into paths.
package_path.Context get pathContext;
MacroFileEntry getFile(String path);
}
abstract class MacroKernelBuilder {
Future<Uint8List> build({
required MacroFileSystem fileSystem,
required List<MacroLibrary> libraries,
});
}
class MacroLibrary {
final Uri uri;
final String path;
final List<MacroClass> classes;
MacroLibrary({
required this.uri,
required this.path,
required this.classes,
});
String get uriStr => uri.toString();
}
/// [MacroFileEntry] for a file with overridden content.
class _OverlayMacroFileEntry implements MacroFileEntry {
@override
final String content;
_OverlayMacroFileEntry(this.content);
@override
bool get exists => true;
}
/// Wrapper around another [MacroFileSystem] that can be configured to
/// provide (or override) content of files.
class _OverlayMacroFileSystem implements MacroFileSystem {
final MacroFileSystem _fileSystem;
/// The mapping from the path to the file content.
final Map<String, String> overlays = {};
_OverlayMacroFileSystem(this._fileSystem);
@override
package_path.Context get pathContext => _fileSystem.pathContext;
@override
MacroFileEntry getFile(String path) {
final overlayContent = overlays[path];
if (overlayContent != null) {
return _OverlayMacroFileEntry(overlayContent);
}
return _fileSystem.getFile(path);
}
}