| // 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; |