// Copyright (c) 2018, 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. library kernel.serializer_combinators; import 'dart:convert' show json; import '../ast.dart' show Node; import '../canonical_name.dart' show CanonicalName; import 'text_serializer.dart' show Tagger; class DeserializationEnvironment { final DeserializationEnvironment? parent; final Map locals = {}; final Map binders = {}; final Map distinctNames = new Map.identity(); DeserializationEnvironment(this.parent); T lookup(String name) => locals[name] ?? parent?.lookup(name) ?? (throw StateError("No local '${name}' found in this scope.")); T addBinder(T node, String distinctName) { if (lookupDistinctName(node) != null) { throw StateError( "Name '${distinctName}' is already declared in this scope."); } distinctNames[node] = distinctName; return binders[distinctName] = node; } // TODO(dmitryas): Consider combining with [addBinder] into a single method. void extend() { locals.addAll(binders); binders.clear(); } String? lookupDistinctName(T object) { return distinctNames[object] ?? parent?.lookupDistinctName(object); } } class SerializationEnvironment { final SerializationEnvironment? parent; final Map locals = new Map.identity(); final Map binders = new Map.identity(); int nameCount; Map distinctNames = new Map.identity(); SerializationEnvironment(this.parent) : nameCount = parent?.nameCount ?? 0; String lookup(T node) => locals[node] ?? parent?.lookup(node) ?? (throw StateError("No name for ${node} found in this scope.")); String addBinder(T node, {String? nameClue}) { final String separator = "^"; final int codeOfZero = "0".codeUnitAt(0); final int codeOfNine = "9".codeUnitAt(0); String prefix; if (nameClue != null) { int prefixLength = nameClue.length - 1; bool isOnlyDigits = true; while (prefixLength >= 0 && nameClue[prefixLength] != separator) { int code = nameClue.codeUnitAt(prefixLength); isOnlyDigits = isOnlyDigits && (codeOfZero <= code && code <= codeOfNine); --prefixLength; } if (prefixLength < 0 || !isOnlyDigits) { prefixLength = nameClue.length; } prefix = nameClue.substring(0, prefixLength); } else { prefix = "ID"; } String distinctName = "$prefix$separator${nameCount++}"; // The following checks for an internal error, not an error caused by the // user. So, an assert is used instead of an exception. assert( lookupDistinctName(node) == null, "Can't assign distinct name '${distinctName}' " "to an object of kind '${node.runtimeType}': " "it's already known by name '${lookupDistinctName(node)}'."); distinctNames[node] = distinctName; return binders[node] = distinctName; } // TODO(dmitryas): Consider combining with [addBinder] into a single method. void extend() { locals.addAll(binders); binders.clear(); } String? lookupDistinctName(T object) { return distinctNames[object] ?? parent?.lookupDistinctName(object); } } class DeserializationState { final DeserializationEnvironment environment; final CanonicalName nameRoot; DeserializationState(this.environment, this.nameRoot); } class SerializationState { final SerializationEnvironment environment; SerializationState(this.environment); } abstract class TextSerializer { const TextSerializer(); T readFrom(Iterator stream, DeserializationState? state); void writeTo(StringBuffer buffer, T object, SerializationState? state); /// True if this serializer/deserializer writes/reads nothing. This is true /// for the serializer [Nothing] and also some serializers derived from it. bool get isEmpty => false; } class Nothing extends TextSerializer { const Nothing(); void readFrom(Iterator stream, DeserializationState? _) {} void writeTo(StringBuffer buffer, void ignored, SerializationState? _) {} bool get isEmpty => true; } class DartString extends TextSerializer { const DartString(); String readFrom(Iterator stream, DeserializationState? _) { Object? current = stream.current; if (current is! String) { throw StateError("Expected an atom, found a list: '${current}'."); } String result = json.decode(current); stream.moveNext(); return result; } void writeTo(StringBuffer buffer, String object, SerializationState? _) { buffer.write(json.encode(object)); } } class DartInt extends TextSerializer { const DartInt(); int readFrom(Iterator stream, DeserializationState? _) { Object? current = stream.current; if (current is! String) { throw StateError("Expected an atom, found a list: '${current}'."); } int result = int.parse(current); stream.moveNext(); return result; } void writeTo(StringBuffer buffer, int object, SerializationState? _) { buffer.write(object); } } class DartDouble extends TextSerializer { const DartDouble(); double readFrom(Iterator stream, DeserializationState? _) { Object? current = stream.current; if (current is! String) { throw StateError("Expected an atom, found a list: '${current}'."); } double result = double.parse(current); stream.moveNext(); return result; } void writeTo(StringBuffer buffer, double object, SerializationState? _) { buffer.write(object); } } class DartBool extends TextSerializer { const DartBool(); bool readFrom(Iterator stream, DeserializationState? _) { Object? current = stream.current; if (current is! String) { throw StateError("Expected an atom, found a list: '${current}'."); } bool result; if (current == "true") { result = true; } else if (current == "false") { result = false; } else { throw StateError("Expected 'true' or 'false', found '${current}'"); } stream.moveNext(); return result; } void writeTo(StringBuffer buffer, bool object, SerializationState? _) { buffer.write(object ? 'true' : 'false'); } } class UriSerializer extends TextSerializer { const UriSerializer(); Uri readFrom(Iterator stream, DeserializationState? state) { String uriAsString = const DartString().readFrom(stream, state); return Uri.parse(uriAsString); } void writeTo(StringBuffer buffer, Uri object, SerializationState? state) { const DartString().writeTo(buffer, object.toString(), state); } } // == Serializers for tagged (disjoint) unions. // // They require a function mapping serializables to a tag string. This is // implemented by Tagger visitors. // A tagged union of serializer/deserializers. class Case extends TextSerializer { final Tagger tagger; final List _tags; final List> _serializers; Case(this.tagger, Map> tagsAndSerializers) : _tags = tagsAndSerializers.keys.toList(), _serializers = tagsAndSerializers.values.toList(); Case.uninitialized(this.tagger) : _tags = [], _serializers = []; void registerTags(Map> tagsAndSerializers) { _tags.addAll(tagsAndSerializers.keys); _serializers.addAll(tagsAndSerializers.values); } T readFrom(Iterator stream, DeserializationState? state) { Object? iterator = stream.current; if (iterator is! Iterator) { throw StateError("Expected list, found atom: '${iterator}'."); } iterator.moveNext(); Object? element = iterator.current; if (element is! String) { throw StateError("Expected atom, found list: '${element}'."); } String tag = element; for (int i = 0; i < _tags.length; ++i) { if (_tags[i] == tag) { iterator.moveNext(); T result = _serializers[i].readFrom(iterator, state); if (iterator.moveNext()) { throw StateError( "Extra data in tagged '${tag}': '${iterator.current}'."); } stream.moveNext(); return result; } } throw StateError("Unrecognized tag '${tag}'."); } void writeTo(StringBuffer buffer, T object, SerializationState? state) { String tag = tagger.tag(object); for (int i = 0; i < _tags.length; ++i) { if (_tags[i] == tag) { buffer.write("(${tag}"); if (!_serializers[i].isEmpty) { buffer.write(" "); } _serializers[i].writeTo(buffer, object, state); buffer.write(")"); return; } } throw StateError("Unrecognized tag '${tag}'."); } } // A serializer/deserializer that unwraps/wraps nodes before serialization and // after deserialization. class Wrapped extends TextSerializer { final S Function(K) unwrap; final K Function(S) wrap; final TextSerializer contents; const Wrapped(this.unwrap, this.wrap, this.contents); K readFrom(Iterator stream, DeserializationState? state) { return wrap(contents.readFrom(stream, state)); } void writeTo(StringBuffer buffer, K object, SerializationState? state) { contents.writeTo(buffer, unwrap(object), state); } bool get isEmpty => contents.isEmpty; } class ScopedUse extends TextSerializer { final DartString stringSerializer = const DartString(); const ScopedUse(); T readFrom(Iterator stream, DeserializationState? state) { if (state == null) { throw StateError( "No deserialization state provided for ${runtimeType}.readFrom."); } return state.environment.lookup(stringSerializer.readFrom(stream, null)) as T; } void writeTo(StringBuffer buffer, T object, SerializationState? state) { if (state == null) { throw StateError( "No serialization state provided for ${runtimeType}.writeTo."); } stringSerializer.writeTo(buffer, state.environment.lookup(object), null); } } // A serializer/deserializer for pairs. class Tuple2Serializer extends TextSerializer> { final TextSerializer first; final TextSerializer second; const Tuple2Serializer(this.first, this.second); Tuple2 readFrom( Iterator stream, DeserializationState? state) { return new Tuple2( first.readFrom(stream, state), second.readFrom(stream, state)); } void writeTo( StringBuffer buffer, Tuple2 object, SerializationState? state) { first.writeTo(buffer, object.first, state); if (!second.isEmpty) buffer.write(' '); second.writeTo(buffer, object.second, state); } } class Tuple2 { final T1 first; final T2 second; const Tuple2(this.first, this.second); } class Tuple3Serializer extends TextSerializer> { final TextSerializer first; final TextSerializer second; final TextSerializer third; const Tuple3Serializer(this.first, this.second, this.third); Tuple3 readFrom( Iterator stream, DeserializationState? state) { return new Tuple3(first.readFrom(stream, state), second.readFrom(stream, state), third.readFrom(stream, state)); } void writeTo(StringBuffer buffer, Tuple3 object, SerializationState? state) { first.writeTo(buffer, object.first, state); if (!second.isEmpty) buffer.write(' '); second.writeTo(buffer, object.second, state); if (!third.isEmpty) buffer.write(' '); third.writeTo(buffer, object.third, state); } } class Tuple3 { final T1 first; final T2 second; final T3 third; const Tuple3(this.first, this.second, this.third); } class Tuple4Serializer extends TextSerializer> { final TextSerializer first; final TextSerializer second; final TextSerializer third; final TextSerializer fourth; const Tuple4Serializer(this.first, this.second, this.third, this.fourth); Tuple4 readFrom( Iterator stream, DeserializationState? state) { return new Tuple4( first.readFrom(stream, state), second.readFrom(stream, state), third.readFrom(stream, state), fourth.readFrom(stream, state)); } void writeTo(StringBuffer buffer, Tuple4 object, SerializationState? state) { first.writeTo(buffer, object.first, state); if (!second.isEmpty) buffer.write(' '); second.writeTo(buffer, object.second, state); if (!third.isEmpty) buffer.write(' '); third.writeTo(buffer, object.third, state); if (!fourth.isEmpty) buffer.write(' '); fourth.writeTo(buffer, object.fourth, state); } } class Tuple4 { final T1 first; final T2 second; final T3 third; final T4 fourth; const Tuple4(this.first, this.second, this.third, this.fourth); } class Tuple5Serializer extends TextSerializer> { final TextSerializer first; final TextSerializer second; final TextSerializer third; final TextSerializer fourth; final TextSerializer fifth; const Tuple5Serializer( this.first, this.second, this.third, this.fourth, this.fifth); Tuple5 readFrom( Iterator stream, DeserializationState? state) { return new Tuple5( first.readFrom(stream, state), second.readFrom(stream, state), third.readFrom(stream, state), fourth.readFrom(stream, state), fifth.readFrom(stream, state)); } void writeTo(StringBuffer buffer, Tuple5 object, SerializationState? state) { first.writeTo(buffer, object.first, state); if (!second.isEmpty) buffer.write(' '); second.writeTo(buffer, object.second, state); if (!third.isEmpty) buffer.write(' '); third.writeTo(buffer, object.third, state); if (!fourth.isEmpty) buffer.write(' '); fourth.writeTo(buffer, object.fourth, state); if (!fifth.isEmpty) buffer.write(' '); fifth.writeTo(buffer, object.fifth, state); } } class Tuple5 { final T1 first; final T2 second; final T3 third; final T4 fourth; final T5 fifth; const Tuple5(this.first, this.second, this.third, this.fourth, this.fifth); } class Tuple6Serializer extends TextSerializer> { final TextSerializer first; final TextSerializer second; final TextSerializer third; final TextSerializer fourth; final TextSerializer fifth; final TextSerializer sixth; const Tuple6Serializer( this.first, this.second, this.third, this.fourth, this.fifth, this.sixth); Tuple6 readFrom( Iterator stream, DeserializationState? state) { return new Tuple6( first.readFrom(stream, state), second.readFrom(stream, state), third.readFrom(stream, state), fourth.readFrom(stream, state), fifth.readFrom(stream, state), sixth.readFrom(stream, state)); } void writeTo(StringBuffer buffer, Tuple6 object, SerializationState? state) { first.writeTo(buffer, object.first, state); if (!second.isEmpty) buffer.write(' '); second.writeTo(buffer, object.second, state); if (!third.isEmpty) buffer.write(' '); third.writeTo(buffer, object.third, state); if (!fourth.isEmpty) buffer.write(' '); fourth.writeTo(buffer, object.fourth, state); if (!fifth.isEmpty) buffer.write(' '); fifth.writeTo(buffer, object.fifth, state); if (!sixth.isEmpty) buffer.write(' '); sixth.writeTo(buffer, object.sixth, state); } } class Tuple6 { final T1 first; final T2 second; final T3 third; final T4 fourth; final T5 fifth; final T6 sixth; const Tuple6( this.first, this.second, this.third, this.fourth, this.fifth, this.sixth); } class Tuple7Serializer extends TextSerializer> { final TextSerializer first; final TextSerializer second; final TextSerializer third; final TextSerializer fourth; final TextSerializer fifth; final TextSerializer sixth; final TextSerializer seventh; const Tuple7Serializer(this.first, this.second, this.third, this.fourth, this.fifth, this.sixth, this.seventh); Tuple7 readFrom( Iterator stream, DeserializationState? state) { return new Tuple7( first.readFrom(stream, state), second.readFrom(stream, state), third.readFrom(stream, state), fourth.readFrom(stream, state), fifth.readFrom(stream, state), sixth.readFrom(stream, state), seventh.readFrom(stream, state)); } void writeTo(StringBuffer buffer, Tuple7 object, SerializationState? state) { first.writeTo(buffer, object.first, state); if (!second.isEmpty) buffer.write(' '); second.writeTo(buffer, object.second, state); if (!third.isEmpty) buffer.write(' '); third.writeTo(buffer, object.third, state); if (!fourth.isEmpty) buffer.write(' '); fourth.writeTo(buffer, object.fourth, state); if (!fifth.isEmpty) buffer.write(' '); fifth.writeTo(buffer, object.fifth, state); if (!sixth.isEmpty) buffer.write(' '); sixth.writeTo(buffer, object.sixth, state); if (!seventh.isEmpty) buffer.write(' '); seventh.writeTo(buffer, object.seventh, state); } } class Tuple7 { final T1 first; final T2 second; final T3 third; final T4 fourth; final T5 fifth; final T6 sixth; final T7 seventh; const Tuple7(this.first, this.second, this.third, this.fourth, this.fifth, this.sixth, this.seventh); } // A serializer/deserializer for lists. class ListSerializer extends TextSerializer> { final TextSerializer elements; const ListSerializer(this.elements); List readFrom(Iterator stream, DeserializationState? state) { Object? iterator = stream.current; if (iterator is! Iterator) { throw StateError("Expected a list, found an atom: '${iterator}'."); } iterator.moveNext(); List result = []; while (iterator.current != null) { result.add(elements.readFrom(iterator, state)); } stream.moveNext(); return result; } void writeTo(StringBuffer buffer, List object, SerializationState? state) { buffer.write('('); for (int i = 0; i < object.length; ++i) { if (i != 0) buffer.write(' '); elements.writeTo(buffer, object[i], state); } buffer.write(')'); } } class Optional extends TextSerializer { final TextSerializer contents; const Optional(this.contents); T? readFrom(Iterator stream, DeserializationState? state) { if (stream.current == '_') { stream.moveNext(); return null; } return contents.readFrom(stream, state); } void writeTo(StringBuffer buffer, T? object, SerializationState? state) { if (object == null) { buffer.write('_'); } else { contents.writeTo(buffer, object, state); } } } /// Introduces a binder to the environment. /// /// Serializes an object and uses it as a binder for the name that is retrieved /// from the object using [nameGetter] and (temporarily) modified using /// [nameSetter]. The binder is added to the enclosing environment. class Binder extends TextSerializer> { final Tuple2Serializer namedContents; Binder(TextSerializer contents) : namedContents = new Tuple2Serializer(const DartString(), contents); Tuple2 readFrom( Iterator stream, DeserializationState? state) { if (state == null) { throw StateError( "No deserialization state provided for ${runtimeType}.readFrom."); } Tuple2 namedObject = namedContents.readFrom(stream, state); String name = namedObject.first; T object = namedObject.second; state.environment.addBinder(object, name); return new Tuple2(name, object); } void writeTo(StringBuffer buffer, Tuple2 namedObject, SerializationState? state) { if (state == null) { throw StateError( "No serialization state provided for ${runtimeType}.writeTo."); } String nameClue = namedObject.first; T object = namedObject.second; String distinctName = state.environment.addBinder(object, nameClue: nameClue); namedContents.writeTo(buffer, new Tuple2(distinctName, object), state); } } /// Binds binders from one term in the other. /// /// Serializes a [Tuple2] of [pattern] and [term], closing [term] over the /// binders found in [pattern]. The binders aren't added to the enclosing /// environment. class Bind extends TextSerializer> { final TextSerializer

pattern; final TextSerializer term; const Bind(this.pattern, this.term); Tuple2 readFrom(Iterator stream, DeserializationState? state) { if (state == null) { throw StateError( "No deserialization state provided for ${runtimeType}.readFrom."); } DeserializationState bindingState = new DeserializationState( new DeserializationEnvironment(state.environment), state.nameRoot); P first = pattern.readFrom(stream, bindingState); bindingState.environment.extend(); T second = term.readFrom(stream, bindingState); return new Tuple2(first, second); } void writeTo( StringBuffer buffer, Tuple2 tuple, SerializationState? state) { if (state == null) { throw StateError( "No serialization state provided for ${runtimeType}.writeTo."); } SerializationState bindingState = new SerializationState(new SerializationEnvironment(state.environment)); pattern.writeTo(buffer, tuple.first, bindingState); bindingState.environment.extend(); buffer.write(' '); term.writeTo(buffer, tuple.second, bindingState); } } /// Nested binding pattern that also binds binders from one term in the other. /// /// Serializes a [Tuple2] of [pattern1] and [pattern2], closing [pattern2] over /// the binders found in [pattern1]. The binders from both [pattern1] and /// [pattern2] are added to the enclosing environment. class Rebind extends TextSerializer> { final TextSerializer

pattern1; final TextSerializer pattern2; const Rebind(this.pattern1, this.pattern2); Tuple2 readFrom(Iterator stream, DeserializationState? state) { if (state == null) { throw StateError( "No deserialization state provided for ${runtimeType}.readFrom."); } P first = pattern1.readFrom(stream, state); DeserializationState closedState = new DeserializationState( new DeserializationEnvironment(state.environment) ..binders.addAll(state.environment.binders) ..extend(), state.nameRoot); T second = pattern2.readFrom(stream, closedState); state.environment.binders.addAll(closedState.environment.binders); return new Tuple2(first, second); } void writeTo( StringBuffer buffer, Tuple2 tuple, SerializationState? state) { if (state == null) { throw StateError( "No serialization state provided for ${runtimeType}.writeTo."); } pattern1.writeTo(buffer, tuple.first, state); SerializationState closedState = new SerializationState(new SerializationEnvironment(state.environment) ..binders.addAll(state.environment.binders) ..extend()); buffer.write(' '); pattern2.writeTo(buffer, tuple.second, closedState); state.environment.binders.addAll(closedState.environment.binders); } } class Zip extends TextSerializer> { final TextSerializer, List>> lists; final T Function(T1, T2) zip; final Tuple2 Function(T) unzip; const Zip(this.lists, this.zip, this.unzip); List readFrom(Iterator stream, DeserializationState? state) { Tuple2, List> toZip = lists.readFrom(stream, state); List firsts = toZip.first; List zipped; if (firsts.isEmpty) { zipped = []; } else { List seconds = toZip.second; zipped = new List.filled(toZip.first.length, zip(firsts[0], seconds[0])); for (int i = 1; i < zipped.length; ++i) { zipped[i] = zip(firsts[i], seconds[i]); } } return zipped; } void writeTo(StringBuffer buffer, List zipped, SerializationState? state) { List firsts; List seconds; if (zipped.isEmpty) { firsts = []; seconds = []; } else { Tuple2 initial = unzip(zipped[0]); firsts = new List.filled(zipped.length, initial.first); seconds = new List.filled(zipped.length, initial.second); for (int i = 1; i < zipped.length; ++i) { Tuple2 tuple = unzip(zipped[i]); firsts[i] = tuple.first; seconds[i] = tuple.second; } } lists.writeTo(buffer, new Tuple2(firsts, seconds), state); } }