Add a byteData serializer/deserializer implementation

With the optimizations suggested this does now equal or outperform the JSON serializer in all scenarios, the latest numbers from the benchmark are as follows:

Isolate.spawn + SerializationMode.jsonClient: 0:00:00.041666
Isolate.spawnUri + SerializationMode.jsonClient: 0:00:00.069551
Separate process + SerializationMode.jsonClient: 0:00:00.177171
Isolate.spawn + SerializationMode.byteDataClient: 0:00:00.040990
Isolate.spawnUri + SerializationMode.byteDataClient: 0:00:00.059319
Separate process + SerializationMode.byteDataClient: 0:00:00.080008

Change-Id: If5431513c7487d8b7af350381e794cbd61c1be42
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/232027
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Auto-Submit: Jake Macdonald <jakemac@google.com>
Commit-Queue: Jake Macdonald <jakemac@google.com>
This commit is contained in:
Jake Macdonald 2022-02-11 23:35:27 +00:00 committed by Commit Bot
parent ea4f04f56d
commit 49d7f5056f
12 changed files with 1155 additions and 423 deletions

View file

@ -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();
}
}

View file

@ -55,17 +55,18 @@ void main(_, SendPort sendPort) {
/// Local function that sends requests and returns responses using [sendPort].
Future<Response> sendRequest(Request request) => _sendRequest(request, sendPort);
withSerializationMode(SerializationMode.client, () {
/// 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 @@ final _responseCompleters = <int, Completer<Response>>{};
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;

View file

@ -132,7 +132,7 @@ class Arguments implements Serializable {
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 @@ class Arguments implements Serializable {
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 @@ class Arguments implements Serializable {
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 @@ class Arguments implements Serializable {
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 @@ enum Phase {
}
/// Used for serializing and deserializing arguments.
enum _ArgumentKind { string, bool, num, list, map, nil }
enum _ArgumentKind { string, bool, double, int, list, map, nil }

View file

@ -19,9 +19,7 @@ class IdentifierImpl extends RemoteInstance implements Identifier {
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 @@ abstract class TypeAnnotationImpl extends RemoteInstance
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 @@ class NamedTypeAnnotationImpl extends TypeAnnotationImpl
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 @@ class FunctionTypeAnnotationImpl extends TypeAnnotationImpl
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 @@ abstract class DeclarationImpl extends RemoteInstance implements Declaration {
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 @@ class ParameterDeclarationImpl extends DeclarationImpl
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 @@ class TypeParameterDeclarationImpl extends DeclarationImpl
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 @@ class FunctionDeclarationImpl extends DeclarationImpl
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 @@ class MethodDeclarationImpl extends FunctionDeclarationImpl
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 @@ class ConstructorDeclarationImpl extends MethodDeclarationImpl
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 @@ class VariableDeclarationImpl extends DeclarationImpl
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 @@ class FieldDeclarationImpl extends VariableDeclarationImpl
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 @@ abstract class TypeDeclarationImpl extends DeclarationImpl
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 @@ class ClassDeclarationImpl extends TypeDeclarationImpl
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 @@ class TypeAliasDeclarationImpl extends TypeDeclarationImpl
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);
}

View file

@ -28,13 +28,13 @@ abstract class Request implements Serializable {
/// 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 @@ class SerializableResponse implements Response, Serializable {
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 @@ class SerializableResponse implements Response, Serializable {
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 @@ class SerializableResponse implements Response, Serializable {
default:
response.serializeNullable(serializer);
}
serializer.addNum(requestId);
serializer.addInt(requestId);
}
}
@ -203,7 +203,7 @@ class LoadMacroRequest extends Request {
@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 @@ class InstantiateMacroRequest extends Request {
: 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 @@ class ExecuteTypesPhaseRequest extends Request {
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 @@ class ExecuteDeclarationsPhaseRequest extends Request {
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 @@ class ExecuteDefinitionsPhaseRequest extends Request {
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 @@ class ResolveTypeRequest extends Request {
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 @@ class IsExactlyTypeRequest extends Request {
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 @@ class IsSubtypeOfRequest extends Request {
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 @@ class ClassIntrospectionRequest extends Request {
@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 @@ class DeclarationOfRequest extends Request {
@override
void serialize(Serializer serializer) {
serializer.addNum(MessageType.declarationOfRequest.index);
serializer.addInt(MessageType.declarationOfRequest.index);
identifier.serialize(serializer);
typeDeclarationResolver.serialize(serializer);
super.serialize(serializer);

View file

@ -57,16 +57,12 @@ abstract class RemoteInstance implements Serializable {
/// [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

View file

@ -164,12 +164,12 @@ class MacroInstanceIdentifierImpl implements MacroInstanceIdentifier {
}
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;

View file

@ -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 @@ import 'remote_instance.dart';
///
/// 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 @@ abstract class Serializer {
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 @@ abstract class Serializer {
/// 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 @@ abstract class Deserializer {
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 @@ class JsonSerializer implements Serializer {
/// 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 @@ class JsonSerializer implements Serializer {
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 @@ class JsonDeserializer implements Deserializer {
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 JsonDeserializer implements Deserializer {
}
}
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 @@ SerializationMode get serializationMode {
return mode;
}
/// Some objects are serialized differently on the client side versus the server
/// side. This indicates the different modes.
enum SerializationMode {
server,
client,
/// 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, as well as the format used.
enum SerializationMode {
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;
}
}
}

View file

@ -6,58 +6,59 @@ import '../api.dart';
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 @@ extension DeserializerExtensions on Deserializer {
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 @@ extension DeserializerExtensions on Deserializer {
}
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 SerializeNullableCode on Code? {
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 @@ extension SerializeCode on Code {
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');

View file

@ -126,18 +126,18 @@ class _SingleIsolatedMacroExecutor extends MacroExecutor {
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 @@ class _SingleIsolatedMacroExecutor extends MacroExecutor {
? 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 @@ class _SingleIsolatedMacroExecutor extends MacroExecutor {
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 @@ class _SingleIsolatedMacroExecutor extends MacroExecutor {
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 @@ class _SingleIsolatedMacroExecutor extends MacroExecutor {
// 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 @@ class _SingleIsolatedMacroExecutor extends MacroExecutor {
// 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 @@ class _SingleIsolatedMacroExecutor extends MacroExecutor {
// 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 @@ class _SingleIsolatedMacroExecutor extends MacroExecutor {
// 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 @@ class _SingleIsolatedMacroExecutor extends MacroExecutor {
// 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 @@ class _SingleIsolatedMacroExecutor extends MacroExecutor {
// 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 @@ class _SingleIsolatedMacroExecutor extends MacroExecutor {
// 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 @@ class _SingleIsolatedMacroExecutor extends MacroExecutor {
/// 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>();

View file

@ -12,76 +12,109 @@ import 'package:test/test.dart';
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 @@ void main() {
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 @@ void expectSerializationEquality(Serializable serializable) {
/// 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');
}
}

View file

@ -215,6 +215,7 @@ communication
compared
compares
compilations
complement
completers
completes
complicating
@ -266,6 +267,7 @@ crypto
csslib
cstefantsova
ctx
customizable
customized
cut
cwd
@ -378,6 +380,7 @@ ef
effects
efficient
efficiently
eight
eighth
elem
eliminating
@ -736,6 +739,7 @@ markdown
masking
masks
master
materialize
mature
mb
mc
@ -1308,6 +1312,7 @@ tput
traced
tracker
traditional
transferable
transformers
transforming
translation
@ -1321,6 +1326,7 @@ trivially
ts
tty
tuple
twos
typeref
u
ufeff
@ -1466,6 +1472,9 @@ xdc
xdfff
xef
xff
xffff
xffffffff
xffffffffffffffff
xi
xj
xk