| Index: lib/json_info_codec.dart
|
| diff --git a/lib/json_info_codec.dart b/lib/json_info_codec.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..1293d305cf548266f9314e85592ad7662cd7cc3e
|
| --- /dev/null
|
| +++ b/lib/json_info_codec.dart
|
| @@ -0,0 +1,415 @@
|
| +// 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.
|
| +
|
| +/// Converters and codecs for converting between JSON and [Info] classes.
|
| +part of dart2js_info.info;
|
| +
|
| +// TODO(sigmund): add unit tests.
|
| +class JsonToAllInfoConverter extends Converter<Map, AllInfo> {
|
| + Map<String, Info> registry;
|
| +
|
| + AllInfo convert(Map json) {
|
| + registry = <String, Info>{};
|
| +
|
| + var result = new AllInfo();
|
| + var elements = json['elements'];
|
| + result.libraries.addAll(elements['library'].values.map(parseLibrary));
|
| + result.classes.addAll(elements['class'].values.map(parseClass));
|
| + result.functions.addAll(elements['function'].values.map(parseFunction));
|
| + result.fields.addAll(elements['field'].values.map(parseField));
|
| + result.typedefs.addAll(elements['typedef'].values.map(parseTypedef));
|
| +
|
| + // TODO(sigmund): remove null check on next breaking version
|
| + var constants = elements['constant'];
|
| + if (constants != null) {
|
| + result.constants.addAll(constants.values.map(parseConstant));
|
| + }
|
| +
|
| + var idMap = {};
|
| + for (var f in result.functions) {
|
| + idMap[f.serializedId] = f;
|
| + }
|
| + for (var f in result.fields) {
|
| + idMap[f.serializedId] = f;
|
| + }
|
| +
|
| + json['holding'].forEach((k, deps) {
|
| + var src = idMap[k];
|
| + assert(src != null);
|
| + for (var dep in deps) {
|
| + var target = idMap[dep['id']];
|
| + assert(target != null);
|
| + src.uses.add(new DependencyInfo(target, dep['mask']));
|
| + }
|
| + });
|
| +
|
| + json['dependencies']?.forEach((k, deps) {
|
| + result.dependencies[idMap[k]] = deps.map((d) => idMap[d]).toList();
|
| + });
|
| +
|
| + result.program = parseProgram(json['program']);
|
| + // todo: version, etc
|
| + return result;
|
| + }
|
| +
|
| + LibraryInfo parseLibrary(Map json) {
|
| + LibraryInfo result = parseId(json['id']);
|
| + result
|
| + ..name = json['name']
|
| + ..uri = Uri.parse(json['canonicalUri'])
|
| + ..outputUnit = parseId(json['outputUnit'])
|
| + ..size = json['size'];
|
| + for (var child in json['children'].map(parseId)) {
|
| + if (child is FunctionInfo) {
|
| + result.topLevelFunctions.add(child);
|
| + } else if (child is FieldInfo) {
|
| + result.topLevelVariables.add(child);
|
| + } else if (child is ClassInfo) {
|
| + result.classes.add(child);
|
| + } else {
|
| + assert(child is TypedefInfo);
|
| + result.typedefs.add(child);
|
| + }
|
| + }
|
| + return result;
|
| + }
|
| +
|
| + ClassInfo parseClass(Map json) {
|
| + ClassInfo result = parseId(json['id']);
|
| + result
|
| + ..name = json['name']
|
| + ..parent = parseId(json['parent'])
|
| + ..outputUnit = parseId(json['outputUnit'])
|
| + ..size = json['size']
|
| + ..isAbstract = json['modifiers']['abstract'] == true;
|
| + assert(result is ClassInfo);
|
| + for (var child in json['children'].map(parseId)) {
|
| + if (child is FunctionInfo) {
|
| + result.functions.add(child);
|
| + } else {
|
| + assert(child is FieldInfo);
|
| + result.fields.add(child);
|
| + }
|
| + }
|
| + return result;
|
| + }
|
| +
|
| + FieldInfo parseField(Map json) {
|
| + FieldInfo result = parseId(json['id']);
|
| + return result
|
| + ..name = json['name']
|
| + ..parent = parseId(json['parent'])
|
| + ..coverageId = json['coverageId']
|
| + ..outputUnit = parseId(json['outputUnit'])
|
| + ..size = json['size']
|
| + ..type = json['type']
|
| + ..inferredType = json['inferredType']
|
| + ..code = json['code']
|
| + ..isConst = json['const'] ?? false
|
| + ..initializer = parseId(json['initializer'])
|
| + ..closures = json['children'].map(parseId).toList();
|
| + }
|
| +
|
| + ConstantInfo parseConstant(Map json) {
|
| + ConstantInfo result = parseId(json['id']);
|
| + return result
|
| + ..code = json['code']
|
| + ..size = json['size'];
|
| + }
|
| +
|
| + TypedefInfo parseTypedef(Map json) {
|
| + TypedefInfo result = parseId(json['id']);
|
| + return result
|
| + ..name = json['name']
|
| + ..parent = parseId(json['parent'])
|
| + ..type = json['type']
|
| + ..size = 0;
|
| + }
|
| +
|
| + ProgramInfo parseProgram(Map json) => new ProgramInfo()
|
| + ..size = json['size']
|
| + ..entrypoint = parseId(json['entrypoint']);
|
| +
|
| + FunctionInfo parseFunction(Map json) {
|
| + FunctionInfo result = parseId(json['id']);
|
| + return result
|
| + ..name = json['name']
|
| + ..parent = parseId(json['parent'])
|
| + ..coverageId = json['coverageId']
|
| + ..outputUnit = parseId(json['outputUnit'])
|
| + ..size = json['size']
|
| + ..type = json['type']
|
| + ..returnType = json['returnType']
|
| + ..inferredReturnType = json['inferredReturnType']
|
| + ..parameters = json['parameters'].map(parseParameter).toList()
|
| + ..code = json['code']
|
| + ..sideEffects = json['sideEffects']
|
| + ..modifiers = parseModifiers(json['modifiers'])
|
| + ..closures = json['children'].map(parseId).toList()
|
| + ..measurements = parseMeasurements(json['measurements']);
|
| + }
|
| +
|
| + ParameterInfo parseParameter(Map json) =>
|
| + new ParameterInfo(json['name'], json['type'], json['declaredType']);
|
| +
|
| + Measurements parseMeasurements(Map json) {
|
| + if (json == null) return null;
|
| + var uri = json['sourceFile'];
|
| + var res = new Measurements(uri == null ? null : Uri.parse(uri));
|
| + for (var key in json.keys) {
|
| + var value = json[key];
|
| + if (value == null) continue;
|
| + if (key == 'entries') {
|
| + value.forEach((metricName, entries) {
|
| + var metric = new Metric.fromName(metricName);
|
| + for (var i = 0; i < entries.length; i += 2) {
|
| + res.record(metric, entries[i], entries[i + 1]);
|
| + }
|
| + });
|
| + } else {
|
| + res.counters[new Metric.fromName(key)] = value;
|
| + }
|
| + }
|
| + return res;
|
| + }
|
| +
|
| + FunctionModifiers parseModifiers(Map<String, bool> json) {
|
| + return new FunctionModifiers(
|
| + isStatic: json['static'] == true,
|
| + isConst: json['const'] == true,
|
| + isFactory: json['factory'] == true,
|
| + isExternal: json['external'] == true);
|
| + }
|
| +
|
| + Info parseId(String serializedId) => registry.putIfAbsent(serializedId, () {
|
| + if (serializedId == null) {
|
| + return null;
|
| + } else if (serializedId.startsWith('function/')) {
|
| + return new FunctionInfo._(serializedId);
|
| + } else if (serializedId.startsWith('library/')) {
|
| + return new LibraryInfo._(serializedId);
|
| + } else if (serializedId.startsWith('class/')) {
|
| + return new ClassInfo._(serializedId);
|
| + } else if (serializedId.startsWith('field/')) {
|
| + return new FieldInfo._(serializedId);
|
| + } else if (serializedId.startsWith('constant/')) {
|
| + return new ConstantInfo._(serializedId);
|
| + } else if (serializedId.startsWith('typedef/')) {
|
| + return new TypedefInfo._(serializedId);
|
| + } else if (serializedId.startsWith('outputUnit/')) {
|
| + return new OutputUnitInfo._(serializedId);
|
| + }
|
| + assert(false);
|
| + });
|
| +}
|
| +
|
| +class AllInfoToJsonConverter extends Converter<AllInfo, Map>
|
| + implements InfoVisitor<Map> {
|
| + Map convert(AllInfo info) => info.accept(this);
|
| +
|
| + Map _visitList(List<Info> infos) {
|
| + var map = <String, Map>{};
|
| + for (var info in infos) {
|
| + map['${info.id}'] = info.accept(this);
|
| + }
|
| + return map;
|
| + }
|
| +
|
| + Map _visitAllInfoElements(AllInfo info) {
|
| + var jsonLibraries = _visitList(info.libraries);
|
| + var jsonClasses = _visitList(info.classes);
|
| + var jsonFunctions = _visitList(info.functions);
|
| + var jsonTypedefs = _visitList(info.typedefs);
|
| + var jsonFields = _visitList(info.fields);
|
| + var jsonConstants = _visitList(info.constants);
|
| + return {
|
| + 'library': jsonLibraries,
|
| + 'class': jsonClasses,
|
| + 'function': jsonFunctions,
|
| + 'typedef': jsonTypedefs,
|
| + 'field': jsonFields,
|
| + 'constant': jsonConstants
|
| + };
|
| + }
|
| +
|
| + Map _visitDependencyInfo(DependencyInfo info) =>
|
| + {'id': info.target.serializedId, 'mask': info.mask};
|
| +
|
| + Map _visitAllInfoHolding(AllInfo allInfo) {
|
| + var map = <String, List>{};
|
| + void helper(CodeInfo info) {
|
| + if (info.uses.isEmpty) return;
|
| + map[info.serializedId] =
|
| + info.uses.map((u) => _visitDependencyInfo(u)).toList();
|
| + }
|
| + allInfo.functions.forEach(helper);
|
| + allInfo.fields.forEach(helper);
|
| + return map;
|
| + }
|
| +
|
| + Map _visitAllInfoDependencies(AllInfo allInfo) {
|
| + var map = <String, List>{};
|
| + allInfo.dependencies.forEach((k, v) {
|
| + map[k.serializedId] = v.map((i) => i.serializedId).toList();
|
| + });
|
| + return map;
|
| + }
|
| +
|
| + Map visitAll(AllInfo info) {
|
| + var elements = _visitAllInfoElements(info);
|
| + var jsonHolding = _visitAllInfoHolding(info);
|
| + var jsonDependencies = _visitAllInfoDependencies(info);
|
| + return {
|
| + 'elements': elements,
|
| + 'holding': jsonHolding,
|
| + 'dependencies': jsonDependencies,
|
| + 'outputUnits': info.outputUnits.map((u) => u.accept(this)).toList(),
|
| + 'dump_version': info.version,
|
| + 'deferredFiles': info.deferredFiles,
|
| + 'dump_minor_version': '${info.minorVersion}',
|
| + 'program': info.program.accept(this)
|
| + };
|
| + }
|
| +
|
| + Map visitProgram(ProgramInfo info) {
|
| + return {
|
| + 'entrypoint': info.entrypoint.serializedId,
|
| + 'size': info.size,
|
| + 'dart2jsVersion': info.dart2jsVersion,
|
| + 'compilationMoment': '${info.compilationMoment}',
|
| + 'compilationDuration': '${info.compilationDuration}',
|
| + 'toJsonDuration': info.toJsonDuration,
|
| + 'dumpInfoDuration': '${info.dumpInfoDuration}',
|
| + 'noSuchMethodEnabled': info.noSuchMethodEnabled,
|
| + 'minified': info.minified,
|
| + };
|
| + }
|
| +
|
| + Map _visitBasicInfo(BasicInfo info) {
|
| + var res = {
|
| + 'id': info.serializedId,
|
| + 'kind': _kindToString(info.kind),
|
| + 'name': info.name,
|
| + 'size': info.size,
|
| + };
|
| + // TODO(sigmund): Omit this also when outputUnit.id == 0 (most code is in
|
| + // the main output unit by default).
|
| + if (info.outputUnit != null) res['outputUnit'] =
|
| + info.outputUnit.serializedId;
|
| + if (info.coverageId != null) res['coverageId'] = info.coverageId;
|
| + if (info.parent != null) res['parent'] = info.parent.serializedId;
|
| + return res;
|
| + }
|
| +
|
| + Map visitLibrary(LibraryInfo info) {
|
| + return _visitBasicInfo(info)
|
| + ..addAll({
|
| + 'children': []
|
| + ..addAll(info.topLevelFunctions.map((f) => f.serializedId))
|
| + ..addAll(info.topLevelVariables.map((v) => v.serializedId))
|
| + ..addAll(info.classes.map((c) => c.serializedId))
|
| + ..addAll(info.typedefs.map((t) => t.serializedId)),
|
| + 'canonicalUri': '${info.uri}',
|
| + });
|
| + }
|
| +
|
| + Map visitClass(ClassInfo info) {
|
| + return _visitBasicInfo(info)
|
| + ..addAll({
|
| + // TODO(sigmund): change format, include only when abstract is true.
|
| + 'modifiers': {'abstract': info.isAbstract},
|
| + 'children': []
|
| + ..addAll(info.fields.map((f) => f.serializedId))
|
| + ..addAll(info.functions.map((m) => m.serializedId))
|
| + });
|
| + }
|
| +
|
| + Map visitField(FieldInfo info) {
|
| + var result = _visitBasicInfo(info)
|
| + ..addAll({
|
| + 'children': info.closures.map((i) => i.serializedId).toList(),
|
| + 'inferredType': info.inferredType,
|
| + 'code': info.code,
|
| + 'type': info.type,
|
| + });
|
| + if (info.isConst) {
|
| + result['const'] = true;
|
| + if (info.initializer != null) result['initializer'] =
|
| + info.initializer.serializedId;
|
| + }
|
| + return result;
|
| + }
|
| +
|
| + Map visitConstant(ConstantInfo info) =>
|
| + _visitBasicInfo(info)..addAll({'code': info.code});
|
| +
|
| + // TODO(sigmund): exclude false values (requires bumping the format version):
|
| + // var res = <String, bool>{};
|
| + // if (isStatic) res['static'] = true;
|
| + // if (isConst) res['const'] = true;
|
| + // if (isFactory) res['factory'] = true;
|
| + // if (isExternal) res['external'] = true;
|
| + // return res;
|
| + Map _visitFunctionModifiers(FunctionModifiers mods) => {
|
| + 'static': mods.isStatic,
|
| + 'const': mods.isConst,
|
| + 'factory': mods.isFactory,
|
| + 'external': mods.isExternal,
|
| + };
|
| +
|
| + Map _visitParameterInfo(ParameterInfo info) =>
|
| + {'name': info.name, 'type': info.type, 'declaredType': info.declaredType};
|
| +
|
| + String _visitMetric(Metric metric) => metric.name;
|
| +
|
| + Map _visitMeasurements(Measurements measurements) {
|
| + if (measurements == null) return null;
|
| + var jsonEntries = <String, List<Map>>{};
|
| + measurements.entries.forEach((metric, values) {
|
| + jsonEntries[_visitMetric(metric)] =
|
| + values.expand((e) => [e.begin, e.end]).toList();
|
| + });
|
| + var json = {'entries': jsonEntries};
|
| + // TODO(sigmund): encode uri as an offset of the URIs available in the parts
|
| + // of the library info.
|
| + if (measurements.uri != null) json['sourceFile'] = '${measurements.uri}';
|
| + if (measurements.counters[Metric.functions] != null) {
|
| + json[_visitMetric(Metric.functions)] =
|
| + measurements.counters[Metric.functions];
|
| + }
|
| + if (measurements.counters[Metric.reachableFunctions] != null) {
|
| + json[_visitMetric(Metric.reachableFunctions)] =
|
| + measurements.counters[Metric.reachableFunctions];
|
| + }
|
| + return json;
|
| + }
|
| +
|
| + Map visitFunction(FunctionInfo info) {
|
| + return _visitBasicInfo(info)
|
| + ..addAll({
|
| + 'children': info.closures.map((i) => i.serializedId).toList(),
|
| + 'modifiers': _visitFunctionModifiers(info.modifiers),
|
| + 'returnType': info.returnType,
|
| + 'inferredReturnType': info.inferredReturnType,
|
| + 'parameters':
|
| + info.parameters.map((p) => _visitParameterInfo(p)).toList(),
|
| + 'sideEffects': info.sideEffects,
|
| + 'inlinedCount': info.inlinedCount,
|
| + 'code': info.code,
|
| + 'type': info.type,
|
| + 'measurements': _visitMeasurements(info.measurements),
|
| + // Note: version 3.2 of dump-info serializes `uses` in a section called
|
| + // `holding` at the top-level.
|
| + });
|
| + }
|
| +
|
| + visitTypedef(TypedefInfo info) => _visitBasicInfo(info)..['type'] = info.type;
|
| +
|
| + visitOutput(OutputUnitInfo info) => _visitBasicInfo(info);
|
| +}
|
| +
|
| +class AllInfoJsonCodec extends Codec<AllInfo, Map> {
|
| + final Converter<AllInfo, Map> encoder = new AllInfoToJsonConverter();
|
| + final Converter<Map, AllInfo> decoder = new JsonToAllInfoConverter();
|
| +}
|
|
|