diff --git a/bin/debug_info.dart b/bin/debug_info.dart index 3c09114..ec39001 100644 --- a/bin/debug_info.dart +++ b/bin/debug_info.dart @@ -67,7 +67,7 @@ validateSize(AllInfo info, String debugLibName) { _fail('$percent% size missing: $accounted (all libs + consts) ' '< $realTotal (total)'); } - var missingTotal = tracker.missing.values.fold(0, (a, b) => a + b); + int missingTotal = tracker.missing.values.fold(0, (a, b) => a + b); if (missingTotal > 0) { var percent = (missingTotal * 100 / realTotal).toStringAsFixed(2); _fail('$percent% size missing in libraries (sum of elements > lib.size)'); diff --git a/lib/info.dart b/lib/info.dart index 9661dcc..e25eb43 100644 --- a/lib/info.dart +++ b/lib/info.dart @@ -104,6 +104,9 @@ class AllInfo { // and we'll include it only once in the output. List constants = []; + /// Information about closures anywhere in the program. + List closures = []; + /// Information about output units (should be just one entry if not using /// deferred loading). List outputUnits = []; @@ -123,7 +126,7 @@ class AllInfo { /// Major version indicating breaking changes in the format. A new version /// means that an old deserialization algorithm will not work with the new /// format. - final int version = 4; + final int version = 5; /// Minor version indicating non-breaking changes in the format. A change in /// this version number means that the json parsing in this library from a @@ -263,7 +266,7 @@ class FieldInfo extends BasicInfo with CodeInfo { String inferredType; /// Nested closures seen in the field initializer. - List closures; + List closures; /// The actual generated code for the field. String code; @@ -321,7 +324,7 @@ class FunctionInfo extends BasicInfo with CodeInfo { FunctionModifiers modifiers; /// Nested closures that appear within the body of this function. - List closures; + List closures; /// The type of this function. String type; @@ -371,6 +374,22 @@ class FunctionInfo extends BasicInfo with CodeInfo { dynamic accept(InfoVisitor visitor) => visitor.visitFunction(this); } +/// Information about a closure, also known as a local function. +class ClosureInfo extends BasicInfo { + static int _ids = 0; + + /// The function that is wrapped by this closure. + FunctionInfo function; + + ClosureInfo( + {String name, OutputUnitInfo outputUnit, int size: 0, this.function}) + : super(InfoKind.closure, _ids++, name, outputUnit, size, null); + + ClosureInfo._(String serializedId) : super._fromId(serializedId); + + dynamic accept(InfoVisitor visitor) => visitor.visitClosure(this); +} + /// Information about how a dependency is used. class DependencyInfo { /// The dependency, either a FunctionInfo or FieldInfo. @@ -407,7 +426,7 @@ class FunctionModifiers { this.isExternal: false}); } -/// Possible values of the `kind` field in the serialied infos. +/// Possible values of the `kind` field in the serialized infos. enum InfoKind { library, clazz, @@ -416,6 +435,7 @@ enum InfoKind { constant, outputUnit, typedef, + closure, } String kindToString(InfoKind kind) { @@ -434,6 +454,8 @@ String kindToString(InfoKind kind) { return 'outputUnit'; case InfoKind.typedef: return 'typedef'; + case InfoKind.closure: + return 'closure'; default: return null; } @@ -461,6 +483,8 @@ InfoKind kindFromString(String kind) { return InfoKind.outputUnit; case 'typedef': return InfoKind.typedef; + case 'closure': + return InfoKind.closure; default: return null; } @@ -476,6 +500,7 @@ abstract class InfoVisitor { T visitConstant(ConstantInfo info); T visitFunction(FunctionInfo info); T visitTypedef(TypedefInfo info); + T visitClosure(ClosureInfo info); T visitOutput(OutputUnitInfo info); } @@ -508,15 +533,18 @@ class RecursiveInfoVisitor extends InfoVisitor { } visitField(FieldInfo info) { - info.closures.forEach(visitFunction); + info.closures.forEach(visitClosure); } visitConstant(ConstantInfo info) {} visitFunction(FunctionInfo info) { - info.closures.forEach(visitFunction); + info.closures.forEach(visitClosure); } visitTypedef(TypedefInfo info) {} visitOutput(OutputUnitInfo info) {} + visitClosure(ClosureInfo info) { + visitFunction(info.function); + } } diff --git a/lib/json_info_codec.dart b/lib/json_info_codec.dart index 5176ce3..8382bb0 100644 --- a/lib/json_info_codec.dart +++ b/lib/json_info_codec.dart @@ -7,10 +7,10 @@ part of dart2js_info.info; // TODO(sigmund): add unit tests. class JsonToAllInfoConverter extends Converter, AllInfo> { - Map registry; + Map registry = {}; AllInfo convert(Map json) { - registry = {}; + registry.clear(); var result = new AllInfo(); var elements = json['elements']; @@ -19,15 +19,13 @@ class JsonToAllInfoConverter extends Converter, AllInfo> { result.classes.addAll((elements['class'] as Map).values.map(parseClass)); result.functions .addAll((elements['function'] as Map).values.map(parseFunction)); + result.closures + .addAll((elements['closure'] as Map).values.map(parseClosure)); result.fields.addAll((elements['field'] as Map).values.map(parseField)); result.typedefs .addAll((elements['typedef'] as Map).values.map(parseTypedef)); - - // TODO(sigmund): remove null check on next breaking version - var constants = elements['constant']; - if (constants != null) { - result.constants.addAll((constants as Map).values.map(parseConstant)); - } + result.constants + .addAll((elements['constant'] as Map).values.map(parseConstant)); var idMap = {}; for (var f in result.functions) { @@ -208,11 +206,23 @@ class JsonToAllInfoConverter extends Converter, AllInfo> { isExternal: json['external'] == true); } + ClosureInfo parseClosure(Map json) { + ClosureInfo result = parseId(json['id']); + return result + ..name = json['name'] + ..parent = parseId(json['parent']) + ..outputUnit = parseId(json['outputUnit']) + ..size = json['size'] + ..function = parseId(json['function']); + } + 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('closure/')) { + return new ClosureInfo._(serializedId); } else if (serializedId.startsWith('library/')) { return new LibraryInfo._(serializedId); } else if (serializedId.startsWith('class/')) { @@ -249,13 +259,15 @@ class AllInfoToJsonConverter extends Converter var jsonTypedefs = _visitList(info.typedefs); var jsonFields = _visitList(info.fields); var jsonConstants = _visitList(info.constants); + var jsonClosures = _visitList(info.closures); return { 'library': jsonLibraries, 'class': jsonClasses, 'function': jsonFunctions, 'typedef': jsonTypedefs, 'field': jsonFields, - 'constant': jsonConstants + 'constant': jsonConstants, + 'closure': jsonClosures, }; } @@ -433,6 +445,11 @@ class AllInfoToJsonConverter extends Converter }); } + Map visitClosure(ClosureInfo info) { + return _visitBasicInfo(info) + ..addAll({'function': info.function.serializedId}); + } + visitTypedef(TypedefInfo info) => _visitBasicInfo(info)..['type'] = info.type; visitOutput(OutputUnitInfo info) => diff --git a/pubspec.yaml b/pubspec.yaml index a5f9d21..2c7d6e9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: dart2js_info -version: 0.3.0 +version: 0.5.0 description: > Libraries and tools to process data produced when running dart2js with --dump-info. diff --git a/test/hello_world/hello_world.js.info.json b/test/hello_world/hello_world.js.info.json index 33eacf6..2bd859d 100644 --- a/test/hello_world/hello_world.js.info.json +++ b/test/hello_world/hello_world.js.info.json @@ -1452,7 +1452,7 @@ "name": "message", "size": 0, "outputUnit": "outputUnit/0", - "coverageId": "4090", + "coverageId": "4087", "parent": "class/143", "children": [], "inferredType": "Value mask: [\"Intercepted function with no arguments.\"] type: [exact=JSString]", @@ -1802,7 +1802,8 @@ "type": "E" } }, - "constant": {} + "constant": {}, + "closure": {} }, "holding": { "function/0": [ @@ -1846,21 +1847,21 @@ "id": "outputUnit/0", "kind": "outputUnit", "name": null, - "size": 10124, + "size": 10116, "imports": [ null ] } ], - "dump_version": 4, + "dump_version": 5, "deferredFiles": {}, "dump_minor_version": "0", "program": { "entrypoint": "function/0", - "size": 10124, + "size": 10116, "dart2jsVersion": null, - "compilationMoment": "2016-09-30 16:14:58.038530", - "compilationDuration": 2726976, + "compilationMoment": "2016-10-06 10:14:59.145665", + "compilationDuration": 2729236, "toJsonDuration": 4000, "dumpInfoDuration": 0, "noSuchMethodEnabled": false, diff --git a/test/parse_test.dart b/test/parse_test.dart index ead1701..8b891dc 100644 --- a/test/parse_test.dart +++ b/test/parse_test.dart @@ -19,10 +19,10 @@ main() { expect(program, isNotNull); expect(program.entrypoint, isNotNull); - expect(program.size, 10124); + expect(program.size, 10116); expect(program.compilationMoment, - DateTime.parse("2016-09-30 16:14:58.038530")); - expect(program.compilationDuration, new Duration(microseconds: 2726976)); + DateTime.parse("2016-10-06 10:14:59.145665")); + expect(program.compilationDuration, new Duration(microseconds: 2729236)); expect(program.toJsonDuration, new Duration(milliseconds: 4)); expect(program.dumpInfoDuration, new Duration(seconds: 0)); expect(program.noSuchMethodEnabled, false);