// 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. library; import 'dart:convert'; import 'info.dart'; import 'src/binary/sink.dart'; import 'src/binary/source.dart'; void encode(AllInfo info, Sink> sink) { BinaryPrinter(BinarySink(sink)).visitAll(info); } AllInfo decode(List data) { return BinaryReader(BinarySource(data)).readAll(); } class BinaryPrinter implements InfoVisitor { 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 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.writeString(info.ramUsage); 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.writeString(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); sink.writeList(info.supers, visitClass); }); } @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); } }); } void _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.writeInt(info.functionKind); 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.writeString(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(); case InfoKind.package: throw StateError( 'Binary serialization is not supported for ' 'PackageInfo', ); } } 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.addAll(source.readList(readLibrary)); info.classes.addAll(source.readList(readClass)); info.classTypes.addAll(source.readList(readClassType)); info.functions.addAll(source.readList(readFunction)); info.typedefs.addAll(source.readList(readTypedef)); info.fields.addAll(source.readList(readField)); info.constants.addAll(source.readList(readConstant)); info.closures.addAll(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 values = source.readList(readInfoWithKind); info.dependencies[key] = values; dependenciesTotal--; } info.outputUnits.addAll(source.readList(readOutput)); Map> map = (jsonDecode(source.readString()) as Map) .cast>(); for (final library in map.values) { if (library['imports'] != null) { // The importMap needs to be typed as >, but the // json parser produces . final importMap = library['imports'] as Map; importMap.forEach((prefix, files) { importMap[prefix] = (files as List).cast(); }); library['imports'] = importMap.cast>(); } } info.deferredFiles = map; info.program = readProgram(); return info; } ProgramInfo readProgram() { final entrypoint = readFunction(); final size = source.readInt(); final ramUsage = source.readString(); final dart2jsVersion = source.readStringOrNull(); final compilationMoment = readDate(); final compilationDuration = readDuration(); final toJsonDuration = Duration(microseconds: 0); final dumpInfoDuration = readDuration(); final noSuchMethodEnabled = source.readBool(); final isRuntimeTypeUsed = source.readBool(); final isIsolateInUse = source.readBool(); final isFunctionApplyUsed = source.readBool(); final isMirrorsUsed = source.readBool(); final minified = source.readBool(); return ProgramInfo( entrypoint: entrypoint, size: size, ramUsage: ramUsage, dart2jsVersion: dart2jsVersion, compilationMoment: compilationMoment, compilationDuration: compilationDuration, toJsonDuration: toJsonDuration, dumpInfoDuration: dumpInfoDuration, noSuchMethodEnabled: noSuchMethodEnabled, isRuntimeTypeUsed: isRuntimeTypeUsed, isIsolateInUse: isIsolateInUse, isFunctionApplyUsed: isFunctionApplyUsed, isMirrorsUsed: isMirrorsUsed, minified: minified, ); } void _readBasicInfo(BasicInfo info) { info.name = source.readString(); 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 info = LibraryInfo.internal(); info.uri = source.readUri(); _readBasicInfo(info); info.topLevelFunctions.addAll(source.readList(readFunction)); info.topLevelVariables.addAll(source.readList(readField)); info.classes.addAll(source.readList(readClass)); info.classTypes.addAll(source.readList(readClassType)); info.typedefs.addAll(source.readList(readTypedef)); LibraryInfo 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 info = ClassInfo.internal(); _readBasicInfo(info); info.isAbstract = source.readBool(); info.fields.addAll(source.readList(readField)); info.functions.addAll(source.readList(readFunction)); info.supers.addAll(source.readList(readClass)); ClassInfo setParent(BasicInfo child) => child.parent = info; info.fields.forEach(setParent); info.functions.forEach(setParent); return info; }); ClassTypeInfo readClassType() => source.readCached(() { var info = ClassTypeInfo.internal(); _readBasicInfo(info); return info; }); FieldInfo readField() => source.readCached(() { 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() { final start = source.readIntOrNull(); final end = source.readIntOrNull(); final text = source.readStringOrNull(); return CodeSpan(start: start, end: end, text: text); } ConstantInfo? _readConstantOrNull() { bool hasOutput = source.readBool(); if (hasOutput) return readConstant(); return null; } ConstantInfo readConstant() => source.readCached(() { 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 info = FunctionInfo.internal(); _readBasicInfo(info); info.functionKind = source.readInt(); 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 info = ClosureInfo.internal(); _readBasicInfo(info); info.function = readFunction(); info.function.parent = info; return info; }); TypedefInfo readTypedef() => source.readCached(() { 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 info = OutputUnitInfo.internal(); _readBasicInfo(info); info.filename = source.readString(); info.imports.addAll(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;