Create a macro executor that runs in a separate isolate group with a precompiled dill.
Also gets a start on the serialization protocol and implements a chunk of it.
Change-Id: Ia16136351a868858d679fe4bae96318d9126d3a2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/226966
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Auto-Submit: Jake Macdonald <jakemac@google.com>
Commit-Queue: Jake Macdonald <jakemac@google.com>
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart b/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart
index 89f169f..1486d1c 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart
@@ -7,10 +7,14 @@
/// The base class representing an arbitrary chunk of Dart code, which may or
/// may not be syntactically or semantically valid yet.
class Code {
- /// All the chunks of [Code] or raw [String]s that comprise this [Code]
- /// object.
+ /// All the chunks of [Code], raw [String]s, or [TypeAnnotation]s that
+ /// comprise this [Code] object.
final List<Object> parts;
+ /// Can be used to more efficiently detect the kind of code, avoiding is
+ /// checks and enabling switch statements.
+ CodeKind get kind => CodeKind.raw;
+
Code.fromString(String code) : parts = [code];
Code.fromParts(this.parts);
@@ -18,6 +22,9 @@
/// A piece of code representing a syntactically valid declaration.
class DeclarationCode extends Code {
+ @override
+ CodeKind get kind => CodeKind.declaration;
+
DeclarationCode.fromString(String code) : super.fromString(code);
DeclarationCode.fromParts(List<Object> parts) : super.fromParts(parts);
@@ -27,6 +34,9 @@
///
/// Should not include any trailing commas,
class ElementCode extends Code {
+ @override
+ CodeKind get kind => CodeKind.element;
+
ElementCode.fromString(String code) : super.fromString(code);
ElementCode.fromParts(List<Object> parts) : super.fromParts(parts);
@@ -34,6 +44,9 @@
/// A piece of code representing a syntactically valid expression.
class ExpressionCode extends Code {
+ @override
+ CodeKind get kind => CodeKind.expression;
+
ExpressionCode.fromString(String code) : super.fromString(code);
ExpressionCode.fromParts(List<Object> parts) : super.fromParts(parts);
@@ -46,6 +59,9 @@
///
/// Both arrow and block function bodies are allowed.
class FunctionBodyCode extends Code {
+ @override
+ CodeKind get kind => CodeKind.functionBody;
+
FunctionBodyCode.fromString(String code) : super.fromString(code);
FunctionBodyCode.fromParts(List<Object> parts) : super.fromParts(parts);
@@ -53,6 +69,9 @@
/// A piece of code representing a syntactically valid identifier.
class IdentifierCode extends Code {
+ @override
+ CodeKind get kind => CodeKind.identifier;
+
IdentifierCode.fromString(String code) : super.fromString(code);
IdentifierCode.fromParts(List<Object> parts) : super.fromParts(parts);
@@ -62,6 +81,9 @@
///
/// This should not include any trailing commas.
class NamedArgumentCode extends Code {
+ @override
+ CodeKind get kind => CodeKind.namedArgument;
+
NamedArgumentCode.fromString(String code) : super.fromString(code);
NamedArgumentCode.fromParts(List<Object> parts) : super.fromParts(parts);
@@ -77,6 +99,9 @@
/// construct and combine these together in a way that creates valid parameter
/// lists.
class ParameterCode extends Code {
+ @override
+ CodeKind get kind => CodeKind.parameter;
+
ParameterCode.fromString(String code) : super.fromString(code);
ParameterCode.fromParts(List<Object> parts) : super.fromParts(parts);
@@ -86,6 +111,9 @@
///
/// Should always end with a semicolon.
class StatementCode extends Code {
+ @override
+ CodeKind get kind => CodeKind.statement;
+
StatementCode.fromString(String code) : super.fromString(code);
StatementCode.fromParts(List<Object> parts) : super.fromParts(parts);
@@ -102,3 +130,15 @@
last,
];
}
+
+enum CodeKind {
+ raw,
+ declaration,
+ element,
+ expression,
+ functionBody,
+ identifier,
+ namedArgument,
+ parameter,
+ statement,
+}
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart b/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart
index e892296..318219b 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart
@@ -14,6 +14,10 @@
/// A [Code] object representation of this type annotation.
Code get code;
+
+ /// Allows you to check the kind of a [TypeAnnotation] in a switch statement,
+ /// and without `is` checks.
+ TypeAnnotationKind get kind;
}
/// The base class for function type declarations.
@@ -29,6 +33,9 @@
/// The type parameters for this function.
Iterable<TypeParameterDeclaration> get typeParameters;
+
+ @override
+ TypeAnnotationKind get kind => TypeAnnotationKind.functionType;
}
/// An unresolved reference to a type.
@@ -41,6 +48,9 @@
/// The type arguments, if applicable.
Iterable<TypeAnnotation> get typeArguments;
+
+ @override
+ TypeAnnotationKind get kind => TypeAnnotationKind.namedType;
}
/// The interface representing a resolved type.
@@ -65,9 +75,15 @@
abstract class Declaration {
/// The name of this declaration.
String get name;
+
+ /// Allows you to check the kind of a [Declaration] in a switch statement,
+ /// and without `is` checks.
+ DeclarationKind get kind;
}
/// A declaration that defines a new type in the program.
+///
+/// See subtypes [ClassDeclaration] and [TypeAliasDeclaration].
abstract class TypeDeclaration implements Declaration {
/// The type parameters defined for this type declaration.
Iterable<TypeParameterDeclaration> get typeParameters;
@@ -89,6 +105,9 @@
/// Information about fields, methods, and constructors must be retrieved from
/// the `builder` objects.
abstract class ClassDeclaration implements TypeDeclaration {
+ @override
+ DeclarationKind get kind => DeclarationKind.clazz;
+
/// Whether this class has an `abstract` modifier.
bool get isAbstract;
@@ -108,8 +127,20 @@
Iterable<TypeParameterDeclaration> get typeParameters;
}
+/// Type alias introspection information.
+abstract class TypeAliasDeclaration extends TypeDeclaration {
+ @override
+ DeclarationKind get kind => DeclarationKind.typeAlias;
+
+ /// The type annotation this is an alias for.
+ TypeAnnotation get type;
+}
+
/// Function introspection information.
abstract class FunctionDeclaration implements Declaration {
+ @override
+ DeclarationKind get kind => DeclarationKind.function;
+
/// Whether this function has an `abstract` modifier.
bool get isAbstract;
@@ -137,6 +168,9 @@
/// Method introspection information.
abstract class MethodDeclaration implements FunctionDeclaration {
+ @override
+ DeclarationKind get kind => DeclarationKind.method;
+
/// The class that defines this method.
TypeAnnotation get definingClass;
}
@@ -149,6 +183,9 @@
/// Variable introspection information.
abstract class VariableDeclaration implements Declaration {
+ @override
+ DeclarationKind get kind => DeclarationKind.variable;
+
/// Whether this function has an `abstract` modifier.
bool get isAbstract;
@@ -164,12 +201,18 @@
/// Field introspection information ..
abstract class FieldDeclaration implements VariableDeclaration {
+ @override
+ DeclarationKind get kind => DeclarationKind.field;
+
/// The class that defines this method.
TypeAnnotation get definingClass;
}
/// Parameter introspection information.
abstract class ParameterDeclaration implements Declaration {
+ @override
+ DeclarationKind get kind => DeclarationKind.parameter;
+
/// The type of this parameter.
TypeAnnotation get type;
@@ -187,6 +230,28 @@
/// Type parameter introspection information.
abstract class TypeParameterDeclaration implements Declaration {
+ @override
+ DeclarationKind get kind => DeclarationKind.typeParameter;
+
/// The bounds for this type parameter, if it has any.
TypeAnnotation? get bounds;
}
+
+// The kinds of type declarations.
+enum DeclarationKind {
+ clazz,
+ constructor,
+ field,
+ function,
+ method,
+ parameter,
+ typeAlias,
+ typeParameter,
+ variable,
+}
+
+// The kinds of type annotations.
+enum TypeAnnotationKind {
+ namedType,
+ functionType,
+}
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart b/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
new file mode 100644
index 0000000..55ea8bd
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
@@ -0,0 +1,143 @@
+// 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);
+ }
+}
+''';
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart
index d4d248d..38a9f4b 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart
@@ -3,13 +3,17 @@
// BSD-style license that can be found in the LICENSE file.
import 'api.dart';
+import 'bootstrap.dart'; // For doc comments only.
+import 'executor_shared/serialization.dart';
/// Exposes a platform specific [MacroExecutor], through a top level
/// `Future<MacroExecutor> start()` function.
///
/// TODO: conditionally load isolate_mirrors_executor.dart once conditional
/// imports of mirrors are supported in AOT (issue #48057).
-import 'fake_executor/fake_executor.dart' as executor_impl show start;
+import 'fake_executor/fake_executor.dart'
+ if (dart.library.isolate) 'isolated_executor/isolated_executor.dart'
+ as executor_impl show start;
/// The interface used by Dart language implementations, in order to load
/// and execute macros, as well as produce library augmentations from those
@@ -22,6 +26,9 @@
/// Returns a platform specific [MacroExecutor]. On unsupported platforms this
/// will be a fake executor object, which will throw an [UnsupportedError] if
/// used.
+ ///
+ /// Note that some implementations will also require calls to [loadMacro]
+ /// to pass a `precompiledKernelUri`.
static Future<MacroExecutor> start() => executor_impl.start();
/// Invoked when an implementation discovers a new macro definition in a
@@ -32,8 +39,17 @@
/// [MacroInstanceIdentifier]s given for this macro will be invalid after
/// that point and should be discarded.
///
+ /// The [precompiledKernelUri] if passed must point to a kernel program for
+ /// the given macro. A bootstrap Dart program can be generated with the
+ /// [bootstrapMacroIsolate] function, and the result should be compiled to
+ /// kernel and passed here.
+ ///
+ /// Some implementations may require [precompiledKernelUri] to be passed, and
+ /// will throw an [UnsupportedError] if it is not.
+ ///
/// Throws an exception if the macro fails to load.
- Future<MacroClassIdentifier> loadMacro(Uri library, String name);
+ Future<MacroClassIdentifier> loadMacro(Uri library, String name,
+ {Uri? precompiledKernelUri});
/// Creates an instance of [macroClass] in the executor, and returns an
/// identifier for that instance.
@@ -81,31 +97,143 @@
///
/// All argument instances must be of type [Code] or a built-in value type that
/// is serializable (num, bool, String, null, etc).
-class Arguments {
+class Arguments implements Serializable {
final List<Object?> positional;
final Map<String, Object?> named;
Arguments(this.positional, this.named);
+
+ factory Arguments.deserialize(Deserializer deserializer) {
+ deserializer
+ ..moveNext()
+ ..expectList();
+ List<Object?> positionalArgs = [
+ for (bool hasNext = deserializer.moveNext();
+ hasNext;
+ hasNext = deserializer.moveNext())
+ _deserializeArg(deserializer, alreadyMoved: true),
+ ];
+ deserializer
+ ..moveNext()
+ ..expectList();
+ Map<String, Object?> namedArgs = {
+ for (bool hasNext = deserializer.moveNext();
+ hasNext;
+ hasNext = deserializer.moveNext())
+ deserializer.expectString(): _deserializeArg(deserializer),
+ };
+ return new Arguments(positionalArgs, namedArgs);
+ }
+
+ static Object? _deserializeArg(Deserializer deserializer,
+ {bool alreadyMoved = false}) {
+ if (!alreadyMoved) deserializer.moveNext();
+ _ArgumentKind kind = _ArgumentKind.values[deserializer.expectNum()];
+ switch (kind) {
+ case _ArgumentKind.nil:
+ return null;
+ case _ArgumentKind.string:
+ deserializer.moveNext();
+ return deserializer.expectString();
+ case _ArgumentKind.bool:
+ deserializer.moveNext();
+ return deserializer.expectBool();
+ case _ArgumentKind.num:
+ deserializer.moveNext();
+ return deserializer.expectNum();
+ case _ArgumentKind.list:
+ deserializer.moveNext();
+ deserializer.expectList();
+ return [
+ for (bool hasNext = deserializer.moveNext();
+ hasNext;
+ hasNext = deserializer.moveNext())
+ _deserializeArg(deserializer, alreadyMoved: true),
+ ];
+ case _ArgumentKind.map:
+ deserializer.moveNext();
+ deserializer.expectList();
+ return {
+ for (bool hasNext = deserializer.moveNext();
+ hasNext;
+ hasNext = deserializer.moveNext())
+ _deserializeArg(deserializer, alreadyMoved: true):
+ _deserializeArg(deserializer),
+ };
+ }
+ }
+
+ void serialize(Serializer serializer) {
+ serializer.startList();
+ for (Object? arg in positional) {
+ _serializeArg(arg, serializer);
+ }
+ serializer.endList();
+
+ serializer.startList();
+ for (MapEntry<String, Object?> arg in named.entries) {
+ serializer.addString(arg.key);
+ _serializeArg(arg.value, serializer);
+ }
+ serializer.endList();
+ }
+
+ static void _serializeArg(Object? arg, Serializer serializer) {
+ if (arg == null) {
+ serializer.addNum(_ArgumentKind.nil.index);
+ } else if (arg is String) {
+ serializer
+ ..addNum(_ArgumentKind.string.index)
+ ..addString(arg);
+ } else if (arg is num) {
+ serializer
+ ..addNum(_ArgumentKind.num.index)
+ ..addNum(arg);
+ } else if (arg is bool) {
+ serializer
+ ..addNum(_ArgumentKind.bool.index)
+ ..addBool(arg);
+ } else if (arg is List) {
+ serializer
+ ..addNum(_ArgumentKind.list.index)
+ ..startList();
+ for (Object? item in arg) {
+ _serializeArg(item, serializer);
+ }
+ serializer.endList();
+ } else if (arg is Map) {
+ serializer
+ ..addNum(_ArgumentKind.map.index)
+ ..startList();
+ for (MapEntry<Object?, Object?> entry in arg.entries) {
+ _serializeArg(entry.key, serializer);
+ _serializeArg(entry.value, serializer);
+ }
+ serializer.endList();
+ } else {
+ throw new UnsupportedError('Unsupported argument type $arg');
+ }
+ }
}
/// An opaque identifier for a macro class, retrieved by
/// [MacroExecutor.loadMacro].
///
/// Used to execute or reload this macro in the future.
-abstract class MacroClassIdentifier {}
+abstract class MacroClassIdentifier implements Serializable {}
/// An opaque identifier for an instance of a macro class, retrieved by
/// [MacroExecutor.instantiateMacro].
///
/// Used to execute or reload this macro in the future.
-abstract class MacroInstanceIdentifier {}
+abstract class MacroInstanceIdentifier implements Serializable {}
/// A summary of the results of running a macro in a given phase.
///
/// All modifications are expressed in terms of library augmentation
/// declarations.
-abstract class MacroExecutionResult {
+abstract class MacroExecutionResult implements Serializable {
/// Any library imports that should be added to support the code used in
/// the augmentations.
Iterable<DeclarationCode> get imports;
@@ -125,3 +253,6 @@
/// This phase allows augmenting existing declarations.
definitions,
}
+
+/// Used for serializing and deserializing arguments.
+enum _ArgumentKind { string, bool, num, list, map, nil }
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/introspection_impls.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/introspection_impls.dart
new file mode 100644
index 0000000..e237127
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/introspection_impls.dart
@@ -0,0 +1,181 @@
+// Copyright (c) 2021, 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 '../api.dart';
+
+abstract class TypeAnnotationImpl implements TypeAnnotation {
+ final bool isNullable;
+
+ TypeAnnotationImpl({required this.isNullable});
+}
+
+class NamedTypeAnnotationImpl extends TypeAnnotationImpl
+ implements NamedTypeAnnotation {
+ @override
+ Code get code => new Code.fromParts([
+ name,
+ if (typeArguments.isNotEmpty) ...[
+ '<',
+ for (TypeAnnotation arg in typeArguments) ...[arg, ','],
+ '>',
+ ],
+ if (isNullable) '?',
+ ]);
+
+ @override
+ final String name;
+
+ @override
+ final List<TypeAnnotation> typeArguments;
+
+ @override
+ TypeAnnotationKind get kind => TypeAnnotationKind.namedType;
+
+ NamedTypeAnnotationImpl({
+ required bool isNullable,
+ required this.name,
+ required this.typeArguments,
+ }) : super(isNullable: isNullable);
+}
+
+class FunctionTypeAnnotationImpl extends TypeAnnotationImpl
+ implements FunctionTypeAnnotation {
+ @override
+ Code get code => new Code.fromParts([
+ returnType,
+ 'Function',
+ if (typeParameters.isNotEmpty) ...[
+ '<',
+ for (TypeParameterDeclaration arg in typeParameters) ...[
+ arg.name,
+ if (arg.bounds != null) ...[' extends ', arg.bounds!],
+ ','
+ ],
+ '>',
+ ],
+ '(',
+ for (ParameterDeclaration positional in positionalParameters) ...[
+ positional.type,
+ ' ${positional.name}',
+ ],
+ if (namedParameters.isNotEmpty) ...[
+ '{',
+ for (ParameterDeclaration named in namedParameters) ...[
+ named.type,
+ ' ${named.name}',
+ ],
+ '}',
+ ],
+ ')',
+ if (isNullable) '?',
+ ]);
+
+ @override
+ final List<ParameterDeclaration> namedParameters;
+
+ @override
+ final List<ParameterDeclaration> positionalParameters;
+
+ @override
+ final TypeAnnotation returnType;
+
+ @override
+ final List<TypeParameterDeclaration> typeParameters;
+
+ @override
+ TypeAnnotationKind get kind => TypeAnnotationKind.functionType;
+
+ FunctionTypeAnnotationImpl({
+ required bool isNullable,
+ required this.namedParameters,
+ required this.positionalParameters,
+ required this.returnType,
+ required this.typeParameters,
+ }) : super(isNullable: isNullable);
+}
+
+class ParameterDeclarationImpl implements ParameterDeclaration {
+ @override
+ final String name;
+
+ @override
+ final Code? defaultValue;
+
+ @override
+ final bool isNamed;
+
+ @override
+ final bool isRequired;
+
+ @override
+ final TypeAnnotation type;
+
+ @override
+ DeclarationKind get kind => DeclarationKind.parameter;
+
+ ParameterDeclarationImpl({
+ required this.name,
+ required this.defaultValue,
+ required this.isNamed,
+ required this.isRequired,
+ required this.type,
+ });
+}
+
+class TypeParameterDeclarationImpl implements TypeParameterDeclaration {
+ @override
+ final String name;
+
+ @override
+ final TypeAnnotation? bounds;
+
+ @override
+ DeclarationKind get kind => DeclarationKind.typeParameter;
+
+ TypeParameterDeclarationImpl({required this.name, required this.bounds});
+}
+
+class FunctionDeclarationImpl implements FunctionDeclaration {
+ @override
+ final String name;
+
+ @override
+ final bool isAbstract;
+
+ @override
+ final bool isExternal;
+
+ @override
+ final bool isGetter;
+
+ @override
+ final bool isSetter;
+
+ @override
+ final List<ParameterDeclaration> namedParameters;
+
+ @override
+ final List<ParameterDeclaration> positionalParameters;
+
+ @override
+ final TypeAnnotation returnType;
+
+ @override
+ final List<TypeParameterDeclaration> typeParameters;
+
+ @override
+ DeclarationKind get kind => DeclarationKind.function;
+
+ FunctionDeclarationImpl({
+ required this.name,
+ required this.isAbstract,
+ required this.isExternal,
+ required this.isGetter,
+ required this.isSetter,
+ required this.namedParameters,
+ required this.positionalParameters,
+ required this.returnType,
+ required this.typeParameters,
+ });
+}
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/protocol.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/protocol.dart
new file mode 100644
index 0000000..3dcc455
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/protocol.dart
@@ -0,0 +1,263 @@
+// Copyright (c) 2021, 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.
+
+/// Defines the objects used for communication between the macro executor and
+/// the isolate or process doing the work of macro loading and execution.
+library _fe_analyzer_shared.src.macros.executor_shared.protocol;
+
+import '../executor.dart';
+import '../api.dart';
+import '../executor_shared/response_impls.dart';
+import 'serialization.dart';
+import 'serialization_extensions.dart';
+
+/// Base class all requests extend, provides a unique id for each request.
+abstract class Request implements Serializable {
+ final int id;
+
+ Request({int? id}) : this.id = id ?? _next++;
+
+ Request.deserialize(Deserializer deserializer)
+ : id = (deserializer..moveNext()).expectNum();
+
+ void serialize(Serializer serializer) => serializer.addNum(id);
+
+ static int _next = 0;
+}
+
+/// A generic response object that contains either a response or an error, and
+/// a unique ID.
+class Response {
+ final Object? response;
+ final Object? error;
+ final int requestId;
+
+ Response({this.response, this.error, required this.requestId})
+ : assert(response != null || error != null),
+ assert(response == null || error == null);
+}
+
+/// A serializable [Response], contains the message type as an enum.
+class SerializableResponse implements Response, Serializable {
+ final Serializable? response;
+ final MessageType responseType;
+ final String? error;
+ final int requestId;
+
+ SerializableResponse({
+ this.error,
+ required this.requestId,
+ this.response,
+ required this.responseType,
+ }) : assert(response != null || error != null),
+ assert(response == null || error == null);
+
+ factory SerializableResponse.deserialize(Deserializer deserializer) {
+ deserializer.moveNext();
+ MessageType responseType = MessageType.values[deserializer.expectNum()];
+ Serializable? response;
+ String? error;
+ switch (responseType) {
+ case MessageType.error:
+ deserializer.moveNext();
+ error = deserializer.expectString();
+ break;
+ case MessageType.macroClassIdentifier:
+ response = new MacroClassIdentifierImpl.deserialize(deserializer);
+ break;
+ case MessageType.macroInstanceIdentifier:
+ response = new MacroInstanceIdentifierImpl.deserialize(deserializer);
+ break;
+ case MessageType.macroExecutionResult:
+ response = new MacroExecutionResultImpl.deserialize(deserializer);
+ break;
+ default:
+ throw new StateError('Unexpected response type $responseType');
+ }
+
+ return new SerializableResponse(
+ responseType: responseType,
+ response: response,
+ error: error,
+ requestId: (deserializer..moveNext()).expectNum());
+ }
+
+ void serialize(Serializer serializer) {
+ serializer.addNum(responseType.index);
+ if (response != null) {
+ response!.serialize(serializer);
+ } else if (error != null) {
+ serializer.addString(error!.toString());
+ }
+ serializer.addNum(requestId);
+ }
+}
+
+/// A request to load a macro in this isolate.
+class LoadMacroRequest extends Request {
+ final Uri library;
+ final String name;
+
+ LoadMacroRequest(this.library, this.name);
+
+ LoadMacroRequest.deserialize(Deserializer deserializer)
+ : library = Uri.parse((deserializer..moveNext()).expectString()),
+ name = (deserializer..moveNext()).expectString(),
+ super.deserialize(deserializer);
+
+ @override
+ void serialize(Serializer serializer) {
+ serializer
+ ..addNum(MessageType.loadMacroRequest.index)
+ ..addString(library.toString())
+ ..addString(name);
+ super.serialize(serializer);
+ }
+}
+
+/// A request to instantiate a macro instance.
+class InstantiateMacroRequest extends Request {
+ final MacroClassIdentifier macroClass;
+ final String constructorName;
+ final Arguments arguments;
+
+ InstantiateMacroRequest(
+ this.macroClass, this.constructorName, this.arguments);
+
+ InstantiateMacroRequest.deserialize(Deserializer deserializer)
+ : macroClass = new MacroClassIdentifierImpl.deserialize(deserializer),
+ constructorName = (deserializer..moveNext()).expectString(),
+ arguments = new Arguments.deserialize(deserializer),
+ super.deserialize(deserializer);
+
+ @override
+ void serialize(Serializer serializer) {
+ serializer.addNum(MessageType.instantiateMacroRequest.index);
+ macroClass.serialize(serializer);
+ serializer.addString(constructorName);
+ arguments.serialize(serializer);
+ super.serialize(serializer);
+ }
+}
+
+/// A request to execute a macro on a particular declaration in the definition
+/// phase.
+class ExecuteDefinitionsPhaseRequest extends Request {
+ final MacroInstanceIdentifier macro;
+ final Declaration declaration;
+
+ /// Client/Server specific implementation, not serialized.
+ final TypeResolver typeResolver;
+
+ /// Client/Server specific implementation, not serialized.
+ final ClassIntrospector classIntrospector;
+
+ /// Client/Server specific implementation, not serialized.
+ final TypeDeclarationResolver typeDeclarationResolver;
+
+ ExecuteDefinitionsPhaseRequest(this.macro, this.declaration,
+ this.typeResolver, this.classIntrospector, this.typeDeclarationResolver);
+
+ /// When deserializing we have already consumed the message type, so we don't
+ /// consume it again.
+ ExecuteDefinitionsPhaseRequest.deserialize(Deserializer deserializer,
+ this.typeResolver, this.classIntrospector, this.typeDeclarationResolver)
+ : macro = new MacroInstanceIdentifierImpl.deserialize(deserializer),
+ declaration = (deserializer..moveNext()).expectDeclaration(),
+ super.deserialize(deserializer);
+
+ void serialize(Serializer serializer) {
+ serializer.addNum(MessageType.executeDefinitionsPhaseRequest.index);
+ macro.serialize(serializer);
+ declaration.serialize(serializer);
+ super.serialize(serializer);
+ }
+}
+
+/// A request to reflect on a type annotation
+class ReflectTypeRequest extends Request {
+ final TypeAnnotation typeAnnotation;
+
+ ReflectTypeRequest(this.typeAnnotation);
+
+ /// When deserializing we have already consumed the message type, so we don't
+ /// consume it again.
+ ReflectTypeRequest.deserialize(Deserializer deserializer)
+ : typeAnnotation = (deserializer..moveNext()).expectTypeAnnotation(),
+ super.deserialize(deserializer);
+
+ void serialize(Serializer serializer) {
+ serializer.addNum(MessageType.reflectTypeRequest.index);
+ typeAnnotation.serialize(serializer);
+ super.serialize(serializer);
+ }
+}
+
+/// TODO: Implement this
+class ClientTypeResolver implements TypeResolver {
+ @override
+ Future<StaticType> resolve(TypeAnnotation typeAnnotation) {
+ // TODO: implement resolve
+ throw new UnimplementedError();
+ }
+}
+
+/// TODO: Implement this
+class ClientClassIntrospector implements ClassIntrospector {
+ @override
+ Future<List<ConstructorDeclaration>> constructorsOf(ClassDeclaration clazz) {
+ // TODO: implement constructorsOf
+ throw new UnimplementedError();
+ }
+
+ @override
+ Future<List<FieldDeclaration>> fieldsOf(ClassDeclaration clazz) {
+ // TODO: implement fieldsOf
+ throw new UnimplementedError();
+ }
+
+ @override
+ Future<List<ClassDeclaration>> interfacesOf(ClassDeclaration clazz) {
+ // TODO: implement interfacesOf
+ throw new UnimplementedError();
+ }
+
+ @override
+ Future<List<MethodDeclaration>> methodsOf(ClassDeclaration clazz) {
+ // TODO: implement methodsOf
+ throw new UnimplementedError();
+ }
+
+ @override
+ Future<List<ClassDeclaration>> mixinsOf(ClassDeclaration clazz) {
+ // TODO: implement mixinsOf
+ throw new UnimplementedError();
+ }
+
+ @override
+ Future<ClassDeclaration?> superclassOf(ClassDeclaration clazz) {
+ // TODO: implement superclassOf
+ throw new UnimplementedError();
+ }
+}
+
+/// TODO: Implement this
+class ClientTypeDeclarationsResolver implements TypeDeclarationResolver {
+ @override
+ Future<TypeDeclaration> declarationOf(NamedStaticType annotation) {
+ // TODO: implement declarationOf
+ throw new UnimplementedError();
+ }
+}
+
+enum MessageType {
+ error,
+ executeDefinitionsPhaseRequest,
+ instantiateMacroRequest,
+ loadMacroRequest,
+ reflectTypeRequest,
+ macroClassIdentifier,
+ macroInstanceIdentifier,
+ macroExecutionResult,
+}
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/response_impls.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/response_impls.dart
new file mode 100644
index 0000000..5155f01
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/response_impls.dart
@@ -0,0 +1,222 @@
+// Copyright (c) 2021, 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:async';
+
+import '../executor.dart';
+import '../api.dart';
+import 'serialization.dart';
+import 'serialization_extensions.dart';
+
+/// Implementation of [MacroClassIdentifier].
+class MacroClassIdentifierImpl implements MacroClassIdentifier {
+ final String id;
+
+ MacroClassIdentifierImpl(Uri library, String name) : id = '$library#$name';
+
+ MacroClassIdentifierImpl.deserialize(Deserializer deserializer)
+ : id = (deserializer..moveNext()).expectString();
+
+ void serialize(Serializer serializer) => serializer.addString(id);
+
+ operator ==(other) => other is MacroClassIdentifierImpl && id == other.id;
+
+ int get hashCode => id.hashCode;
+}
+
+/// Implementation of [MacroInstanceIdentifier].
+class MacroInstanceIdentifierImpl implements MacroInstanceIdentifier {
+ static int _next = 0;
+
+ final int id;
+
+ MacroInstanceIdentifierImpl() : id = _next++;
+
+ MacroInstanceIdentifierImpl.deserialize(Deserializer deserializer)
+ : id = (deserializer..moveNext()).expectNum();
+
+ void serialize(Serializer serializer) => serializer.addNum(id);
+
+ operator ==(other) => other is MacroInstanceIdentifierImpl && id == other.id;
+
+ int get hashCode => id;
+}
+
+/// Implementation of [MacroExecutionResult].
+class MacroExecutionResultImpl implements MacroExecutionResult {
+ @override
+ final List<DeclarationCode> augmentations;
+
+ @override
+ final List<DeclarationCode> imports;
+
+ MacroExecutionResultImpl({
+ List<DeclarationCode>? augmentations,
+ List<DeclarationCode>? imports,
+ }) : augmentations = augmentations ?? [],
+ imports = imports ?? [];
+
+ factory MacroExecutionResultImpl.deserialize(Deserializer deserializer) {
+ deserializer.moveNext();
+ deserializer.expectList();
+ List<DeclarationCode> augmentations = [
+ for (bool hasNext = deserializer.moveNext();
+ hasNext;
+ hasNext = deserializer.moveNext())
+ deserializer.expectCode()
+ ];
+ deserializer.moveNext();
+ deserializer.expectList();
+ List<DeclarationCode> imports = [
+ for (bool hasNext = deserializer.moveNext();
+ hasNext;
+ hasNext = deserializer.moveNext())
+ deserializer.expectCode()
+ ];
+
+ return new MacroExecutionResultImpl(
+ augmentations: augmentations,
+ imports: imports,
+ );
+ }
+
+ void serialize(Serializer serializer) {
+ serializer.startList();
+ for (DeclarationCode augmentation in augmentations) {
+ augmentation.serialize(serializer);
+ }
+ serializer.endList();
+ serializer.startList();
+ for (DeclarationCode import in imports) {
+ import.serialize(serializer);
+ }
+ serializer.endList();
+ }
+}
+
+/// Implementation of [FunctionDefinitionBuilder].
+class FunctionDefinitionBuilderImpl implements FunctionDefinitionBuilder {
+ final TypeResolver typeResolver;
+ final TypeDeclarationResolver typeDeclarationResolver;
+ final ClassIntrospector classIntrospector;
+
+ /// The declaration this is a builder for.
+ final FunctionDeclaration declaration;
+
+ /// The final result, will be built up over `augment` calls.
+ final MacroExecutionResultImpl result;
+
+ FunctionDefinitionBuilderImpl(this.declaration, this.typeResolver,
+ this.typeDeclarationResolver, this.classIntrospector)
+ : result = new MacroExecutionResultImpl();
+
+ FunctionDefinitionBuilderImpl.deserialize(Deserializer deserializer,
+ this.typeResolver, this.typeDeclarationResolver, this.classIntrospector)
+ : declaration =
+ (deserializer..moveNext()).expectDeclaration<FunctionDeclaration>(),
+ result = new MacroExecutionResultImpl.deserialize(deserializer);
+
+ void serialize(Serializer serializer) {
+ // Note that the `typeResolver`, `typeDeclarationResolver`, and
+ // `classIntrospector` are not serialized. These have custom implementations
+ // on the client/server side.
+ declaration.serialize(serializer);
+ result.serialize(serializer);
+ }
+
+ @override
+ void augment(FunctionBodyCode body) {
+ result.augmentations.add(new DeclarationCode.fromParts([
+ 'augment ',
+ declaration.returnType.code,
+ ' ',
+ declaration.name,
+ if (declaration.typeParameters.isNotEmpty) ...[
+ '<',
+ for (TypeParameterDeclaration typeParam
+ in declaration.typeParameters) ...[
+ typeParam.name,
+ if (typeParam.bounds != null) ...['extends ', typeParam.bounds!.code],
+ if (typeParam != declaration.typeParameters.last) ', ',
+ ],
+ '>',
+ ],
+ '(',
+ for (ParameterDeclaration positionalRequired
+ in declaration.positionalParameters.where((p) => p.isRequired)) ...[
+ new ParameterCode.fromParts([
+ positionalRequired.type.code,
+ ' ',
+ positionalRequired.name,
+ ]),
+ ', '
+ ],
+ if (declaration.positionalParameters.any((p) => !p.isRequired)) ...[
+ '[',
+ for (ParameterDeclaration positionalOptional in declaration
+ .positionalParameters
+ .where((p) => !p.isRequired)) ...[
+ new ParameterCode.fromParts([
+ positionalOptional.type.code,
+ ' ',
+ positionalOptional.name,
+ ]),
+ ', ',
+ ],
+ ']',
+ ],
+ if (declaration.namedParameters.isNotEmpty) ...[
+ '{',
+ for (ParameterDeclaration named in declaration.namedParameters) ...[
+ new ParameterCode.fromParts([
+ if (named.isRequired) 'required ',
+ named.type.code,
+ ' ',
+ named.name,
+ if (named.defaultValue != null) ...[
+ ' = ',
+ named.defaultValue!,
+ ],
+ ]),
+ ', ',
+ ],
+ '}',
+ ],
+ ') ',
+ body,
+ ]));
+ }
+
+ @override
+ Future<List<ConstructorDeclaration>> constructorsOf(ClassDeclaration clazz) =>
+ classIntrospector.constructorsOf(clazz);
+
+ @override
+ Future<List<FieldDeclaration>> fieldsOf(ClassDeclaration clazz) =>
+ classIntrospector.fieldsOf(clazz);
+
+ @override
+ Future<List<ClassDeclaration>> interfacesOf(ClassDeclaration clazz) =>
+ classIntrospector.interfacesOf(clazz);
+
+ @override
+ Future<List<MethodDeclaration>> methodsOf(ClassDeclaration clazz) =>
+ classIntrospector.methodsOf(clazz);
+
+ @override
+ Future<List<ClassDeclaration>> mixinsOf(ClassDeclaration clazz) =>
+ classIntrospector.mixinsOf(clazz);
+
+ @override
+ Future<TypeDeclaration> declarationOf(NamedStaticType annotation) =>
+ typeDeclarationResolver.declarationOf(annotation);
+
+ @override
+ Future<ClassDeclaration?> superclassOf(ClassDeclaration clazz) =>
+ classIntrospector.superclassOf(clazz);
+
+ @override
+ Future<StaticType> resolve(TypeAnnotation typeAnnotation) =>
+ typeResolver.resolve(typeAnnotation);
+}
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization.dart
new file mode 100644
index 0000000..f5f9343
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization.dart
@@ -0,0 +1,229 @@
+// Copyright (c) 2021, 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:async';
+
+/// All serialization must be done in a serialization Zone, which tells it
+/// whether we are the client or server.
+T withSerializationMode<T>(SerializationMode mode, T Function() fn) =>
+ runZoned(fn, zoneValues: {#serializationMode: mode});
+
+/// Serializable interface
+abstract class Serializable {
+ /// Serializes this object using [serializer].
+ void serialize(Serializer serializer);
+}
+
+/// A push based object serialization interface.
+abstract class Serializer {
+ /// Serializes a [String].
+ void addString(String value);
+
+ /// Serializes a nullable [String].
+ void addNullableString(String? value);
+
+ /// Serializes a [num].
+ void addNum(num value);
+
+ /// Serializes a nullable [num].
+ void addNullableNum(num? value);
+
+ /// Serializes a [bool].
+ void addBool(bool value);
+
+ /// Serializes a nullable [bool].
+ void addNullableBool(bool? value);
+
+ /// Serializes a `null` literal.
+ void addNull();
+
+ /// Used to signal the start of an arbitrary length list of items.
+ void startList();
+
+ /// Used to signal the end of an arbitrary length list of items.
+ void endList();
+}
+
+/// A pull based object deserialization interface.
+///
+/// You must call [moveNext] before reading any items, and in order to advance
+/// to the next item.
+abstract class Deserializer {
+ /// Checks if the current value is a null, returns `true` if so and `false`
+ /// otherwise.
+ bool checkNull();
+
+ /// Reads the current value as a non-nullable [String].
+ bool expectBool();
+
+ /// Reads the current value as a nullable [bool].
+ bool? expectNullableBool();
+
+ /// Reads the current value as a non-nullable [String].
+ T expectNum<T extends num>();
+
+ /// Reads the current value as a nullable [num].
+ num? expectNullableNum();
+
+ /// Reads the current value as a non-nullable [String].
+ String expectString();
+
+ /// Reads the current value as a nullable [String].
+ String? expectNullableString();
+
+ /// Asserts that the current item is the start of a list.
+ ///
+ /// An example for how to read from a list is as follows:
+ ///
+ /// var json = JsonReader.fromString(source);
+ /// I know it's a list of strings.
+ ///
+ /// ```
+ /// var result = <String>[];
+ /// deserializer.moveNext();
+ /// deserializer.expectList();
+ /// while (json.moveNext()) {
+ /// result.add(json.expectString());
+ /// }
+ /// // Can now read later items, but need to call `moveNext` again to move
+ /// // past the list.
+ /// deserializer.moveNext();
+ /// deserializer.expectBool();
+ /// ```
+ void expectList();
+
+ /// Moves to the next item, returns `false` if there are no more items to
+ /// read.
+ ///
+ /// If inside of a list, this returns `false` when the end of the list is
+ /// reached, and moves back to the parent, but does not advance it, so another
+ /// call to `moveNext` is needed. See example in the [expectList] docs.
+ bool moveNext();
+}
+
+class JsonSerializer implements Serializer {
+ /// The full result.
+ final _result = <Object?>[];
+
+ /// A path to the current list we are modifying.
+ late List<List<Object?>> _path = [_result];
+
+ /// Returns the result as an unmodifiable [Iterable].
+ ///
+ /// Asserts that all [List] entries have not been closed with [endList].
+ Iterable<Object?> get result {
+ assert(_path.length == 1);
+ return _result;
+ }
+
+ @override
+ void addBool(bool value) => _path.last.add(value);
+ @override
+ void addNullableBool(bool? value) => _path.last.add(value);
+
+ @override
+ void addNum(num value) => _path.last.add(value);
+ @override
+ void addNullableNum(num? value) => _path.last.add(value);
+
+ @override
+ void addString(String value) => _path.last.add(value);
+ @override
+ void addNullableString(String? value) => _path.last.add(value);
+
+ @override
+ void addNull() => _path.last.add(null);
+
+ @override
+ void startList() {
+ List<Object?> sublist = [];
+ _path.last.add(sublist);
+ _path.add(sublist);
+ }
+
+ @override
+ void endList() {
+ _path.removeLast();
+ }
+}
+
+class JsonDeserializer implements Deserializer {
+ /// The root source list to read from.
+ final Iterable<Object?> _source;
+
+ /// The path to the current iterator we are reading from.
+ late List<Iterator<Object?>> _path = [];
+
+ /// Whether we have received our first [moveNext] call.
+ bool _initialized = false;
+
+ /// Initialize this deserializer from `_source`.
+ JsonDeserializer(this._source);
+
+ @override
+ bool checkNull() => _expectValue<Object?>() == null;
+
+ @override
+ void expectList() => _path.add(_expectValue<Iterable<Object?>>().iterator);
+
+ @override
+ bool expectBool() => _expectValue();
+ @override
+ bool? expectNullableBool() => _expectValue();
+
+ @override
+ T expectNum<T extends num>() => _expectValue();
+ @override
+ num? expectNullableNum() => _expectValue();
+
+ @override
+ String expectString() => _expectValue();
+ @override
+ String? expectNullableString() => _expectValue();
+
+ /// Reads the current value and casts it to [T].
+ T _expectValue<T>() {
+ if (!_initialized) {
+ throw new StateError(
+ 'You must call `moveNext()` before reading any values.');
+ }
+ return _path.last.current as T;
+ }
+
+ @override
+ bool moveNext() {
+ if (!_initialized) {
+ _path.add(_source.iterator);
+ _initialized = true;
+ }
+
+ // Move the current iterable, if its at the end of its items remove it from
+ // the current path and return false.
+ if (!_path.last.moveNext()) {
+ _path.removeLast();
+ return false;
+ }
+
+ return true;
+ }
+}
+
+/// Must be set using `withSerializationMode` before doing any serialization or
+/// deserialization.
+SerializationMode get serializationMode {
+ SerializationMode? mode =
+ Zone.current[#serializationMode] as SerializationMode?;
+ if (mode == null) {
+ throw new StateError('No SerializationMode set, you must do all '
+ 'serialization inside a call to `withSerializationMode`.');
+ }
+ return mode;
+}
+
+/// Some objects are serialized differently on the client side versus the server
+/// side. This indicates the different modes.
+enum SerializationMode {
+ server,
+ client,
+}
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization_extensions.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization_extensions.dart
new file mode 100644
index 0000000..01cb768
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization_extensions.dart
@@ -0,0 +1,416 @@
+import 'package:_fe_analyzer_shared/src/macros/executor_shared/introspection_impls.dart';
+
+import 'serialization.dart';
+import '../api.dart';
+
+extension DeserializerExtensions on Deserializer {
+ TypeAnnotation expectTypeAnnotation() {
+ int id = expectNum();
+ switch (serializationMode) {
+ case SerializationMode.server:
+ return _typeAnnotationsById[id]!;
+ case SerializationMode.client:
+ TypeAnnotation typeAnnotation;
+ moveNext();
+ TypeAnnotationKind type = TypeAnnotationKind.values[expectNum()];
+ moveNext();
+ switch (type) {
+ case TypeAnnotationKind.namedType:
+ typeAnnotation = _expectNamedTypeAnnotation();
+ break;
+ case TypeAnnotationKind.functionType:
+ typeAnnotation = _expectFunctionTypeAnnotation();
+ break;
+ }
+ _typeAnnotationIds[typeAnnotation] = id;
+ return typeAnnotation;
+ }
+ }
+
+ NamedTypeAnnotation _expectNamedTypeAnnotation() {
+ bool isNullable = expectBool();
+ moveNext();
+ String name = expectString();
+ moveNext();
+ expectList();
+ List<TypeAnnotation> typeArguments = [
+ for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
+ expectTypeAnnotation(),
+ ];
+ return new NamedTypeAnnotationImpl(
+ isNullable: isNullable, name: name, typeArguments: typeArguments);
+ }
+
+ FunctionTypeAnnotation _expectFunctionTypeAnnotation() {
+ bool isNullable = expectBool();
+
+ moveNext();
+ TypeAnnotation returnType = expectTypeAnnotation();
+
+ moveNext();
+ expectList();
+ List<ParameterDeclaration> positionalParameters = [
+ for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
+ expectDeclaration(),
+ ];
+
+ moveNext();
+ expectList();
+ List<ParameterDeclaration> namedParameters = [
+ for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
+ expectDeclaration(),
+ ];
+
+ moveNext();
+ expectList();
+ List<TypeParameterDeclaration> typeParameters = [
+ for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
+ expectDeclaration(),
+ ];
+
+ return new FunctionTypeAnnotationImpl(
+ isNullable: isNullable,
+ returnType: returnType,
+ positionalParameters: positionalParameters,
+ namedParameters: namedParameters,
+ typeParameters: typeParameters);
+ }
+
+ T expectDeclaration<T extends Declaration>() {
+ DeclarationKind kind = DeclarationKind.values[expectNum()];
+ moveNext();
+ switch (kind) {
+ case DeclarationKind.parameter:
+ return _expectParameterDeclaration() as T;
+ case DeclarationKind.typeParameter:
+ return _expectTypeParameterDeclaration() as T;
+ case DeclarationKind.function:
+ return _expectFunctionDeclaration() as T;
+ default:
+ throw new UnimplementedError('Cant deserialize $kind yet');
+ }
+ }
+
+ ParameterDeclaration _expectParameterDeclaration() {
+ String name = expectString();
+ moveNext();
+ Code? defaultValue;
+ if (!checkNull()) {
+ defaultValue = expectCode();
+ }
+ bool isNamed = expectBool();
+ moveNext();
+ bool isRequired = expectBool();
+ moveNext();
+ TypeAnnotation type = expectTypeAnnotation();
+
+ return new ParameterDeclarationImpl(
+ defaultValue: defaultValue,
+ isNamed: isNamed,
+ isRequired: isRequired,
+ name: name,
+ type: type);
+ }
+
+ TypeParameterDeclaration _expectTypeParameterDeclaration() {
+ String name = expectString();
+ moveNext();
+ TypeAnnotation? bounds;
+ if (!checkNull()) {
+ bounds = expectTypeAnnotation();
+ }
+ return new TypeParameterDeclarationImpl(name: name, bounds: bounds);
+ }
+
+ FunctionDeclaration _expectFunctionDeclaration() {
+ String name = expectString();
+ moveNext();
+ bool isAbstract = expectBool();
+ moveNext();
+ bool isExternal = expectBool();
+ moveNext();
+ bool isGetter = expectBool();
+ moveNext();
+ bool isSetter = expectBool();
+
+ moveNext();
+ expectList();
+ List<ParameterDeclaration> namedParameters = [
+ for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
+ expectDeclaration()
+ ];
+
+ moveNext();
+ expectList();
+ List<ParameterDeclaration> positionalParameters = [
+ for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
+ expectDeclaration()
+ ];
+
+ moveNext();
+ TypeAnnotation returnType = expectTypeAnnotation();
+
+ moveNext();
+ expectList();
+ List<TypeParameterDeclaration> typeParameters = [
+ for (bool hasNext = moveNext(); hasNext; hasNext = moveNext())
+ expectDeclaration()
+ ];
+ return new FunctionDeclarationImpl(
+ name: name,
+ isAbstract: isAbstract,
+ isExternal: isExternal,
+ isGetter: isGetter,
+ isSetter: isSetter,
+ namedParameters: namedParameters,
+ positionalParameters: positionalParameters,
+ returnType: returnType,
+ typeParameters: typeParameters);
+ }
+
+ T expectCode<T extends Code>() {
+ CodeKind kind = CodeKind.values[expectNum()];
+ moveNext();
+ expectList();
+ List<Object> parts = [];
+ while (moveNext()) {
+ CodePartKind partKind = CodePartKind.values[expectNum()];
+ moveNext();
+ switch (partKind) {
+ case CodePartKind.code:
+ parts.add(expectCode());
+ break;
+ case CodePartKind.string:
+ parts.add(expectString());
+ break;
+ case CodePartKind.typeAnnotation:
+ parts.add(expectTypeAnnotation());
+ break;
+ }
+ }
+
+ switch (kind) {
+ case CodeKind.raw:
+ return new Code.fromParts(parts) as T;
+ case CodeKind.declaration:
+ return new DeclarationCode.fromParts(parts) as T;
+ case CodeKind.element:
+ return new ElementCode.fromParts(parts) as T;
+ case CodeKind.expression:
+ return new ExpressionCode.fromParts(parts) as T;
+ case CodeKind.functionBody:
+ return new FunctionBodyCode.fromParts(parts) as T;
+ case CodeKind.identifier:
+ return new IdentifierCode.fromParts(parts) as T;
+ case CodeKind.namedArgument:
+ return new NamedArgumentCode.fromParts(parts) as T;
+ case CodeKind.parameter:
+ return new ParameterCode.fromParts(parts) as T;
+ case CodeKind.statement:
+ return new StatementCode.fromParts(parts) as T;
+ }
+ }
+}
+
+/// On the server side we keep track of type annotations by their ID.
+final _typeAnnotationsById = <int, TypeAnnotation>{};
+
+/// On the client side we keep an expando of ids on [TypeAnnotation]s.
+final _typeAnnotationIds = new Expando<int>();
+
+/// Incrementing ids for [TypeAnnotationImpl]s
+int _nextTypeAnnotationId = 0;
+
+extension SerializeTypeAnnotation on TypeAnnotation {
+ void serialize(Serializer serializer) {
+ TypeAnnotation self = this;
+ if (self is NamedTypeAnnotationImpl) {
+ self.serialize(serializer);
+ } else if (self is FunctionTypeAnnotationImpl) {
+ self.serialize(serializer);
+ } else {
+ throw new UnsupportedError(
+ 'Type ${this.runtimeType} is not supported for serialization.');
+ }
+ }
+}
+
+/// Does the parts of serialization for type annotations that is shared between
+/// implementations.
+///
+/// Returns `false` if we should continue serializing the rest of the object, or
+/// `true` if the object is fully serialized (just an ID).
+bool _doSharedTypeAnnotationSerialization(Serializer serializer,
+ TypeAnnotation typeAnnotation, TypeAnnotationKind kind) {
+ switch (serializationMode) {
+ case SerializationMode.client:
+ // Only send the ID from the client side, and assume we have one.
+ int id = _typeAnnotationIds[typeAnnotation]!;
+ serializer.addNum(id);
+ return true;
+ case SerializationMode.server:
+ // Server side we may create new ids for unseen annotations,
+ // and continue to serialize the rest of the annotation.
+ int id = _typeAnnotationIds[typeAnnotation] ?? _nextTypeAnnotationId++;
+ // TODO: We should clean these up at some point.
+ _typeAnnotationsById[id] = typeAnnotation;
+ serializer.addNum(id);
+ break;
+ }
+ serializer.addNum(kind.index);
+ serializer.addBool(typeAnnotation.isNullable);
+ return false;
+}
+
+extension SerializeNamedTypeAnnotation on NamedTypeAnnotation {
+ void serialize(Serializer serializer) {
+ if (_doSharedTypeAnnotationSerialization(
+ serializer, this, TypeAnnotationKind.namedType)) {
+ return;
+ }
+ serializer.addString(name);
+ serializer.startList();
+ for (TypeAnnotation typeArg in typeArguments) {
+ typeArg.serialize(serializer);
+ }
+ serializer.endList();
+ }
+}
+
+extension SerializeFunctionTypeAnnotation on FunctionTypeAnnotation {
+ void serialize(Serializer serializer) {
+ if (_doSharedTypeAnnotationSerialization(
+ serializer, this, TypeAnnotationKind.functionType)) {
+ return;
+ }
+
+ returnType.serialize(serializer);
+
+ serializer.startList();
+ for (ParameterDeclaration param in positionalParameters) {
+ param.serialize(serializer);
+ }
+ serializer.endList();
+
+ serializer.startList();
+ for (ParameterDeclaration param in namedParameters) {
+ param.serialize(serializer);
+ }
+ serializer.endList();
+
+ serializer.startList();
+ for (TypeParameterDeclaration typeParam in typeParameters) {
+ typeParam.serialize(serializer);
+ }
+ serializer.endList();
+ }
+}
+
+/// Does the shared parts of [Declaration] serialization.
+void _serializeDeclaration(Serializer serializer, Declaration declaration) {
+ serializer.addNum(declaration.kind.index);
+ serializer.addString(declaration.name);
+}
+
+/// Checks the type and deserializes it appropriately.
+extension SerializeDeclaration on Declaration {
+ void serialize(Serializer serializer) {
+ switch (kind) {
+ case DeclarationKind.parameter:
+ (this as ParameterDeclaration).serialize(serializer);
+ break;
+ case DeclarationKind.typeParameter:
+ (this as TypeParameterDeclaration).serialize(serializer);
+ break;
+ case DeclarationKind.function:
+ (this as FunctionDeclaration).serialize(serializer);
+ break;
+ default:
+ throw new UnimplementedError('Cant serialize $kind yet');
+ }
+ }
+}
+
+extension SerializeParameterDeclaration on ParameterDeclaration {
+ void serialize(Serializer serializer) {
+ _serializeDeclaration(serializer, this);
+ if (defaultValue == null) {
+ serializer.addNull();
+ } else {
+ defaultValue!.serialize(serializer);
+ }
+ serializer.addBool(isNamed);
+ serializer.addBool(isRequired);
+ type.serialize(serializer);
+ }
+}
+
+extension SerializeTypeParameterDeclaration on TypeParameterDeclaration {
+ void serialize(Serializer serializer) {
+ _serializeDeclaration(serializer, this);
+ TypeAnnotation? bounds = this.bounds;
+ if (bounds == null) {
+ serializer.addNull();
+ } else {
+ bounds.serialize(serializer);
+ }
+ }
+}
+
+extension SerializeFunctionDeclaration on FunctionDeclaration {
+ void serialize(Serializer serializer) {
+ _serializeDeclaration(serializer, this);
+ serializer
+ ..addBool(isAbstract)
+ ..addBool(isExternal)
+ ..addBool(isGetter)
+ ..addBool(isSetter)
+ ..startList();
+ for (ParameterDeclaration named in namedParameters) {
+ named.serialize(serializer);
+ }
+ serializer
+ ..endList()
+ ..startList();
+ for (ParameterDeclaration positional in positionalParameters) {
+ positional.serialize(serializer);
+ }
+ serializer.endList();
+ returnType.serialize(serializer);
+ serializer.startList();
+ for (TypeParameterDeclaration param in typeParameters) {
+ param.serialize(serializer);
+ }
+ serializer.endList();
+ }
+}
+
+extension SerializeCode on Code {
+ void serialize(Serializer serializer) {
+ serializer
+ ..addNum(kind.index)
+ ..startList();
+ for (Object part in parts) {
+ if (part is String) {
+ serializer
+ ..addNum(CodePartKind.string.index)
+ ..addString(part);
+ } else if (part is Code) {
+ serializer.addNum(CodePartKind.code.index);
+ part.serialize(serializer);
+ } else if (part is TypeAnnotation) {
+ serializer.addNum(CodePartKind.typeAnnotation.index);
+ part.serialize(serializer);
+ } else {
+ throw new StateError('Unrecognized code part $part');
+ }
+ }
+ serializer.endList();
+ }
+}
+
+enum CodePartKind {
+ string,
+ code,
+ typeAnnotation,
+}
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/isolate_mirrors_executor.dart b/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/isolate_mirrors_executor.dart
index 48b0229..136a6d5 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/isolate_mirrors_executor.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/isolate_mirrors_executor.dart
@@ -7,7 +7,7 @@
import 'dart:mirrors';
import 'isolate_mirrors_impl.dart';
-import 'protocol.dart';
+import '../executor_shared/protocol.dart';
import '../executor.dart';
import '../api.dart';
@@ -29,10 +29,10 @@
final SendPort _sendPort;
/// The stream of responses from the [_macroIsolate].
- final Stream<GenericResponse> _responseStream;
+ final Stream<Response> _responseStream;
/// A map of response completers by request id.
- final _responseCompleters = <int, Completer<GenericResponse>>{};
+ final _responseCompleters = <int, Completer<Response>>{};
/// A function that should be invoked when shutting down this executor
/// to perform any necessary cleanup.
@@ -41,7 +41,7 @@
_IsolateMirrorMacroExecutor._(
this._macroIsolate, this._sendPort, this._responseStream, this._onClose) {
_responseStream.listen((event) {
- Completer<GenericResponse>? completer =
+ Completer<Response>? completer =
_responseCompleters.remove(event.requestId);
if (completer == null) {
throw new StateError(
@@ -57,13 +57,13 @@
static Future<MacroExecutor> start() async {
ReceivePort receivePort = new ReceivePort();
Completer<SendPort> sendPortCompleter = new Completer<SendPort>();
- StreamController<GenericResponse> responseStreamController =
- new StreamController<GenericResponse>(sync: true);
+ StreamController<Response> responseStreamController =
+ new StreamController<Response>(sync: true);
receivePort.listen((message) {
if (!sendPortCompleter.isCompleted) {
sendPortCompleter.complete(message as SendPort);
} else {
- responseStreamController.add(message as GenericResponse);
+ responseStreamController.add(message as Response);
}
}).onDone(responseStreamController.close);
Isolate macroIsolate = await Isolate.spawn(spawn, receivePort.sendPort);
@@ -124,18 +124,24 @@
new InstantiateMacroRequest(macroClass, constructor, arguments));
@override
- Future<MacroClassIdentifier> loadMacro(Uri library, String name) =>
- _sendRequest(new LoadMacroRequest(library, name));
+ Future<MacroClassIdentifier> loadMacro(Uri library, String name,
+ {Uri? precompiledKernelUri}) {
+ if (precompiledKernelUri != null) {
+ // TODO: Implement support?
+ throw new UnsupportedError(
+ 'The IsolateMirrorsExecutor does not support precompiled dill files');
+ }
+ return _sendRequest(new LoadMacroRequest(library, name));
+ }
/// Sends a request and returns the response, casting it to the expected
/// type.
Future<T> _sendRequest<T>(Request request) async {
_sendPort.send(request);
- Completer<GenericResponse<T>> completer =
- new Completer<GenericResponse<T>>();
+ Completer<Response> completer = new Completer<Response>();
_responseCompleters[request.id] = completer;
- GenericResponse<T> response = await completer.future;
- T? result = response.response;
+ Response response = await completer.future;
+ T? result = response.response as T?;
if (result != null) return result;
throw response.error!;
}
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/isolate_mirrors_impl.dart b/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/isolate_mirrors_impl.dart
index 3cf24a5..e6848f8 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/isolate_mirrors_impl.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/isolate_mirrors_impl.dart
@@ -6,8 +6,8 @@
import 'dart:isolate';
import 'dart:mirrors';
-import 'protocol.dart';
-import '../executor.dart';
+import '../executor_shared/response_impls.dart';
+import '../executor_shared/protocol.dart';
import '../api.dart';
/// Spawns a new isolate for loading and executing macros.
@@ -15,33 +15,28 @@
ReceivePort receivePort = new ReceivePort();
sendPort.send(receivePort.sendPort);
receivePort.listen((message) async {
+ Response response;
if (message is LoadMacroRequest) {
- GenericResponse<MacroClassIdentifier> response =
- await _loadMacro(message);
- sendPort.send(response);
+ response = await _loadMacro(message);
} else if (message is InstantiateMacroRequest) {
- GenericResponse<MacroInstanceIdentifier> response =
- await _instantiateMacro(message);
- sendPort.send(response);
+ response = await _instantiateMacro(message);
} else if (message is ExecuteDefinitionsPhaseRequest) {
- GenericResponse<MacroExecutionResult> response =
- await _executeDefinitionsPhase(message);
- sendPort.send(response);
+ response = await _executeDefinitionsPhase(message);
} else {
throw new StateError('Unrecognized event type $message');
}
+ sendPort.send(response);
});
}
/// Maps macro identifiers to class mirrors.
-final _macroClasses = <_MacroClassIdentifier, ClassMirror>{};
+final _macroClasses = <MacroClassIdentifierImpl, ClassMirror>{};
/// Handles [LoadMacroRequest]s.
-Future<GenericResponse<MacroClassIdentifier>> _loadMacro(
- LoadMacroRequest request) async {
+Future<Response> _loadMacro(LoadMacroRequest request) async {
try {
- _MacroClassIdentifier identifier =
- new _MacroClassIdentifier(request.library, request.name);
+ MacroClassIdentifierImpl identifier =
+ new MacroClassIdentifierImpl(request.library, request.name);
if (_macroClasses.containsKey(identifier)) {
throw new UnsupportedError(
'Reloading macros is not supported by this implementation');
@@ -51,18 +46,17 @@
ClassMirror macroClass =
libMirror.declarations[new Symbol(request.name)] as ClassMirror;
_macroClasses[identifier] = macroClass;
- return new GenericResponse(response: identifier, requestId: request.id);
+ return new Response(response: identifier, requestId: request.id);
} catch (e) {
- return new GenericResponse(error: e, requestId: request.id);
+ return new Response(error: e, requestId: request.id);
}
}
/// Maps macro instance identifiers to instances.
-final _macroInstances = <_MacroInstanceIdentifier, Macro>{};
+final _macroInstances = <MacroInstanceIdentifierImpl, Macro>{};
/// Handles [InstantiateMacroRequest]s.
-Future<GenericResponse<MacroInstanceIdentifier>> _instantiateMacro(
- InstantiateMacroRequest request) async {
+Future<Response> _instantiateMacro(InstantiateMacroRequest request) async {
try {
ClassMirror? clazz = _macroClasses[request.macroClass];
if (clazz == null) {
@@ -73,16 +67,15 @@
for (MapEntry<String, Object?> entry in request.arguments.named.entries)
new Symbol(entry.key): entry.value,
}).reflectee as Macro;
- _MacroInstanceIdentifier identifier = new _MacroInstanceIdentifier();
+ MacroInstanceIdentifierImpl identifier = new MacroInstanceIdentifierImpl();
_macroInstances[identifier] = instance;
- return new GenericResponse<MacroInstanceIdentifier>(
- response: identifier, requestId: request.id);
+ return new Response(response: identifier, requestId: request.id);
} catch (e) {
- return new GenericResponse(error: e, requestId: request.id);
+ return new Response(error: e, requestId: request.id);
}
}
-Future<GenericResponse<MacroExecutionResult>> _executeDefinitionsPhase(
+Future<Response> _executeDefinitionsPhase(
ExecuteDefinitionsPhaseRequest request) async {
try {
Macro? instance = _macroInstances[request.macro];
@@ -93,163 +86,18 @@
Declaration declaration = request.declaration;
if (instance is FunctionDefinitionMacro &&
declaration is FunctionDeclaration) {
- _FunctionDefinitionBuilder builder = new _FunctionDefinitionBuilder(
+ FunctionDefinitionBuilderImpl builder = new FunctionDefinitionBuilderImpl(
declaration,
request.typeResolver,
request.typeDeclarationResolver,
request.classIntrospector);
await instance.buildDefinitionForFunction(declaration, builder);
- return new GenericResponse(
- response: builder.result, requestId: request.id);
+ return new Response(response: builder.result, requestId: request.id);
} else {
throw new UnsupportedError(
('Only FunctionDefinitionMacros are supported currently'));
}
} catch (e) {
- return new GenericResponse(error: e, requestId: request.id);
+ return new Response(error: e, requestId: request.id);
}
}
-
-/// Our implementation of [MacroClassIdentifier].
-class _MacroClassIdentifier implements MacroClassIdentifier {
- final String id;
-
- _MacroClassIdentifier(Uri library, String name) : id = '$library#$name';
-
- operator ==(other) => other is _MacroClassIdentifier && id == other.id;
-
- int get hashCode => id.hashCode;
-}
-
-/// Our implementation of [MacroInstanceIdentifier].
-class _MacroInstanceIdentifier implements MacroInstanceIdentifier {
- static int _next = 0;
-
- final int id;
-
- _MacroInstanceIdentifier() : id = _next++;
-
- operator ==(other) => other is _MacroInstanceIdentifier && id == other.id;
-
- int get hashCode => id;
-}
-
-/// Our implementation of [MacroExecutionResult].
-class _MacroExecutionResult implements MacroExecutionResult {
- @override
- final List<DeclarationCode> augmentations = <DeclarationCode>[];
-
- @override
- final List<DeclarationCode> imports = <DeclarationCode>[];
-}
-
-/// Custom implementation of [FunctionDefinitionBuilder].
-class _FunctionDefinitionBuilder implements FunctionDefinitionBuilder {
- final TypeResolver typeResolver;
- final TypeDeclarationResolver typeDeclarationResolver;
- final ClassIntrospector classIntrospector;
-
- /// The declaration this is a builder for.
- final FunctionDeclaration declaration;
-
- /// The final result, will be built up over `augment` calls.
- final _MacroExecutionResult result = new _MacroExecutionResult();
-
- _FunctionDefinitionBuilder(this.declaration, this.typeResolver,
- this.typeDeclarationResolver, this.classIntrospector);
-
- @override
- void augment(FunctionBodyCode body) {
- result.augmentations.add(new DeclarationCode.fromParts([
- 'augment ',
- declaration.returnType.code,
- ' ',
- declaration.name,
- if (declaration.typeParameters.isNotEmpty) ...[
- '<',
- for (TypeParameterDeclaration typeParam
- in declaration.typeParameters) ...[
- typeParam.name,
- if (typeParam.bounds != null) ...['extends ', typeParam.bounds!.code],
- if (typeParam != declaration.typeParameters.last) ', ',
- ],
- '>',
- ],
- '(',
- for (ParameterDeclaration positionalRequired
- in declaration.positionalParameters.where((p) => p.isRequired)) ...[
- new ParameterCode.fromParts([
- positionalRequired.type.code,
- ' ',
- positionalRequired.name,
- ]),
- ', '
- ],
- if (declaration.positionalParameters.any((p) => !p.isRequired)) ...[
- '[',
- for (ParameterDeclaration positionalOptional in declaration
- .positionalParameters
- .where((p) => !p.isRequired)) ...[
- new ParameterCode.fromParts([
- positionalOptional.type.code,
- ' ',
- positionalOptional.name,
- ]),
- ', ',
- ],
- ']',
- ],
- if (declaration.namedParameters.isNotEmpty) ...[
- '{',
- for (ParameterDeclaration named in declaration.namedParameters) ...[
- new ParameterCode.fromParts([
- if (named.isRequired) 'required ',
- named.type.code,
- ' ',
- named.name,
- if (named.defaultValue != null) ...[
- ' = ',
- named.defaultValue!,
- ],
- ]),
- ', ',
- ],
- '}',
- ],
- ') ',
- body,
- ]));
- }
-
- @override
- Future<List<ConstructorDeclaration>> constructorsOf(ClassDeclaration clazz) =>
- classIntrospector.constructorsOf(clazz);
-
- @override
- Future<List<FieldDeclaration>> fieldsOf(ClassDeclaration clazz) =>
- classIntrospector.fieldsOf(clazz);
-
- @override
- Future<List<ClassDeclaration>> interfacesOf(ClassDeclaration clazz) =>
- classIntrospector.interfacesOf(clazz);
-
- @override
- Future<List<MethodDeclaration>> methodsOf(ClassDeclaration clazz) =>
- classIntrospector.methodsOf(clazz);
-
- @override
- Future<List<ClassDeclaration>> mixinsOf(ClassDeclaration clazz) =>
- classIntrospector.mixinsOf(clazz);
-
- @override
- Future<TypeDeclaration> declarationOf(NamedStaticType annotation) =>
- typeDeclarationResolver.declarationOf(annotation);
-
- @override
- Future<ClassDeclaration?> superclassOf(ClassDeclaration clazz) =>
- classIntrospector.superclassOf(clazz);
-
- @override
- Future<StaticType> resolve(TypeAnnotation typeAnnotation) =>
- typeResolver.resolve(typeAnnotation);
-}
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/protocol.dart b/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/protocol.dart
deleted file mode 100644
index 96a2724..0000000
--- a/pkg/_fe_analyzer_shared/lib/src/macros/isolate_mirrors_executor/protocol.dart
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (c) 2021, 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.
-
-/// Defines the objects used for communication between the macro executor and
-/// the isolate doing the work of macro loading and execution.
-library protocol;
-
-import '../executor.dart';
-import '../api.dart';
-
-/// Base class all requests extend, provides a unique id for each request.
-class Request {
- final int id;
-
- Request() : id = _next++;
-
- static int _next = 0;
-}
-
-/// A generic response object that is either an instance of [T] or an error.
-class GenericResponse<T> {
- final T? response;
- final Object? error;
- final int requestId;
-
- GenericResponse({this.response, this.error, required this.requestId})
- : assert(response != null || error != null),
- assert(response == null || error == null);
-}
-
-/// A request to load a macro in this isolate.
-class LoadMacroRequest extends Request {
- final Uri library;
- final String name;
-
- LoadMacroRequest(this.library, this.name);
-}
-
-/// A request to instantiate a macro instance.
-class InstantiateMacroRequest extends Request {
- final MacroClassIdentifier macroClass;
- final String constructorName;
- final Arguments arguments;
-
- InstantiateMacroRequest(
- this.macroClass, this.constructorName, this.arguments);
-}
-
-/// A request to execute a macro on a particular declaration in the definition
-/// phase.
-class ExecuteDefinitionsPhaseRequest extends Request {
- final MacroInstanceIdentifier macro;
- final Declaration declaration;
- final TypeResolver typeResolver;
- final ClassIntrospector classIntrospector;
- final TypeDeclarationResolver typeDeclarationResolver;
-
- ExecuteDefinitionsPhaseRequest(this.macro, this.declaration,
- this.typeResolver, this.classIntrospector, this.typeDeclarationResolver);
-}
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
new file mode 100644
index 0000000..b33dd94
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/isolated_executor/isolated_executor.dart
@@ -0,0 +1,227 @@
+// Copyright (c) 2021, 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:async';
+import 'dart:isolate';
+
+import '../api.dart';
+import '../executor_shared/protocol.dart';
+import '../executor_shared/serialization.dart';
+import '../executor_shared/response_impls.dart';
+import '../executor.dart';
+
+/// Returns an instance of [_IsolatedMacroExecutor].
+///
+/// This is the only public api exposed by this library.
+Future<MacroExecutor> start() async => new _IsolatedMacroExecutor();
+
+/// A [MacroExecutor] implementation which spawns a separate isolate for each
+/// macro that is loaded. Each of these is wrapped in its own
+/// [_SingleIsolatedMacroExecutor] which requests are delegated to.
+///
+/// This implementation requires precompiled kernel files when loading macros,
+/// (you must pass a `precompiledKernelUri` to [loadMacro]).
+///
+/// Spawned isolates are not ran in the same isolate group, so objects are
+/// serialized between isolates.
+class _IsolatedMacroExecutor implements MacroExecutor {
+ /// Individual executors indexed by [MacroClassIdentifier] or
+ /// [MacroInstanceIdentifier].
+ final _executors = <Object, _SingleIsolatedMacroExecutor>{};
+
+ @override
+ Future<String> buildAugmentationLibrary(
+ Iterable<MacroExecutionResult> macroResults) {
+ // TODO: implement buildAugmentationLibrary
+ throw new UnimplementedError();
+ }
+
+ @override
+ void close() {
+ for (_SingleIsolatedMacroExecutor executor in _executors.values) {
+ executor.close();
+ }
+ }
+
+ @override
+ Future<MacroExecutionResult> executeDeclarationsPhase(
+ MacroInstanceIdentifier macro,
+ Declaration declaration,
+ TypeResolver typeResolver,
+ ClassIntrospector classIntrospector) =>
+ _executors[macro]!.executeDeclarationsPhase(
+ macro, declaration, typeResolver, classIntrospector);
+
+ @override
+ Future<MacroExecutionResult> executeDefinitionsPhase(
+ MacroInstanceIdentifier macro,
+ Declaration declaration,
+ TypeResolver typeResolver,
+ ClassIntrospector classIntrospector,
+ TypeDeclarationResolver typeDeclarationResolver) =>
+ _executors[macro]!.executeDefinitionsPhase(macro, declaration,
+ typeResolver, classIntrospector, typeDeclarationResolver);
+
+ @override
+ Future<MacroExecutionResult> executeTypesPhase(
+ MacroInstanceIdentifier macro, Declaration declaration) =>
+ _executors[macro]!.executeTypesPhase(macro, declaration);
+
+ @override
+ Future<MacroInstanceIdentifier> instantiateMacro(
+ MacroClassIdentifier macroClass,
+ String constructor,
+ Arguments arguments) async {
+ _SingleIsolatedMacroExecutor executor = _executors[macroClass]!;
+ MacroInstanceIdentifier instance =
+ await executor.instantiateMacro(macroClass, constructor, arguments);
+ _executors[instance] = executor;
+ return instance;
+ }
+
+ @override
+ Future<MacroClassIdentifier> loadMacro(Uri library, String name,
+ {Uri? precompiledKernelUri}) async {
+ if (precompiledKernelUri == null) {
+ throw new UnsupportedError(
+ 'This environment requires a non-null `precompiledKernelUri` to be '
+ 'passed when loading macros.');
+ }
+ MacroClassIdentifier identifier =
+ new MacroClassIdentifierImpl(library, name);
+ _executors.remove(identifier)?.close();
+
+ _SingleIsolatedMacroExecutor executor =
+ await _SingleIsolatedMacroExecutor.start(
+ library, name, precompiledKernelUri);
+ _executors[identifier] = executor;
+ return identifier;
+ }
+}
+
+class _SingleIsolatedMacroExecutor extends MacroExecutor {
+ /// The stream on which we receive responses.
+ final Stream<Response> responseStream;
+
+ /// The send port where we should send requests.
+ final SendPort sendPort;
+
+ /// A function that should be invoked when shutting down this executor
+ /// to perform any necessary cleanup.
+ final void Function() onClose;
+
+ /// A map of response completers by request id.
+ final responseCompleters = <int, Completer<Response>>{};
+
+ _SingleIsolatedMacroExecutor(
+ {required this.onClose,
+ required this.responseStream,
+ required this.sendPort}) {
+ responseStream.listen((event) {
+ Completer<Response>? completer =
+ responseCompleters.remove(event.requestId);
+ if (completer == null) {
+ throw new StateError(
+ 'Got a response for an unrecognized request id ${event.requestId}');
+ }
+ completer.complete(event);
+ });
+ }
+
+ static Future<_SingleIsolatedMacroExecutor> start(
+ Uri library, String name, Uri precompiledKernelUri) async {
+ ReceivePort receivePort = new ReceivePort();
+ Isolate isolate =
+ await Isolate.spawnUri(precompiledKernelUri, [], receivePort.sendPort);
+ Completer<SendPort> sendPortCompleter = new Completer();
+ StreamController<Response> responseStreamController =
+ new StreamController(sync: true);
+ receivePort.listen((message) {
+ if (!sendPortCompleter.isCompleted) {
+ sendPortCompleter.complete(message as SendPort);
+ } else {
+ withSerializationMode(SerializationMode.server, () {
+ JsonDeserializer deserializer =
+ new JsonDeserializer(message as List<Object?>);
+ SerializableResponse response =
+ new SerializableResponse.deserialize(deserializer);
+ responseStreamController.add(response);
+ });
+ }
+ }).onDone(responseStreamController.close);
+
+ return new _SingleIsolatedMacroExecutor(
+ onClose: () {
+ receivePort.close();
+ isolate.kill();
+ },
+ responseStream: responseStreamController.stream,
+ sendPort: await sendPortCompleter.future);
+ }
+
+ @override
+ void close() => onClose();
+
+ /// These calls are handled by the higher level executor.
+ @override
+ Future<String> buildAugmentationLibrary(
+ Iterable<MacroExecutionResult> macroResults) =>
+ throw new StateError('Unreachable');
+
+ @override
+ Future<MacroExecutionResult> executeDeclarationsPhase(
+ MacroInstanceIdentifier macro,
+ Declaration declaration,
+ TypeResolver typeResolver,
+ ClassIntrospector classIntrospector) {
+ // TODO: implement executeDeclarationsPhase
+ throw new UnimplementedError();
+ }
+
+ @override
+ Future<MacroExecutionResult> executeDefinitionsPhase(
+ MacroInstanceIdentifier macro,
+ Declaration declaration,
+ TypeResolver typeResolver,
+ ClassIntrospector classIntrospector,
+ TypeDeclarationResolver typeDeclarationResolver) =>
+ _sendRequest(new ExecuteDefinitionsPhaseRequest(macro, declaration,
+ typeResolver, classIntrospector, typeDeclarationResolver));
+
+ @override
+ Future<MacroExecutionResult> executeTypesPhase(
+ MacroInstanceIdentifier macro, Declaration declaration) {
+ // TODO: implement executeTypesPhase
+ throw new UnimplementedError();
+ }
+
+ @override
+ Future<MacroInstanceIdentifier> instantiateMacro(
+ MacroClassIdentifier macroClass,
+ String constructor,
+ Arguments arguments) =>
+ _sendRequest(
+ new InstantiateMacroRequest(macroClass, constructor, arguments));
+
+ /// These calls are handled by the higher level executor.
+ @override
+ Future<MacroClassIdentifier> loadMacro(Uri library, String name,
+ {Uri? precompiledKernelUri}) =>
+ throw new StateError('Unreachable');
+
+ /// Sends a [request] and handles the response, casting it to the expected
+ /// type or throwing the error provided.
+ Future<T> _sendRequest<T>(Request request) =>
+ withSerializationMode(SerializationMode.server, () async {
+ JsonSerializer serializer = new JsonSerializer();
+ request.serialize(serializer);
+ sendPort.send(serializer.result);
+ Completer<Response> completer = new Completer<Response>();
+ responseCompleters[request.id] = completer;
+ Response response = await completer.future;
+ T? result = response.response as T?;
+ if (result != null) return result;
+ throw response.error!;
+ });
+}
diff --git a/pkg/_fe_analyzer_shared/test/macros/executor_shared/serialization_test.dart b/pkg/_fe_analyzer_shared/test/macros/executor_shared/serialization_test.dart
new file mode 100644
index 0000000..c069423
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/test/macros/executor_shared/serialization_test.dart
@@ -0,0 +1,78 @@
+// Copyright (c) 2021, 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:_fe_analyzer_shared/src/macros/executor_shared/serialization.dart';
+import 'package:test/test.dart';
+
+void main() {
+ group('json serializer', () {
+ test('can serialize and deserialize data', () {
+ var serializer = JsonSerializer();
+ serializer
+ ..addNum(1)
+ ..addNullableNum(null)
+ ..addString('hello')
+ ..addNullableString(null)
+ ..startList()
+ ..addBool(true)
+ ..startList()
+ ..addNull()
+ ..endList()
+ ..addNullableBool(null)
+ ..endList()
+ ..addNum(1.0)
+ ..startList()
+ ..endList();
+ expect(
+ serializer.result,
+ equals([
+ 1,
+ null,
+ 'hello',
+ null,
+ [
+ true,
+ [null],
+ null
+ ],
+ 1.0,
+ [],
+ ]));
+ var deserializer = JsonDeserializer(serializer.result);
+ expect(deserializer.moveNext(), true);
+ expect(deserializer.expectNum(), 1);
+ expect(deserializer.moveNext(), true);
+ expect(deserializer.expectNullableNum(), null);
+ expect(deserializer.moveNext(), true);
+ expect(deserializer.expectString(), 'hello');
+ expect(deserializer.moveNext(), true);
+ expect(deserializer.expectNullableString(), null);
+ expect(deserializer.moveNext(), true);
+
+ deserializer.expectList();
+ expect(deserializer.moveNext(), true);
+ expect(deserializer.expectBool(), true);
+ expect(deserializer.moveNext(), true);
+
+ deserializer.expectList();
+ expect(deserializer.moveNext(), true);
+ expect(deserializer.checkNull(), true);
+ expect(deserializer.moveNext(), false);
+
+ expect(deserializer.moveNext(), true);
+ expect(deserializer.expectNullableBool(), null);
+ expect(deserializer.moveNext(), false);
+
+ // Have to move the parent again to advance it past the list entry.
+ expect(deserializer.moveNext(), true);
+ expect(deserializer.expectNum(), 1.0);
+ expect(deserializer.moveNext(), true);
+
+ deserializer.expectList();
+ expect(deserializer.moveNext(), false);
+
+ expect(deserializer.moveNext(), false);
+ });
+ });
+}
diff --git a/pkg/_fe_analyzer_shared/test/macros/isolate_mirror_executor/isolate_mirror_executor_test.dart b/pkg/_fe_analyzer_shared/test/macros/isolate_mirror_executor/isolate_mirror_executor_test.dart
index ab7b057..7056208 100644
--- a/pkg/_fe_analyzer_shared/test/macros/isolate_mirror_executor/isolate_mirror_executor_test.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/isolate_mirror_executor/isolate_mirror_executor_test.dart
@@ -6,6 +6,7 @@
import 'package:_fe_analyzer_shared/src/macros/api.dart';
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/isolate_mirrors_executor/isolate_mirrors_executor.dart'
as mirrorExecutor;
@@ -26,7 +27,7 @@
test('can load macros and create instances', () async {
var clazzId = await executor.loadMacro(
// Tests run from the root of the repo.
- File('pkg/_fe_analyzer_shared/test/macros/isolate_mirror_executor/simple_macro.dart')
+ File('pkg/_fe_analyzer_shared/test/macros/simple_macro.dart')
.absolute
.uri,
'SimpleMacro');
@@ -49,7 +50,7 @@
var definitionResult = await executor.executeDefinitionsPhase(
instanceId,
- _FunctionDeclaration(
+ FunctionDeclarationImpl(
isAbstract: false,
isExternal: false,
isGetter: false,
@@ -57,8 +58,8 @@
name: 'foo',
namedParameters: [],
positionalParameters: [],
- returnType:
- _TypeAnnotation(Code.fromString('String'), isNullable: false),
+ returnType: NamedTypeAnnotationImpl(
+ name: 'String', isNullable: false, typeArguments: const []),
typeParameters: [],
),
_FakeTypeResolver(),
@@ -82,63 +83,14 @@
with Fake
implements TypeDeclarationResolver {}
-class _FunctionDeclaration implements FunctionDeclaration {
- @override
- final bool isAbstract;
-
- @override
- final bool isExternal;
-
- @override
- final bool isGetter;
-
- @override
- final bool isSetter;
-
- @override
- final String name;
-
- @override
- final Iterable<ParameterDeclaration> namedParameters;
-
- @override
- final Iterable<ParameterDeclaration> positionalParameters;
-
- @override
- final TypeAnnotation returnType;
-
- @override
- final Iterable<TypeParameterDeclaration> typeParameters;
-
- _FunctionDeclaration({
- required this.isAbstract,
- required this.isExternal,
- required this.isGetter,
- required this.isSetter,
- required this.name,
- required this.namedParameters,
- required this.positionalParameters,
- required this.returnType,
- required this.typeParameters,
- });
-}
-
-class _TypeAnnotation implements TypeAnnotation {
- @override
- final Code code;
-
- @override
- final bool isNullable;
-
- _TypeAnnotation(this.code, {required this.isNullable});
-}
-
extension _ on Code {
StringBuffer debugString([StringBuffer? buffer]) {
buffer ??= StringBuffer();
for (var part in parts) {
if (part is Code) {
part.debugString(buffer);
+ } else if (part is TypeAnnotation) {
+ part.code.debugString(buffer);
} else {
buffer.write(part.toString());
}
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
new file mode 100644
index 0000000..4659971
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/test/macros/isolated_executor/isolated_executor_test.dart
@@ -0,0 +1,122 @@
+// Copyright (c) 2021, 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:io';
+import 'dart:isolate';
+
+import 'package:_fe_analyzer_shared/src/macros/api.dart';
+import 'package:_fe_analyzer_shared/src/macros/bootstrap.dart';
+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/isolated_executor/isolated_executor.dart'
+ as isolatedExecutor;
+
+import 'package:test/fake.dart';
+import 'package:test/test.dart';
+
+void main() {
+ late MacroExecutor executor;
+ late Directory tmpDir;
+
+ setUp(() async {
+ executor = await isolatedExecutor.start();
+ tmpDir = Directory.systemTemp.createTempSync('isolated_executor_test');
+ });
+
+ tearDown(() {
+ if (tmpDir.existsSync()) tmpDir.deleteSync(recursive: true);
+ executor.close();
+ });
+
+ test('can load macros and create instances', () async {
+ // Tests run from the root of the repo.
+ var macroUri = File('pkg/_fe_analyzer_shared/test/macros/simple_macro.dart')
+ .absolute
+ .uri;
+ var macroName = 'SimpleMacro';
+
+ var bootstrapContent =
+ bootstrapMacroIsolate(macroUri.toString(), macroName, ['', 'named']);
+ var bootstrapFile = File(tmpDir.uri.resolve('main.dart').toFilePath())
+ ..writeAsStringSync(bootstrapContent);
+ var kernelOutputFile =
+ File(tmpDir.uri.resolve('main.dart.dill').toFilePath());
+ var result = await Process.run(Platform.resolvedExecutable, [
+ '--snapshot=${kernelOutputFile.uri.toFilePath()}',
+ '--snapshot-kind=kernel',
+ '--packages=${(await Isolate.packageConfig)!}',
+ bootstrapFile.uri.toFilePath(),
+ ]);
+ expect(result.exitCode, 0,
+ reason: 'stdout: ${result.stdout}\nstderr: ${result.stderr}');
+
+ var clazzId = await executor.loadMacro(macroUri, macroName,
+ precompiledKernelUri: kernelOutputFile.uri);
+ expect(clazzId, isNotNull, reason: 'Can load a macro.');
+
+ var instanceId =
+ await executor.instantiateMacro(clazzId, '', Arguments([], {}));
+ expect(instanceId, isNotNull,
+ reason: 'Can create an instance with no arguments.');
+
+ instanceId =
+ await executor.instantiateMacro(clazzId, '', Arguments([1, 2], {}));
+ expect(instanceId, isNotNull,
+ reason: 'Can create an instance with positional arguments.');
+
+ instanceId = await executor.instantiateMacro(
+ clazzId, 'named', Arguments([], {'x': 1, 'y': 2}));
+ expect(instanceId, isNotNull,
+ reason: 'Can create an instance with named arguments.');
+
+ var definitionResult = await executor.executeDefinitionsPhase(
+ instanceId,
+ FunctionDeclarationImpl(
+ isAbstract: false,
+ isExternal: false,
+ isGetter: false,
+ isSetter: false,
+ name: 'foo',
+ namedParameters: [],
+ positionalParameters: [],
+ returnType: NamedTypeAnnotationImpl(
+ name: 'String', isNullable: false, typeArguments: const []),
+ typeParameters: [],
+ ),
+ _FakeTypeResolver(),
+ _FakeClassIntrospector(),
+ _FakeTypeDeclarationResolver());
+ expect(definitionResult.augmentations, hasLength(1));
+ expect(definitionResult.augmentations.first.debugString().toString(),
+ equalsIgnoringWhitespace('''
+ augment String foo() {
+ print('x: 1, y: 2');
+ return augment super();
+ }'''));
+ });
+}
+
+class _FakeClassIntrospector with Fake implements ClassIntrospector {}
+
+class _FakeTypeResolver with Fake implements TypeResolver {}
+
+class _FakeTypeDeclarationResolver
+ with Fake
+ implements TypeDeclarationResolver {}
+
+extension _ on Code {
+ StringBuffer debugString([StringBuffer? buffer]) {
+ buffer ??= StringBuffer();
+ for (var part in parts) {
+ if (part is Code) {
+ part.debugString(buffer);
+ } else if (part is TypeAnnotation) {
+ part.code.debugString(buffer);
+ } else {
+ buffer.write(part.toString());
+ }
+ }
+ return buffer;
+ }
+}
diff --git a/pkg/_fe_analyzer_shared/test/macros/isolate_mirror_executor/simple_macro.dart b/pkg/_fe_analyzer_shared/test/macros/simple_macro.dart
similarity index 100%
rename from pkg/_fe_analyzer_shared/test/macros/isolate_mirror_executor/simple_macro.dart
rename to pkg/_fe_analyzer_shared/test/macros/simple_macro.dart
diff --git a/pkg/front_end/test/macros/macro_test.dart b/pkg/front_end/test/macros/macro_test.dart
index 7a4dee8..2ceca95 100644
--- a/pkg/front_end/test/macros/macro_test.dart
+++ b/pkg/front_end/test/macros/macro_test.dart
@@ -5,6 +5,7 @@
import 'dart:io' show Directory, Platform;
import 'package:_fe_analyzer_shared/src/macros/api.dart';
import 'package:_fe_analyzer_shared/src/macros/executor.dart';
+import 'package:_fe_analyzer_shared/src/macros/executor_shared/serialization.dart';
import 'package:_fe_analyzer_shared/src/testing/id.dart' show ActualData, Id;
import 'package:_fe_analyzer_shared/src/testing/id_testing.dart';
import 'package:_fe_analyzer_shared/src/testing/features.dart';
@@ -292,7 +293,12 @@
}
@override
- Future<MacroClassIdentifier> loadMacro(Uri library, String name) async {
+ Future<MacroClassIdentifier> loadMacro(Uri library, String name,
+ {Uri? precompiledKernelUri}) async {
+ if (precompiledKernelUri != null) {
+ throw new UnsupportedError(
+ 'Precompiled kernel not supported for this implementation.');
+ }
_MacroClassIdentifier id = new _MacroClassIdentifier(library, name);
macroClasses.add(id);
return id;
@@ -320,6 +326,9 @@
@override
String toString() => 'MacroClassIdentifier($uri,$className)';
+
+ @override
+ void serialize(Serializer serializer) => throw UnimplementedError();
}
class _MacroInstanceIdentifier implements MacroInstanceIdentifier {
@@ -330,4 +339,7 @@
_MacroInstanceIdentifier(this.macroClass, this.constructor, this.arguments);
String toText() => '${macroClass.toText()}/${constructor}()';
+
+ @override
+ void serialize(Serializer serializer) => throw UnimplementedError();
}
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index 613e0b7..8b9b65b4 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -141,6 +141,7 @@
bof
bom
bones
+bootstrap
bother
boundness
boxed
@@ -326,6 +327,7 @@
degrades
degree
del
+delegated
delimit
delimiting
demands
@@ -420,6 +422,7 @@
engineered
enhanced
enters
+entrypoint
entrypointish
enumerates
env
@@ -436,6 +439,7 @@
eval
execute
executor
+executors
exhausted
existence
existentially
@@ -603,6 +607,7 @@
impl
impl+
implementers
+impls
imply
implying
importantly
@@ -614,6 +619,7 @@
inconsistency
increased
incremented
+incrementing
independently
indexer
indexing
@@ -668,6 +674,7 @@
is64
isolate
isolated
+isolates
issuecomment
issuing
iterables
@@ -840,6 +847,7 @@
newest
nextnext
ni
+nil
nine
nj
nk
@@ -969,6 +977,7 @@
pre
prebuild
prebuilt
+precompiled
preexisted
preexisting
premark
@@ -1226,6 +1235,7 @@
spaced
sparse
spawn
+spawned
spawns
spec
spec'ed
@@ -1483,6 +1493,7 @@
vegorov
verbosity
versa
+versus
viable
vice
video