blob: 89bbf59cce429ef94bba84781067f64d1510dad5 [file] [log] [blame]
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'remote_instance.dart';
/// All serialization must be done in a serialization Zone, which tells it
/// whether we are the client or server.
///
/// 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) =>
runZoned(fn, zoneValues: {
#serializationMode: mode,
if (mode == SerializationMode.server)
remoteInstanceZoneKey: <int, RemoteInstance>{}
});
/// Serializable interface
abstract class Serializable {
/// Serializes this object using [serializer].
void serialize(Serializer serializer);
}
/// A push based object serialization interface.
abstract class Serializer {
/// Serializes a [String].
void addString(String value);
/// Serializes a nullable [String].
void addNullableString(String? value);
/// Serializes a [num].
void addNum(num value);
/// Serializes a nullable [num].
void addNullableNum(num? value);
/// Serializes a [bool].
void addBool(bool value);
/// Serializes a nullable [bool].
void addNullableBool(bool? value);
/// Serializes a `null` literal.
void addNull();
/// Used to signal the start of an arbitrary length list of items.
void startList();
/// Used to signal the end of an arbitrary length list of items.
void endList();
}
/// A pull based object deserialization interface.
///
/// You must call [moveNext] before reading any items, and in order to advance
/// to the next item.
abstract class Deserializer {
/// Checks if the current value is a null, returns `true` if so and `false`
/// otherwise.
bool checkNull();
/// Reads the current value as a non-nullable [String].
bool expectBool();
/// Reads the current value as a nullable [bool].
bool? expectNullableBool();
/// Reads the current value as a non-nullable [String].
T expectNum<T extends num>();
/// Reads the current value as a nullable [num].
num? expectNullableNum();
/// Reads the current value as a non-nullable [String].
String expectString();
/// Reads the current value as a nullable [String].
String? expectNullableString();
/// Asserts that the current item is the start of a list.
///
/// An example for how to read from a list is as follows:
///
/// var json = JsonReader.fromString(source);
/// I know it's a list of strings.
///
/// ```
/// var result = <String>[];
/// deserializer.moveNext();
/// deserializer.expectList();
/// while (json.moveNext()) {
/// result.add(json.expectString());
/// }
/// // Can now read later items, but need to call `moveNext` again to move
/// // past the list.
/// deserializer.moveNext();
/// deserializer.expectBool();
/// ```
void expectList();
/// Moves to the next item, returns `false` if there are no more items to
/// read.
///
/// If inside of a list, this returns `false` when the end of the list is
/// reached, and moves back to the parent, but does not advance it, so another
/// call to `moveNext` is needed. See example in the [expectList] docs.
bool moveNext();
}
class JsonSerializer implements Serializer {
/// The full result.
final _result = <Object?>[];
/// A path to the current list we are modifying.
late List<List<Object?>> _path = [_result];
/// Returns the result as an unmodifiable [Iterable].
///
/// Asserts that all [List] entries have not been closed with [endList].
Iterable<Object?> get result {
assert(_path.length == 1);
return _result;
}
@override
void addBool(bool value) => _path.last.add(value);
@override
void addNullableBool(bool? value) => _path.last.add(value);
@override
void addNum(num value) => _path.last.add(value);
@override
void addNullableNum(num? value) => _path.last.add(value);
@override
void addString(String value) => _path.last.add(value);
@override
void addNullableString(String? value) => _path.last.add(value);
@override
void addNull() => _path.last.add(null);
@override
void startList() {
List<Object?> sublist = [];
_path.last.add(sublist);
_path.add(sublist);
}
@override
void endList() {
_path.removeLast();
}
}
class JsonDeserializer implements Deserializer {
/// The root source list to read from.
final Iterable<Object?> _source;
/// The path to the current iterator we are reading from.
late List<Iterator<Object?>> _path = [];
/// Whether we have received our first [moveNext] call.
bool _initialized = false;
/// Initialize this deserializer from `_source`.
JsonDeserializer(this._source);
@override
bool checkNull() => _expectValue<Object?>() == null;
@override
void expectList() => _path.add(_expectValue<Iterable<Object?>>().iterator);
@override
bool expectBool() => _expectValue();
@override
bool? expectNullableBool() => _expectValue();
@override
T expectNum<T extends num>() => _expectValue();
@override
num? expectNullableNum() => _expectValue();
@override
String expectString() => _expectValue();
@override
String? expectNullableString() => _expectValue();
/// Reads the current value and casts it to [T].
T _expectValue<T>() {
if (!_initialized) {
throw new StateError(
'You must call `moveNext()` before reading any values.');
}
return _path.last.current as T;
}
@override
bool moveNext() {
if (!_initialized) {
_path.add(_source.iterator);
_initialized = true;
}
// Move the current iterable, if its at the end of its items remove it from
// the current path and return false.
if (!_path.last.moveNext()) {
_path.removeLast();
return false;
}
return true;
}
}
/// Must be set using `withSerializationMode` before doing any serialization or
/// deserialization.
SerializationMode get serializationMode {
SerializationMode? mode =
Zone.current[#serializationMode] as SerializationMode?;
if (mode == null) {
throw new StateError('No SerializationMode set, you must do all '
'serialization inside a call to `withSerializationMode`.');
}
return mode;
}
/// Some objects are serialized differently on the client side versus the server
/// side. This indicates the different modes.
enum SerializationMode {
server,
client,
}