get TypeResolver and StaticType interfaces working
Change-Id: I7cebff43f1079679525bd11c1622d277b55c1501
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/228060
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Jake Macdonald <jakemac@google.com>
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/api/builders.dart b/pkg/_fe_analyzer_shared/lib/src/macros/api/builders.dart
index 74bf73c..9a507f4 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/api/builders.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/api/builders.dart
@@ -30,7 +30,7 @@
/// may be asked to run in this state during the development cycle. It is
/// helpful for users if macros provide a best effort implementation in that
/// case or handle the error in a useful way.
- Future<StaticType> resolve(TypeAnnotation typeAnnotation);
+ Future<StaticType> resolve(covariant TypeAnnotation typeAnnotation);
}
/// The api used to introspect on a [ClassDeclaration].
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 09c2de9..c0f8a9f 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart
@@ -49,10 +49,10 @@
/// compared to other static types.
abstract class StaticType {
/// Returns true if this is a subtype of [other].
- Future<bool> isSubtypeOf(StaticType other);
+ Future<bool> isSubtypeOf(covariant StaticType other);
/// Returns true if this is an identical type to [other].
- Future<bool> isExactly(StaticType other);
+ Future<bool> isExactly(covariant StaticType other);
}
/// A subtype of [StaticType] representing types that can be resolved by name
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart b/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
index 332144f..158363f 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
@@ -27,6 +27,7 @@
import 'dart:isolate';
import 'package:_fe_analyzer_shared/src/macros/executor_shared/introspection_impls.dart';
+import 'package:_fe_analyzer_shared/src/macros/executor_shared/remote_instance.dart';
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';
@@ -39,27 +40,33 @@
///
/// Supports the client side of the macro expansion protocol.
void main(_, SendPort sendPort) {
+ /// Local function that sends requests and returns responses using [sendPort].
+ Future<Response> sendRequest(Request request) => _sendRequest(request, sendPort);
+
withSerializationMode(SerializationMode.client, () {
ReceivePort receivePort = new ReceivePort();
sendPort.send(receivePort.sendPort);
+
receivePort.listen((message) async {
var deserializer = JsonDeserializer(message as Iterable<Object?>)
..moveNext();
+ int zoneId = deserializer.expectNum();
+ deserializer..moveNext();
var type = MessageType.values[deserializer.expectNum()];
var serializer = JsonSerializer();
switch (type) {
case MessageType.instantiateMacroRequest:
- var request = InstantiateMacroRequest.deserialize(deserializer);
+ var request = new InstantiateMacroRequest.deserialize(deserializer, zoneId);
(await _instantiateMacro(request)).serialize(serializer);
break;
case MessageType.executeDefinitionsPhaseRequest:
- var request = ExecuteDefinitionsPhaseRequest.deserialize(
- deserializer,
- ClientTypeResolver(),
- ClientClassIntrospector(),
- ClientTypeDeclarationsResolver());
- (await _executeDefinitionsPhase(request)).serialize(serializer);
+ var request = new ExecuteDefinitionsPhaseRequest.deserialize(deserializer, zoneId);
+ (await _executeDefinitionsPhase(request, sendRequest)).serialize(serializer);
break;
+ case MessageType.response:
+ var response = new SerializableResponse.deserialize(deserializer, zoneId);
+ _responseCompleters.remove(response.requestId)!.complete(response);
+ return;
default:
throw new StateError('Unhandled event type \$type');
}
@@ -102,31 +109,46 @@
response: identifier,
requestId: request.id,
serializationZoneId: request.serializationZoneId);
- } catch (e) {
+ } catch (e, s) {
return new SerializableResponse(
responseType: MessageType.error,
error: e.toString(),
+ stackTrace: s.toString(),
requestId: request.id,
serializationZoneId: request.serializationZoneId);
}
}
Future<SerializableResponse> _executeDefinitionsPhase(
- ExecuteDefinitionsPhaseRequest request) async {
+ ExecuteDefinitionsPhaseRequest request,
+ Future<Response> Function(Request request) sendRequest) async {
try {
Macro? instance = _macroInstances[request.macro];
if (instance == null) {
throw new StateError('Unrecognized macro instance \${request.macro}\\n'
'Known instances: \$_macroInstances)');
}
+ var typeResolver = ClientTypeResolver(
+ sendRequest,
+ remoteInstance: request.typeResolver,
+ serializationZoneId: request.serializationZoneId);
+ var typeDeclarationResolver = ClientTypeDeclarationResolver(
+ sendRequest,
+ remoteInstance: request.typeDeclarationResolver,
+ serializationZoneId: request.serializationZoneId);
+ var classIntrospector = ClientClassIntrospector(
+ sendRequest,
+ remoteInstance: request.classIntrospector,
+ serializationZoneId: request.serializationZoneId);
+
Declaration declaration = request.declaration;
if (instance is FunctionDefinitionMacro &&
declaration is FunctionDeclarationImpl) {
FunctionDefinitionBuilderImpl builder = new FunctionDefinitionBuilderImpl(
declaration,
- request.typeResolver,
- request.typeDeclarationResolver,
- request.classIntrospector);
+ typeResolver,
+ typeDeclarationResolver,
+ classIntrospector);
await instance.buildDefinitionForFunction(declaration, builder);
return new SerializableResponse(
responseType: MessageType.macroExecutionResult,
@@ -137,12 +159,28 @@
throw new UnsupportedError(
('Only FunctionDefinitionMacros are supported currently'));
}
- } catch (e) {
+ } catch (e, s) {
return new SerializableResponse(
responseType: MessageType.error,
error: e.toString(),
+ stackTrace: s.toString(),
requestId: request.id,
serializationZoneId: request.serializationZoneId);
}
}
+
+/// Holds on to response completers by request id.
+final _responseCompleters = <int, Completer<Response>>{};
+
+/// Serializes [request], sends it to [sendPort], and sets up a [Completer] in
+/// [_responseCompleters] to handle the response.
+Future<Response> _sendRequest(Request request, SendPort sendPort) {
+ Completer<Response> completer = Completer();
+ _responseCompleters[request.id] = completer;
+ JsonSerializer serializer = JsonSerializer();
+ serializer.addNum(request.serializationZoneId);
+ request.serialize(serializer);
+ sendPort.send(serializer.result);
+ return completer.future;
+}
''';
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
index 68d0f94..3f9e55c 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/protocol.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/protocol.dart
@@ -6,6 +6,8 @@
/// the isolate or process doing the work of macro loading and execution.
library _fe_analyzer_shared.src.macros.executor_shared.protocol;
+import 'package:meta/meta.dart';
+
import '../executor.dart';
import '../api.dart';
import '../executor_shared/response_impls.dart';
@@ -22,13 +24,16 @@
Request({int? id, required this.serializationZoneId})
: this.id = id ?? _next++;
- Request.deserialize(Deserializer deserializer)
- : serializationZoneId = (deserializer..moveNext()).expectNum(),
- id = (deserializer..moveNext()).expectNum();
+ /// The [serializationZoneId] is a part of the header and needs to be parsed
+ /// before deserializing objects, and then passed in here.
+ Request.deserialize(Deserializer deserializer, this.serializationZoneId)
+ : id = (deserializer..moveNext()).expectNum();
- void serialize(Serializer serializer) => serializer
- ..addNum(serializationZoneId)
- ..addNum(id);
+ /// The [serializationZoneId] needs to be separately serialized before the
+ /// rest of the object. This is not done by the instances themselves but by
+ /// the macro implementations.
+ @mustCallSuper
+ void serialize(Serializer serializer) => serializer.addNum(id);
static int _next = 0;
}
@@ -38,10 +43,15 @@
class Response {
final Object? response;
final Object? error;
+ final String? stackTrace;
final int requestId;
- Response({this.response, this.error, required this.requestId})
- : assert(response != null || error != null),
+ Response({
+ this.response,
+ this.error,
+ this.stackTrace,
+ required this.requestId,
+ }) : assert(response != null || error != null),
assert(response == null || error == null);
}
@@ -50,11 +60,13 @@
final Serializable? response;
final MessageType responseType;
final String? error;
+ final String? stackTrace;
final int requestId;
final int serializationZoneId;
SerializableResponse({
this.error,
+ this.stackTrace,
required this.requestId,
this.response,
required this.responseType,
@@ -70,10 +82,13 @@
MessageType responseType = MessageType.values[deserializer.expectNum()];
Serializable? response;
String? error;
+ String? stackTrace;
switch (responseType) {
case MessageType.error:
deserializer.moveNext();
error = deserializer.expectString();
+ deserializer.moveNext();
+ stackTrace = deserializer.expectNullableString();
break;
case MessageType.macroClassIdentifier:
response = new MacroClassIdentifierImpl.deserialize(deserializer);
@@ -84,6 +99,12 @@
case MessageType.macroExecutionResult:
response = new MacroExecutionResultImpl.deserialize(deserializer);
break;
+ case MessageType.staticType:
+ response = RemoteInstance.deserialize(deserializer);
+ break;
+ case MessageType.boolean:
+ response = new BooleanValue.deserialize(deserializer);
+ break;
default:
throw new StateError('Unexpected response type $responseType');
}
@@ -92,6 +113,7 @@
responseType: responseType,
response: response,
error: error,
+ stackTrace: stackTrace,
requestId: (deserializer..moveNext()).expectNum(),
serializationZoneId: serializationZoneId);
}
@@ -99,16 +121,30 @@
void serialize(Serializer serializer) {
serializer
..addNum(serializationZoneId)
+ ..addNum(MessageType.response.index)
..addNum(responseType.index);
if (response != null) {
response!.serialize(serializer);
} else if (error != null) {
serializer.addString(error!.toString());
+ serializer.addNullableString(stackTrace);
}
serializer.addNum(requestId);
}
}
+class BooleanValue implements Serializable {
+ final bool value;
+
+ BooleanValue(this.value);
+
+ BooleanValue.deserialize(Deserializer deserializer)
+ : value = (deserializer..moveNext()).expectBool();
+
+ @override
+ void serialize(Serializer serializer) => serializer..addBool(value);
+}
+
/// A request to load a macro in this isolate.
class LoadMacroRequest extends Request {
final Uri library;
@@ -117,10 +153,11 @@
LoadMacroRequest(this.library, this.name, {required int serializationZoneId})
: super(serializationZoneId: serializationZoneId);
- LoadMacroRequest.deserialize(Deserializer deserializer)
+ LoadMacroRequest.deserialize(
+ Deserializer deserializer, int serializationZoneId)
: library = Uri.parse((deserializer..moveNext()).expectString()),
name = (deserializer..moveNext()).expectString(),
- super.deserialize(deserializer);
+ super.deserialize(deserializer, serializationZoneId);
@override
void serialize(Serializer serializer) {
@@ -142,11 +179,12 @@
{required int serializationZoneId})
: super(serializationZoneId: serializationZoneId);
- InstantiateMacroRequest.deserialize(Deserializer deserializer)
+ InstantiateMacroRequest.deserialize(
+ Deserializer deserializer, int serializationZoneId)
: macroClass = new MacroClassIdentifierImpl.deserialize(deserializer),
constructorName = (deserializer..moveNext()).expectString(),
arguments = new Arguments.deserialize(deserializer),
- super.deserialize(deserializer);
+ super.deserialize(deserializer, serializationZoneId);
@override
void serialize(Serializer serializer) {
@@ -164,14 +202,9 @@
final MacroInstanceIdentifier macro;
final DeclarationImpl 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;
+ final RemoteInstanceImpl typeResolver;
+ final RemoteInstanceImpl classIntrospector;
+ final RemoteInstanceImpl typeDeclarationResolver;
ExecuteDefinitionsPhaseRequest(this.macro, this.declaration,
this.typeResolver, this.classIntrospector, this.typeDeclarationResolver,
@@ -180,51 +213,175 @@
/// 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)
+ ExecuteDefinitionsPhaseRequest.deserialize(
+ Deserializer deserializer, int serializationZoneId)
: macro = new MacroInstanceIdentifierImpl.deserialize(deserializer),
declaration = RemoteInstance.deserialize(deserializer),
- super.deserialize(deserializer);
+ typeResolver = RemoteInstance.deserialize(deserializer),
+ classIntrospector = RemoteInstance.deserialize(deserializer),
+ typeDeclarationResolver = RemoteInstance.deserialize(deserializer),
+ super.deserialize(deserializer, serializationZoneId);
void serialize(Serializer serializer) {
serializer.addNum(MessageType.executeDefinitionsPhaseRequest.index);
macro.serialize(serializer);
declaration.serialize(serializer);
+ typeResolver.serialize(serializer);
+ classIntrospector.serialize(serializer);
+ typeDeclarationResolver.serialize(serializer);
+
super.serialize(serializer);
}
}
/// A request to reflect on a type annotation
-class ReflectTypeRequest extends Request {
+class ResolveTypeRequest extends Request {
final TypeAnnotationImpl typeAnnotation;
+ final RemoteInstanceImpl typeResolver;
- ReflectTypeRequest(this.typeAnnotation, {required int serializationZoneId})
+ ResolveTypeRequest(this.typeAnnotation, this.typeResolver,
+ {required int serializationZoneId})
: super(serializationZoneId: serializationZoneId);
/// When deserializing we have already consumed the message type, so we don't
/// consume it again.
- ReflectTypeRequest.deserialize(Deserializer deserializer)
+ ResolveTypeRequest.deserialize(
+ Deserializer deserializer, int serializationZoneId)
: typeAnnotation = RemoteInstance.deserialize(deserializer),
- super.deserialize(deserializer);
+ typeResolver = RemoteInstance.deserialize(deserializer),
+ super.deserialize(deserializer, serializationZoneId);
void serialize(Serializer serializer) {
- serializer.addNum(MessageType.reflectTypeRequest.index);
+ serializer.addNum(MessageType.resolveTypeRequest.index);
typeAnnotation.serialize(serializer);
+ typeResolver.serialize(serializer);
super.serialize(serializer);
}
}
-/// TODO: Implement this
+/// A request to check if a type is exactly another type.
+class IsExactlyTypeRequest extends Request {
+ final RemoteInstanceImpl leftType;
+ final RemoteInstanceImpl rightType;
+
+ IsExactlyTypeRequest(this.leftType, this.rightType,
+ {required int serializationZoneId})
+ : super(serializationZoneId: serializationZoneId);
+
+ /// When deserializing we have already consumed the message type, so we don't
+ /// consume it again.
+ IsExactlyTypeRequest.deserialize(
+ Deserializer deserializer, int serializationZoneId)
+ : leftType = RemoteInstance.deserialize(deserializer),
+ rightType = RemoteInstance.deserialize(deserializer),
+ super.deserialize(deserializer, serializationZoneId);
+
+ void serialize(Serializer serializer) {
+ serializer.addNum(MessageType.isExactlyTypeRequest.index);
+ leftType.serialize(serializer);
+ rightType.serialize(serializer);
+ super.serialize(serializer);
+ }
+}
+
+/// A request to check if a type is exactly another type.
+class IsSubtypeOfRequest extends Request {
+ final ClientStaticTypeImpl leftType;
+ final ClientStaticTypeImpl rightType;
+
+ IsSubtypeOfRequest(this.leftType, this.rightType,
+ {required int serializationZoneId})
+ : super(serializationZoneId: serializationZoneId);
+
+ /// When deserializing we have already consumed the message type, so we don't
+ /// consume it again.
+ IsSubtypeOfRequest.deserialize(
+ Deserializer deserializer, int serializationZoneId)
+ : leftType = RemoteInstance.deserialize(deserializer),
+ rightType = RemoteInstance.deserialize(deserializer),
+ super.deserialize(deserializer, serializationZoneId);
+
+ void serialize(Serializer serializer) {
+ serializer.addNum(MessageType.isSubtypeOfRequest.index);
+ leftType.remoteInstance.serialize(serializer);
+ rightType.remoteInstance.serialize(serializer);
+ super.serialize(serializer);
+ }
+}
+
+/// Client side implementation of a [TypeResolver], which creates a
+/// [ResolveTypeRequest] and passes it to a given [sendRequest] function which
+/// can return the [Response].
class ClientTypeResolver implements TypeResolver {
+ /// The actual remote instance of this type resolver.
+ final RemoteInstanceImpl remoteInstance;
+
+ /// The ID of the zone in which to find the original type resolver.
+ final int serializationZoneId;
+
+ /// A function that can send a request and return a response using an
+ /// arbitrary communication channel.
+ final Future<Response> Function(Request request) _sendRequest;
+
+ ClientTypeResolver(this._sendRequest,
+ {required this.remoteInstance, required this.serializationZoneId});
+
@override
- Future<StaticType> resolve(TypeAnnotation typeAnnotation) {
- // TODO: implement resolve
- throw new UnimplementedError();
+ Future<StaticType> resolve(TypeAnnotationImpl typeAnnotation) async {
+ ResolveTypeRequest request = new ResolveTypeRequest(
+ typeAnnotation, remoteInstance,
+ serializationZoneId: serializationZoneId);
+ RemoteInstanceImpl remoteType =
+ _handleResponse(await _sendRequest(request));
+ return new ClientStaticTypeImpl(_sendRequest,
+ remoteInstance: remoteType, serializationZoneId: serializationZoneId);
+ }
+}
+
+class ClientStaticTypeImpl implements StaticType {
+ /// The actual remote instance of this static type.
+ final RemoteInstanceImpl remoteInstance;
+
+ final int serializationZoneId;
+
+ /// A function that can send a request and return a response using an
+ /// arbitrary communication channel.
+ final Future<Response> Function(Request request) sendRequest;
+
+ ClientStaticTypeImpl(this.sendRequest,
+ {required this.remoteInstance, required this.serializationZoneId});
+
+ @override
+ Future<bool> isExactly(ClientStaticTypeImpl other) async {
+ IsExactlyTypeRequest request = new IsExactlyTypeRequest(
+ this.remoteInstance, other.remoteInstance,
+ serializationZoneId: serializationZoneId);
+ return _handleResponse<BooleanValue>(await sendRequest(request)).value;
+ }
+
+ @override
+ Future<bool> isSubtypeOf(ClientStaticTypeImpl other) async {
+ IsSubtypeOfRequest request = new IsSubtypeOfRequest(this, other,
+ serializationZoneId: serializationZoneId);
+ return _handleResponse<BooleanValue>(await sendRequest(request)).value;
}
}
/// TODO: Implement this
class ClientClassIntrospector implements ClassIntrospector {
+ /// The actual remote instance of this type resolver.
+ final RemoteInstanceImpl remoteInstance;
+
+ /// The ID of the zone in which to find the original type resolver.
+ final int serializationZoneId;
+
+ /// A function that can send a request and return a response using an
+ /// arbitrary communication channel.
+ final Future<Response> Function(Request request) sendRequest;
+
+ ClientClassIntrospector(this.sendRequest,
+ {required this.remoteInstance, required this.serializationZoneId});
+
@override
Future<List<ConstructorDeclaration>> constructorsOf(ClassDeclaration clazz) {
// TODO: implement constructorsOf
@@ -263,7 +420,20 @@
}
/// TODO: Implement this
-class ClientTypeDeclarationsResolver implements TypeDeclarationResolver {
+class ClientTypeDeclarationResolver implements TypeDeclarationResolver {
+ /// The actual remote instance of this type resolver.
+ final RemoteInstanceImpl remoteInstance;
+
+ /// The ID of the zone in which to find the original type resolver.
+ final int serializationZoneId;
+
+ /// A function that can send a request and return a response using an
+ /// arbitrary communication channel.
+ final Future<Response> Function(Request request) sendRequest;
+
+ ClientTypeDeclarationResolver(this.sendRequest,
+ {required this.remoteInstance, required this.serializationZoneId});
+
@override
Future<TypeDeclaration> declarationOf(NamedStaticType annotation) {
// TODO: implement declarationOf
@@ -271,13 +441,39 @@
}
}
+/// An exception that occurred remotely, the exception object and stack trace
+/// are serialized as [String]s and both included in the [toString] output.
+class RemoteException implements Exception {
+ final String error;
+ final String? stackTrace;
+
+ RemoteException(this.error, [this.stackTrace]);
+
+ String toString() =>
+ 'RemoteException: $error${stackTrace == null ? '' : '\n\n$stackTrace'}';
+}
+
+/// Either returns the actual response from [response], casted to [T], or throws
+/// a [RemoteException] with the given error and stack trace.
+T _handleResponse<T>(Response response) {
+ if (response.response != null) {
+ return response.response as T;
+ }
+ throw new RemoteException(response.error!.toString(), response.stackTrace);
+}
+
enum MessageType {
+ boolean,
error,
executeDefinitionsPhaseRequest,
instantiateMacroRequest,
+ isExactlyTypeRequest,
+ isSubtypeOfRequest,
loadMacroRequest,
- reflectTypeRequest,
+ resolveTypeRequest,
macroClassIdentifier,
macroInstanceIdentifier,
macroExecutionResult,
+ response,
+ staticType,
}
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/remote_instance.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/remote_instance.dart
index 5dcd1f8..59c26fd 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/remote_instance.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/remote_instance.dart
@@ -70,6 +70,20 @@
}
}
+/// A remote instance which is just a pointer to some server side instance of
+/// a generic object.
+///
+/// The wrapped object is not serialized.
+class RemoteInstanceImpl extends RemoteInstance {
+ /// Always null on the client side, has an actual instance on the server side.
+ final Object? instance;
+
+ @override
+ RemoteInstanceKind get kind => RemoteInstanceKind.instance;
+
+ RemoteInstanceImpl({required int id, this.instance}) : super(id);
+}
+
// The kinds of instances.
enum RemoteInstanceKind {
classDeclaration,
@@ -83,4 +97,6 @@
typeAliasDeclaration,
typeParameterDeclaration,
variableDeclaration,
+ // A generic instance that is just a pointer to some server side instance.
+ instance,
}
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
index 1fafb40..c950279 100644
--- 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
@@ -11,23 +11,29 @@
case SerializationMode.client:
moveNext();
RemoteInstanceKind kind = RemoteInstanceKind.values[expectNum()];
- moveNext();
switch (kind) {
case RemoteInstanceKind.namedTypeAnnotation:
+ moveNext();
return _expectNamedTypeAnnotation(id) as T;
case RemoteInstanceKind.functionTypeAnnotation:
+ moveNext();
return _expectFunctionTypeAnnotation(id) as T;
case RemoteInstanceKind.functionDeclaration:
+ moveNext();
return _expectFunctionDeclaration(id) as T;
case RemoteInstanceKind.parameterDeclaration:
+ moveNext();
return _expectParameterDeclaration(id) as T;
case RemoteInstanceKind.typeParameterDeclaration:
+ moveNext();
return _expectTypeParameterDeclaration(id) as T;
+ case RemoteInstanceKind.instance:
+ return new RemoteInstanceImpl(id: id) as T;
default:
throw new UnsupportedError('Unsupported remote object kind: $kind');
}
case SerializationMode.server:
- return RemoteInstance.cached(id);
+ return RemoteInstance.cached(id) as T;
}
}
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 24c7d80..f5a7287 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
@@ -9,6 +9,7 @@
import 'isolate_mirrors_impl.dart';
import '../executor_shared/introspection_impls.dart';
import '../executor_shared/protocol.dart';
+import '../executor_shared/remote_instance.dart';
import '../executor.dart';
import '../api.dart';
@@ -106,8 +107,15 @@
TypeResolver typeResolver,
ClassIntrospector classIntrospector,
TypeDeclarationResolver typeDeclarationResolver) =>
- _sendRequest(new ExecuteDefinitionsPhaseRequest(macro, declaration,
- typeResolver, classIntrospector, typeDeclarationResolver,
+ _sendRequest(new ExecuteDefinitionsPhaseRequest(
+ macro,
+ declaration,
+ new RemoteInstanceImpl(
+ instance: typeResolver, id: RemoteInstance.uniqueId),
+ new RemoteInstanceImpl(
+ instance: classIntrospector, id: RemoteInstance.uniqueId),
+ new RemoteInstanceImpl(
+ instance: typeDeclarationResolver, id: RemoteInstance.uniqueId),
// Serialization zones are not necessary in this executor.
serializationZoneId: -1));
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 fe7e32e..4673d17 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
@@ -89,9 +89,9 @@
declaration is FunctionDeclarationImpl) {
FunctionDefinitionBuilderImpl builder = new FunctionDefinitionBuilderImpl(
declaration,
- request.typeResolver,
- request.typeDeclarationResolver,
- request.classIntrospector);
+ request.typeResolver.instance as TypeResolver,
+ request.typeDeclarationResolver.instance as TypeDeclarationResolver,
+ request.classIntrospector.instance as ClassIntrospector);
await instance.buildDefinitionForFunction(declaration, builder);
return new Response(response: builder.result, requestId: request.id);
} else {
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
index 4b564b6..b2b3b49 100644
--- 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
@@ -5,6 +5,8 @@
import 'dart:async';
import 'dart:isolate';
+import 'package:_fe_analyzer_shared/src/macros/executor_shared/remote_instance.dart';
+
import '../api.dart';
import '../executor_shared/introspection_impls.dart';
import '../executor_shared/protocol.dart';
@@ -103,7 +105,7 @@
class _SingleIsolatedMacroExecutor extends MacroExecutor {
/// The stream on which we receive responses.
- final Stream<Response> responseStream;
+ final Stream<Object> messageStream;
/// The send port where we should send requests.
final SendPort sendPort;
@@ -126,16 +128,85 @@
_SingleIsolatedMacroExecutor(
{required this.onClose,
- required this.responseStream,
+ required this.messageStream,
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);
+ messageStream.listen((message) {
+ withSerializationMode(SerializationMode.server, () {
+ JsonDeserializer deserializer =
+ new JsonDeserializer(message as List<Object?>);
+ // Every object starts with a zone ID which dictates the zone in which
+ // we should deserialize the message.
+ deserializer.moveNext();
+ int zoneId = deserializer.expectNum();
+ Zone zone = serializationZones[zoneId]!;
+ zone.run(() async {
+ deserializer.moveNext();
+ MessageType messageType =
+ MessageType.values[deserializer.expectNum()];
+ switch (messageType) {
+ case MessageType.response:
+ SerializableResponse response =
+ new SerializableResponse.deserialize(deserializer, zoneId);
+ Completer<Response>? completer =
+ responseCompleters.remove(response.requestId);
+ if (completer == null) {
+ throw new StateError(
+ 'Got a response for an unrecognized request id '
+ '${response.requestId}');
+ }
+ completer.complete(response);
+ break;
+ case MessageType.resolveTypeRequest:
+ ResolveTypeRequest request =
+ new ResolveTypeRequest.deserialize(deserializer, zoneId);
+ SerializableResponse response = new SerializableResponse(
+ response: new RemoteInstanceImpl(
+ id: RemoteInstance.uniqueId,
+ instance:
+ await (request.typeResolver.instance as TypeResolver)
+ .resolve(request.typeAnnotation)),
+ requestId: request.id,
+ responseType: MessageType.staticType,
+ serializationZoneId: zoneId);
+ JsonSerializer serializer = new JsonSerializer();
+ response.serialize(serializer);
+ sendPort.send(serializer.result);
+ break;
+ case MessageType.isExactlyTypeRequest:
+ IsExactlyTypeRequest request =
+ new IsExactlyTypeRequest.deserialize(deserializer, zoneId);
+ StaticType leftType = request.leftType.instance as StaticType;
+ StaticType rightType = request.leftType.instance as StaticType;
+ SerializableResponse response = new SerializableResponse(
+ response:
+ new BooleanValue(await leftType.isExactly(rightType)),
+ requestId: request.id,
+ responseType: MessageType.boolean,
+ serializationZoneId: zoneId);
+ JsonSerializer serializer = new JsonSerializer();
+ response.serialize(serializer);
+ sendPort.send(serializer.result);
+ break;
+ case MessageType.isSubtypeOfRequest:
+ IsExactlyTypeRequest request =
+ new IsExactlyTypeRequest.deserialize(deserializer, zoneId);
+ StaticType leftType = request.leftType.instance as StaticType;
+ StaticType rightType = request.leftType.instance as StaticType;
+ SerializableResponse response = new SerializableResponse(
+ response:
+ new BooleanValue(await leftType.isSubtypeOf(rightType)),
+ requestId: request.id,
+ responseType: MessageType.boolean,
+ serializationZoneId: zoneId);
+ JsonSerializer serializer = new JsonSerializer();
+ response.serialize(serializer);
+ sendPort.send(serializer.result);
+ break;
+ default:
+ throw new StateError('Unexpected message type $messageType');
+ }
+ });
+ });
});
}
@@ -145,31 +216,22 @@
Isolate isolate =
await Isolate.spawnUri(precompiledKernelUri, [], receivePort.sendPort);
Completer<SendPort> sendPortCompleter = new Completer();
- StreamController<Response> responseStreamController =
+ StreamController<Object> messageStreamController =
new StreamController(sync: true);
receivePort.listen((message) {
if (!sendPortCompleter.isCompleted) {
sendPortCompleter.complete(message as SendPort);
} else {
- JsonDeserializer deserializer =
- new JsonDeserializer(message as List<Object?>);
- // Ever object starts with a zone ID which dictates the zone in which we
- // should deserialize the message.
- deserializer.moveNext();
- int zoneId = deserializer.expectNum();
- Zone zone = serializationZones[zoneId]!;
- SerializableResponse response = zone.run(
- () => new SerializableResponse.deserialize(deserializer, zoneId));
- responseStreamController.add(response);
+ messageStreamController.add(message);
}
- }).onDone(responseStreamController.close);
+ }).onDone(messageStreamController.close);
return new _SingleIsolatedMacroExecutor(
onClose: () {
receivePort.close();
isolate.kill();
},
- responseStream: responseStreamController.stream,
+ messageStream: messageStreamController.stream,
sendPort: await sendPortCompleter.future);
}
@@ -199,8 +261,15 @@
TypeResolver typeResolver,
ClassIntrospector classIntrospector,
TypeDeclarationResolver typeDeclarationResolver) =>
- _sendRequest((zoneId) => new ExecuteDefinitionsPhaseRequest(macro,
- declaration, typeResolver, classIntrospector, typeDeclarationResolver,
+ _sendRequest((zoneId) => new ExecuteDefinitionsPhaseRequest(
+ macro,
+ declaration,
+ new RemoteInstanceImpl(
+ instance: typeResolver, id: RemoteInstance.uniqueId),
+ new RemoteInstanceImpl(
+ instance: classIntrospector, id: RemoteInstance.uniqueId),
+ new RemoteInstanceImpl(
+ instance: typeDeclarationResolver, id: RemoteInstance.uniqueId),
serializationZoneId: zoneId));
@override
@@ -225,14 +294,16 @@
{Uri? precompiledKernelUri}) =>
throw new StateError('Unreachable');
- /// Sends a [request] and handles the response, casting it to the expected
- /// type or throwing the error provided.
+ /// Creates a [Request] with a given serialization zone ID, and handles the
+ /// response, casting it to the expected type or throwing the error provided.
Future<T> _sendRequest<T>(Request Function(int) requestFactory) =>
withSerializationMode(SerializationMode.server, () async {
int zoneId = _nextSerializationZoneId++;
serializationZones[zoneId] = Zone.current;
Request request = requestFactory(zoneId);
JsonSerializer serializer = new JsonSerializer();
+ // It is our responsibility to add the zone ID header.
+ serializer.addNum(zoneId);
request.serialize(serializer);
sendPort.send(serializer.result);
Completer<Response> completer = new Completer<Response>();
@@ -241,7 +312,8 @@
Response response = await completer.future;
T? result = response.response as T?;
if (result != null) return result;
- throw response.error!;
+ throw new RemoteException(
+ response.error!.toString(), response.stackTrace);
} finally {
// Clean up the zone after the request is done.
serializationZones.remove(zoneId);
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 aae2ad0..c1f7c1d 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
@@ -4,16 +4,16 @@
import 'dart:io';
-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/executor_shared/remote_instance.dart';
import 'package:_fe_analyzer_shared/src/macros/isolate_mirrors_executor/isolate_mirrors_executor.dart'
as mirrorExecutor;
-import 'package:test/fake.dart';
import 'package:test/test.dart';
+import '../util.dart';
+
void main() {
late MacroExecutor executor;
late File simpleMacroFile;
@@ -55,6 +55,11 @@
expect(instanceId, isNotNull,
reason: 'Can create an instance with named arguments.');
+ var returnType = NamedTypeAnnotationImpl(
+ id: RemoteInstance.uniqueId,
+ name: 'String',
+ isNullable: false,
+ typeArguments: const []);
var definitionResult = await executor.executeDefinitionsPhase(
instanceId,
FunctionDeclarationImpl(
@@ -66,16 +71,13 @@
name: 'foo',
namedParameters: [],
positionalParameters: [],
- returnType: NamedTypeAnnotationImpl(
- id: RemoteInstance.uniqueId,
- name: 'String',
- isNullable: false,
- typeArguments: const []),
+ returnType: returnType,
typeParameters: [],
),
- _FakeTypeResolver(),
- _FakeClassIntrospector(),
- _FakeTypeDeclarationResolver());
+ TestTypeResolver(
+ {returnType: TestStaticType('dart:core', 'String', [])}),
+ FakeClassIntrospector(),
+ FakeTypeDeclarationResolver());
expect(definitionResult.augmentations, hasLength(1));
expect(definitionResult.augmentations.first.debugString().toString(),
equalsIgnoringWhitespace('''
@@ -85,27 +87,3 @@
}'''));
});
}
-
-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/isolated_executor/isolated_executor_test.dart b/pkg/_fe_analyzer_shared/test/macros/isolated_executor/isolated_executor_test.dart
index c4cac8b..1bdb14f 100644
--- 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
@@ -5,7 +5,6 @@
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';
@@ -13,9 +12,10 @@
import 'package:_fe_analyzer_shared/src/macros/isolated_executor/isolated_executor.dart'
as isolatedExecutor;
-import 'package:test/fake.dart';
import 'package:test/test.dart';
+import '../util.dart';
+
void main() {
late MacroExecutor executor;
late Directory tmpDir;
@@ -40,7 +40,7 @@
executor.close();
});
- test('can load macros and create instances', () async {
+ test('can load and run macros', () async {
var macroUri = simpleMacroFile.absolute.uri;
var macroName = 'SimpleMacro';
@@ -78,6 +78,11 @@
expect(instanceId, isNotNull,
reason: 'Can create an instance with named arguments.');
+ var returnType = NamedTypeAnnotationImpl(
+ id: RemoteInstance.uniqueId,
+ name: 'String',
+ isNullable: false,
+ typeArguments: const []);
var definitionResult = await executor.executeDefinitionsPhase(
instanceId,
FunctionDeclarationImpl(
@@ -89,16 +94,13 @@
name: 'foo',
namedParameters: [],
positionalParameters: [],
- returnType: NamedTypeAnnotationImpl(
- id: RemoteInstance.uniqueId,
- name: 'String',
- isNullable: false,
- typeArguments: const []),
+ returnType: returnType,
typeParameters: [],
),
- _FakeTypeResolver(),
- _FakeClassIntrospector(),
- _FakeTypeDeclarationResolver());
+ TestTypeResolver(
+ {returnType: TestStaticType('dart:core', 'String', [])}),
+ FakeClassIntrospector(),
+ FakeTypeDeclarationResolver());
expect(definitionResult.augmentations, hasLength(1));
expect(definitionResult.augmentations.first.debugString().toString(),
equalsIgnoringWhitespace('''
@@ -108,27 +110,3 @@
}'''));
});
}
-
-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/simple_macro.dart b/pkg/_fe_analyzer_shared/test/macros/simple_macro.dart
index b95da0b..ff31b12 100644
--- a/pkg/_fe_analyzer_shared/test/macros/simple_macro.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/simple_macro.dart
@@ -18,13 +18,23 @@
@override
FutureOr<void> buildDefinitionForFunction(
- FunctionDeclaration method, FunctionDefinitionBuilder builder) {
+ FunctionDeclaration method, FunctionDefinitionBuilder builder) async {
if (method.namedParameters
.followedBy(method.positionalParameters)
.isNotEmpty) {
throw ArgumentError(
'This macro can only be run on functions with no arguments!');
}
+
+ // Test the type resolver and static type interfaces
+ var staticReturnType = await builder.resolve(method.returnType);
+ if (!(await staticReturnType.isExactly(staticReturnType))) {
+ throw StateError('The return type should be exactly equal to itself!');
+ }
+ if (!(await staticReturnType.isSubtypeOf(staticReturnType))) {
+ throw StateError('The return type should be a subtype of itself!');
+ }
+
builder.augment(FunctionBodyCode.fromString('''{
print('x: $x, y: $y');
return augment super();
diff --git a/pkg/_fe_analyzer_shared/test/macros/util.dart b/pkg/_fe_analyzer_shared/test/macros/util.dart
new file mode 100644
index 0000000..179f50b
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/test/macros/util.dart
@@ -0,0 +1,60 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:_fe_analyzer_shared/src/macros/api.dart';
+
+import 'package:test/fake.dart';
+
+class FakeClassIntrospector with Fake implements ClassIntrospector {}
+
+class FakeTypeDeclarationResolver with Fake implements TypeDeclarationResolver {
+}
+
+class TestTypeResolver implements TypeResolver {
+ final Map<TypeAnnotation, StaticType> staticTypes;
+
+ TestTypeResolver(this.staticTypes);
+
+ @override
+ Future<StaticType> resolve(covariant TypeAnnotation typeAnnotation) async {
+ return staticTypes[typeAnnotation]!;
+ }
+}
+
+// Doesn't handle generics etc but thats ok for now
+class TestStaticType implements StaticType {
+ final String library;
+ final String name;
+ final List<TestStaticType> superTypes;
+
+ TestStaticType(this.library, this.name, this.superTypes);
+
+ @override
+ Future<bool> isExactly(TestStaticType other) async => _isExactly(other);
+
+ @override
+ Future<bool> isSubtypeOf(TestStaticType other) async =>
+ _isExactly(other) ||
+ superTypes.any((superType) => superType._isExactly(other));
+
+ bool _isExactly(TestStaticType other) =>
+ identical(other, this) ||
+ (library == other.library && name == other.name);
+}
+
+extension DebugCodeString 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/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index e105550..ca03543 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -1110,6 +1110,7 @@
reloading
remapped
remedy
+remotely
removal
remover
renames