blob: 55ea8bda1d1dd2df3b358c175ee0685246e1baaf [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.
/// Generates a Dart program for a given macro, which can be compiled and then
/// passed as a precompiled kernel file to `MacroExecutor.loadMacro`.
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());
}
const String _importMarker = '{{IMPORT}}';
const String _macroConstructorEntriesMarker = '{{MACRO_CONSTRUCTOR_ENTRIES}}';
const String template = '''
import 'dart:async';
import 'dart:isolate';
import 'package:_fe_analyzer_shared/src/macros/executor_shared/response_impls.dart';
import 'package:_fe_analyzer_shared/src/macros/executor_shared/serialization.dart';
import 'package:_fe_analyzer_shared/src/macros/executor_shared/protocol.dart';
import 'package:_fe_analyzer_shared/src/macros/executor.dart';
import 'package:_fe_analyzer_shared/src/macros/api.dart';
$_importMarker
/// Entrypoint to be spawned with [Isolate.spawnUri].
///
/// Supports the client side of the macro expansion protocol.
void main(_, SendPort sendPort) {
withSerializationMode(SerializationMode.client, () {
ReceivePort receivePort = new ReceivePort();
sendPort.send(receivePort.sendPort);
receivePort.listen((message) async {
var deserializer = JsonDeserializer(message as Iterable<Object?>)
..moveNext();
var type = MessageType.values[deserializer.expectNum()];
var serializer = JsonSerializer();
switch (type) {
case MessageType.instantiateMacroRequest:
var request = InstantiateMacroRequest.deserialize(deserializer);
(await _instantiateMacro(request)).serialize(serializer);
break;
case MessageType.executeDefinitionsPhaseRequest:
var request = ExecuteDefinitionsPhaseRequest.deserialize(
deserializer,
ClientTypeResolver(),
ClientClassIntrospector(),
ClientTypeDeclarationsResolver());
(await _executeDefinitionsPhase(request)).serialize(serializer);
break;
default:
throw new StateError('Unhandled event type \$type');
}
sendPort.send(serializer.result);
});
});
}
/// Maps macro identifiers to constructors.
final _macroConstructors = <MacroClassIdentifierImpl, Map<String, Macro Function()>>{
$_macroConstructorEntriesMarker
};
/// Maps macro instance identifiers to instances.
final _macroInstances = <MacroInstanceIdentifierImpl, Macro>{};
/// Handles [InstantiateMacroRequest]s.
Future<SerializableResponse> _instantiateMacro(
InstantiateMacroRequest request) async {
try {
var constructors = _macroConstructors[request.macroClass];
if (constructors == null) {
throw new ArgumentError('Unrecognized macro class \${request.macroClass}');
}
var constructor = constructors[request.constructorName];
if (constructor == null) {
throw new ArgumentError(
'Unrecognized constructor name "\${request.constructorName}" for '
'macro class "\${request.macroClass}".');
}
var instance = Function.apply(constructor, request.arguments.positional, {
for (MapEntry<String, Object?> entry in request.arguments.named.entries)
new Symbol(entry.key): entry.value,
}) as Macro;
var identifier = new MacroInstanceIdentifierImpl();
_macroInstances[identifier] = instance;
return new SerializableResponse(
responseType: MessageType.macroInstanceIdentifier,
response: identifier,
requestId: request.id);
} catch (e) {
return new SerializableResponse(
responseType: MessageType.error,
error: e.toString(),
requestId: request.id);
}
}
Future<SerializableResponse> _executeDefinitionsPhase(
ExecuteDefinitionsPhaseRequest request) async {
try {
Macro? instance = _macroInstances[request.macro];
if (instance == null) {
throw new StateError('Unrecognized macro instance \${request.macro}\\n'
'Known instances: \$_macroInstances)');
}
Declaration declaration = request.declaration;
if (instance is FunctionDefinitionMacro &&
declaration is FunctionDeclaration) {
FunctionDefinitionBuilderImpl builder = new FunctionDefinitionBuilderImpl(
declaration,
request.typeResolver,
request.typeDeclarationResolver,
request.classIntrospector);
await instance.buildDefinitionForFunction(declaration, builder);
return new SerializableResponse(
responseType: MessageType.macroExecutionResult,
response: builder.result,
requestId: request.id);
} else {
throw new UnsupportedError(
('Only FunctionDefinitionMacros are supported currently'));
}
} catch (e) {
return new SerializableResponse(
responseType: MessageType.error,
error: e.toString(),
requestId: request.id);
}
}
''';