| // 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. |
| |
| /// Converters and codecs for converting between Protobuf and [Info] classes. |
| import 'dart:convert'; |
| |
| import 'package:fixnum/fixnum.dart'; |
| |
| import 'info.dart'; |
| import 'src/proto/info.pb.dart'; |
| import 'src/util.dart'; |
| |
| export 'src/proto/info.pb.dart'; |
| |
| class ProtoToAllInfoConverter extends Converter<AllInfoPB, AllInfo> { |
| @override |
| AllInfo convert(AllInfoPB input) { |
| // TODO(lorenvs): Implement this conversion. It is unlikely to to be used |
| // by production code since the goal of the proto codec is to consume this |
| // information from other languages. However, it is useful for roundtrip |
| // testing, so we should support it. |
| throw UnimplementedError('ProtoToAllInfoConverter is not implemented'); |
| } |
| } |
| |
| class AllInfoToProtoConverter extends Converter<AllInfo, AllInfoPB> { |
| final Map<Info, Id> ids = {}; |
| final Set<int> usedIds = <int>{}; |
| |
| Id idFor(Info info) { |
| if (info == null) return null; |
| var serializedId = ids[info]; |
| if (serializedId != null) return serializedId; |
| |
| assert(info is LibraryInfo || |
| info is ConstantInfo || |
| info is OutputUnitInfo || |
| info.parent != null); |
| |
| int id; |
| if (info is ConstantInfo) { |
| // No name and no parent, so `longName` isn't helpful |
| assert(info.name == null); |
| assert(info.parent == null); |
| assert(info.code != null); |
| // Instead, use the content of the code. |
| id = info.code.first.text.hashCode; |
| } else { |
| id = longName(info, useLibraryUri: true, forId: true).hashCode; |
| } |
| while (!usedIds.add(id)) { |
| id++; |
| } |
| serializedId = Id(info.kind, id); |
| return ids[info] = serializedId; |
| } |
| |
| @override |
| AllInfoPB convert(AllInfo input) => _convertToAllInfoPB(input); |
| |
| DependencyInfoPB _convertToDependencyInfoPB(DependencyInfo info) { |
| var result = DependencyInfoPB() |
| ..targetId = idFor(info.target)?.serializedId; |
| if (info.mask != null) { |
| result.mask = info.mask; |
| } |
| return result; |
| } |
| |
| static ParameterInfoPB _convertToParameterInfoPB(ParameterInfo info) { |
| return ParameterInfoPB() |
| ..name = info.name |
| ..type = info.type |
| ..declaredType = info.declaredType; |
| } |
| |
| LibraryInfoPB _convertToLibraryInfoPB(LibraryInfo info) { |
| final proto = LibraryInfoPB()..uri = info.uri.toString(); |
| |
| proto.childrenIds |
| .addAll(info.topLevelFunctions.map((func) => idFor(func).serializedId)); |
| proto.childrenIds.addAll( |
| info.topLevelVariables.map((field) => idFor(field).serializedId)); |
| proto.childrenIds |
| .addAll(info.classes.map((clazz) => idFor(clazz).serializedId)); |
| proto.childrenIds.addAll( |
| info.classTypes.map((classType) => idFor(classType).serializedId)); |
| proto.childrenIds |
| .addAll(info.typedefs.map((def) => idFor(def).serializedId)); |
| |
| return proto; |
| } |
| |
| ClassInfoPB _convertToClassInfoPB(ClassInfo info) { |
| final proto = ClassInfoPB()..isAbstract = info.isAbstract; |
| |
| proto.childrenIds |
| .addAll(info.functions.map((func) => idFor(func).serializedId)); |
| proto.childrenIds |
| .addAll(info.fields.map((field) => idFor(field).serializedId)); |
| |
| return proto; |
| } |
| |
| ClassTypeInfoPB _convertToClassTypeInfoPB(ClassTypeInfo info) { |
| return ClassTypeInfoPB(); |
| } |
| |
| static FunctionModifiersPB _convertToFunctionModifiers( |
| FunctionModifiers modifiers) { |
| return FunctionModifiersPB() |
| ..isStatic = modifiers.isStatic |
| ..isConst = modifiers.isConst |
| ..isFactory = modifiers.isFactory |
| ..isExternal = modifiers.isExternal; |
| } |
| |
| FunctionInfoPB _convertToFunctionInfoPB(FunctionInfo info) { |
| final proto = FunctionInfoPB() |
| ..functionModifiers = _convertToFunctionModifiers(info.modifiers) |
| ..inlinedCount = info.inlinedCount ?? 0; |
| |
| if (info.returnType != null) { |
| proto.returnType = info.returnType; |
| } |
| |
| if (info.inferredReturnType != null) { |
| proto.inferredReturnType = info.inferredReturnType; |
| } |
| |
| if (info.code != null) { |
| proto.code = info.code.map((c) => c.text).join('\n'); |
| } |
| |
| if (info.sideEffects != null) { |
| proto.sideEffects = info.sideEffects; |
| } |
| |
| proto.childrenIds |
| .addAll(info.closures.map(((closure) => idFor(closure).serializedId))); |
| proto.parameters.addAll(info.parameters.map(_convertToParameterInfoPB)); |
| |
| return proto; |
| } |
| |
| FieldInfoPB _convertToFieldInfoPB(FieldInfo info) { |
| final proto = FieldInfoPB() |
| ..type = info.type |
| ..inferredType = info.inferredType |
| ..isConst = info.isConst; |
| |
| if (info.code != null) { |
| proto.code = info.code.map((c) => c.text).join('\n'); |
| } |
| |
| if (info.initializer != null) { |
| proto.initializerId = idFor(info.initializer).serializedId; |
| } |
| |
| proto.childrenIds |
| .addAll(info.closures.map((closure) => idFor(closure).serializedId)); |
| |
| return proto; |
| } |
| |
| static ConstantInfoPB _convertToConstantInfoPB(ConstantInfo info) { |
| return ConstantInfoPB()..code = info.code.map((c) => c.text).join('\n'); |
| } |
| |
| static OutputUnitInfoPB _convertToOutputUnitInfoPB(OutputUnitInfo info) { |
| final proto = OutputUnitInfoPB(); |
| proto.imports.addAll(info.imports.where((import) => import != null)); |
| return proto; |
| } |
| |
| static TypedefInfoPB _convertToTypedefInfoPB(TypedefInfo info) { |
| return TypedefInfoPB()..type = info.type; |
| } |
| |
| ClosureInfoPB _convertToClosureInfoPB(ClosureInfo info) { |
| return ClosureInfoPB()..functionId = idFor(info.function).serializedId; |
| } |
| |
| InfoPB _convertToInfoPB(Info info) { |
| final proto = InfoPB() |
| ..id = idFor(info).id |
| ..serializedId = idFor(info).serializedId |
| ..size = info.size; |
| |
| if (info.name != null) { |
| proto.name = info.name; |
| } |
| |
| if (info.parent != null) { |
| proto.parentId = idFor(info.parent).serializedId; |
| } |
| |
| if (info.coverageId != null) { |
| proto.coverageId = info.coverageId; |
| } |
| |
| if (info is BasicInfo && info.outputUnit != null) { |
| // TODO(lorenvs): Similar to the JSON codec, omit this for the default |
| // output unit. At the moment, there is no easy way to identify which |
| // output unit is the default on [OutputUnitInfo]. |
| proto.outputUnitId = idFor(info.outputUnit).serializedId; |
| } |
| |
| if (info is CodeInfo) { |
| proto.uses.addAll(info.uses.map(_convertToDependencyInfoPB)); |
| } |
| |
| if (info is LibraryInfo) { |
| proto.libraryInfo = _convertToLibraryInfoPB(info); |
| } else if (info is ClassInfo) { |
| proto.classInfo = _convertToClassInfoPB(info); |
| } else if (info is ClassTypeInfo) { |
| proto.classTypeInfo = _convertToClassTypeInfoPB(info); |
| } else if (info is FunctionInfo) { |
| proto.functionInfo = _convertToFunctionInfoPB(info); |
| } else if (info is FieldInfo) { |
| proto.fieldInfo = _convertToFieldInfoPB(info); |
| } else if (info is ConstantInfo) { |
| proto.constantInfo = _convertToConstantInfoPB(info); |
| } else if (info is OutputUnitInfo) { |
| proto.outputUnitInfo = _convertToOutputUnitInfoPB(info); |
| } else if (info is TypedefInfo) { |
| proto.typedefInfo = _convertToTypedefInfoPB(info); |
| } else if (info is ClosureInfo) { |
| proto.closureInfo = _convertToClosureInfoPB(info); |
| } |
| |
| return proto; |
| } |
| |
| ProgramInfoPB _convertToProgramInfoPB(ProgramInfo info) { |
| var result = ProgramInfoPB() |
| ..entrypointId = idFor(info.entrypoint).serializedId |
| ..size = info.size |
| ..compilationMoment = Int64(info.compilationMoment.microsecondsSinceEpoch) |
| ..compilationDuration = Int64(info.compilationDuration.inMicroseconds) |
| ..toProtoDuration = Int64(info.toJsonDuration.inMicroseconds) |
| ..dumpInfoDuration = Int64(info.dumpInfoDuration.inMicroseconds) |
| ..noSuchMethodEnabled = info.noSuchMethodEnabled ?? false |
| ..isRuntimeTypeUsed = info.isRuntimeTypeUsed ?? false |
| ..isIsolateUsed = info.isIsolateInUse ?? false |
| ..isFunctionApplyUsed = info.isFunctionApplyUsed ?? false |
| ..isMirrorsUsed = info.isMirrorsUsed ?? false |
| ..minified = info.minified ?? false; |
| |
| if (info.dart2jsVersion != null) { |
| result.dart2jsVersion = info.dart2jsVersion; |
| } |
| return result; |
| } |
| |
| Iterable<MapEntry<String, InfoPB>> _convertToAllInfosEntries<T extends Info>( |
| Iterable<T> infos) sync* { |
| for (final info in infos) { |
| final infoProto = _convertToInfoPB(info); |
| final entry = MapEntry<String, InfoPB>(infoProto.serializedId, infoProto); |
| yield entry; |
| } |
| } |
| |
| static LibraryDeferredImportsPB _convertToLibraryDeferredImportsPB( |
| String libraryUri, Map<String, dynamic> fields) { |
| final proto = LibraryDeferredImportsPB() |
| ..libraryUri = libraryUri |
| ..libraryName = fields['name'] ?? '<unnamed>'; |
| |
| Map<String, List<String>> imports = fields['imports']; |
| imports.forEach((prefix, files) { |
| final import = DeferredImportPB()..prefix = prefix; |
| import.files.addAll(files); |
| proto.imports.add(import); |
| }); |
| |
| return proto; |
| } |
| |
| AllInfoPB _convertToAllInfoPB(AllInfo info) { |
| final proto = AllInfoPB()..program = _convertToProgramInfoPB(info.program); |
| |
| proto.allInfos.addEntries(_convertToAllInfosEntries(info.libraries)); |
| proto.allInfos.addEntries(_convertToAllInfosEntries(info.classes)); |
| proto.allInfos.addEntries(_convertToAllInfosEntries(info.classTypes)); |
| proto.allInfos.addEntries(_convertToAllInfosEntries(info.functions)); |
| proto.allInfos.addEntries(_convertToAllInfosEntries(info.fields)); |
| proto.allInfos.addEntries(_convertToAllInfosEntries(info.constants)); |
| proto.allInfos.addEntries(_convertToAllInfosEntries(info.outputUnits)); |
| proto.allInfos.addEntries(_convertToAllInfosEntries(info.typedefs)); |
| proto.allInfos.addEntries(_convertToAllInfosEntries(info.closures)); |
| |
| info.deferredFiles?.forEach((libraryUri, fields) { |
| proto.deferredImports |
| .add(_convertToLibraryDeferredImportsPB(libraryUri, fields)); |
| }); |
| |
| return proto; |
| } |
| } |
| |
| /// A codec for converting [AllInfo] to a protobuf format. |
| /// |
| /// This codec is still experimental, and will likely crash on certain output |
| /// from dart2js. |
| class AllInfoProtoCodec extends Codec<AllInfo, AllInfoPB> { |
| @override |
| final Converter<AllInfo, AllInfoPB> encoder = AllInfoToProtoConverter(); |
| @override |
| final Converter<AllInfoPB, AllInfo> decoder = ProtoToAllInfoConverter(); |
| } |
| |
| class Id { |
| final InfoKind kind; |
| final int id; |
| |
| Id(this.kind, this.id); |
| |
| String get serializedId => '${kindToString(kind)}/$id'; |
| } |