blob: 118ebf77f13309276e12f613f6e0420c8736d138 [file] [log] [blame]
// 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 '../api.dart';
import '../executor.dart';
import 'exception_impls.dart';
import 'introspection_impls.dart';
import 'remote_instance.dart';
import '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;
final int serializationZoneId;
Request({int? id, required this.serializationZoneId}) : id = id ?? _next++;
/// 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()).expectInt();
/// 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.
@override
void serialize(Serializer serializer) => serializer.addInt(id);
static int _next = 0;
}
/// A generic response object that contains either a response or an exception,
/// and a unique ID.
class Response {
final Object? response;
final MacroException? exception;
final int requestId;
final MessageType responseType;
Response({
this.response,
this.exception,
required this.requestId,
required this.responseType,
}) : assert(response != null || exception != null),
assert(response == null || exception == null);
}
/// A serializable [Response], contains the message type as an enum.
class SerializableResponse implements Response, Serializable {
@override
final Serializable? response;
@override
final MessageType responseType;
@override
final MacroExceptionImpl? exception;
@override
final int requestId;
final int serializationZoneId;
SerializableResponse({
this.exception,
required this.requestId,
this.response,
required this.responseType,
required this.serializationZoneId,
});
/// You must first parse the [serializationZoneId] yourself, and then
/// call this function in that zone, and pass the ID.
factory SerializableResponse.deserialize(
Deserializer deserializer, int serializationZoneId) {
deserializer.moveNext();
MessageType responseType = MessageType.values[deserializer.expectInt()];
Serializable? response;
MacroExceptionImpl? exception;
switch (responseType) {
case MessageType.exception:
deserializer.moveNext();
exception = deserializer.expectRemoteInstance();
break;
case MessageType.macroInstanceIdentifier:
response = MacroInstanceIdentifierImpl.deserialize(deserializer);
break;
case MessageType.macroExecutionResult:
response = MacroExecutionResultImpl.deserialize(deserializer);
break;
case MessageType.staticType:
case MessageType.namedStaticType:
response = RemoteInstance.deserialize(deserializer);
break;
case MessageType.boolean:
response = BooleanValue.deserialize(deserializer);
break;
case MessageType.declarationList:
response = DeclarationList.deserialize(deserializer);
break;
case MessageType.remoteInstance:
deserializer.moveNext();
if (!deserializer.checkNull()) {
response = deserializer.expectRemoteInstance();
}
break;
default:
throw StateError('Unexpected response type $responseType');
}
return SerializableResponse(
responseType: responseType,
response: response,
exception: exception,
requestId: (deserializer..moveNext()).expectInt(),
serializationZoneId: serializationZoneId);
}
@override
void serialize(Serializer serializer) {
serializer
..addInt(serializationZoneId)
..addInt(MessageType.response.index)
..addInt(responseType.index);
switch (responseType) {
case MessageType.exception:
exception!.serialize(serializer);
break;
default:
response.serializeNullable(serializer);
}
serializer.addInt(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 serialized list of [Declaration]s.
class DeclarationList<T extends DeclarationImpl> implements Serializable {
final List<T> declarations;
DeclarationList(this.declarations);
DeclarationList.deserialize(Deserializer deserializer)
: declarations = [
for (bool hasNext = (deserializer
..moveNext()
..expectList())
.moveNext();
hasNext;
hasNext = deserializer.moveNext())
deserializer.expectRemoteInstance(),
];
@override
void serialize(Serializer serializer) {
serializer.startList();
for (DeclarationImpl declaration in declarations) {
declaration.serialize(serializer);
}
serializer.endList();
}
}
/// A request to load a macro in this isolate.
class LoadMacroRequest extends Request {
final Uri library;
final String name;
LoadMacroRequest(this.library, this.name,
{required super.serializationZoneId});
LoadMacroRequest.deserialize(super.deserializer, super.serializationZoneId)
: library = Uri.parse((deserializer..moveNext()).expectString()),
name = (deserializer..moveNext()).expectString(),
super.deserialize();
@override
void serialize(Serializer serializer) {
serializer
..addInt(MessageType.loadMacroRequest.index)
..addString(library.toString())
..addString(name);
super.serialize(serializer);
}
}
/// A request to instantiate a macro instance.
class InstantiateMacroRequest extends Request {
final Uri library;
final String name;
final String constructor;
final Arguments arguments;
/// The ID to assign to the identifier, this needs to come from the requesting
/// side so that it is unique.
final int instanceId;
InstantiateMacroRequest(this.library, this.name, this.constructor,
this.arguments, this.instanceId,
{required super.serializationZoneId});
InstantiateMacroRequest.deserialize(
super.deserializer, super.serializationZoneId)
: library = (deserializer..moveNext()).expectUri(),
name = (deserializer..moveNext()).expectString(),
constructor = (deserializer..moveNext()).expectString(),
arguments = Arguments.deserialize(deserializer),
instanceId = (deserializer..moveNext()).expectInt(),
super.deserialize();
@override
void serialize(Serializer serializer) {
serializer
..addInt(MessageType.instantiateMacroRequest.index)
..addUri(library)
..addString(name)
..addString(constructor)
..addSerializable(arguments)
..addInt(instanceId);
super.serialize(serializer);
}
}
/// A request to dispose a macro instance by ID.
class DisposeMacroRequest extends Request {
final MacroInstanceIdentifier identifier;
DisposeMacroRequest(this.identifier, {required super.serializationZoneId});
DisposeMacroRequest.deserialize(super.deserializer, super.serializationZoneId)
: identifier = MacroInstanceIdentifierImpl.deserialize(deserializer),
super.deserialize();
@override
void serialize(Serializer serializer) {
serializer
..addInt(MessageType.disposeMacroRequest.index)
..addSerializable(identifier);
super.serialize(serializer);
}
}
/// Base class for the requests to execute a macro in a certain phase.
abstract class ExecutePhaseRequest extends Request {
final MacroInstanceIdentifier macro;
final RemoteInstance target;
final RemoteInstanceImpl introspector;
MessageType get kind;
ExecutePhaseRequest(this.macro, this.target, this.introspector,
{required super.serializationZoneId});
/// When deserializing we have already consumed the message type, so we don't
/// consume it again.
ExecutePhaseRequest.deserialize(super.deserializer, super.serializationZoneId)
: macro = MacroInstanceIdentifierImpl.deserialize(deserializer),
target = RemoteInstance.deserialize(deserializer),
introspector = RemoteInstance.deserialize(deserializer),
super.deserialize();
@override
void serialize(Serializer serializer) {
serializer.addInt(kind.index);
macro.serialize(serializer);
target.serialize(serializer);
introspector.serialize(serializer);
super.serialize(serializer);
}
}
/// A request to execute a macro on a particular declaration in the types phase.
class ExecuteTypesPhaseRequest extends ExecutePhaseRequest {
@override
MessageType get kind => MessageType.executeTypesPhaseRequest;
ExecuteTypesPhaseRequest(super.macro, super.target, super.identifierResolver,
{required super.serializationZoneId});
ExecuteTypesPhaseRequest.deserialize(
super.deserializer, super.serializationZoneId)
: super.deserialize();
}
/// A request to execute a macro on a particular declaration in the types phase.
class ExecuteDeclarationsPhaseRequest extends ExecutePhaseRequest {
@override
MessageType get kind => MessageType.executeDeclarationsPhaseRequest;
ExecuteDeclarationsPhaseRequest(
super.macro, super.target, super.identifierResolver,
{required super.serializationZoneId});
ExecuteDeclarationsPhaseRequest.deserialize(
super.deserializer, super.serializationZoneId)
: super.deserialize();
}
/// A request to execute a macro on a particular declaration in the types phase.
class ExecuteDefinitionsPhaseRequest extends ExecutePhaseRequest {
@override
MessageType get kind => MessageType.executeDefinitionsPhaseRequest;
ExecuteDefinitionsPhaseRequest(
super.macro, super.target, super.identifierResolver,
{required super.serializationZoneId});
ExecuteDefinitionsPhaseRequest.deserialize(
super.deserializer, super.serializationZoneId)
: super.deserialize();
}
/// A request to destroy a remote instance zone by id.
class DestroyRemoteInstanceZoneRequest extends Request {
DestroyRemoteInstanceZoneRequest({required super.serializationZoneId});
DestroyRemoteInstanceZoneRequest.deserialize(
super.deserializer, super.serializationZoneId)
: super.deserialize();
@override
void serialize(Serializer serializer) {
serializer.addInt(MessageType.destroyRemoteInstanceZoneRequest.index);
super.serialize(serializer);
}
}
class IntrospectionRequest extends Request {
final RemoteInstanceImpl introspector;
IntrospectionRequest(this.introspector, {required super.serializationZoneId});
IntrospectionRequest.deserialize(
super.deserializer, super.serializationZoneId)
: introspector = RemoteInstance.deserialize(deserializer),
super.deserialize();
@override
void serialize(Serializer serializer) {
introspector.serialize(serializer);
super.serialize(serializer);
}
}
/// A request to create a resolved identifier.
class ResolveIdentifierRequest extends IntrospectionRequest {
final Uri library;
final String name;
/// When deserializing we have already consumed the message type, so we don't
/// consume it again.
ResolveIdentifierRequest(this.library, this.name, super.introspector,
{required super.serializationZoneId});
ResolveIdentifierRequest.deserialize(
super.deserializer, super.serializationZoneId)
: library = Uri.parse((deserializer..moveNext()).expectString()),
name = (deserializer..moveNext()).expectString(),
super.deserialize();
@override
void serialize(Serializer serializer) {
serializer
..addInt(MessageType.resolveIdentifierRequest.index)
..addString(library.toString())
..addString(name);
super.serialize(serializer);
}
}
/// A request to resolve on a type annotation code object
class ResolveTypeRequest extends IntrospectionRequest {
final TypeAnnotationCode typeAnnotationCode;
ResolveTypeRequest(this.typeAnnotationCode, super.introspector,
{required super.serializationZoneId});
/// When deserializing we have already consumed the message type, so we don't
/// consume it again.
ResolveTypeRequest.deserialize(super.deserializer, super.serializationZoneId)
: typeAnnotationCode = (deserializer..moveNext()).expectCode(),
super.deserialize();
@override
void serialize(Serializer serializer) {
serializer.addInt(MessageType.resolveTypeRequest.index);
typeAnnotationCode.serialize(serializer);
super.serialize(serializer);
}
}
/// 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 super.serializationZoneId});
/// When deserializing we have already consumed the message type, so we don't
/// consume it again.
IsExactlyTypeRequest.deserialize(
super.deserializer, super.serializationZoneId)
: leftType = RemoteInstance.deserialize(deserializer),
rightType = RemoteInstance.deserialize(deserializer),
super.deserialize();
@override
void serialize(Serializer serializer) {
serializer.addInt(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 RemoteInstanceImpl leftType;
final RemoteInstanceImpl rightType;
IsSubtypeOfRequest(this.leftType, this.rightType,
{required super.serializationZoneId});
/// When deserializing we have already consumed the message type, so we don't
/// consume it again.
IsSubtypeOfRequest.deserialize(super.deserializer, super.serializationZoneId)
: leftType = RemoteInstance.deserialize(deserializer),
rightType = RemoteInstance.deserialize(deserializer),
super.deserialize();
@override
void serialize(Serializer serializer) {
serializer.addInt(MessageType.isSubtypeOfRequest.index);
leftType.serialize(serializer);
rightType.serialize(serializer);
super.serialize(serializer);
}
}
/// A general request class for all requests coming from methods on the
/// [DeclarationPhaseIntrospector] interface that are related to a single type.
class TypeIntrospectorRequest extends IntrospectionRequest {
final Object declaration;
final MessageType requestKind;
TypeIntrospectorRequest(
this.declaration, super.introspector, this.requestKind,
{required super.serializationZoneId});
/// When deserializing we have already consumed the message type, so we don't
/// consume it again and it should instead be passed in here.
TypeIntrospectorRequest.deserialize(
Deserializer deserializer, this.requestKind, int serializationZoneId)
: declaration = RemoteInstance.deserialize(deserializer),
super.deserialize(deserializer, serializationZoneId);
@override
void serialize(Serializer serializer) {
serializer.addInt(requestKind.index);
(declaration as Serializable).serialize(serializer);
super.serialize(serializer);
}
}
/// A request to get a [Declaration] for an [identifier].
///
/// Used for both the `typeDeclarationOf` and `declarationOf` requests. A cast
/// is done on the client side to ensure only [TypeDeclaration]s are returned
/// from `typeDeclarationOf`.
class DeclarationOfRequest extends IntrospectionRequest {
final IdentifierImpl identifier;
final MessageType kind;
DeclarationOfRequest(this.identifier, this.kind, super.introspector,
{required super.serializationZoneId})
: assert(kind == MessageType.typeDeclarationOfRequest ||
kind == MessageType.declarationOfRequest);
/// When deserializing we have already consumed the message type, so we don't
/// consume it again.
DeclarationOfRequest.deserialize(
super.deserializer, super.serializationZoneId, this.kind)
: assert(kind == MessageType.typeDeclarationOfRequest ||
kind == MessageType.declarationOfRequest),
identifier = RemoteInstance.deserialize(deserializer),
super.deserialize();
@override
void serialize(Serializer serializer) {
serializer.addInt(kind.index);
identifier.serialize(serializer);
super.serialize(serializer);
}
}
/// A request to get an inferred [TypeAnnotation] for an
/// [OmittedTypeAnnotation].
class InferTypeRequest extends IntrospectionRequest {
final OmittedTypeAnnotationImpl omittedType;
InferTypeRequest(this.omittedType, super.introspector,
{required super.serializationZoneId});
/// When deserializing we have already consumed the message type, so we don't
/// consume it again.
InferTypeRequest.deserialize(super.deserializer, super.serializationZoneId)
: omittedType = RemoteInstance.deserialize(deserializer),
super.deserialize();
@override
void serialize(Serializer serializer) {
serializer.addInt(MessageType.inferTypeRequest.index);
omittedType.serialize(serializer);
super.serialize(serializer);
}
}
/// A request to get all the top level [Declaration]s in a [Library].
class DeclarationsOfRequest extends IntrospectionRequest {
final LibraryImpl library;
DeclarationsOfRequest(this.library, super.introspector,
{required super.serializationZoneId});
/// When deserializing we have already consumed the message type, so we don't
/// consume it again.
DeclarationsOfRequest.deserialize(
super.deserializer, super.serializationZoneId)
: library = RemoteInstance.deserialize(deserializer),
super.deserialize();
@override
void serialize(Serializer serializer) {
serializer.addInt(MessageType.topLevelDeclarationsOfRequest.index);
library.serialize(serializer);
super.serialize(serializer);
}
}
/// The base class for the client side introspectors from any phase, as well as
/// client side [StaticType]s.
///
/// These convert all method calls into RPCs, sent via [_sendRequest].
base class ClientIntrospector {
/// The actual remote instance to call methods on.
final RemoteInstanceImpl remoteInstance;
/// The ID of the zone in which to find the original builder.
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;
ClientIntrospector(this._sendRequest,
{required this.remoteInstance, required this.serializationZoneId});
}
/// Client side implementation of an [TypeBuilder], which creates converts all
/// method calls to remote procedure calls and sends them using [_sendRequest].
final class ClientTypePhaseIntrospector extends ClientIntrospector
implements TypePhaseIntrospector {
ClientTypePhaseIntrospector(super._sendRequest,
{required super.remoteInstance, required super.serializationZoneId});
@override
Future<Identifier> resolveIdentifier(Uri library, String name) async {
ResolveIdentifierRequest request = ResolveIdentifierRequest(
library, name, remoteInstance,
serializationZoneId: serializationZoneId);
return _handleResponse(await _sendRequest(request));
}
}
/// Client side implementation of a [DeclarationBuilder].
final class ClientDeclarationPhaseIntrospector
extends ClientTypePhaseIntrospector
implements DeclarationPhaseIntrospector {
ClientDeclarationPhaseIntrospector(super._sendRequest,
{required super.remoteInstance, required super.serializationZoneId});
@override
Future<StaticType> resolve(TypeAnnotationCode typeAnnotation) async {
ResolveTypeRequest request = ResolveTypeRequest(
typeAnnotation, remoteInstance,
serializationZoneId: serializationZoneId);
RemoteInstanceImpl remoteType =
_handleResponse(await _sendRequest(request));
return switch (remoteType.kind) {
RemoteInstanceKind.namedStaticType => ClientNamedStaticTypeImpl(
_sendRequest,
remoteInstance: remoteType,
serializationZoneId: serializationZoneId),
RemoteInstanceKind.staticType => ClientStaticTypeImpl(_sendRequest,
remoteInstance: remoteType, serializationZoneId: serializationZoneId),
_ => throw StateError(
'Expected either a StaticType or NamedStaticType but got '
'${remoteType.kind}'),
};
}
@override
Future<List<ConstructorDeclaration>> constructorsOf(
TypeDeclaration type) async {
TypeIntrospectorRequest request = TypeIntrospectorRequest(
type, remoteInstance, MessageType.constructorsOfRequest,
serializationZoneId: serializationZoneId);
return _handleResponse<DeclarationList>(await _sendRequest(request))
.declarations
// TODO: Refactor so we can remove this cast
.cast();
}
@override
Future<List<EnumValueDeclaration>> valuesOf(EnumDeclaration enumType) async {
TypeIntrospectorRequest request = TypeIntrospectorRequest(
enumType, remoteInstance, MessageType.valuesOfRequest,
serializationZoneId: serializationZoneId);
return _handleResponse<DeclarationList>(await _sendRequest(request))
.declarations
// TODO: Refactor so we can remove this cast
.cast();
}
@override
Future<List<FieldDeclaration>> fieldsOf(TypeDeclaration type) async {
TypeIntrospectorRequest request = TypeIntrospectorRequest(
type, remoteInstance, MessageType.fieldsOfRequest,
serializationZoneId: serializationZoneId);
return _handleResponse<DeclarationList>(await _sendRequest(request))
.declarations
// TODO: Refactor so we can remove this cast
.cast();
}
@override
Future<List<MethodDeclaration>> methodsOf(TypeDeclaration type) async {
TypeIntrospectorRequest request = TypeIntrospectorRequest(
type, remoteInstance, MessageType.methodsOfRequest,
serializationZoneId: serializationZoneId);
return _handleResponse<DeclarationList>(await _sendRequest(request))
.declarations
// TODO: Refactor so we can remove this cast
.cast();
}
@override
Future<List<TypeDeclaration>> typesOf(Library library) async {
TypeIntrospectorRequest request = TypeIntrospectorRequest(
library, remoteInstance, MessageType.typesOfRequest,
serializationZoneId: serializationZoneId);
return _handleResponse<DeclarationList>(await _sendRequest(request))
.declarations
// TODO: Refactor so we can remove this cast.
.cast();
}
@override
Future<TypeDeclaration> typeDeclarationOf(IdentifierImpl identifier) async {
DeclarationOfRequest request = DeclarationOfRequest(
identifier, MessageType.typeDeclarationOfRequest, remoteInstance,
serializationZoneId: serializationZoneId);
return _handleResponse<TypeDeclaration>(await _sendRequest(request));
}
}
/// Client side implementation of a [StaticType].
base class ClientStaticTypeImpl extends ClientIntrospector
implements StaticType {
ClientStaticTypeImpl(super._sendRequest,
{required super.remoteInstance, required super.serializationZoneId});
@override
Future<bool> isExactly(ClientStaticTypeImpl other) async {
IsExactlyTypeRequest request = IsExactlyTypeRequest(
remoteInstance, other.remoteInstance,
serializationZoneId: serializationZoneId);
return _handleResponse<BooleanValue>(await _sendRequest(request)).value;
}
@override
Future<bool> isSubtypeOf(ClientStaticTypeImpl other) async {
IsSubtypeOfRequest request = IsSubtypeOfRequest(
remoteInstance, other.remoteInstance,
serializationZoneId: serializationZoneId);
return _handleResponse<BooleanValue>(await _sendRequest(request)).value;
}
}
/// Named variant of the [ClientStaticTypeImpl].
final class ClientNamedStaticTypeImpl extends ClientStaticTypeImpl
implements NamedStaticType {
ClientNamedStaticTypeImpl(super.sendRequest,
{required super.remoteInstance, required super.serializationZoneId});
}
/// Client side implementation of a [DeclarationBuilder].
final class ClientDefinitionPhaseIntrospector
extends ClientDeclarationPhaseIntrospector
implements DefinitionPhaseIntrospector {
ClientDefinitionPhaseIntrospector(super._sendRequest,
{required super.remoteInstance, required super.serializationZoneId});
@override
Future<Declaration> declarationOf(IdentifierImpl identifier) async {
DeclarationOfRequest request = DeclarationOfRequest(
identifier, MessageType.declarationOfRequest, remoteInstance,
serializationZoneId: serializationZoneId);
return _handleResponse<Declaration>(await _sendRequest(request));
}
@override
Future<TypeAnnotation> inferType(
OmittedTypeAnnotationImpl omittedType) async {
InferTypeRequest request = InferTypeRequest(omittedType, remoteInstance,
serializationZoneId: serializationZoneId);
return _handleResponse<TypeAnnotation>(await _sendRequest(request));
}
@override
Future<List<Declaration>> topLevelDeclarationsOf(LibraryImpl library) async {
DeclarationsOfRequest request = DeclarationsOfRequest(
library, remoteInstance,
serializationZoneId: serializationZoneId);
return _handleResponse<DeclarationList>(await _sendRequest(request))
.declarations;
}
@override
Future<TypeDeclaration> typeDeclarationOf(IdentifierImpl identifier) async {
DeclarationOfRequest request = DeclarationOfRequest(
identifier, MessageType.typeDeclarationOfRequest, remoteInstance,
serializationZoneId: serializationZoneId);
return _handleResponse<TypeDeclaration>(await _sendRequest(request));
}
}
/// Either returns the actual response from [response], casted to [T], or throws
/// a [MacroException].
T _handleResponse<T>(Response response) {
if (response.responseType == MessageType.exception) {
throw response.exception!;
}
return response.response as T;
}
enum MessageType {
boolean,
constructorsOfRequest,
declarationOfRequest,
declarationList,
destroyRemoteInstanceZoneRequest,
disposeMacroRequest,
exception,
valuesOfRequest,
fieldsOfRequest,
methodsOfRequest,
executeDeclarationsPhaseRequest,
executeDefinitionsPhaseRequest,
executeTypesPhaseRequest,
instantiateMacroRequest,
resolveIdentifierRequest,
resolveTypeRequest,
inferTypeRequest,
isExactlyTypeRequest,
isSubtypeOfRequest,
loadMacroRequest,
remoteInstance,
macroInstanceIdentifier,
macroExecutionResult,
namedStaticType,
response,
staticType,
topLevelDeclarationsOfRequest,
typeDeclarationOfRequest,
typesOfRequest,
}