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