blob: 88c73d04ecf82c55201a7fb57571a676e4a22489 [file] [log] [blame]
// 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.
part of 'serialization.dart';
/// Base implementation of [DataSink] using [DataSinkMixin] to implement
/// convenience methods.
abstract class AbstractDataSink extends DataSinkMixin implements DataSink {
/// If `true`, serialization of every data kind is preceded by a [DataKind]
/// value.
///
/// This is used for debugging data inconsistencies between serialization
/// and deserialization.
final bool useDataKinds;
/// Visitor used for serializing [DartType]s.
DartTypeWriter _dartTypeWriter;
/// Visitor used for serializing [ir.DartType]s.
DartTypeNodeWriter _dartTypeNodeWriter;
/// Stack of tags used when [useDataKinds] is `true` to help debugging section
/// inconsistencies between serialization and deserialization.
List<String> _tags;
/// Map of [_MemberData] object for serialized kernel member nodes.
Map<ir.Member, _MemberData> _memberData = {};
IndexedSink<String> _stringIndex;
IndexedSink<Uri> _uriIndex;
IndexedSink<ir.Member> _memberNodeIndex;
IndexedSink<ImportEntity> _importIndex;
IndexedSink<ConstantValue> _constantIndex;
Map<Type, IndexedSink> _generalCaches = {};
EntityWriter _entityWriter = const EntityWriter();
CodegenWriter _codegenWriter;
final Map<String, int> tagFrequencyMap;
ir.Member _currentMemberContext;
_MemberData _currentMemberData;
AbstractDataSink({this.useDataKinds: false, this.tagFrequencyMap}) {
_dartTypeWriter = new DartTypeWriter(this);
_dartTypeNodeWriter = new DartTypeNodeWriter(this);
_stringIndex = new IndexedSink<String>(this);
_uriIndex = new IndexedSink<Uri>(this);
_memberNodeIndex = new IndexedSink<ir.Member>(this);
_importIndex = new IndexedSink<ImportEntity>(this);
_constantIndex = new IndexedSink<ConstantValue>(this);
}
@override
void begin(String tag) {
if (tagFrequencyMap != null) {
tagFrequencyMap[tag] ??= 0;
tagFrequencyMap[tag]++;
}
if (useDataKinds) {
_tags ??= <String>[];
_tags.add(tag);
_begin(tag);
}
}
@override
void end(Object tag) {
if (useDataKinds) {
_end(tag);
String existingTag = _tags.removeLast();
assert(existingTag == tag,
"Unexpected tag end. Expected $existingTag, found $tag.");
}
}
@override
void inMemberContext(ir.Member context, void f()) {
ir.Member oldMemberContext = _currentMemberContext;
_MemberData oldMemberData = _currentMemberData;
_currentMemberContext = context;
_currentMemberData = null;
f();
_currentMemberData = oldMemberData;
_currentMemberContext = oldMemberContext;
}
_MemberData get currentMemberData {
assert(_currentMemberContext != null,
"DataSink has no current member context.");
return _currentMemberData ??= _memberData[_currentMemberContext] ??=
new _MemberData(_currentMemberContext);
}
@override
void writeCached<E>(E value, void f(E value)) {
IndexedSink sink = _generalCaches[E] ??= new IndexedSink<E>(this);
sink.write(value, (v) => f(v));
}
@override
void writeSourceSpan(SourceSpan value) {
_writeDataKind(DataKind.sourceSpan);
_writeUri(value.uri);
_writeIntInternal(value.begin);
_writeIntInternal(value.end);
}
@override
void writeDartType(DartType value, {bool allowNull: false}) {
_writeDataKind(DataKind.dartType);
_writeDartType(value, [], allowNull: allowNull);
}
void _writeDartType(
DartType value, List<FunctionTypeVariable> functionTypeVariables,
{bool allowNull: false}) {
if (value == null) {
if (!allowNull) {
throw new UnsupportedError("Missing DartType is not allowed.");
}
writeEnum(DartTypeKind.none);
} else {
_dartTypeWriter.visit(value, functionTypeVariables);
}
}
@override
void writeDartTypeNode(ir.DartType value, {bool allowNull: false}) {
_writeDataKind(DataKind.dartTypeNode);
_writeDartTypeNode(value, [], allowNull: allowNull);
}
void _writeDartTypeNode(
ir.DartType value, List<ir.TypeParameter> functionTypeVariables,
{bool allowNull: false}) {
if (value == null) {
if (!allowNull) {
throw new UnsupportedError("Missing ir.DartType node is not allowed.");
}
writeEnum(DartTypeNodeKind.none);
} else {
value.accept1(_dartTypeNodeWriter, functionTypeVariables);
}
}
@override
void writeMemberNode(ir.Member value) {
_writeDataKind(DataKind.memberNode);
_writeMemberNode(value);
}
void _writeMemberNode(ir.Member value) {
_memberNodeIndex.write(value, _writeMemberNodeInternal);
}
void _writeMemberNodeInternal(ir.Member value) {
ir.Class cls = value.enclosingClass;
if (cls != null) {
_writeEnumInternal(MemberContextKind.cls);
_writeClassNode(cls);
_writeString(_computeMemberName(value));
} else {
_writeEnumInternal(MemberContextKind.library);
_writeLibraryNode(value.enclosingLibrary);
_writeString(_computeMemberName(value));
}
}
@override
void writeClassNode(ir.Class value) {
_writeDataKind(DataKind.classNode);
_writeClassNode(value);
}
void _writeClassNode(ir.Class value) {
_writeLibraryNode(value.enclosingLibrary);
_writeString(value.name);
}
@override
void writeTypedefNode(ir.Typedef value) {
_writeDataKind(DataKind.typedefNode);
_writeTypedefNode(value);
}
void _writeTypedefNode(ir.Typedef value) {
_writeLibraryNode(value.enclosingLibrary);
_writeString(value.name);
}
@override
void writeLibraryNode(ir.Library value) {
_writeDataKind(DataKind.libraryNode);
_writeLibraryNode(value);
}
void _writeLibraryNode(ir.Library value) {
_writeUri(value.importUri);
}
@override
void writeEnum(dynamic value) {
_writeDataKind(DataKind.enumValue);
_writeEnumInternal(value);
}
@override
void writeBool(bool value) {
assert(value != null);
_writeDataKind(DataKind.bool);
_writeBool(value);
}
void _writeBool(bool value) {
_writeIntInternal(value ? 1 : 0);
}
@override
void writeUri(Uri value) {
assert(value != null);
_writeDataKind(DataKind.uri);
_writeUri(value);
}
@override
void writeString(String value) {
assert(value != null);
_writeDataKind(DataKind.string);
_writeString(value);
}
@override
void writeInt(int value) {
assert(value != null);
assert(value >= 0 && value >> 30 == 0);
_writeDataKind(DataKind.uint30);
_writeIntInternal(value);
}
@override
void writeTreeNode(ir.TreeNode value) {
_writeDataKind(DataKind.treeNode);
_writeTreeNode(value, null);
}
@override
void writeTreeNodeInContext(ir.TreeNode value) {
writeTreeNodeInContextInternal(value, currentMemberData);
}
void writeTreeNodeInContextInternal(
ir.TreeNode value, _MemberData memberData) {
_writeDataKind(DataKind.treeNode);
_writeTreeNode(value, memberData);
}
@override
void writeTreeNodeOrNullInContext(ir.TreeNode value) {
writeBool(value != null);
if (value != null) {
writeTreeNodeInContextInternal(value, currentMemberData);
}
}
@override
void writeTreeNodesInContext(Iterable<ir.TreeNode> values,
{bool allowNull: false}) {
if (values == null) {
assert(allowNull);
writeInt(0);
} else {
writeInt(values.length);
for (ir.TreeNode value in values) {
writeTreeNodeInContextInternal(value, currentMemberData);
}
}
}
@override
void writeTreeNodeMapInContext<V>(Map<ir.TreeNode, V> map, void f(V value),
{bool allowNull: false}) {
if (map == null) {
assert(allowNull);
writeInt(0);
} else {
writeInt(map.length);
map.forEach((ir.TreeNode key, V value) {
writeTreeNodeInContextInternal(key, currentMemberData);
f(value);
});
}
}
_MemberData _getMemberData(ir.TreeNode node) {
ir.TreeNode member = node;
while (member is! ir.Member) {
if (member == null) {
throw new UnsupportedError("No enclosing member of TreeNode "
"$node (${node.runtimeType})");
}
member = member.parent;
}
_writeMemberNode(member);
return _memberData[member] ??= new _MemberData(member);
}
void _writeTreeNode(ir.TreeNode value, _MemberData memberData) {
if (value is ir.Class) {
_writeEnumInternal(_TreeNodeKind.cls);
_writeClassNode(value);
} else if (value is ir.Member) {
_writeEnumInternal(_TreeNodeKind.member);
_writeMemberNode(value);
} else if (value is ir.VariableDeclaration &&
value.parent is ir.FunctionDeclaration) {
_writeEnumInternal(_TreeNodeKind.functionDeclarationVariable);
_writeTreeNode(value.parent, memberData);
} else if (value is ir.FunctionNode) {
_writeEnumInternal(_TreeNodeKind.functionNode);
_writeFunctionNode(value, memberData);
} else if (value is ir.TypeParameter) {
_writeEnumInternal(_TreeNodeKind.typeParameter);
_writeTypeParameter(value, memberData);
} else if (value is ConstantReference) {
_writeEnumInternal(_TreeNodeKind.constant);
memberData ??= _getMemberData(value.expression);
_writeTreeNode(value.expression, memberData);
int index =
memberData.getIndexByConstant(value.expression, value.constant);
_writeIntInternal(index);
} else {
_writeEnumInternal(_TreeNodeKind.node);
memberData ??= _getMemberData(value);
int index = memberData.getIndexByTreeNode(value);
assert(
index != null,
"No TreeNode index found for ${value.runtimeType} "
"found in ${memberData}.");
_writeIntInternal(index);
}
}
void _writeFunctionNode(ir.FunctionNode value, _MemberData memberData) {
ir.TreeNode parent = value.parent;
if (parent is ir.Procedure) {
_writeEnumInternal(_FunctionNodeKind.procedure);
_writeMemberNode(parent);
} else if (parent is ir.Constructor) {
_writeEnumInternal(_FunctionNodeKind.constructor);
_writeMemberNode(parent);
} else if (parent is ir.FunctionExpression) {
_writeEnumInternal(_FunctionNodeKind.functionExpression);
_writeTreeNode(parent, memberData);
} else if (parent is ir.FunctionDeclaration) {
_writeEnumInternal(_FunctionNodeKind.functionDeclaration);
_writeTreeNode(parent, memberData);
} else {
throw new UnsupportedError(
"Unsupported FunctionNode parent ${parent.runtimeType}");
}
}
@override
void writeTypeParameterNode(ir.TypeParameter value) {
_writeDataKind(DataKind.typeParameterNode);
_writeTypeParameter(value, null);
}
void _writeTypeParameter(ir.TypeParameter value, _MemberData memberData) {
ir.TreeNode parent = value.parent;
if (parent is ir.Class) {
_writeEnumInternal(_TypeParameterKind.cls);
_writeClassNode(parent);
_writeIntInternal(parent.typeParameters.indexOf(value));
} else if (parent is ir.FunctionNode) {
_writeEnumInternal(_TypeParameterKind.functionNode);
_writeFunctionNode(parent, memberData);
_writeIntInternal(parent.typeParameters.indexOf(value));
} else {
throw new UnsupportedError(
"Unsupported TypeParameter parent ${parent.runtimeType}");
}
}
void _writeDataKind(DataKind kind) {
if (useDataKinds) _writeEnumInternal(kind);
}
@override
void writeLibrary(IndexedLibrary value) {
_entityWriter.writeLibraryToDataSink(this, value);
}
@override
void writeClass(IndexedClass value) {
_entityWriter.writeClassToDataSink(this, value);
}
@override
void writeTypedef(IndexedTypedef value) {
_entityWriter.writeTypedefToDataSink(this, value);
}
@override
void writeMember(IndexedMember value) {
_entityWriter.writeMemberToDataSink(this, value);
}
@override
void writeTypeVariable(IndexedTypeVariable value) {
_entityWriter.writeTypeVariableToDataSink(this, value);
}
@override
void writeLocal(Local local) {
if (local is JLocal) {
writeEnum(LocalKind.jLocal);
writeMember(local.memberContext);
writeInt(local.localIndex);
} else if (local is ThisLocal) {
writeEnum(LocalKind.thisLocal);
writeClass(local.enclosingClass);
} else if (local is BoxLocal) {
writeEnum(LocalKind.boxLocal);
writeClass(local.container);
} else if (local is AnonymousClosureLocal) {
writeEnum(LocalKind.anonymousClosureLocal);
writeClass(local.closureClass);
} else if (local is TypeVariableLocal) {
writeEnum(LocalKind.typeVariableLocal);
writeDartType(local.typeVariable);
} else {
throw new UnsupportedError("Unsupported local ${local.runtimeType}");
}
}
@override
void writeConstant(ConstantValue value) {
_writeDataKind(DataKind.constant);
_writeConstant(value);
}
@override
void writeDoubleValue(double value) {
_writeDataKind(DataKind.double);
_writeDoubleValue(value);
}
void _writeDoubleValue(double value) {
ByteData data = new ByteData(8);
data.setFloat64(0, value);
writeInt(data.getUint16(0));
writeInt(data.getUint16(2));
writeInt(data.getUint16(4));
writeInt(data.getUint16(6));
}
@override
void writeIntegerValue(int value) {
_writeDataKind(DataKind.int);
_writeBigInt(new BigInt.from(value));
}
void _writeBigInt(BigInt value) {
writeString(value.toString());
}
void _writeConstant(ConstantValue value) {
_constantIndex.write(value, _writeConstantInternal);
}
void _writeConstantInternal(ConstantValue value) {
_writeEnumInternal(value.kind);
switch (value.kind) {
case ConstantValueKind.BOOL:
BoolConstantValue constant = value;
writeBool(constant.boolValue);
break;
case ConstantValueKind.INT:
IntConstantValue constant = value;
_writeBigInt(constant.intValue);
break;
case ConstantValueKind.DOUBLE:
DoubleConstantValue constant = value;
_writeDoubleValue(constant.doubleValue);
break;
case ConstantValueKind.STRING:
StringConstantValue constant = value;
writeString(constant.stringValue);
break;
case ConstantValueKind.NULL:
break;
case ConstantValueKind.FUNCTION:
FunctionConstantValue constant = value;
IndexedFunction function = constant.element;
writeMember(function);
writeDartType(constant.type);
break;
case ConstantValueKind.LIST:
ListConstantValue constant = value;
writeDartType(constant.type);
writeConstants(constant.entries);
break;
case ConstantValueKind.SET:
constant_system.JavaScriptSetConstant constant = value;
writeDartType(constant.type);
writeConstant(constant.entries);
break;
case ConstantValueKind.MAP:
constant_system.JavaScriptMapConstant constant = value;
writeDartType(constant.type);
writeConstant(constant.keyList);
writeConstants(constant.values);
writeConstantOrNull(constant.protoValue);
writeBool(constant.onlyStringKeys);
break;
case ConstantValueKind.CONSTRUCTED:
ConstructedConstantValue constant = value;
writeDartType(constant.type);
writeMemberMap(constant.fields,
(MemberEntity member, ConstantValue value) => writeConstant(value));
break;
case ConstantValueKind.TYPE:
TypeConstantValue constant = value;
writeDartType(constant.representedType);
writeDartType(constant.type);
break;
case ConstantValueKind.INSTANTIATION:
InstantiationConstantValue constant = value;
writeDartTypes(constant.typeArguments);
writeConstant(constant.function);
break;
case ConstantValueKind.NON_CONSTANT:
break;
case ConstantValueKind.INTERCEPTOR:
InterceptorConstantValue constant = value;
writeClass(constant.cls);
break;
case ConstantValueKind.DEFERRED_GLOBAL:
DeferredGlobalConstantValue constant = value;
writeConstant(constant.referenced);
writeOutputUnitReference(constant.unit);
break;
case ConstantValueKind.DUMMY_INTERCEPTOR:
DummyInterceptorConstantValue constant = value;
writeAbstractValue(constant.abstractValue);
break;
case ConstantValueKind.UNREACHABLE:
break;
case ConstantValueKind.JS_NAME:
JsNameConstantValue constant = value;
writeJsNode(constant.name);
break;
}
}
void _writeString(String value) {
_stringIndex.write(value, _writeStringInternal);
}
void _writeUri(Uri value) {
_uriIndex.write(value, _writeUriInternal);
}
@override
void writeImport(ImportEntity value) {
_writeDataKind(DataKind.import);
_writeImport(value);
}
void _writeImport(ImportEntity value) {
_importIndex.write(value, _writeImportInternal);
}
void _writeImportInternal(ImportEntity value) {
// TODO(johnniwinther): Do we need to serialize non-deferred imports?
writeStringOrNull(value.name);
_writeUri(value.uri);
_writeUri(value.enclosingLibraryUri);
_writeBool(value.isDeferred);
}
@override
void registerEntityWriter(EntityWriter writer) {
assert(writer != null);
_entityWriter = writer;
}
@override
void registerCodegenWriter(CodegenWriter writer) {
assert(writer != null);
assert(_codegenWriter == null);
_codegenWriter = writer;
}
@override
void writeOutputUnitReference(OutputUnit value) {
assert(
_codegenWriter != null,
"Can not serialize an OutputUnit reference "
"without a registered codegen writer.");
_codegenWriter.writeOutputUnitReference(this, value);
}
@override
void writeAbstractValue(AbstractValue value) {
assert(_codegenWriter != null,
"Can not serialize an AbstractValue without a registered codegen writer.");
_codegenWriter.writeAbstractValue(this, value);
}
@override
void writeJsNode(js.Node value) {
assert(_codegenWriter != null,
"Can not serialize a JS ndoe without a registered codegen writer.");
_codegenWriter.writeJsNode(this, value);
}
/// Actual serialization of a section begin tag, implemented by subclasses.
void _begin(String tag);
/// Actual serialization of a section end tag, implemented by subclasses.
void _end(String tag);
/// Actual serialization of a URI value, implemented by subclasses.
void _writeUriInternal(Uri value);
/// Actual serialization of a String value, implemented by subclasses.
void _writeStringInternal(String value);
/// Actual serialization of a non-negative integer value, implemented by
/// subclasses.
void _writeIntInternal(int value);
/// Actual serialization of an enum value, implemented by subclasses.
void _writeEnumInternal(dynamic value);
}