Version 2.17.0-103.0.dev

Merge commit '49d7f5056f8934922ab5cd3a7635a1748f6db052' into 'dev'
diff --git a/pkg/_fe_analyzer_shared/benchmark/macros/serialization_benchmark.dart b/pkg/_fe_analyzer_shared/benchmark/macros/serialization_benchmark.dart
new file mode 100644
index 0000000..550e718
--- /dev/null
+++ b/pkg/_fe_analyzer_shared/benchmark/macros/serialization_benchmark.dart
@@ -0,0 +1,311 @@
+// 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 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+import 'dart:isolate';
+import 'dart:typed_data';
+
+import 'package:_fe_analyzer_shared/src/macros/executor_shared/serialization.dart';
+
+void main() async {
+  for (var serializationMode in [
+    SerializationMode.jsonClient,
+    SerializationMode.byteDataClient
+  ]) {
+    await withSerializationMode(serializationMode, () async {
+      await _isolateSpawnBenchmarks();
+      await _isolateSpawnUriBenchmarks();
+      await _separateProcessBenchmarks();
+    });
+  }
+}
+
+Future<void> _isolateSpawnBenchmarks() async {
+  void Function(SendPort) childIsolateFn(SerializationMode mode) =>
+      (SendPort sendPort) => withSerializationMode(mode, () {
+            var isolateReceivePort = ReceivePort();
+            isolateReceivePort.listen((data) {
+              deserialize(data);
+              var result = serialize();
+              result = result is Uint8List
+                  ? TransferableTypedData.fromList([result])
+                  : result;
+              sendPort.send(result);
+            });
+            sendPort.send(isolateReceivePort.sendPort);
+          });
+
+  Completer? responseCompleter;
+  late SendPort sendPort;
+
+  var receivePort = ReceivePort();
+
+  var isolate = await Isolate.spawn(
+      childIsolateFn(serializationMode), receivePort.sendPort);
+
+  final sendPortCompleter = Completer<SendPort>();
+  receivePort.listen((data) {
+    if (!sendPortCompleter.isCompleted) {
+      sendPortCompleter.complete(data);
+    } else {
+      responseCompleter!.complete(data);
+    }
+  });
+  sendPort = await sendPortCompleter.future;
+
+  // warmup
+  for (var i = 0; i < 100; i++) {
+    responseCompleter = Completer();
+    var result = serialize();
+    result =
+        result is Uint8List ? TransferableTypedData.fromList([result]) : result;
+    sendPort.send(result);
+    deserialize(await responseCompleter.future);
+  }
+  // measure
+  var watch = Stopwatch()..start();
+  for (var i = 0; i < 100; i++) {
+    responseCompleter = Completer();
+    var result = serialize();
+    result =
+        result is Uint8List ? TransferableTypedData.fromList([result]) : result;
+    sendPort.send(result);
+    deserialize(await responseCompleter.future);
+  }
+  print('Isolate.spawn + $serializationMode: ${watch.elapsed}');
+
+  receivePort.close();
+  isolate.kill();
+}
+
+Future<void> _isolateSpawnUriBenchmarks() async {
+  Completer? responseCompleter;
+  late SendPort sendPort;
+
+  var receivePort = ReceivePort();
+
+  var isolate = await Isolate.spawnUri(
+      Uri.dataFromString(childProgram(serializationMode)),
+      [],
+      receivePort.sendPort);
+
+  final sendPortCompleter = Completer<SendPort>();
+  receivePort.listen((data) {
+    if (!sendPortCompleter.isCompleted) {
+      sendPortCompleter.complete(data);
+    } else {
+      responseCompleter!.complete(data);
+    }
+  });
+  sendPort = await sendPortCompleter.future;
+
+  // warmup
+  for (var i = 0; i < 100; i++) {
+    responseCompleter = Completer();
+    var result = serialize();
+    result =
+        result is Uint8List ? TransferableTypedData.fromList([result]) : result;
+    sendPort.send(result);
+    deserialize(await responseCompleter.future);
+  }
+  // measure
+  var watch = Stopwatch()..start();
+  for (var i = 0; i < 100; i++) {
+    responseCompleter = Completer();
+    var result = serialize();
+    result =
+        result is Uint8List ? TransferableTypedData.fromList([result]) : result;
+    sendPort.send(result);
+    deserialize(await responseCompleter.future);
+  }
+  print('Isolate.spawnUri + $serializationMode: ${watch.elapsed}');
+
+  receivePort.close();
+  isolate.kill();
+}
+
+Future<void> _separateProcessBenchmarks() async {
+  Completer? responseCompleter;
+
+  var tmpDir = Directory.systemTemp.createTempSync('serialize_bench');
+  try {
+    var file = File(tmpDir.uri.resolve('main.dart').toFilePath());
+    file.writeAsStringSync(childProgram(serializationMode));
+    var process = await Process.start(Platform.resolvedExecutable, [
+      '--packages=' + (await Isolate.packageConfig)!.toFilePath(),
+      file.path,
+    ]);
+
+    var listeners = <StreamSubscription>[
+      process.stderr.listen((event) {
+        print('stderr: ${utf8.decode(event)}');
+      }),
+      process.stdout.listen((data) {
+        responseCompleter!.complete(data);
+      }),
+    ];
+
+    // warmup
+    for (var i = 0; i < 100; i++) {
+      responseCompleter = Completer();
+      var result = serialize();
+      if (result is List<int>) {
+        process.stdin.add(result);
+      } else {
+        process.stdin.writeln(jsonEncode(result));
+      }
+      deserialize(await responseCompleter.future);
+    }
+    // measure
+    var watch = Stopwatch()..start();
+    for (var i = 0; i < 100; i++) {
+      responseCompleter = Completer();
+      var result = serialize();
+      if (result is List<int>) {
+        process.stdin.add(result);
+      } else {
+        process.stdin.writeln(jsonEncode(result));
+      }
+      deserialize(await responseCompleter.future);
+    }
+    print('Separate process + $serializationMode: ${watch.elapsed}');
+
+    listeners.forEach((l) => l.cancel());
+    process.kill();
+  } catch (e, s) {
+    print('Error running benchmark \n$e\n\n$s');
+  } finally {
+    tmpDir.deleteSync(recursive: true);
+  }
+}
+
+String childProgram(SerializationMode mode) => '''
+      import 'dart:convert';
+      import 'dart:io';
+      import 'dart:isolate';
+      import 'dart:typed_data';
+
+      import 'package:_fe_analyzer_shared/src/macros/executor_shared/serialization.dart';
+
+      void main(_, [SendPort? sendPort]) {
+        var mode = $mode;
+        withSerializationMode(mode, () {
+          if (sendPort != null) {
+              var isolateReceivePort = ReceivePort();
+              isolateReceivePort.listen((data) {
+                deserialize(data);
+                var result = serialize();
+                result = result is Uint8List
+                    ? TransferableTypedData.fromList([result])
+                    : result;
+                sendPort.send(result);
+              });
+              sendPort.send(isolateReceivePort.sendPort);
+          } else {
+            // We allow one empty line to work around some weird data.
+            var allowEmpty = true;
+            stdin.listen((data) {
+              if (mode == SerializationMode.jsonClient || mode == SerializationMode.jsonServer) {
+                var json = utf8.decode(data).trimRight();
+                // On exit we tend to get extra empty lines sometimes?
+                if (json.isEmpty && allowEmpty) {
+                  allowEmpty = false;
+                  return;
+                }
+                deserialize(jsonDecode(json));
+                stdout.write(jsonEncode(serialize()));
+              } else {
+                deserialize(data);
+                stdout.add(serialize() as List<int>);
+              }
+            });
+          }
+        });
+      }
+
+      Object? serialize() {
+        var serializer = serializerFactory();
+        for (var i = 0; i < 100; i++) {
+          serializer.addInt(i * 100);
+          serializer.addString('foo' * i);
+          serializer.addBool(i % 2 == 0);
+          serializer.startList();
+          for (var j = 0; j < 10; j++) {
+            serializer.addDouble(i * 5);
+          }
+          serializer.endList();
+          serializer.addNull();
+        }
+        return serializer.result;
+      }
+
+      void deserialize(Object? result) {
+        result = result is TransferableTypedData
+            ? result.materialize().asUint8List()
+            : result;
+        var deserializer = deserializerFactory(result);
+        while (deserializer.moveNext()) {
+          deserializer
+            ..expectInt()
+            ..moveNext()
+            ..expectString()
+            ..moveNext()
+            ..expectBool()
+            ..moveNext()
+            ..expectList();
+          while (deserializer.moveNext()) {
+            deserializer.expectDouble();
+          }
+          deserializer
+            ..moveNext()
+            ..checkNull();
+        }
+      }''';
+
+Object? serialize() {
+  var serializer = serializerFactory();
+  for (var i = -50; i < 50; i++) {
+    serializer.addInt(i % 2 * 100);
+    serializer.addString('foo' * i);
+    serializer.addBool(i < 0);
+    serializer.startList();
+    for (var j = 0.0; j < 10; j++) {
+      serializer.addDouble(i * j);
+    }
+    serializer.endList();
+    serializer.addNull();
+  }
+  return serializer.result;
+}
+
+void deserialize(Object? result) {
+  result = result is TransferableTypedData
+      ? result.materialize().asUint8List()
+      : result;
+  if (serializationMode == SerializationMode.jsonClient ||
+      serializationMode == SerializationMode.jsonServer) {
+    if (result is List<int>) {
+      result = jsonDecode(utf8.decode(result));
+    }
+  }
+  var deserializer = deserializerFactory(result);
+  while (deserializer.moveNext()) {
+    deserializer
+      ..expectInt()
+      ..moveNext()
+      ..expectString()
+      ..moveNext()
+      ..expectBool()
+      ..moveNext()
+      ..expectList();
+    while (deserializer.moveNext()) {
+      deserializer.expectDouble();
+    }
+    deserializer
+      ..moveNext()
+      ..checkNull();
+  }
+}
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart b/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
index 635c67b..f423fcf 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
@@ -55,17 +55,18 @@
   /// Local function that sends requests and returns responses using [sendPort].
   Future<Response> sendRequest(Request request) => _sendRequest(request, sendPort);
 
-  withSerializationMode(SerializationMode.client, () {
+  /// TODO: More directly support customizable serialization types.
+  withSerializationMode(SerializationMode.jsonClient, () {
     ReceivePort receivePort = new ReceivePort();
     sendPort.send(receivePort.sendPort);
 
     receivePort.listen((message) async {
-      var deserializer = JsonDeserializer(message as Iterable<Object?>)
+      var deserializer = deserializerFactory(message)
           ..moveNext();
-      int zoneId = deserializer.expectNum();
+      int zoneId = deserializer.expectInt();
       deserializer..moveNext();
-      var type = MessageType.values[deserializer.expectNum()];
-      var serializer = JsonSerializer();
+      var type = MessageType.values[deserializer.expectInt()];
+      var serializer = serializerFactory();
       switch (type) {
         case MessageType.instantiateMacroRequest:
           var request = new InstantiateMacroRequest.deserialize(deserializer, zoneId);
@@ -248,8 +249,8 @@
 Future<Response> _sendRequest(Request request, SendPort sendPort) {
   Completer<Response> completer = Completer();
   _responseCompleters[request.id] = completer;
-  JsonSerializer serializer = JsonSerializer();
-  serializer.addNum(request.serializationZoneId);
+  Serializer serializer = serializerFactory();
+  serializer.addInt(request.serializationZoneId);
   request.serialize(serializer);
   sendPort.send(serializer.result);
   return completer.future;
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart
index 7238af4..94b8930 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor.dart
@@ -132,7 +132,7 @@
   static Object? _deserializeArg(Deserializer deserializer,
       {bool alreadyMoved = false}) {
     if (!alreadyMoved) deserializer.moveNext();
-    _ArgumentKind kind = _ArgumentKind.values[deserializer.expectNum()];
+    _ArgumentKind kind = _ArgumentKind.values[deserializer.expectInt()];
     switch (kind) {
       case _ArgumentKind.nil:
         return null;
@@ -142,9 +142,12 @@
       case _ArgumentKind.bool:
         deserializer.moveNext();
         return deserializer.expectBool();
-      case _ArgumentKind.num:
+      case _ArgumentKind.int:
         deserializer.moveNext();
-        return deserializer.expectNum();
+        return deserializer.expectInt();
+      case _ArgumentKind.double:
+        deserializer.moveNext();
+        return deserializer.expectDouble();
       case _ArgumentKind.list:
         deserializer.moveNext();
         deserializer.expectList();
@@ -184,22 +187,26 @@
 
   static void _serializeArg(Object? arg, Serializer serializer) {
     if (arg == null) {
-      serializer.addNum(_ArgumentKind.nil.index);
+      serializer.addInt(_ArgumentKind.nil.index);
     } else if (arg is String) {
       serializer
-        ..addNum(_ArgumentKind.string.index)
+        ..addInt(_ArgumentKind.string.index)
         ..addString(arg);
-    } else if (arg is num) {
+    } else if (arg is int) {
       serializer
-        ..addNum(_ArgumentKind.num.index)
-        ..addNum(arg);
+        ..addInt(_ArgumentKind.int.index)
+        ..addInt(arg);
+    } else if (arg is double) {
+      serializer
+        ..addInt(_ArgumentKind.double.index)
+        ..addDouble(arg);
     } else if (arg is bool) {
       serializer
-        ..addNum(_ArgumentKind.bool.index)
+        ..addInt(_ArgumentKind.bool.index)
         ..addBool(arg);
     } else if (arg is List) {
       serializer
-        ..addNum(_ArgumentKind.list.index)
+        ..addInt(_ArgumentKind.list.index)
         ..startList();
       for (Object? item in arg) {
         _serializeArg(item, serializer);
@@ -207,7 +214,7 @@
       serializer.endList();
     } else if (arg is Map) {
       serializer
-        ..addNum(_ArgumentKind.map.index)
+        ..addInt(_ArgumentKind.map.index)
         ..startList();
       for (MapEntry<Object?, Object?> entry in arg.entries) {
         _serializeArg(entry.key, serializer);
@@ -311,4 +318,4 @@
 }
 
 /// Used for serializing and deserializing arguments.
-enum _ArgumentKind { string, bool, num, list, map, nil }
+enum _ArgumentKind { string, bool, double, int, list, map, nil }
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/introspection_impls.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/introspection_impls.dart
index baf884d..ebf86e2 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/introspection_impls.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/introspection_impls.dart
@@ -19,9 +19,7 @@
   void serialize(Serializer serializer) {
     super.serialize(serializer);
     // Client side we don't encode anything but the ID.
-    if (serializationMode == SerializationMode.client) {
-      return;
-    }
+    if (serializationMode.isClient) return;
 
     serializer.addString(name);
   }
@@ -37,9 +35,7 @@
   void serialize(Serializer serializer) {
     super.serialize(serializer);
     // Client side we don't encode anything but the ID.
-    if (serializationMode == SerializationMode.client) {
-      return;
-    }
+    if (serializationMode.isClient) return;
 
     serializer.addBool(isNullable);
   }
@@ -76,9 +72,7 @@
   void serialize(Serializer serializer) {
     super.serialize(serializer);
     // Client side we don't encode anything but the ID.
-    if (serializationMode == SerializationMode.client) {
-      return;
-    }
+    if (serializationMode.isClient) return;
 
     identifier.serialize(serializer);
     serializer.startList();
@@ -138,9 +132,7 @@
   void serialize(Serializer serializer) {
     super.serialize(serializer);
     // Client side we don't encode anything but the ID.
-    if (serializationMode == SerializationMode.client) {
-      return;
-    }
+    if (serializationMode.isClient) return;
 
     returnType.serialize(serializer);
 
@@ -173,9 +165,7 @@
   void serialize(Serializer serializer) {
     super.serialize(serializer);
     // Client side we don't encode anything but the ID.
-    if (serializationMode == SerializationMode.client) {
-      return;
-    }
+    if (serializationMode.isClient) return;
 
     identifier.serialize(serializer);
   }
@@ -207,9 +197,7 @@
   void serialize(Serializer serializer) {
     super.serialize(serializer);
     // Client side we don't encode anything but the ID.
-    if (serializationMode == SerializationMode.client) {
-      return;
-    }
+    if (serializationMode.isClient) return;
 
     serializer.addBool(isNamed);
     serializer.addBool(isRequired);
@@ -241,9 +229,7 @@
   void serialize(Serializer serializer) {
     super.serialize(serializer);
     // Client side we don't encode anything but the ID.
-    if (serializationMode == SerializationMode.client) {
-      return;
-    }
+    if (serializationMode.isClient) return;
 
     TypeAnnotationImpl? bound = this.bound;
     if (bound == null) {
@@ -308,9 +294,7 @@
   void serialize(Serializer serializer) {
     super.serialize(serializer);
     // Client side we don't encode anything but the ID.
-    if (serializationMode == SerializationMode.client) {
-      return;
-    }
+    if (serializationMode.isClient) return;
 
     serializer
       ..addBool(isAbstract)
@@ -380,9 +364,7 @@
   void serialize(Serializer serializer) {
     super.serialize(serializer);
     // Client side we don't encode anything but the ID.
-    if (serializationMode == SerializationMode.client) {
-      return;
-    }
+    if (serializationMode.isClient) return;
 
     definingClass.serialize(serializer);
   }
@@ -433,9 +415,7 @@
   void serialize(Serializer serializer) {
     super.serialize(serializer);
     // Client side we don't encode anything but the ID.
-    if (serializationMode == SerializationMode.client) {
-      return;
-    }
+    if (serializationMode.isClient) return;
 
     serializer.addBool(isFactory);
   }
@@ -471,9 +451,7 @@
   void serialize(Serializer serializer) {
     super.serialize(serializer);
     // Client side we don't encode anything but the ID.
-    if (serializationMode == SerializationMode.client) {
-      return;
-    }
+    if (serializationMode.isClient) return;
 
     serializer
       ..addBool(isExternal)
@@ -513,9 +491,7 @@
   void serialize(Serializer serializer) {
     super.serialize(serializer);
     // Client side we don't encode anything but the ID.
-    if (serializationMode == SerializationMode.client) {
-      return;
-    }
+    if (serializationMode.isClient) return;
 
     definingClass.serialize(serializer);
   }
@@ -535,9 +511,7 @@
   void serialize(Serializer serializer) {
     super.serialize(serializer);
     // Client side we don't encode anything but the ID.
-    if (serializationMode == SerializationMode.client) {
-      return;
-    }
+    if (serializationMode.isClient) return;
 
     serializer..startList();
     for (TypeParameterDeclarationImpl param in typeParameters) {
@@ -585,9 +559,7 @@
   void serialize(Serializer serializer) {
     super.serialize(serializer);
     // Client side we don't encode anything but the ID.
-    if (serializationMode == SerializationMode.client) {
-      return;
-    }
+    if (serializationMode.isClient) return;
 
     serializer.startList();
     for (TypeAnnotationImpl interface in interfaces) {
@@ -628,9 +600,7 @@
   void serialize(Serializer serializer) {
     super.serialize(serializer);
     // Client side we don't encode anything but the ID.
-    if (serializationMode == SerializationMode.client) {
-      return;
-    }
+    if (serializationMode.isClient) return;
 
     aliasedType.serialize(serializer);
   }
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 f3a3bf7..50a6743 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
@@ -28,13 +28,13 @@
   /// 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();
+      : 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.
   @mustCallSuper
-  void serialize(Serializer serializer) => serializer.addNum(id);
+  void serialize(Serializer serializer) => serializer.addInt(id);
 
   static int _next = 0;
 }
@@ -81,7 +81,7 @@
   factory SerializableResponse.deserialize(
       Deserializer deserializer, int serializationZoneId) {
     deserializer.moveNext();
-    MessageType responseType = MessageType.values[deserializer.expectNum()];
+    MessageType responseType = MessageType.values[deserializer.expectInt()];
     Serializable? response;
     String? error;
     String? stackTrace;
@@ -126,15 +126,15 @@
         response: response,
         error: error,
         stackTrace: stackTrace,
-        requestId: (deserializer..moveNext()).expectNum(),
+        requestId: (deserializer..moveNext()).expectInt(),
         serializationZoneId: serializationZoneId);
   }
 
   void serialize(Serializer serializer) {
     serializer
-      ..addNum(serializationZoneId)
-      ..addNum(MessageType.response.index)
-      ..addNum(responseType.index);
+      ..addInt(serializationZoneId)
+      ..addInt(MessageType.response.index)
+      ..addInt(responseType.index);
     switch (responseType) {
       case MessageType.error:
         serializer.addString(error!.toString());
@@ -143,7 +143,7 @@
       default:
         response.serializeNullable(serializer);
     }
-    serializer.addNum(requestId);
+    serializer.addInt(requestId);
   }
 }
 
@@ -203,7 +203,7 @@
   @override
   void serialize(Serializer serializer) {
     serializer
-      ..addNum(MessageType.loadMacroRequest.index)
+      ..addInt(MessageType.loadMacroRequest.index)
       ..addString(library.toString())
       ..addString(name);
     super.serialize(serializer);
@@ -230,16 +230,16 @@
       : macroClass = new MacroClassIdentifierImpl.deserialize(deserializer),
         constructorName = (deserializer..moveNext()).expectString(),
         arguments = new Arguments.deserialize(deserializer),
-        instanceId = (deserializer..moveNext()).expectNum(),
+        instanceId = (deserializer..moveNext()).expectInt(),
         super.deserialize(deserializer, serializationZoneId);
 
   @override
   void serialize(Serializer serializer) {
-    serializer.addNum(MessageType.instantiateMacroRequest.index);
+    serializer.addInt(MessageType.instantiateMacroRequest.index);
     macroClass.serialize(serializer);
     serializer.addString(constructorName);
     arguments.serialize(serializer);
-    serializer.addNum(instanceId);
+    serializer.addInt(instanceId);
     super.serialize(serializer);
   }
 }
@@ -262,7 +262,7 @@
         super.deserialize(deserializer, serializationZoneId);
 
   void serialize(Serializer serializer) {
-    serializer.addNum(MessageType.executeTypesPhaseRequest.index);
+    serializer.addInt(MessageType.executeTypesPhaseRequest.index);
     macro.serialize(serializer);
     declaration.serialize(serializer);
 
@@ -295,7 +295,7 @@
         super.deserialize(deserializer, serializationZoneId);
 
   void serialize(Serializer serializer) {
-    serializer.addNum(MessageType.executeDeclarationsPhaseRequest.index);
+    serializer.addInt(MessageType.executeDeclarationsPhaseRequest.index);
     macro.serialize(serializer);
     declaration.serialize(serializer);
     typeResolver.serialize(serializer);
@@ -332,7 +332,7 @@
         super.deserialize(deserializer, serializationZoneId);
 
   void serialize(Serializer serializer) {
-    serializer.addNum(MessageType.executeDefinitionsPhaseRequest.index);
+    serializer.addInt(MessageType.executeDefinitionsPhaseRequest.index);
     macro.serialize(serializer);
     declaration.serialize(serializer);
     typeResolver.serialize(serializer);
@@ -361,7 +361,7 @@
         super.deserialize(deserializer, serializationZoneId);
 
   void serialize(Serializer serializer) {
-    serializer.addNum(MessageType.resolveTypeRequest.index);
+    serializer.addInt(MessageType.resolveTypeRequest.index);
     typeAnnotationCode.serialize(serializer);
     typeResolver.serialize(serializer);
     super.serialize(serializer);
@@ -386,7 +386,7 @@
         super.deserialize(deserializer, serializationZoneId);
 
   void serialize(Serializer serializer) {
-    serializer.addNum(MessageType.isExactlyTypeRequest.index);
+    serializer.addInt(MessageType.isExactlyTypeRequest.index);
     leftType.serialize(serializer);
     rightType.serialize(serializer);
     super.serialize(serializer);
@@ -411,7 +411,7 @@
         super.deserialize(deserializer, serializationZoneId);
 
   void serialize(Serializer serializer) {
-    serializer.addNum(MessageType.isSubtypeOfRequest.index);
+    serializer.addInt(MessageType.isSubtypeOfRequest.index);
     leftType.serialize(serializer);
     rightType.serialize(serializer);
     super.serialize(serializer);
@@ -440,7 +440,7 @@
 
   @override
   void serialize(Serializer serializer) {
-    serializer.addNum(requestKind.index);
+    serializer.addInt(requestKind.index);
     classDeclaration.serialize(serializer);
     classIntrospector.serialize(serializer);
     super.serialize(serializer);
@@ -466,7 +466,7 @@
 
   @override
   void serialize(Serializer serializer) {
-    serializer.addNum(MessageType.declarationOfRequest.index);
+    serializer.addInt(MessageType.declarationOfRequest.index);
     identifier.serialize(serializer);
     typeDeclarationResolver.serialize(serializer);
     super.serialize(serializer);
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 7ba94f8..3940b7e 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
@@ -57,16 +57,12 @@
   /// [SerializationMode.client], so that only an ID is sent.
   @mustCallSuper
   void serialize(Serializer serializer) {
-    serializer.addNum(id);
-    switch (serializationMode) {
-      case SerializationMode.client:
-        // We only send the ID from the client side.
-        return;
-      case SerializationMode.server:
-        serializer.addNum(kind.index);
-        _remoteInstanceCache[id] = this;
-        return;
-    }
+    serializer.addInt(id);
+    // We only send the ID from the client side.
+    if (serializationMode.isClient) return;
+
+    serializer.addInt(kind.index);
+    _remoteInstanceCache[id] = this;
   }
 
   @override
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/response_impls.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/response_impls.dart
index 2e1d0cc..3686842 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/response_impls.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/response_impls.dart
@@ -164,12 +164,12 @@
   }
 
   MacroInstanceIdentifierImpl.deserialize(Deserializer deserializer)
-      : id = (deserializer..moveNext()).expectNum(),
-        _interfaces = (deserializer..moveNext()).expectNum();
+      : id = (deserializer..moveNext()).expectInt(),
+        _interfaces = (deserializer..moveNext()).expectInt();
 
   void serialize(Serializer serializer) => serializer
-    ..addNum(id)
-    ..addNum(_interfaces);
+    ..addInt(id)
+    ..addInt(_interfaces);
 
   operator ==(other) => other is MacroInstanceIdentifierImpl && id == other.id;
 
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization.dart
index 89bbf59..8adf1a1 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor_shared/serialization.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:async';
+import 'dart:typed_data';
 
 import 'remote_instance.dart';
 
@@ -11,11 +12,15 @@
 ///
 /// In [SerializationMode.server], sets up a remote instance cache to use when
 /// deserializing remote instances back to their original instance.
-T withSerializationMode<T>(SerializationMode mode, T Function() fn) =>
+T withSerializationMode<T>(
+  SerializationMode mode,
+  T Function() fn, {
+  Serializer Function()? serializerFactory,
+  Deserializer Function(Object? data)? deserializerFactory,
+}) =>
     runZoned(fn, zoneValues: {
       #serializationMode: mode,
-      if (mode == SerializationMode.server)
-        remoteInstanceZoneKey: <int, RemoteInstance>{}
+      if (!mode.isClient) remoteInstanceZoneKey: <int, RemoteInstance>{}
     });
 
 /// Serializable interface
@@ -30,19 +35,28 @@
   void addString(String value);
 
   /// Serializes a nullable [String].
-  void addNullableString(String? value);
+  void addNullableString(String? value) =>
+      value == null ? addNull() : addString(value);
 
-  /// Serializes a [num].
-  void addNum(num value);
+  /// Serializes a [double].
+  void addDouble(double value);
 
-  /// Serializes a nullable [num].
-  void addNullableNum(num? value);
+  /// Serializes a nullable [double].
+  void addNullableDouble(double? value) =>
+      value == null ? addNull() : addDouble(value);
+
+  /// Serializes an [int].
+  void addInt(int value);
+
+  /// Serializes a nullable [int].
+  void addNullableInt(int? value) => value == null ? addNull() : addInt(value);
 
   /// Serializes a [bool].
   void addBool(bool value);
 
   /// Serializes a nullable [bool].
-  void addNullableBool(bool? value);
+  void addNullableBool(bool? value) =>
+      value == null ? addNull() : addBool(value);
 
   /// Serializes a `null` literal.
   void addNull();
@@ -52,6 +66,9 @@
 
   /// Used to signal the end of an arbitrary length list of items.
   void endList();
+
+  /// Returns the resulting serialized object.
+  Object? get result;
 }
 
 /// A pull based object deserialization interface.
@@ -67,19 +84,25 @@
   bool expectBool();
 
   /// Reads the current value as a nullable [bool].
-  bool? expectNullableBool();
+  bool? expectNullableBool() => checkNull() ? null : expectBool();
 
-  /// Reads the current value as a non-nullable [String].
-  T expectNum<T extends num>();
+  /// Reads the current value as a non-nullable [double].
+  double expectDouble();
 
-  /// Reads the current value as a nullable [num].
-  num? expectNullableNum();
+  /// Reads the current value as a nullable [double].
+  double? expectNullableDouble() => checkNull() ? null : expectDouble();
+
+  /// Reads the current value as a non-nullable [int].
+  int expectInt();
+
+  /// Reads the current value as a nullable [int].
+  int? expectNullableInt() => checkNull() ? null : expectInt();
 
   /// Reads the current value as a non-nullable [String].
   String expectString();
 
   /// Reads the current value as a nullable [String].
-  String? expectNullableString();
+  String? expectNullableString() => checkNull() ? null : expectString();
 
   /// Asserts that the current item is the start of a list.
   ///
@@ -121,6 +144,7 @@
   /// Returns the result as an unmodifiable [Iterable].
   ///
   /// Asserts that all [List] entries have not been closed with [endList].
+  @override
   Iterable<Object?> get result {
     assert(_path.length == 1);
     return _result;
@@ -132,9 +156,14 @@
   void addNullableBool(bool? value) => _path.last.add(value);
 
   @override
-  void addNum(num value) => _path.last.add(value);
+  void addDouble(double value) => _path.last.add(value);
   @override
-  void addNullableNum(num? value) => _path.last.add(value);
+  void addNullableDouble(double? value) => _path.last.add(value);
+
+  @override
+  void addInt(int value) => _path.last.add(value);
+  @override
+  void addNullableInt(int? value) => _path.last.add(value);
 
   @override
   void addString(String value) => _path.last.add(value);
@@ -182,9 +211,14 @@
   bool? expectNullableBool() => _expectValue();
 
   @override
-  T expectNum<T extends num>() => _expectValue();
+  double expectDouble() => _expectValue();
   @override
-  num? expectNullableNum() => _expectValue();
+  double? expectNullableDouble() => _expectValue();
+
+  @override
+  int expectInt() => _expectValue();
+  @override
+  int? expectNullableInt() => _expectValue();
 
   @override
   String expectString() => _expectValue();
@@ -218,6 +252,308 @@
   }
 }
 
+class ByteDataSerializer extends Serializer {
+  final BytesBuilder _builder = new BytesBuilder();
+
+  // Re-usable 8 byte list and view for encoding doubles.
+  final Uint8List _eightByteList = new Uint8List(8);
+  late final ByteData _eightByteListData =
+      new ByteData.sublistView(_eightByteList);
+
+  @override
+  void addBool(bool value) => _builder
+      .addByte(value ? DataKind.boolTrue.index : DataKind.boolFalse.index);
+
+  @override
+  void addDouble(double value) {
+    _eightByteListData.setFloat64(0, value);
+    _builder
+      ..addByte(DataKind.float64.index)
+      ..add(_eightByteList);
+  }
+
+  @override
+  void addNull() => _builder.addByte(DataKind.nil.index);
+
+  @override
+  void addInt(int value) {
+    if (value >= 0x0) {
+      if (value + DataKind.values.length <= 0xff) {
+        _builder..addByte(value + DataKind.values.length);
+      } else if (value <= 0xff) {
+        _builder
+          ..addByte(DataKind.uint8.index)
+          ..addByte(value);
+      } else if (value <= 0xffff) {
+        _builder
+          ..addByte(DataKind.uint16.index)
+          ..addByte(value >> 8)
+          ..addByte(value);
+      } else if (value <= 0xffffffff) {
+        _builder
+          ..addByte(DataKind.uint32.index)
+          ..addByte(value >> 24)
+          ..addByte(value >> 16)
+          ..addByte(value >> 8)
+          ..addByte(value);
+      } else {
+        _builder
+          ..addByte(DataKind.uint64.index)
+          ..addByte(value >> 56)
+          ..addByte(value >> 48)
+          ..addByte(value >> 40)
+          ..addByte(value >> 32)
+          ..addByte(value >> 24)
+          ..addByte(value >> 16)
+          ..addByte(value >> 8)
+          ..addByte(value);
+      }
+    } else {
+      if (value >= -0x80) {
+        _builder
+          ..addByte(DataKind.int8.index)
+          ..addByte(value);
+      } else if (value >= -0x8000) {
+        _builder
+          ..addByte(DataKind.int16.index)
+          ..addByte(value >> 8)
+          ..addByte(value);
+      } else if (value >= -0x8000000) {
+        _builder
+          ..addByte(DataKind.int32.index)
+          ..addByte(value >> 24)
+          ..addByte(value >> 16)
+          ..addByte(value >> 8)
+          ..addByte(value);
+      } else {
+        _builder
+          ..addByte(DataKind.int64.index)
+          ..addByte(value >> 56)
+          ..addByte(value >> 48)
+          ..addByte(value >> 40)
+          ..addByte(value >> 32)
+          ..addByte(value >> 24)
+          ..addByte(value >> 16)
+          ..addByte(value >> 8)
+          ..addByte(value);
+      }
+    }
+  }
+
+  @override
+  void addString(String value) {
+    for (int i = 0; i < value.length; i++) {
+      if (value.codeUnitAt(i) > 0xff) {
+        _addTwoByteString(value);
+        return;
+      }
+    }
+    _addOneByteString(value);
+  }
+
+  void _addOneByteString(String value) {
+    _builder.addByte(DataKind.oneByteString.index);
+    addInt(value.length);
+    for (int i = 0; i < value.length; i++) {
+      _builder.addByte(value.codeUnitAt(i));
+    }
+  }
+
+  void _addTwoByteString(String value) {
+    _builder.addByte(DataKind.twoByteString.index);
+    addInt(value.length);
+    for (int i = 0; i < value.length; i++) {
+      int codeUnit = value.codeUnitAt(i);
+      switch (Endian.host) {
+        case Endian.little:
+          _builder
+            ..addByte(codeUnit)
+            ..addByte(codeUnit >> 8);
+          break;
+        case Endian.big:
+          _builder
+            ..addByte(codeUnit >> 8)
+            ..addByte(codeUnit);
+          break;
+      }
+    }
+  }
+
+  @override
+  void endList() => _builder.addByte(DataKind.endList.index);
+
+  @override
+  void startList() => _builder.addByte(DataKind.startList.index);
+
+  @override
+  Uint8List get result => _builder.takeBytes();
+}
+
+class ByteDataDeserializer extends Deserializer {
+  final ByteData _bytes;
+  int _byteOffset = 0;
+  int? _byteOffsetIncrement = 0;
+
+  ByteDataDeserializer(this._bytes);
+
+  /// Reads the next [DataKind] and advances [_byteOffset].
+  DataKind _readKind([int offset = 0]) {
+    int value = _bytes.getUint8(_byteOffset + offset);
+    if (value < DataKind.values.length) {
+      return DataKind.values[value];
+    } else {
+      return DataKind.directEncodedUint8;
+    }
+  }
+
+  @override
+  bool checkNull() {
+    _byteOffsetIncrement = 1;
+    return _readKind() == DataKind.nil;
+  }
+
+  @override
+  bool expectBool() {
+    DataKind kind = _readKind();
+    _byteOffsetIncrement = 1;
+    if (kind == DataKind.boolTrue) {
+      return true;
+    } else if (kind == DataKind.boolFalse) {
+      return false;
+    } else {
+      throw new StateError('Expected a bool but found a $kind');
+    }
+  }
+
+  @override
+  double expectDouble() {
+    DataKind kind = _readKind();
+    if (kind != DataKind.float64) {
+      throw new StateError('Expected a double but found a $kind');
+    }
+    _byteOffsetIncrement = 9;
+    return _bytes.getFloat64(_byteOffset + 1);
+  }
+
+  @override
+  int expectInt() => _expectInt(0);
+
+  int _expectInt(int offset) {
+    DataKind kind = _readKind(offset);
+    if (kind == DataKind.directEncodedUint8) {
+      _byteOffsetIncrement = offset + 1;
+      return _bytes.getUint8(_byteOffset + offset) - DataKind.values.length;
+    }
+    offset += 1;
+    int result;
+    switch (kind) {
+      case DataKind.int8:
+        result = _bytes.getInt8(_byteOffset + offset);
+        _byteOffsetIncrement = 1 + offset;
+        break;
+      case DataKind.int16:
+        result = _bytes.getInt16(_byteOffset + offset);
+        _byteOffsetIncrement = 2 + offset;
+        break;
+      case DataKind.int32:
+        result = _bytes.getInt32(_byteOffset + offset);
+        _byteOffsetIncrement = 4 + offset;
+        break;
+      case DataKind.int64:
+        result = _bytes.getInt64(_byteOffset + offset);
+        _byteOffsetIncrement = 8 + offset;
+        break;
+      case DataKind.uint8:
+        result = _bytes.getUint8(_byteOffset + offset);
+        _byteOffsetIncrement = 1 + offset;
+        break;
+      case DataKind.uint16:
+        result = _bytes.getUint16(_byteOffset + offset);
+        _byteOffsetIncrement = 2 + offset;
+        break;
+      case DataKind.uint32:
+        result = _bytes.getUint32(_byteOffset + offset);
+        _byteOffsetIncrement = 4 + offset;
+        break;
+      case DataKind.uint64:
+        result = _bytes.getUint64(_byteOffset + offset);
+        _byteOffsetIncrement = 8 + offset;
+        break;
+      default:
+        throw new StateError('Expected an int but found a $kind');
+    }
+    return result;
+  }
+
+  @override
+  void expectList() {
+    DataKind kind = _readKind();
+    if (kind != DataKind.startList) {
+      throw new StateError('Expected the start to a list but found a $kind');
+    }
+    _byteOffsetIncrement = 1;
+  }
+
+  @override
+  String expectString() {
+    DataKind kind = _readKind();
+    int length = _expectInt(1);
+    int offset = _byteOffsetIncrement! + _byteOffset;
+    if (kind == DataKind.oneByteString) {
+      _byteOffsetIncrement = _byteOffsetIncrement! + length;
+      return new String.fromCharCodes(
+          _bytes.buffer.asUint8List(offset, length));
+    } else if (kind == DataKind.twoByteString) {
+      length = length * 2;
+      _byteOffsetIncrement = _byteOffsetIncrement! + length;
+      Uint8List bytes =
+          new Uint8List.fromList(_bytes.buffer.asUint8List(offset, length));
+      return new String.fromCharCodes(bytes.buffer.asUint16List());
+    } else {
+      throw new StateError('Expected a string but found a $kind');
+    }
+  }
+
+  @override
+  bool moveNext() {
+    int? increment = _byteOffsetIncrement;
+    _byteOffsetIncrement = null;
+    if (increment == null) {
+      throw new StateError("Can't move until consuming the current element");
+    }
+    _byteOffset += increment;
+    if (_byteOffset >= _bytes.lengthInBytes) {
+      return false;
+    } else if (_readKind() == DataKind.endList) {
+      // You don't explicitly consume list end markers.
+      _byteOffsetIncrement = 1;
+      return false;
+    } else {
+      return true;
+    }
+  }
+}
+
+enum DataKind {
+  nil,
+  boolTrue,
+  boolFalse,
+  directEncodedUint8, // Encoded in the kind byte.
+  startList,
+  endList,
+  int8,
+  int16,
+  int32,
+  int64,
+  uint8,
+  uint16,
+  uint32,
+  uint64,
+  float64,
+  oneByteString,
+  twoByteString,
+}
+
 /// Must be set using `withSerializationMode` before doing any serialization or
 /// deserialization.
 SerializationMode get serializationMode {
@@ -230,9 +566,50 @@
   return mode;
 }
 
+/// Returns the current deserializer factory for the zone.
+Deserializer Function(Object?) get deserializerFactory {
+  switch (serializationMode) {
+    case SerializationMode.byteDataClient:
+    case SerializationMode.byteDataServer:
+      return (Object? message) => new ByteDataDeserializer(
+          new ByteData.sublistView(message as Uint8List));
+    case SerializationMode.jsonClient:
+    case SerializationMode.jsonServer:
+      return (Object? message) =>
+          new JsonDeserializer(message as Iterable<Object?>);
+  }
+}
+
+/// Returns the current serializer factory for the zone.
+Serializer Function() get serializerFactory {
+  switch (serializationMode) {
+    case SerializationMode.byteDataClient:
+    case SerializationMode.byteDataServer:
+      return () => new ByteDataSerializer();
+    case SerializationMode.jsonClient:
+    case SerializationMode.jsonServer:
+      return () => new JsonSerializer();
+  }
+}
+
 /// Some objects are serialized differently on the client side versus the server
-/// side. This indicates the different modes.
+/// side. This indicates the different modes, as well as the format used.
 enum SerializationMode {
-  server,
-  client,
+  byteDataClient,
+  byteDataServer,
+  jsonServer,
+  jsonClient,
+}
+
+extension IsClient on SerializationMode {
+  bool get isClient {
+    switch (this) {
+      case SerializationMode.byteDataClient:
+      case SerializationMode.jsonClient:
+        return true;
+      case SerializationMode.byteDataServer:
+      case SerializationMode.jsonServer:
+        return false;
+    }
+  }
 }
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 191509f..5a6df71 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
@@ -6,58 +6,59 @@
 
 extension DeserializerExtensions on Deserializer {
   T expectRemoteInstance<T>() {
-    int id = expectNum();
-    switch (serializationMode) {
-      case SerializationMode.client:
+    int id = expectInt();
+
+    // Server side we just return the cached remote instance by ID.
+    if (!serializationMode.isClient) {
+      return RemoteInstance.cached(id) as T;
+    }
+
+    moveNext();
+    RemoteInstanceKind kind = RemoteInstanceKind.values[expectInt()];
+    switch (kind) {
+      case RemoteInstanceKind.classIntrospector:
+      case RemoteInstanceKind.namedStaticType:
+      case RemoteInstanceKind.staticType:
+      case RemoteInstanceKind.typeDeclarationResolver:
+      case RemoteInstanceKind.typeResolver:
+        // These are simple wrappers, just pass in the kind
+        return new RemoteInstanceImpl(id: id, kind: kind) as T;
+      case RemoteInstanceKind.classDeclaration:
         moveNext();
-        RemoteInstanceKind kind = RemoteInstanceKind.values[expectNum()];
-        switch (kind) {
-          case RemoteInstanceKind.classIntrospector:
-          case RemoteInstanceKind.namedStaticType:
-          case RemoteInstanceKind.staticType:
-          case RemoteInstanceKind.typeDeclarationResolver:
-          case RemoteInstanceKind.typeResolver:
-            // These are simple wrappers, just pass in the kind
-            return new RemoteInstanceImpl(id: id, kind: kind) as T;
-          case RemoteInstanceKind.classDeclaration:
-            moveNext();
-            return _expectClassDeclaration(id) as T;
-          case RemoteInstanceKind.constructorDeclaration:
-            moveNext();
-            return _expectConstructorDeclaration(id) as T;
-          case RemoteInstanceKind.fieldDeclaration:
-            moveNext();
-            return _expectFieldDeclaration(id) as T;
-          case RemoteInstanceKind.functionDeclaration:
-            moveNext();
-            return _expectFunctionDeclaration(id) as T;
-          case RemoteInstanceKind.functionTypeAnnotation:
-            moveNext();
-            return _expectFunctionTypeAnnotation(id) as T;
-          case RemoteInstanceKind.identifier:
-            moveNext();
-            return _expectIdentifier(id) as T;
-          case RemoteInstanceKind.methodDeclaration:
-            moveNext();
-            return _expectMethodDeclaration(id) as T;
-          case RemoteInstanceKind.namedTypeAnnotation:
-            moveNext();
-            return _expectNamedTypeAnnotation(id) as T;
-          case RemoteInstanceKind.parameterDeclaration:
-            moveNext();
-            return _expectParameterDeclaration(id) as T;
-          case RemoteInstanceKind.typeAliasDeclaration:
-            moveNext();
-            return _expectTypeAliasDeclaration(id) as T;
-          case RemoteInstanceKind.typeParameterDeclaration:
-            moveNext();
-            return _expectTypeParameterDeclaration(id) as T;
-          case RemoteInstanceKind.variableDeclaration:
-            moveNext();
-            return _expectVariableDeclaration(id) as T;
-        }
-      case SerializationMode.server:
-        return RemoteInstance.cached(id) as T;
+        return _expectClassDeclaration(id) as T;
+      case RemoteInstanceKind.constructorDeclaration:
+        moveNext();
+        return _expectConstructorDeclaration(id) as T;
+      case RemoteInstanceKind.fieldDeclaration:
+        moveNext();
+        return _expectFieldDeclaration(id) as T;
+      case RemoteInstanceKind.functionDeclaration:
+        moveNext();
+        return _expectFunctionDeclaration(id) as T;
+      case RemoteInstanceKind.functionTypeAnnotation:
+        moveNext();
+        return _expectFunctionTypeAnnotation(id) as T;
+      case RemoteInstanceKind.identifier:
+        moveNext();
+        return _expectIdentifier(id) as T;
+      case RemoteInstanceKind.methodDeclaration:
+        moveNext();
+        return _expectMethodDeclaration(id) as T;
+      case RemoteInstanceKind.namedTypeAnnotation:
+        moveNext();
+        return _expectNamedTypeAnnotation(id) as T;
+      case RemoteInstanceKind.parameterDeclaration:
+        moveNext();
+        return _expectParameterDeclaration(id) as T;
+      case RemoteInstanceKind.typeAliasDeclaration:
+        moveNext();
+        return _expectTypeAliasDeclaration(id) as T;
+      case RemoteInstanceKind.typeParameterDeclaration:
+        moveNext();
+        return _expectTypeParameterDeclaration(id) as T;
+      case RemoteInstanceKind.variableDeclaration:
+        moveNext();
+        return _expectVariableDeclaration(id) as T;
     }
   }
 
@@ -222,7 +223,7 @@
     expectList();
     List<Object> parts = [];
     while (moveNext()) {
-      _CodePartKind partKind = _CodePartKind.values[expectNum()];
+      _CodePartKind partKind = _CodePartKind.values[expectInt()];
       moveNext();
       switch (partKind) {
         case _CodePartKind.code:
@@ -240,7 +241,7 @@
   }
 
   T expectCode<T extends Code>() {
-    CodeKind kind = CodeKind.values[expectNum()];
+    CodeKind kind = CodeKind.values[expectInt()];
 
     switch (kind) {
       case CodeKind.raw:
@@ -309,7 +310,7 @@
 
 extension SerializeCode on Code {
   void serialize(Serializer serializer) {
-    serializer.addNum(kind.index);
+    serializer.addInt(kind.index);
     switch (kind) {
       case CodeKind.namedTypeAnnotation:
         NamedTypeAnnotationCode self = this as NamedTypeAnnotationCode;
@@ -366,13 +367,13 @@
         for (Object part in parts) {
           if (part is String) {
             serializer
-              ..addNum(_CodePartKind.string.index)
+              ..addInt(_CodePartKind.string.index)
               ..addString(part);
           } else if (part is Code) {
-            serializer.addNum(_CodePartKind.code.index);
+            serializer.addInt(_CodePartKind.code.index);
             part.serialize(serializer);
           } else if (part is IdentifierImpl) {
-            serializer.addNum(_CodePartKind.identifier.index);
+            serializer.addInt(_CodePartKind.identifier.index);
             part.serialize(serializer);
           } else {
             throw new StateError('Unrecognized code part $part');
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 6902870..dcd267c 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
@@ -126,18 +126,18 @@
       required this.messageStream,
       required this.sendPort}) {
     messageStream.listen((message) {
-      withSerializationMode(SerializationMode.server, () {
-        JsonDeserializer deserializer =
-            new JsonDeserializer(message as List<Object?>);
+      withSerializationMode(SerializationMode.jsonServer, () {
+        Deserializer deserializer =
+            deserializerFactory(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();
+        int zoneId = deserializer.expectInt();
         Zone zone = serializationZones[zoneId]!;
         zone.run(() async {
           deserializer.moveNext();
           MessageType messageType =
-              MessageType.values[deserializer.expectNum()];
+              MessageType.values[deserializer.expectInt()];
           switch (messageType) {
             case MessageType.response:
               SerializableResponse response =
@@ -169,7 +169,7 @@
                       ? MessageType.namedStaticType
                       : MessageType.staticType,
                   serializationZoneId: zoneId);
-              JsonSerializer serializer = new JsonSerializer();
+              Serializer serializer = serializerFactory();
               response.serialize(serializer);
               sendPort.send(serializer.result);
               break;
@@ -184,7 +184,7 @@
                   requestId: request.id,
                   responseType: MessageType.boolean,
                   serializationZoneId: zoneId);
-              JsonSerializer serializer = new JsonSerializer();
+              Serializer serializer = serializerFactory();
               response.serialize(serializer);
               sendPort.send(serializer.result);
               break;
@@ -199,7 +199,7 @@
                   requestId: request.id,
                   responseType: MessageType.boolean,
                   serializationZoneId: zoneId);
-              JsonSerializer serializer = new JsonSerializer();
+              Serializer serializer = serializerFactory();
               response.serialize(serializer);
               sendPort.send(serializer.result);
               break;
@@ -215,7 +215,7 @@
                       // TODO: Consider refactoring to avoid the need for this.
                       as TypeDeclarationImpl),
                   serializationZoneId: zoneId);
-              JsonSerializer serializer = new JsonSerializer();
+              Serializer serializer = serializerFactory();
               response.serialize(serializer);
               sendPort.send(serializer.result);
               break;
@@ -233,7 +233,7 @@
                       // TODO: Consider refactoring to avoid the need for this.
                       .cast<ConstructorDeclarationImpl>()),
                   serializationZoneId: zoneId);
-              JsonSerializer serializer = new JsonSerializer();
+              Serializer serializer = serializerFactory();
               response.serialize(serializer);
               sendPort.send(serializer.result);
               break;
@@ -251,7 +251,7 @@
                       // TODO: Consider refactoring to avoid the need for this.
                       .cast<FieldDeclarationImpl>()),
                   serializationZoneId: zoneId);
-              JsonSerializer serializer = new JsonSerializer();
+              Serializer serializer = serializerFactory();
               response.serialize(serializer);
               sendPort.send(serializer.result);
               break;
@@ -269,7 +269,7 @@
                       // TODO: Consider refactoring to avoid the need for this.
                       .cast<ClassDeclarationImpl>()),
                   serializationZoneId: zoneId);
-              JsonSerializer serializer = new JsonSerializer();
+              Serializer serializer = serializerFactory();
               response.serialize(serializer);
               sendPort.send(serializer.result);
               break;
@@ -287,7 +287,7 @@
                       // TODO: Consider refactoring to avoid the need for this.
                       .cast<MethodDeclarationImpl>()),
                   serializationZoneId: zoneId);
-              JsonSerializer serializer = new JsonSerializer();
+              Serializer serializer = serializerFactory();
               response.serialize(serializer);
               sendPort.send(serializer.result);
               break;
@@ -305,7 +305,7 @@
                       // TODO: Consider refactoring to avoid the need for this.
                       .cast<ClassDeclarationImpl>()),
                   serializationZoneId: zoneId);
-              JsonSerializer serializer = new JsonSerializer();
+              Serializer serializer = serializerFactory();
               response.serialize(serializer);
               sendPort.send(serializer.result);
               break;
@@ -323,7 +323,7 @@
                       // TODO: Consider refactoring to avoid the need for this.
                       as ClassDeclarationImpl?,
                   serializationZoneId: zoneId);
-              JsonSerializer serializer = new JsonSerializer();
+              Serializer serializer = serializerFactory();
               response.serialize(serializer);
               sendPort.send(serializer.result);
               break;
@@ -436,13 +436,13 @@
   /// 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 {
+      withSerializationMode(SerializationMode.jsonServer, () async {
         int zoneId = _nextSerializationZoneId++;
         serializationZones[zoneId] = Zone.current;
         Request request = requestFactory(zoneId);
-        JsonSerializer serializer = new JsonSerializer();
+        Serializer serializer = serializerFactory();
         // It is our responsibility to add the zone ID header.
-        serializer.addNum(zoneId);
+        serializer.addInt(zoneId);
         request.serialize(serializer);
         sendPort.send(serializer.result);
         Completer<Response> completer = new Completer<Response>();
diff --git a/pkg/_fe_analyzer_shared/test/macros/executor_shared/serialization_test.dart b/pkg/_fe_analyzer_shared/test/macros/executor_shared/serialization_test.dart
index 879ae11..67cc977 100644
--- a/pkg/_fe_analyzer_shared/test/macros/executor_shared/serialization_test.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/executor_shared/serialization_test.dart
@@ -12,76 +12,109 @@
 import '../util.dart';
 
 void main() {
-  group('json serializer', () {
-    test('can serialize and deserialize basic data', () {
-      var serializer = JsonSerializer();
-      serializer
-        ..addNum(1)
-        ..addNullableNum(null)
-        ..addString('hello')
-        ..addNullableString(null)
-        ..startList()
-        ..addBool(true)
-        ..startList()
-        ..addNull()
-        ..endList()
-        ..addNullableBool(null)
-        ..endList()
-        ..addNum(1.0)
-        ..startList()
-        ..endList();
-      expect(
-          serializer.result,
-          equals([
-            1,
-            null,
-            'hello',
-            null,
-            [
-              true,
-              [null],
-              null
-            ],
-            1.0,
-            [],
-          ]));
-      var deserializer = JsonDeserializer(serializer.result);
-      expect(deserializer.moveNext(), true);
-      expect(deserializer.expectNum(), 1);
-      expect(deserializer.moveNext(), true);
-      expect(deserializer.expectNullableNum(), null);
-      expect(deserializer.moveNext(), true);
-      expect(deserializer.expectString(), 'hello');
-      expect(deserializer.moveNext(), true);
-      expect(deserializer.expectNullableString(), null);
-      expect(deserializer.moveNext(), true);
+  for (var mode in [
+    SerializationMode.jsonClient,
+    SerializationMode.jsonServer,
+    SerializationMode.byteDataClient,
+    SerializationMode.byteDataServer,
+  ]) {
+    test('$mode can serialize and deserialize basic data', () {
+      withSerializationMode(mode, () {
+        var serializer = serializerFactory();
+        serializer
+          ..addInt(0)
+          ..addInt(1)
+          ..addInt(0xff)
+          ..addInt(0xffff)
+          ..addInt(0xffffffff)
+          ..addInt(0xffffffffffffffff)
+          ..addInt(-1)
+          ..addInt(-0x80)
+          ..addInt(-0x8000)
+          ..addInt(-0x80000000)
+          ..addInt(-0x8000000000000000)
+          ..addNullableInt(null)
+          ..addString('hello')
+          ..addString('€') // Requires a two byte string
+          ..addString('𐐷') // Requires two, 16 bit code units
+          ..addNullableString(null)
+          ..startList()
+          ..addBool(true)
+          ..startList()
+          ..addNull()
+          ..endList()
+          ..addNullableBool(null)
+          ..endList()
+          ..addDouble(1.0)
+          ..startList()
+          ..endList();
+        var deserializer = deserializerFactory(serializer.result);
+        expect(deserializer.moveNext(), true);
+        expect(deserializer.expectInt(), 0);
+        expect(deserializer.moveNext(), true);
+        expect(deserializer.expectInt(), 1);
+        expect(deserializer.moveNext(), true);
+        expect(deserializer.expectInt(), 0xff);
+        expect(deserializer.moveNext(), true);
+        expect(deserializer.expectInt(), 0xffff);
+        expect(deserializer.moveNext(), true);
+        expect(deserializer.expectInt(), 0xffffffff);
+        expect(deserializer.moveNext(), true);
+        expect(deserializer.expectInt(), 0xffffffffffffffff);
+        expect(deserializer.moveNext(), true);
+        expect(deserializer.expectInt(), -1);
+        expect(deserializer.moveNext(), true);
+        expect(deserializer.expectInt(), -0x80);
+        expect(deserializer.moveNext(), true);
+        expect(deserializer.expectInt(), -0x8000);
+        expect(deserializer.moveNext(), true);
+        expect(deserializer.expectInt(), -0x80000000);
+        expect(deserializer.moveNext(), true);
+        expect(deserializer.expectInt(), -0x8000000000000000);
+        expect(deserializer.moveNext(), true);
+        expect(deserializer.expectNullableInt(), null);
+        expect(deserializer.moveNext(), true);
+        expect(deserializer.expectString(), 'hello');
+        expect(deserializer.moveNext(), true);
+        expect(deserializer.expectString(), '€');
+        expect(deserializer.moveNext(), true);
+        expect(deserializer.expectString(), '𐐷');
+        expect(deserializer.moveNext(), true);
+        expect(deserializer.expectNullableString(), null);
+        expect(deserializer.moveNext(), true);
 
-      deserializer.expectList();
-      expect(deserializer.moveNext(), true);
-      expect(deserializer.expectBool(), true);
-      expect(deserializer.moveNext(), true);
+        deserializer.expectList();
+        expect(deserializer.moveNext(), true);
+        expect(deserializer.expectBool(), true);
+        expect(deserializer.moveNext(), true);
 
-      deserializer.expectList();
-      expect(deserializer.moveNext(), true);
-      expect(deserializer.checkNull(), true);
-      expect(deserializer.moveNext(), false);
+        deserializer.expectList();
+        expect(deserializer.moveNext(), true);
+        expect(deserializer.checkNull(), true);
+        expect(deserializer.moveNext(), false);
 
-      expect(deserializer.moveNext(), true);
-      expect(deserializer.expectNullableBool(), null);
-      expect(deserializer.moveNext(), false);
+        expect(deserializer.moveNext(), true);
+        expect(deserializer.expectNullableBool(), null);
+        expect(deserializer.moveNext(), false);
 
-      // Have to move the parent again to advance it past the list entry.
-      expect(deserializer.moveNext(), true);
-      expect(deserializer.expectNum(), 1.0);
-      expect(deserializer.moveNext(), true);
+        // Have to move the parent again to advance it past the list entry.
+        expect(deserializer.moveNext(), true);
+        expect(deserializer.expectDouble(), 1.0);
+        expect(deserializer.moveNext(), true);
 
-      deserializer.expectList();
-      expect(deserializer.moveNext(), false);
+        deserializer.expectList();
+        expect(deserializer.moveNext(), false);
 
-      expect(deserializer.moveNext(), false);
+        expect(deserializer.moveNext(), false);
+      });
     });
+  }
 
-    test('remote instances', () async {
+  for (var mode in [
+    SerializationMode.byteDataServer,
+    SerializationMode.jsonServer
+  ]) {
+    test('remote instances in $mode', () async {
       var string = NamedTypeAnnotationImpl(
           id: RemoteInstance.uniqueId,
           isNullable: false,
@@ -93,203 +126,219 @@
           isNullable: false,
           identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Foo'),
           typeArguments: [string]);
-      Object? serializedFoo;
-      var serializer = JsonSerializer();
 
-      withSerializationMode(SerializationMode.server, () {
+      withSerializationMode(mode, () {
+        var serializer = serializerFactory();
         foo.serialize(serializer);
-        serializedFoo = serializer.result;
-        var response = roundTrip(serializedFoo);
-        var deserializer = JsonDeserializer(response as List<Object?>);
+        var response = roundTrip(serializer.result);
+        var deserializer = deserializerFactory(response);
         var instance = RemoteInstance.deserialize(deserializer);
         expect(instance, foo);
       });
     });
+  }
 
-    group('declarations', () {
-      final barType = NamedTypeAnnotationImpl(
-          id: RemoteInstance.uniqueId,
-          isNullable: false,
-          identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Bar'),
-          typeArguments: []);
-      final fooType = NamedTypeAnnotationImpl(
-          id: RemoteInstance.uniqueId,
-          isNullable: true,
-          identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Foo'),
-          typeArguments: [barType]);
+  group('declarations', () {
+    final barType = NamedTypeAnnotationImpl(
+        id: RemoteInstance.uniqueId,
+        isNullable: false,
+        identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Bar'),
+        typeArguments: []);
+    final fooType = NamedTypeAnnotationImpl(
+        id: RemoteInstance.uniqueId,
+        isNullable: true,
+        identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Foo'),
+        typeArguments: [barType]);
 
-      test('NamedTypeAnnotation', () {
-        expectSerializationEquality(fooType);
-      });
+    for (var mode in [
+      SerializationMode.byteDataServer,
+      SerializationMode.jsonServer
+    ]) {
+      group('with mode $mode', () {
+        test('NamedTypeAnnotation', () {
+          expectSerializationEquality(fooType, mode);
+        });
 
-      final fooNamedParam = ParameterDeclarationImpl(
-          id: RemoteInstance.uniqueId,
-          isNamed: true,
-          isRequired: true,
-          identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'foo'),
-          type: fooType);
+        final fooNamedParam = ParameterDeclarationImpl(
+            id: RemoteInstance.uniqueId,
+            isNamed: true,
+            isRequired: true,
+            identifier:
+                IdentifierImpl(id: RemoteInstance.uniqueId, name: 'foo'),
+            type: fooType);
 
-      final barPositionalParam = ParameterDeclarationImpl(
-          id: RemoteInstance.uniqueId,
-          isNamed: false,
-          isRequired: false,
-          identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'bar'),
-          type: barType);
+        final barPositionalParam = ParameterDeclarationImpl(
+            id: RemoteInstance.uniqueId,
+            isNamed: false,
+            isRequired: false,
+            identifier:
+                IdentifierImpl(id: RemoteInstance.uniqueId, name: 'bar'),
+            type: barType);
 
-      final zapTypeParam = TypeParameterDeclarationImpl(
-          id: RemoteInstance.uniqueId,
-          identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Zap'),
-          bound: barType);
-
-      // Transitively tests `TypeParameterDeclaration` and
-      // `ParameterDeclaration`.
-      test('FunctionTypeAnnotation', () {
-        var functionType = FunctionTypeAnnotationImpl(
-          id: RemoteInstance.uniqueId,
-          isNullable: true,
-          namedParameters: [fooNamedParam],
-          positionalParameters: [barPositionalParam],
-          returnType: fooType,
-          typeParameters: [zapTypeParam],
-        );
-        expectSerializationEquality(functionType);
-      });
-
-      test('FunctionDeclaration', () {
-        var function = FunctionDeclarationImpl(
+        final zapTypeParam = TypeParameterDeclarationImpl(
             id: RemoteInstance.uniqueId,
             identifier:
-                IdentifierImpl(id: RemoteInstance.uniqueId, name: 'name'),
-            isAbstract: true,
-            isExternal: false,
-            isGetter: true,
-            isOperator: false,
-            isSetter: false,
-            namedParameters: [],
-            positionalParameters: [],
-            returnType: fooType,
-            typeParameters: []);
-        expectSerializationEquality(function);
-      });
+                IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Zap'),
+            bound: barType);
 
-      test('MethodDeclaration', () {
-        var method = MethodDeclarationImpl(
+        // Transitively tests `TypeParameterDeclaration` and
+        // `ParameterDeclaration`.
+        test('FunctionTypeAnnotation', () {
+          var functionType = FunctionTypeAnnotationImpl(
             id: RemoteInstance.uniqueId,
-            identifier:
-                IdentifierImpl(id: RemoteInstance.uniqueId, name: 'zorp'),
-            isAbstract: false,
-            isExternal: false,
-            isGetter: false,
-            isOperator: false,
-            isSetter: true,
+            isNullable: true,
             namedParameters: [fooNamedParam],
             positionalParameters: [barPositionalParam],
             returnType: fooType,
             typeParameters: [zapTypeParam],
-            definingClass: fooType.identifier);
-        expectSerializationEquality(method);
-      });
+          );
+          expectSerializationEquality(functionType, mode);
+        });
 
-      test('ConstructorDeclaration', () {
-        var constructor = ConstructorDeclarationImpl(
-          id: RemoteInstance.uniqueId,
-          identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'new'),
-          isAbstract: false,
-          isExternal: false,
-          isGetter: false,
-          isOperator: true,
-          isSetter: false,
-          namedParameters: [fooNamedParam],
-          positionalParameters: [barPositionalParam],
-          returnType: fooType,
-          typeParameters: [zapTypeParam],
-          definingClass: fooType.identifier,
-          isFactory: true,
-        );
-        expectSerializationEquality(constructor);
-      });
+        test('FunctionDeclaration', () {
+          var function = FunctionDeclarationImpl(
+              id: RemoteInstance.uniqueId,
+              identifier:
+                  IdentifierImpl(id: RemoteInstance.uniqueId, name: 'name'),
+              isAbstract: true,
+              isExternal: false,
+              isGetter: true,
+              isOperator: false,
+              isSetter: false,
+              namedParameters: [],
+              positionalParameters: [],
+              returnType: fooType,
+              typeParameters: []);
+          expectSerializationEquality(function, mode);
+        });
 
-      test('VariableDeclaration', () {
-        var bar = VariableDeclarationImpl(
-          id: RemoteInstance.uniqueId,
-          identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'bar'),
-          isExternal: true,
-          isFinal: false,
-          isLate: true,
-          type: barType,
-        );
-        expectSerializationEquality(bar);
-      });
+        test('MethodDeclaration', () {
+          var method = MethodDeclarationImpl(
+              id: RemoteInstance.uniqueId,
+              identifier:
+                  IdentifierImpl(id: RemoteInstance.uniqueId, name: 'zorp'),
+              isAbstract: false,
+              isExternal: false,
+              isGetter: false,
+              isOperator: false,
+              isSetter: true,
+              namedParameters: [fooNamedParam],
+              positionalParameters: [barPositionalParam],
+              returnType: fooType,
+              typeParameters: [zapTypeParam],
+              definingClass: fooType.identifier);
+          expectSerializationEquality(method, mode);
+        });
 
-      test('FieldDeclaration', () {
-        var bar = FieldDeclarationImpl(
-          id: RemoteInstance.uniqueId,
-          identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'bar'),
-          isExternal: false,
-          isFinal: true,
-          isLate: false,
-          type: barType,
-          definingClass: fooType.identifier,
-        );
-        expectSerializationEquality(bar);
-      });
+        test('ConstructorDeclaration', () {
+          var constructor = ConstructorDeclarationImpl(
+            id: RemoteInstance.uniqueId,
+            identifier:
+                IdentifierImpl(id: RemoteInstance.uniqueId, name: 'new'),
+            isAbstract: false,
+            isExternal: false,
+            isGetter: false,
+            isOperator: true,
+            isSetter: false,
+            namedParameters: [fooNamedParam],
+            positionalParameters: [barPositionalParam],
+            returnType: fooType,
+            typeParameters: [zapTypeParam],
+            definingClass: fooType.identifier,
+            isFactory: true,
+          );
+          expectSerializationEquality(constructor, mode);
+        });
 
-      var objectType = NamedTypeAnnotationImpl(
-        id: RemoteInstance.uniqueId,
-        identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Object'),
-        isNullable: false,
-        typeArguments: [],
-      );
-      var serializableType = NamedTypeAnnotationImpl(
-        id: RemoteInstance.uniqueId,
-        identifier:
-            IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Serializable'),
-        isNullable: false,
-        typeArguments: [],
-      );
+        test('VariableDeclaration', () {
+          var bar = VariableDeclarationImpl(
+            id: RemoteInstance.uniqueId,
+            identifier:
+                IdentifierImpl(id: RemoteInstance.uniqueId, name: 'bar'),
+            isExternal: true,
+            isFinal: false,
+            isLate: true,
+            type: barType,
+          );
+          expectSerializationEquality(bar, mode);
+        });
 
-      test('ClassDeclaration', () {
-        var fooClass = ClassDeclarationImpl(
-          id: RemoteInstance.uniqueId,
-          identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Foo'),
-          interfaces: [barType],
-          isAbstract: true,
-          isExternal: false,
-          mixins: [serializableType],
-          superclass: objectType,
-          typeParameters: [zapTypeParam],
-        );
-        expectSerializationEquality(fooClass);
-      });
+        test('FieldDeclaration', () {
+          var bar = FieldDeclarationImpl(
+            id: RemoteInstance.uniqueId,
+            identifier:
+                IdentifierImpl(id: RemoteInstance.uniqueId, name: 'bar'),
+            isExternal: false,
+            isFinal: true,
+            isLate: false,
+            type: barType,
+            definingClass: fooType.identifier,
+          );
+          expectSerializationEquality(bar, mode);
+        });
 
-      test('TypeAliasDeclaration', () {
-        var typeAlias = TypeAliasDeclarationImpl(
+        var objectType = NamedTypeAnnotationImpl(
           id: RemoteInstance.uniqueId,
           identifier:
-              IdentifierImpl(id: RemoteInstance.uniqueId, name: 'FooOfBar'),
-          typeParameters: [zapTypeParam],
-          aliasedType: NamedTypeAnnotationImpl(
-              id: RemoteInstance.uniqueId,
-              isNullable: false,
-              identifier:
-                  IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Foo'),
-              typeArguments: [barType]),
+              IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Object'),
+          isNullable: false,
+          typeArguments: [],
         );
-        expectSerializationEquality(typeAlias);
+        var serializableType = NamedTypeAnnotationImpl(
+          id: RemoteInstance.uniqueId,
+          identifier:
+              IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Serializable'),
+          isNullable: false,
+          typeArguments: [],
+        );
+
+        test('ClassDeclaration', () {
+          var fooClass = ClassDeclarationImpl(
+            id: RemoteInstance.uniqueId,
+            identifier:
+                IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Foo'),
+            interfaces: [barType],
+            isAbstract: true,
+            isExternal: false,
+            mixins: [serializableType],
+            superclass: objectType,
+            typeParameters: [zapTypeParam],
+          );
+          expectSerializationEquality(fooClass, mode);
+        });
+
+        test('TypeAliasDeclaration', () {
+          var typeAlias = TypeAliasDeclarationImpl(
+            id: RemoteInstance.uniqueId,
+            identifier:
+                IdentifierImpl(id: RemoteInstance.uniqueId, name: 'FooOfBar'),
+            typeParameters: [zapTypeParam],
+            aliasedType: NamedTypeAnnotationImpl(
+                id: RemoteInstance.uniqueId,
+                isNullable: false,
+                identifier:
+                    IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Foo'),
+                typeArguments: [barType]),
+          );
+          expectSerializationEquality(typeAlias, mode);
+        });
       });
-    });
+    }
   });
 }
 
 /// Serializes [serializable] in server mode, then deserializes it in client
 /// mode, and checks that all the fields are the same.
-void expectSerializationEquality(Serializable serializable) {
-  var serializer = JsonSerializer();
-  withSerializationMode(SerializationMode.server, () {
+void expectSerializationEquality(
+    Serializable serializable, SerializationMode serverMode) {
+  late Object? serialized;
+  withSerializationMode(serverMode, () {
+    var serializer = serializerFactory();
     serializable.serialize(serializer);
+    serialized = serializer.result;
   });
-  withSerializationMode(SerializationMode.client, () {
-    var deserializer = JsonDeserializer(serializer.result);
+  withSerializationMode(_clientModeForServerMode(serverMode), () {
+    var deserializer = deserializerFactory(serialized);
     var deserialized = (deserializer..moveNext()).expectRemoteInstance();
     if (deserialized is Declaration) {
       expect(serializable, deepEqualsDeclaration(deserialized));
@@ -303,12 +352,23 @@
 
 /// Deserializes [serialized] in client mode and sends it back.
 Object? roundTrip<Declaration>(Object? serialized) {
-  return withSerializationMode(SerializationMode.client, () {
-    var deserializer = JsonDeserializer(serialized as List<Object?>);
+  return withSerializationMode(_clientModeForServerMode(serializationMode), () {
+    var deserializer = deserializerFactory(serialized);
     var instance =
         RemoteInstance.deserialize<NamedTypeAnnotationImpl>(deserializer);
-    var serializer = JsonSerializer();
+    var serializer = serializerFactory();
     instance.serialize(serializer);
     return serializer.result;
   });
 }
+
+SerializationMode _clientModeForServerMode(SerializationMode serverMode) {
+  switch (serverMode) {
+    case SerializationMode.byteDataServer:
+      return SerializationMode.byteDataClient;
+    case SerializationMode.jsonServer:
+      return SerializationMode.jsonClient;
+    default:
+      throw StateError('Expected to be running in a server mode');
+  }
+}
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart b/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
index 53b553c..4fbeca3 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
@@ -9,6 +9,7 @@
 import 'package:analysis_server/src/services/completion/dart/closure_contributor.dart';
 import 'package:analysis_server/src/services/completion/dart/combinator_contributor.dart';
 import 'package:analysis_server/src/services/completion/dart/documentation_cache.dart';
+import 'package:analysis_server/src/services/completion/dart/enum_constant_constructor_contributor.dart';
 import 'package:analysis_server/src/services/completion/dart/extension_member_contributor.dart';
 import 'package:analysis_server/src/services/completion/dart/feature_computer.dart';
 import 'package:analysis_server/src/services/completion/dart/field_formal_contributor.dart';
@@ -142,6 +143,7 @@
       ArgListContributor(request, builder),
       ClosureContributor(request, builder),
       CombinatorContributor(request, builder),
+      EnumConstantConstructorContributor(request, builder),
       ExtensionMemberContributor(request, builder),
       FieldFormalContributor(request, builder),
       KeywordContributor(request, builder),
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/enum_constant_constructor_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/enum_constant_constructor_contributor.dart
new file mode 100644
index 0000000..2680e24
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/completion/dart/enum_constant_constructor_contributor.dart
@@ -0,0 +1,63 @@
+// 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:analysis_server/src/provisional/completion/dart/completion_dart.dart';
+import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
+import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
+import 'package:analyzer/dart/analysis/features.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
+
+/// A contributor that produces suggestions for constructors to be invoked
+/// in enum constants.
+class EnumConstantConstructorContributor extends DartCompletionContributor {
+  EnumConstantConstructorContributor(
+    DartCompletionRequest request,
+    SuggestionBuilder builder,
+  ) : super(request, builder);
+
+  @override
+  Future<void> computeSuggestions() async {
+    if (!request.featureSet.isEnabled(Feature.enhanced_enums)) {
+      return;
+    }
+
+    // TODO(scheglov) It seems unfortunate that we have to re-discover
+    // the location in contributors. This is the work of `OpType`, so why
+    // doesn't it provide all these enclosing `EnumConstantDeclaration`,
+    // `ConstructorSelector`, `EnumDeclaration`?
+    var node = request.target.containingNode;
+    if (node is! ConstructorSelector) {
+      return;
+    }
+
+    if (request.opType.completionLocation != 'ConstructorSelector_name') {
+      return;
+    }
+
+    var arguments = node.parent;
+    if (arguments is! EnumConstantArguments) {
+      return;
+    }
+
+    var enumConstant = arguments.parent;
+    if (enumConstant is! EnumConstantDeclaration) {
+      return;
+    }
+
+    var enumDeclaration = enumConstant.parent;
+    if (enumDeclaration is! EnumDeclaration) {
+      return;
+    }
+
+    var enumElement = enumDeclaration.declaredElement as ClassElement;
+    for (var constructor in enumElement.constructors) {
+      builder.suggestConstructor(
+        constructor,
+        hasClassName: true,
+        tearOff: true,
+      );
+    }
+  }
+}
diff --git a/pkg/analysis_server/test/services/completion/dart/arglist_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/arglist_contributor_test.dart
index ac6847a..5ed76b7 100644
--- a/pkg/analysis_server/test/services/completion/dart/arglist_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/arglist_contributor_test.dart
@@ -738,6 +738,16 @@
 ''');
     await computeAndCheck();
 
+    // Enum constant.
+    addTestSource2('''
+$languageVersionLine
+enum E {
+  v$arguments;
+  const E$parameters;
+}
+''');
+    await computeAndCheck();
+
     // Function expression invocation.
     addTestSource2('''
 $languageVersionLine
diff --git a/pkg/analysis_server/test/services/completion/dart/location/enum_test.dart b/pkg/analysis_server/test/services/completion/dart/location/enum_test.dart
index 89bc1b5..295cbdd 100644
--- a/pkg/analysis_server/test/services/completion/dart/location/enum_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/location/enum_test.dart
@@ -213,4 +213,130 @@
 
     check(response).suggestions.isEmpty;
   }
+
+  Future<void> test_constantName_dot_name_x_argumentList_named() async {
+    var response = await getTestCodeSuggestions('''
+enum E {
+  v.foo0^();
+  const E.foo01();
+  const E.foo02();
+  const E.bar01();
+}
+''');
+
+    if (isProtocolVersion2) {
+      check(response)
+        ..hasReplacement(left: 4)
+        ..suggestions.matchesInAnyOrder([
+          (suggestion) => suggestion
+            ..completion.isEqualTo('foo01')
+            ..isConstructorInvocation,
+          (suggestion) => suggestion
+            ..completion.isEqualTo('foo02')
+            ..isConstructorInvocation,
+        ]);
+    }
+  }
+
+  Future<void> test_constantName_dot_name_x_semicolon_named() async {
+    var response = await getTestCodeSuggestions('''
+enum E {
+  v.foo0^;
+  const E.foo01();
+  const E.foo02();
+  const E.bar01();
+}
+''');
+
+    if (isProtocolVersion2) {
+      check(response)
+        ..hasReplacement(left: 4)
+        ..suggestions.matchesInAnyOrder([
+          (suggestion) => suggestion
+            ..completion.isEqualTo('foo01')
+            ..isConstructorInvocation,
+          (suggestion) => suggestion
+            ..completion.isEqualTo('foo02')
+            ..isConstructorInvocation,
+        ]);
+    }
+  }
+
+  Future<void> test_constantName_dot_x_argumentList_named() async {
+    var response = await getTestCodeSuggestions('''
+enum E {
+  v.^();
+  const E.foo01();
+  const E.foo02();
+}
+''');
+
+    check(response).suggestions.matchesInAnyOrder([
+      (suggestion) => suggestion
+        ..completion.isEqualTo('foo01')
+        ..isConstructorInvocation,
+      (suggestion) => suggestion
+        ..completion.isEqualTo('foo02')
+        ..isConstructorInvocation,
+    ]);
+  }
+
+  Future<void> test_constantName_dot_x_semicolon_named() async {
+    var response = await getTestCodeSuggestions('''
+enum E {
+  v.^;
+  const E.foo01();
+  const E.foo02();
+}
+''');
+
+    check(response).suggestions.matchesInAnyOrder([
+      (suggestion) => suggestion
+        ..completion.isEqualTo('foo01')
+        ..isConstructorInvocation,
+      (suggestion) => suggestion
+        ..completion.isEqualTo('foo02')
+        ..isConstructorInvocation,
+    ]);
+  }
+
+  Future<void> test_constantName_dot_x_semicolon_unnamed_declared() async {
+    var response = await getTestCodeSuggestions('''
+enum E {
+  v.^;
+  const E();
+}
+''');
+
+    check(response).suggestions.matches([
+      (suggestion) => suggestion
+        ..completion.isEqualTo('new')
+        ..isConstructorInvocation,
+    ]);
+  }
+
+  Future<void> test_constantName_dot_x_unnamed_implicit() async {
+    var response = await getTestCodeSuggestions('''
+enum E {
+  v.^
+}
+''');
+
+    check(response).suggestions.matches([
+      (suggestion) => suggestion
+        ..completion.isEqualTo('new')
+        ..isConstructorInvocation,
+    ]);
+  }
+
+  Future<void> test_constantName_dot_x_unnamed_language216() async {
+    var response = await getTestCodeSuggestions('''
+// @dart = 2.16
+enum E {
+  v.^
+}
+''');
+
+    check(response).suggestions.isEmpty;
+  }
 }
diff --git a/pkg/analyzer/lib/src/fasta/ast_builder.dart b/pkg/analyzer/lib/src/fasta/ast_builder.dart
index e04f113..8046f63 100644
--- a/pkg/analyzer/lib/src/fasta/ast_builder.dart
+++ b/pkg/analyzer/lib/src/fasta/ast_builder.dart
@@ -2840,17 +2840,18 @@
     var argumentList = tmpArguments?.argumentList;
 
     TypeArgumentListImpl? typeArguments;
-    ConstructorSelectorImpl? constructorName;
+    ConstructorSelectorImpl? constructorSelector;
     if (tmpConstructor != null) {
       typeArguments = tmpConstructor.type2.typeArguments;
       var constructorNamePeriod = tmpConstructor.period;
       var constructorNameId = tmpConstructor.name;
       if (constructorNamePeriod != null && constructorNameId != null) {
-        constructorName = ConstructorSelectorImpl(
+        constructorSelector = ConstructorSelectorImpl(
           period: constructorNamePeriod,
           name: constructorNameId,
         );
       }
+      // enum E { v<int> }
       if (typeArguments != null && argumentList == null) {
         errorReporter.errorReporter?.reportErrorForNode(
           ParserErrorCode.ENUM_CONSTANT_WITH_TYPE_ARGUMENTS_WITHOUT_ARGUMENTS,
@@ -2858,6 +2859,10 @@
         );
         argumentList = _syntheticArgumentList(typeArguments.endToken);
       }
+      // enum E { v.^ }
+      if (constructorSelector != null) {
+        argumentList ??= _syntheticArgumentList(constructorSelector.endToken);
+      }
     }
 
     // Replace the constant to include arguments.
@@ -2868,7 +2873,7 @@
         name: constant.name,
         arguments: EnumConstantArgumentsImpl(
           typeArguments: typeArguments,
-          constructorSelector: constructorName,
+          constructorSelector: constructorSelector,
           argumentList: argumentList,
         ),
       );
diff --git a/pkg/analyzer/test/src/fasta/ast_builder_test.dart b/pkg/analyzer/test/src/fasta/ast_builder_test.dart
index cdc7a64..5edc30a 100644
--- a/pkg/analyzer/test/src/fasta/ast_builder_test.dart
+++ b/pkg/analyzer/test/src/fasta/ast_builder_test.dart
@@ -58,6 +58,78 @@
     expect(member.initializers, hasLength(1));
   }
 
+  void test_enum_constant_name_dot() {
+    var parseResult = parseStringWithErrors(r'''
+enum E {
+  v.
+}
+''');
+    parseResult.assertErrors([
+      error(ParserErrorCode.MISSING_IDENTIFIER, 14, 1),
+    ]);
+
+    var v = parseResult.findNode.enumConstantDeclaration('v.');
+    check(v).name.name.isEqualTo('v');
+    check(v).arguments.isNotNull
+      ..typeArguments.isNull
+      ..constructorSelector.isNotNull.name.isSynthetic
+      ..argumentList.isSynthetic;
+  }
+
+  void test_enum_constant_name_dot_identifier_semicolon() {
+    var parseResult = parseStringWithErrors(r'''
+enum E {
+  v.named;
+}
+''');
+    parseResult.assertNoErrors();
+
+    var v = parseResult.findNode.enumConstantDeclaration('v.');
+    check(v).name.name.isEqualTo('v');
+    check(v).arguments.isNotNull
+      ..typeArguments.isNull
+      ..constructorSelector.isNotNull.name.name.isEqualTo('named')
+      ..argumentList.isSynthetic;
+  }
+
+  void test_enum_constant_name_dot_semicolon() {
+    var parseResult = parseStringWithErrors(r'''
+enum E {
+  v.;
+}
+''');
+    parseResult.assertErrors([
+      error(ParserErrorCode.MISSING_IDENTIFIER, 13, 1),
+    ]);
+
+    var v = parseResult.findNode.enumConstantDeclaration('v.');
+    check(v).name.name.isEqualTo('v');
+    check(v).arguments.isNotNull
+      ..typeArguments.isNull
+      ..constructorSelector.isNotNull.name.isSynthetic
+      ..argumentList.isSynthetic;
+  }
+
+  void test_enum_constant_name_typeArguments_dot() {
+    var parseResult = parseStringWithErrors(r'''
+enum E {
+  v<int>.
+}
+''');
+    parseResult.assertErrors([
+      error(ParserErrorCode.ENUM_CONSTANT_WITH_TYPE_ARGUMENTS_WITHOUT_ARGUMENTS,
+          12, 5),
+      error(ParserErrorCode.MISSING_IDENTIFIER, 19, 1),
+    ]);
+
+    var v = parseResult.findNode.enumConstantDeclaration('v<int>.');
+    check(v).name.name.isEqualTo('v');
+    check(v).arguments.isNotNull
+      ..typeArguments.isNotNull
+      ..constructorSelector.isNotNull.name.isSynthetic
+      ..argumentList.isSynthetic;
+  }
+
   void test_enum_constant_withTypeArgumentsWithoutArguments() {
     var parseResult = parseStringWithErrors(r'''
 enum E<T> {
diff --git a/pkg/analyzer/test/util/ast_check.dart b/pkg/analyzer/test/util/ast_check.dart
index 65e0da9..8119c60 100644
--- a/pkg/analyzer/test/util/ast_check.dart
+++ b/pkg/analyzer/test/util/ast_check.dart
@@ -41,6 +41,15 @@
   }
 }
 
+extension ConstructorSelectorExtension on CheckTarget<ConstructorSelector> {
+  CheckTarget<SimpleIdentifier> get name {
+    return nest(
+      value.name,
+      (selected) => 'has name ${valueStr(selected)}',
+    );
+  }
+}
+
 extension EnumConstantArgumentsExtension on CheckTarget<EnumConstantArguments> {
   CheckTarget<ArgumentList> get argumentList {
     return nest(
@@ -49,6 +58,13 @@
     );
   }
 
+  CheckTarget<ConstructorSelector?> get constructorSelector {
+    return nest(
+      value.constructorSelector,
+      (selected) => 'has constructorSelector ${valueStr(selected)}',
+    );
+  }
+
   CheckTarget<TypeArgumentList?> get typeArguments {
     return nest(
       value.typeArguments,
@@ -116,6 +132,12 @@
     );
   }
 
+  void get isSynthetic {
+    if (!value.token.isSynthetic) {
+      fail('Is not synthetic');
+    }
+  }
+
   CheckTarget<String> get name {
     return nest(
       value.name,
diff --git a/pkg/analyzer_plugin/lib/src/utilities/completion/completion_target.dart b/pkg/analyzer_plugin/lib/src/utilities/completion/completion_target.dart
index ce716df..0ce5403 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/completion/completion_target.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/completion/completion_target.dart
@@ -291,6 +291,12 @@
       Element? executable;
       if (invocation is Annotation) {
         executable = invocation.element;
+      } else if (invocation is EnumConstantArguments) {
+        var enumConstant = invocation.parent;
+        if (enumConstant is! EnumConstantDeclaration) {
+          return null;
+        }
+        executable = enumConstant.constructorElement;
       } else if (invocation is InstanceCreationExpression) {
         executable = invocation.constructorName.staticElement;
       } else if (invocation is MethodInvocation) {
diff --git a/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart b/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
index 9d6bce2..74aee3b 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
@@ -450,6 +450,13 @@
   }
 
   @override
+  void visitConstructorSelector(ConstructorSelector node) {
+    if (identical(entity, node.name)) {
+      optype.completionLocation = 'ConstructorSelector_name';
+    }
+  }
+
+  @override
   void visitContinueStatement(ContinueStatement node) {
     if (node.label == null || identical(entity, node.label)) {
       optype.includeStatementLabelSuggestions = true;
@@ -1369,6 +1376,8 @@
       var parent = node.parent;
       if (parent is Annotation) {
         return 'annotation';
+      } else if (parent is EnumConstantArguments) {
+        return 'enumConstantArguments';
       } else if (parent is ExtensionOverride) {
         return 'extensionOverride';
       } else if (parent is FunctionExpressionInvocation) {
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index 10299a1..a8d2705 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -215,6 +215,7 @@
 compared
 compares
 compilations
+complement
 completers
 completes
 complicating
@@ -266,6 +267,7 @@
 csslib
 cstefantsova
 ctx
+customizable
 customized
 cut
 cwd
@@ -378,6 +380,7 @@
 effects
 efficient
 efficiently
+eight
 eighth
 elem
 eliminating
@@ -736,6 +739,7 @@
 masking
 masks
 master
+materialize
 mature
 mb
 mc
@@ -1308,6 +1312,7 @@
 traced
 tracker
 traditional
+transferable
 transformers
 transforming
 translation
@@ -1321,6 +1326,7 @@
 ts
 tty
 tuple
+twos
 typeref
 u
 ufeff
@@ -1466,6 +1472,9 @@
 xdfff
 xef
 xff
+xffff
+xffffffff
+xffffffffffffffff
 xi
 xj
 xk
diff --git a/tools/VERSION b/tools/VERSION
index a9db075..a1cb1b8 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 17
 PATCH 0
-PRERELEASE 102
+PRERELEASE 103
 PRERELEASE_PATCH 0
\ No newline at end of file