mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 02:27:39 +00:00
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:
parent
ea4f04f56d
commit
49d7f5056f
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue