blob: 23b626d6e4b6670957b7608ee3607f56a39342fc [file] [log] [blame]
// Copyright (c) 2015, 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.
/// Info serialization to a binary form.
///
/// Unlike the JSON codec, this serialization is designed to be streamed.
import 'dart:convert';
import 'src/binary/sink.dart';
import 'src/binary/source.dart';
import 'info.dart';
void encode(AllInfo info, Sink<List<int>> sink) {
BinaryPrinter(BinarySink(sink)).visitAll(info);
}
AllInfo decode(List<int> data) {
return BinaryReader(BinarySource(data)).readAll();
}
class BinaryPrinter implements InfoVisitor<void> {
final BinarySink sink;
BinaryPrinter(this.sink);
void writeDate(DateTime date) {
sink.writeString(date.toIso8601String());
}
void writeDuration(Duration duration) {
sink.writeInt(duration.inMicroseconds);
}
void writeInfoWithKind(Info info) {
sink.writeEnum(info.kind);
info.accept(this);
}
@override
void visitAll(AllInfo info) {
sink.writeInt(info.version);
sink.writeInt(info.minorVersion);
sink.writeList(info.libraries, visitLibrary);
// TODO(sigmund): synthesize the following lists instead of serializing the
// values again.
sink.writeList(info.classes, visitClass);
sink.writeList(info.classTypes, visitClassType);
sink.writeList(info.functions, visitFunction);
sink.writeList(info.typedefs, visitTypedef);
sink.writeList(info.fields, visitField);
sink.writeList(info.constants, visitConstant);
sink.writeList(info.closures, visitClosure);
void writeDependencies(CodeInfo info) {
sink.writeList(info.uses, _writeDependencyInfo);
}
info.fields.forEach(writeDependencies);
info.functions.forEach(writeDependencies);
sink.writeInt(info.dependencies.length);
info.dependencies.forEach((Info key, List<Info> values) {
writeInfoWithKind(key);
sink.writeList(values, writeInfoWithKind);
});
sink.writeList(info.outputUnits, visitOutput);
sink.writeString(jsonEncode(info.deferredFiles));
visitProgram(info.program);
sink.close();
}
@override
void visitProgram(ProgramInfo info) {
visitFunction(info.entrypoint);
sink.writeInt(info.size);
sink.writeStringOrNull(info.dart2jsVersion);
writeDate(info.compilationMoment);
writeDuration(info.compilationDuration);
// Note: we don't record the 'toJsonDuration' field. Consider deleting it?
writeDuration(info.dumpInfoDuration);
sink.writeBool(info.noSuchMethodEnabled);
sink.writeBool(info.isRuntimeTypeUsed);
sink.writeBool(info.isIsolateInUse);
sink.writeBool(info.isFunctionApplyUsed);
sink.writeBool(info.isMirrorsUsed);
sink.writeBool(info.minified);
}
void _visitBasicInfo(BasicInfo info) {
sink.writeStringOrNull(info.name);
sink.writeInt(info.size);
sink.writeStringOrNull(info.coverageId);
_writeOutputOrNull(info.outputUnit);
// Note: parent-pointers are not serialized, they get deduced during deserialization.
}
@override
void visitLibrary(LibraryInfo library) {
sink.writeCached(library, (LibraryInfo info) {
sink.writeUri(info.uri);
_visitBasicInfo(info);
sink.writeList(info.topLevelFunctions, visitFunction);
sink.writeList(info.topLevelVariables, visitField);
sink.writeList(info.classes, visitClass);
sink.writeList(info.classTypes, visitClassType);
sink.writeList(info.typedefs, visitTypedef);
});
}
@override
void visitClass(ClassInfo cls) {
sink.writeCached(cls, (ClassInfo info) {
_visitBasicInfo(info);
sink.writeBool(info.isAbstract);
sink.writeList(info.fields, visitField);
sink.writeList(info.functions, visitFunction);
});
}
@override
void visitClassType(ClassTypeInfo cls) {
sink.writeCached(cls, (ClassTypeInfo info) {
_visitBasicInfo(info);
});
}
@override
void visitField(FieldInfo field) {
sink.writeCached(field, (FieldInfo info) {
_visitBasicInfo(info);
sink.writeList(info.closures, visitClosure);
sink.writeString(info.inferredType);
sink.writeList(info.code, _visitCodeSpan);
sink.writeString(info.type);
sink.writeBool(info.isConst);
if (info.isConst) {
_writeConstantOrNull(info.initializer);
}
});
}
_visitCodeSpan(CodeSpan code) {
sink.writeIntOrNull(code.start);
sink.writeIntOrNull(code.end);
sink.writeStringOrNull(code.text);
}
void _writeConstantOrNull(ConstantInfo info) {
sink.writeBool(info != null);
if (info != null) {
visitConstant(info);
}
}
@override
void visitConstant(ConstantInfo constant) {
sink.writeCached(constant, (ConstantInfo info) {
_visitBasicInfo(info);
sink.writeList(info.code, _visitCodeSpan);
});
}
void _visitFunctionModifiers(FunctionModifiers mods) {
int value = 0;
if (mods.isStatic) value |= _staticMask;
if (mods.isConst) value |= _constMask;
if (mods.isFactory) value |= _factoryMask;
if (mods.isExternal) value |= _externalMask;
sink.writeInt(value);
}
void _visitParameterInfo(ParameterInfo info) {
sink.writeString(info.name);
sink.writeString(info.type);
sink.writeString(info.declaredType);
}
@override
void visitFunction(FunctionInfo function) {
sink.writeCached(function, (FunctionInfo info) {
_visitBasicInfo(info);
sink.writeList(info.closures, visitClosure);
_visitFunctionModifiers(info.modifiers);
sink.writeString(info.returnType);
sink.writeString(info.inferredReturnType);
sink.writeList(info.parameters, _visitParameterInfo);
sink.writeString(info.sideEffects);
sink.writeIntOrNull(info.inlinedCount);
sink.writeList(info.code, _visitCodeSpan);
sink.writeString(info.type);
});
}
void _writeDependencyInfo(DependencyInfo info) {
writeInfoWithKind(info.target);
sink.writeStringOrNull(info.mask);
}
@override
void visitClosure(ClosureInfo closure) {
sink.writeCached(closure, (ClosureInfo info) {
_visitBasicInfo(info);
visitFunction(info.function);
});
}
@override
void visitTypedef(TypedefInfo typedef) {
sink.writeCached(typedef, (TypedefInfo info) {
_visitBasicInfo(info);
sink.writeString(info.type);
});
}
void _writeOutputOrNull(OutputUnitInfo info) {
sink.writeBool(info != null);
if (info != null) {
visitOutput(info);
}
}
@override
void visitOutput(OutputUnitInfo output) {
sink.writeCached(output, (OutputUnitInfo info) {
_visitBasicInfo(info);
sink.writeStringOrNull(info.filename);
sink.writeList(info.imports, sink.writeString);
});
}
}
class BinaryReader {
final BinarySource source;
BinaryReader(this.source);
DateTime readDate() {
return DateTime.parse(source.readString());
}
Duration readDuration() {
return Duration(microseconds: source.readInt());
}
Info readInfoWithKind() {
InfoKind kind = source.readEnum(InfoKind.values);
switch (kind) {
case InfoKind.library:
return readLibrary();
case InfoKind.clazz:
return readClass();
case InfoKind.classType:
return readClassType();
case InfoKind.function:
return readFunction();
case InfoKind.field:
return readField();
case InfoKind.constant:
return readConstant();
case InfoKind.outputUnit:
return readOutput();
case InfoKind.typedef:
return readTypedef();
case InfoKind.closure:
return readClosure();
}
return null;
}
AllInfo readAll() {
var info = AllInfo();
int version = source.readInt();
int minorVersion = source.readInt();
if (info.version != version || info.minorVersion != minorVersion) {
print("warning: data was encoded with format version "
"$version.$minorVersion, but decoded with "
"${info.version}.${info.minorVersion}");
}
info.libraries = source.readList(readLibrary);
info.classes = source.readList(readClass);
info.classTypes = source.readList(readClassType);
info.functions = source.readList(readFunction);
info.typedefs = source.readList(readTypedef);
info.fields = source.readList(readField);
info.constants = source.readList(readConstant);
info.closures = source.readList(readClosure);
void readDependencies(CodeInfo info) {
info.uses = source.readList(_readDependencyInfo);
}
info.fields.forEach(readDependencies);
info.functions.forEach(readDependencies);
int dependenciesTotal = source.readInt();
while (dependenciesTotal > 0) {
Info key = readInfoWithKind();
List<Info> values = source.readList(readInfoWithKind);
info.dependencies[key] = values;
dependenciesTotal--;
}
info.outputUnits = source.readList(readOutput);
Map<String, Map<String, dynamic>> map =
jsonDecode(source.readString()).cast<String, Map<String, dynamic>>();
for (final library in map.values) {
if (library['imports'] != null) {
// The importMap needs to be typed as <String, List<String>>, but the
// json parser produces <String, dynamic>.
final importMap = library['imports'] as Map<String, dynamic>;
importMap.forEach((prefix, files) {
importMap[prefix] = (files as List<dynamic>).cast<String>();
});
library['imports'] = importMap.cast<String, List<String>>();
}
}
info.deferredFiles = map;
info.program = readProgram();
return info;
}
ProgramInfo readProgram() {
var info = ProgramInfo();
info.entrypoint = readFunction();
info.size = source.readInt();
info.dart2jsVersion = source.readStringOrNull();
info.compilationMoment = readDate();
info.compilationDuration = readDuration();
info.toJsonDuration = Duration(microseconds: 0);
info.dumpInfoDuration = readDuration();
info.noSuchMethodEnabled = source.readBool();
info.isRuntimeTypeUsed = source.readBool();
info.isIsolateInUse = source.readBool();
info.isFunctionApplyUsed = source.readBool();
info.isMirrorsUsed = source.readBool();
info.minified = source.readBool();
return info;
}
void _readBasicInfo(BasicInfo info) {
info.name = source.readStringOrNull();
info.size = source.readInt();
info.coverageId = source.readStringOrNull();
info.outputUnit = _readOutputOrNull();
// Note: parent pointers are added when deserializing parent nodes.
}
LibraryInfo readLibrary() => source.readCached<LibraryInfo>(() {
LibraryInfo info = LibraryInfo.internal();
info.uri = source.readUri();
_readBasicInfo(info);
info.topLevelFunctions = source.readList(readFunction);
info.topLevelVariables = source.readList(readField);
info.classes = source.readList(readClass);
info.classTypes = source.readList(readClassType);
info.typedefs = source.readList(readTypedef);
setParent(BasicInfo child) => child.parent = info;
info.topLevelFunctions.forEach(setParent);
info.topLevelVariables.forEach(setParent);
info.classes.forEach(setParent);
info.classTypes.forEach(setParent);
info.typedefs.forEach(setParent);
return info;
});
ClassInfo readClass() => source.readCached<ClassInfo>(() {
ClassInfo info = ClassInfo.internal();
_readBasicInfo(info);
info.isAbstract = source.readBool();
info.fields = source.readList(readField);
info.functions = source.readList(readFunction);
setParent(BasicInfo child) => child.parent = info;
info.fields.forEach(setParent);
info.functions.forEach(setParent);
return info;
});
ClassTypeInfo readClassType() => source.readCached<ClassTypeInfo>(() {
var info = ClassTypeInfo.internal();
_readBasicInfo(info);
return info;
});
FieldInfo readField() => source.readCached<FieldInfo>(() {
FieldInfo info = FieldInfo.internal();
_readBasicInfo(info);
info.closures = source.readList(readClosure);
info.inferredType = source.readString();
info.code = source.readList(_readCodeSpan);
info.type = source.readString();
info.isConst = source.readBool();
if (info.isConst) {
info.initializer = _readConstantOrNull();
}
for (var c in info.closures) {
c.parent = info;
}
return info;
});
CodeSpan _readCodeSpan() {
return CodeSpan()
..start = source.readIntOrNull()
..end = source.readIntOrNull()
..text = source.readStringOrNull();
}
ConstantInfo _readConstantOrNull() {
bool hasOutput = source.readBool();
if (hasOutput) return readConstant();
return null;
}
ConstantInfo readConstant() => source.readCached<ConstantInfo>(() {
ConstantInfo info = ConstantInfo.internal();
_readBasicInfo(info);
info.code = source.readList(_readCodeSpan);
return info;
});
FunctionModifiers _readFunctionModifiers() {
int value = source.readInt();
return FunctionModifiers(
isStatic: value & _staticMask != 0,
isConst: value & _constMask != 0,
isFactory: value & _factoryMask != 0,
isExternal: value & _externalMask != 0);
}
ParameterInfo _readParameterInfo() {
return ParameterInfo(
source.readString(), source.readString(), source.readString());
}
FunctionInfo readFunction() => source.readCached<FunctionInfo>(() {
FunctionInfo info = FunctionInfo.internal();
_readBasicInfo(info);
info.closures = source.readList(readClosure);
info.modifiers = _readFunctionModifiers();
info.returnType = source.readString();
info.inferredReturnType = source.readString();
info.parameters = source.readList(_readParameterInfo);
info.sideEffects = source.readString();
info.inlinedCount = source.readIntOrNull();
info.code = source.readList(_readCodeSpan);
info.type = source.readString();
for (var c in info.closures) {
c.parent = info;
}
return info;
});
DependencyInfo _readDependencyInfo() =>
DependencyInfo(readInfoWithKind(), source.readStringOrNull());
ClosureInfo readClosure() => source.readCached<ClosureInfo>(() {
ClosureInfo info = ClosureInfo.internal();
_readBasicInfo(info);
info.function = readFunction();
info.function.parent = info;
return info;
});
TypedefInfo readTypedef() => source.readCached<TypedefInfo>(() {
TypedefInfo info = TypedefInfo.internal();
_readBasicInfo(info);
info.type = source.readString();
return info;
});
OutputUnitInfo _readOutputOrNull() {
bool hasOutput = source.readBool();
if (hasOutput) return readOutput();
return null;
}
OutputUnitInfo readOutput() => source.readCached<OutputUnitInfo>(() {
OutputUnitInfo info = OutputUnitInfo.internal();
_readBasicInfo(info);
info.filename = source.readStringOrNull();
info.imports = source.readList(source.readString);
return info;
});
}
const int _staticMask = 1 << 3;
const int _constMask = 1 << 2;
const int _factoryMask = 1 << 1;
const int _externalMask = 1 << 0;