| Index: pkg/compiler/lib/src/dump_info.dart
|
| diff --git a/pkg/compiler/lib/src/dump_info.dart b/pkg/compiler/lib/src/dump_info.dart
|
| index 3b8f8bce2ae20e3e9706acfda1458e90fc9531bc..5e0d4fe05f83c052f82c3e1f32a4b9aecf00e62f 100644
|
| --- a/pkg/compiler/lib/src/dump_info.dart
|
| +++ b/pkg/compiler/lib/src/dump_info.dart
|
| @@ -4,188 +4,98 @@
|
|
|
| library dump_info;
|
|
|
| -import 'dart:convert' show
|
| - HtmlEscape,
|
| - JsonEncoder,
|
| - StringConversionSink,
|
| - ChunkedConversionSink;
|
| +import 'dart:convert'
|
| + show HtmlEscape, JsonEncoder, StringConversionSink, ChunkedConversionSink;
|
|
|
| import 'elements/elements.dart';
|
| import 'elements/visitor.dart';
|
| -import 'dart2jslib.dart' show
|
| - Backend,
|
| - CodeBuffer,
|
| - Compiler,
|
| - CompilerTask,
|
| - MessageKind;
|
| +import 'dart2jslib.dart'
|
| + show Backend, CodeBuffer, Compiler, CompilerTask, MessageKind;
|
| import 'types/types.dart' show TypeMask;
|
| import 'deferred_load.dart' show OutputUnit;
|
| +import 'info/info.dart';
|
| import 'js_backend/js_backend.dart' show JavaScriptBackend;
|
| import 'js_emitter/full_emitter/emitter.dart' as full show Emitter;
|
| import 'js/js.dart' as jsAst;
|
| import 'universe/universe.dart' show Selector, UniverseSelector;
|
| import 'util/util.dart' show NO_LOCATION_SPANNABLE;
|
|
|
| -/// Maps objects to an id. Supports lookups in
|
| -/// both directions.
|
| -class IdMapper<T>{
|
| - Map<int, T> _idToElement = {};
|
| - Map<T, int> _elementToId = {};
|
| - int _idCounter = 0;
|
| - final String name;
|
| -
|
| - IdMapper(this.name);
|
| -
|
| - Iterable<T> get elements => _elementToId.keys;
|
| -
|
| - String add(T e) {
|
| - if (_elementToId.containsKey(e)) {
|
| - return name + "/${_elementToId[e]}";
|
| - }
|
| -
|
| - _idToElement[_idCounter] = e;
|
| - _elementToId[e] = _idCounter;
|
| - _idCounter += 1;
|
| - return name + "/${_idCounter - 1}";
|
| - }
|
| -}
|
| -
|
| -class GroupedIdMapper {
|
| - // Mappers for specific kinds of elements.
|
| - IdMapper<LibraryElement> _library = new IdMapper('library');
|
| - IdMapper<TypedefElement> _typedef = new IdMapper('typedef');
|
| - IdMapper<FieldElement> _field = new IdMapper('field');
|
| - IdMapper<ClassElement> _class = new IdMapper('class');
|
| - IdMapper<FunctionElement> _function = new IdMapper('function');
|
| - IdMapper<OutputUnit> _outputUnit = new IdMapper('outputUnit');
|
| -
|
| - Iterable<Element> get functions => _function.elements;
|
| -
|
| - // Convert this database of elements into JSON for rendering
|
| - Map<String, dynamic> _toJson(ElementToJsonVisitor elementToJson) {
|
| - Map<String, dynamic> json = {};
|
| - var m = [_library, _typedef, _field, _class, _function];
|
| - for (IdMapper mapper in m) {
|
| - Map<String, dynamic> innerMapper = {};
|
| - mapper._idToElement.forEach((k, v) {
|
| - // All these elements are already cached in the
|
| - // jsonCache, so this is just an access.
|
| - var elementJson = elementToJson.process(v);
|
| - if (elementJson != null) {
|
| - innerMapper["$k"] = elementJson;
|
| - }
|
| - });
|
| - json[mapper.name] = innerMapper;
|
| - }
|
| - return json;
|
| - }
|
| -}
|
| -
|
| -class ElementToJsonVisitor
|
| - extends BaseElementVisitor<Map<String, dynamic>, dynamic> {
|
| - final GroupedIdMapper mapper = new GroupedIdMapper();
|
| +class ElementInfoCollector extends BaseElementVisitor<Info, dynamic> {
|
| final Compiler compiler;
|
|
|
| - final Map<Element, Map<String, dynamic>> jsonCache = {};
|
| -
|
| - String dart2jsVersion;
|
| + final AllInfo result = new AllInfo();
|
| + final Map<Element, Info> _elementToInfo = <Element, Info>{};
|
| + final Map<OutputUnit, OutputUnitInfo> _outputToInfo = {};
|
|
|
| - ElementToJsonVisitor(this.compiler);
|
| + ElementInfoCollector(this.compiler);
|
|
|
| - void run() {
|
| - dart2jsVersion = compiler.hasBuildId ? compiler.buildId : null;
|
| + void run() => compiler.libraryLoader.libraries.forEach(visit);
|
|
|
| - for (LibraryElement library in compiler.libraryLoader.libraries.toList()) {
|
| - visit(library);
|
| - }
|
| - }
|
| -
|
| - Map<String, dynamic> visit(Element e, [_]) => e.accept(this, null);
|
| + Info visit(Element e, [_]) => e.accept(this, null);
|
|
|
| - // If keeping the element is in question (like if a function has a size
|
| - // of zero), only keep it if it holds dependencies to elsewhere.
|
| + /// Whether to emit information about [element].
|
| + ///
|
| + /// By default we emit information for any element that contributes to the
|
| + /// output size. Either becuase the it is a function being emitted or inlined,
|
| + /// or because it is an element that holds dependencies to other elements.
|
| bool shouldKeep(Element element) {
|
| - return compiler.dumpInfoTask.selectorsFromElement.containsKey(element)
|
| - || compiler.dumpInfoTask.inlineCount.containsKey(element);
|
| - }
|
| -
|
| - Map<String, dynamic> toJson() {
|
| - return mapper._toJson(this);
|
| + return compiler.dumpInfoTask.selectorsFromElement.containsKey(element) ||
|
| + compiler.dumpInfoTask.inlineCount.containsKey(element);
|
| }
|
|
|
| - // Memoization of the JSON creating process.
|
| - Map<String, dynamic> process(Element element) {
|
| - return jsonCache.putIfAbsent(element, () => visit(element));
|
| + /// Visits [element] and produces it's corresponding info.
|
| + Info process(Element element) {
|
| + // TODO(sigmund): change the visit order to eliminate the need to check
|
| + // whether or not an element has been processed.
|
| + return _elementToInfo.putIfAbsent(element, () => visit(element));
|
| }
|
|
|
| - // Returns the id of an [element] if it has already been processed.
|
| - // If the element has not been processed, this function does not
|
| - // process it, and simply returns null instead.
|
| - String idOf(Element element) {
|
| - if (jsonCache.containsKey(element) && jsonCache[element] != null) {
|
| - return jsonCache[element]['id'];
|
| - } else {
|
| - return null;
|
| - }
|
| - }
|
| -
|
| - Map<String, dynamic> visitElement(Element element, _) {
|
| - return null;
|
| - }
|
| + Info visitElement(Element element, _) => null;
|
|
|
| - Map<String, dynamic> visitConstructorBodyElement(
|
| - ConstructorBodyElement e, _) {
|
| + FunctionInfo visitConstructorBodyElement(ConstructorBodyElement e, _) {
|
| return visitFunctionElement(e.constructor, _);
|
| }
|
|
|
| - Map<String, dynamic> visitLibraryElement(LibraryElement element, _) {
|
| - var id = mapper._library.add(element);
|
| - List<String> children = <String>[];
|
| -
|
| + LibraryInfo visitLibraryElement(LibraryElement element, _) {
|
| String libname = element.getLibraryName();
|
| libname = libname == "" ? "<unnamed>" : libname;
|
| -
|
| int size = compiler.dumpInfoTask.sizeOf(element);
|
| -
|
| - LibraryElement contentsOfLibrary = element.isPatched
|
| - ? element.patch : element;
|
| - contentsOfLibrary.forEachLocalMember((Element member) {
|
| - Map<String, dynamic> childJson = this.process(member);
|
| - if (childJson == null) return;
|
| - children.add(childJson['id']);
|
| + LibraryInfo info =
|
| + new LibraryInfo(libname, element.canonicalUri, null, size);
|
| + _elementToInfo[element] = info;
|
| +
|
| + LibraryElement realElement = element.isPatched ? element.patch : element;
|
| + realElement.forEachLocalMember((Element member) {
|
| + Info child = this.process(member);
|
| + if (child is ClassInfo) {
|
| + info.classes.add(child);
|
| + } else if (child is FunctionInfo) {
|
| + info.topLevelFunctions.add(child);
|
| + } else if (child is FieldInfo) {
|
| + info.topLevelVariables.add(child);
|
| + } else if (child is TypedefInfo) {
|
| + info.typedefs.add(child);
|
| + } else if (child != null) {
|
| + print('unexpected child of $info: $child ==> ${child.runtimeType}');
|
| + assert(false);
|
| + }
|
| });
|
|
|
| - if (children.length == 0 && !shouldKeep(element)) {
|
| - return null;
|
| - }
|
| -
|
| - return {
|
| - 'kind': 'library',
|
| - 'name': libname,
|
| - 'size': size,
|
| - 'id': id,
|
| - 'children': children,
|
| - 'canonicalUri': element.canonicalUri.toString()
|
| - };
|
| + if (info.isEmpty && !shouldKeep(element)) return null;
|
| + result.libraries.add(info);
|
| + return info;
|
| }
|
|
|
| - Map<String, dynamic> visitTypedefElement(TypedefElement element, _) {
|
| - String id = mapper._typedef.add(element);
|
| - return element.alias == null
|
| - ? null
|
| - : {
|
| - 'id': id,
|
| - 'type': element.alias.toString(),
|
| - 'kind': 'typedef',
|
| - 'name': element.name
|
| - };
|
| + TypedefInfo visitTypedefElement(TypedefElement element, _) {
|
| + if (element.alias == null) return null;
|
| + TypedefInfo info = new TypedefInfo(element.name, '${element.alias}',
|
| + _unitInfoForElement(element));
|
| + _elementToInfo[element] = info;
|
| + result.typedefs.add(info);
|
| + return info;
|
| }
|
|
|
| - Map<String, dynamic> visitFieldElement(FieldElement element, _) {
|
| - String id = mapper._field.add(element);
|
| - List<String> children = [];
|
| - StringBuffer emittedCode = compiler.dumpInfoTask.codeOf(element);
|
| -
|
| + FieldInfo visitFieldElement(FieldElement element, _) {
|
| TypeMask inferredType =
|
| compiler.typesTask.getGuaranteedTypeOfElement(element);
|
| // If a field has an empty inferred type it is never used.
|
| @@ -195,196 +105,197 @@ class ElementToJsonVisitor
|
|
|
| int size = compiler.dumpInfoTask.sizeOf(element);
|
| String code;
|
| -
|
| + StringBuffer emittedCode = compiler.dumpInfoTask.codeOf(element);
|
| if (emittedCode != null) {
|
| size += emittedCode.length;
|
| code = emittedCode.toString();
|
| }
|
|
|
| + FieldInfo info = new FieldInfo(
|
| + name: element.name,
|
| + type: '${element.type}',
|
| + inferredType: '$inferredType',
|
| + size: size,
|
| + code: code,
|
| + outputUnit: _unitInfoForElement(element));
|
| + _elementToInfo[element] = info;
|
| +
|
| + List<FunctionInfo> nestedClosures = <FunctionInfo>[];
|
| for (Element closure in element.nestedClosures) {
|
| - var childJson = this.process(closure);
|
| - if (childJson != null) {
|
| - children.add(childJson['id']);
|
| - if (childJson.containsKey('size')) {
|
| - size += childJson['size'];
|
| + Info child = this.process(closure);
|
| + if (child != null) {
|
| + ClassInfo parent = this.process(closure.enclosingElement);
|
| + if (parent != null) {
|
| + child.name = "${parent.name}.${child.name}";
|
| }
|
| + nestedClosures.add(child);
|
| + size += child.size;
|
| }
|
| }
|
| -
|
| - OutputUnit outputUnit =
|
| - compiler.deferredLoadTask.outputUnitForElement(element);
|
| -
|
| - return {
|
| - 'id': id,
|
| - 'kind': 'field',
|
| - 'type': element.type.toString(),
|
| - 'inferredType': inferredType.toString(),
|
| - 'name': element.name,
|
| - 'children': children,
|
| - 'size': size,
|
| - 'code': code,
|
| - 'outputUnit': mapper._outputUnit.add(outputUnit)
|
| - };
|
| + info.closures = nestedClosures;
|
| + result.fields.add(info);
|
| + return info;
|
| }
|
|
|
| - Map<String, dynamic> visitClassElement(ClassElement element, _) {
|
| - String id = mapper._class.add(element);
|
| - List<String> children = [];
|
| + ClassInfo visitClassElement(ClassElement element, _) {
|
| + ClassInfo classInfo = new ClassInfo(
|
| + name: element.name,
|
| + isAbstract: element.isAbstract,
|
| + outputUnit: _unitInfoForElement(element));
|
| + _elementToInfo[element] = classInfo;
|
|
|
| int size = compiler.dumpInfoTask.sizeOf(element);
|
| - JavaScriptBackend backend = compiler.backend;
|
| -
|
| - Map<String, dynamic> modifiers = { 'abstract': element.isAbstract };
|
| -
|
| element.forEachLocalMember((Element member) {
|
| - Map<String, dynamic> childJson = this.process(member);
|
| - if (childJson != null) {
|
| - children.add(childJson['id']);
|
| -
|
| - // Closures are placed in the library namespace, but
|
| - // we want to attribute them to a function, and by
|
| - // extension, this class. Process and add the sizes
|
| - // here.
|
| - if (member is MemberElement) {
|
| - for (Element closure in member.nestedClosures) {
|
| - Map<String, dynamic> child = this.process(closure);
|
| -
|
| - // Look for the parent element of this closure which should
|
| - // be a class. If it exists, set the display name to
|
| - // the name of the class + the name of the closure function.
|
| - Element parent = closure.enclosingElement;
|
| - Map<String, dynamic> processedParent = this.process(parent);
|
| - if (processedParent != null) {
|
| - child['name'] = "${processedParent['name']}.${child['name']}";
|
| - }
|
| -
|
| - if (child != null) {
|
| - size += child['size'];
|
| - }
|
| + Info info = this.process(member);
|
| + if (info == null) return;
|
| + if (info is FieldInfo) {
|
| + classInfo.fields.add(info);
|
| + } else {
|
| + assert(info is FunctionInfo);
|
| + classInfo.functions.add(info);
|
| + }
|
| +
|
| + // Closures are placed in the library namespace, but we want to attribute
|
| + // them to a function, and by extension, this class. Process and add the
|
| + // sizes here.
|
| + if (member is MemberElement) {
|
| + for (Element closure in member.nestedClosures) {
|
| + FunctionInfo closureInfo = this.process(closure);
|
| + if (closureInfo == null) continue;
|
| +
|
| + // TODO(sigmund): remove this legacy update on the name, represent the
|
| + // information explicitly in the info format.
|
| + // Look for the parent element of this closure might be the enclosing
|
| + // class or an enclosing function.
|
| + Element parent = closure.enclosingElement;
|
| + ClassInfo parentInfo = this.process(parent);
|
| + if (parentInfo != null) {
|
| + closureInfo.name = "${parentInfo.name}.${closureInfo.name}";
|
| }
|
| + size += closureInfo.size;
|
| }
|
| }
|
| });
|
|
|
| + classInfo.size = size;
|
| +
|
| // Omit element if it is not needed.
|
| + JavaScriptBackend backend = compiler.backend;
|
| if (!backend.emitter.neededClasses.contains(element) &&
|
| - children.length == 0) {
|
| + classInfo.fields.isEmpty &&
|
| + classInfo.functions.isEmpty) {
|
| return null;
|
| }
|
| -
|
| - OutputUnit outputUnit =
|
| - compiler.deferredLoadTask.outputUnitForElement(element);
|
| -
|
| - return {
|
| - 'name': element.name,
|
| - 'size': size,
|
| - 'kind': 'class',
|
| - 'modifiers': modifiers,
|
| - 'children': children,
|
| - 'id': id,
|
| - 'outputUnit': mapper._outputUnit.add(outputUnit)
|
| - };
|
| + result.classes.add(classInfo);
|
| + return classInfo;
|
| }
|
|
|
| - Map<String, dynamic> visitFunctionElement(FunctionElement element, _) {
|
| - String id = mapper._function.add(element);
|
| - String name = element.name;
|
| - String kind = "function";
|
| - List<String> children = [];
|
| - List<Map<String, dynamic>> parameters = [];
|
| - String inferredReturnType = null;
|
| - String returnType = null;
|
| - String sideEffects = null;
|
| -
|
| - StringBuffer emittedCode = compiler.dumpInfoTask.codeOf(element);
|
| + FunctionInfo visitFunctionElement(FunctionElement element, _) {
|
| int size = compiler.dumpInfoTask.sizeOf(element);
|
| + if (size == 0 && !shouldKeep(element)) return null;
|
|
|
| - Map<String, dynamic> modifiers = {
|
| - 'static': element.isStatic,
|
| - 'const': element.isConst,
|
| - 'factory': element.isFactoryConstructor,
|
| - 'external': element.isPatched
|
| - };
|
| -
|
| + String name = element.name;
|
| + int kind = FunctionInfo.TOP_LEVEL_FUNCTION_KIND;
|
| var enclosingElement = element.enclosingElement;
|
| if (enclosingElement.isField ||
|
| enclosingElement.isFunction ||
|
| element.isClosure ||
|
| enclosingElement.isConstructor) {
|
| - kind = "closure";
|
| + kind = FunctionInfo.CLOSURE_FUNCTION_KIND;
|
| name = "<unnamed>";
|
| - } else if (modifiers['static']) {
|
| - kind = 'function';
|
| + } else if (element.isStatic) {
|
| + kind = FunctionInfo.TOP_LEVEL_FUNCTION_KIND;
|
| } else if (enclosingElement.isClass) {
|
| - kind = 'method';
|
| + kind = FunctionInfo.METHOD_FUNCTION_KIND;
|
| }
|
|
|
| if (element.isConstructor) {
|
| - name == ""
|
| - ? "${element.enclosingElement.name}"
|
| - : "${element.enclosingElement.name}.${element.name}";
|
| - kind = "constructor";
|
| + name = name == ""
|
| + ? "${element.enclosingElement.name}"
|
| + : "${element.enclosingElement.name}.${element.name}";
|
| + kind = FunctionInfo.CONSTRUCTOR_FUNCTION_KIND;
|
| }
|
|
|
| + FunctionModifiers modifiers = new FunctionModifiers(
|
| + isStatic: element.isStatic,
|
| + isConst: element.isConst,
|
| + isFactory: element.isFactoryConstructor,
|
| + isExternal: element.isPatched);
|
| + StringBuffer emittedCode = compiler.dumpInfoTask.codeOf(element);
|
| + String code = emittedCode == null ? null : '$emittedCode';
|
| +
|
| + List<ParameterInfo> parameters = <ParameterInfo>[];
|
| if (element.hasFunctionSignature) {
|
| FunctionSignature signature = element.functionSignature;
|
| signature.forEachParameter((parameter) {
|
| - parameters.add({
|
| - 'name': parameter.name,
|
| - 'type': '${compiler.typesTask.getGuaranteedTypeOfElement(parameter)}',
|
| - 'declaredType': '${parameter.node.type}'
|
| - });
|
| + parameters.add(new ParameterInfo(
|
| + parameter.name,
|
| + '${compiler.typesTask.getGuaranteedTypeOfElement(parameter)}',
|
| + '${parameter.node.type}'));
|
| });
|
| }
|
|
|
| - if (element.isInstanceMember && !element.isAbstract &&
|
| + String returnType = null;
|
| + // TODO(sigmund): why all these checks?
|
| + if (element.isInstanceMember &&
|
| + !element.isAbstract &&
|
| compiler.world.allFunctions.contains(element)) {
|
| returnType = '${element.type.returnType}';
|
| }
|
| - inferredReturnType =
|
| + String inferredReturnType =
|
| '${compiler.typesTask.getGuaranteedReturnTypeOfElement(element)}';
|
| - sideEffects = compiler.world.getSideEffectsOfElement(element).toString();
|
| + String sideEffects = '${compiler.world.getSideEffectsOfElement(element)}';
|
|
|
| + int inlinedCount = compiler.dumpInfoTask.inlineCount[element];
|
| + if (inlinedCount == null) inlinedCount = 0;
|
| +
|
| + FunctionInfo info = new FunctionInfo(
|
| + name: name,
|
| + modifiers: modifiers,
|
| + size: size,
|
| + returnType: returnType,
|
| + inferredReturnType: inferredReturnType,
|
| + parameters: parameters,
|
| + sideEffects: sideEffects,
|
| + inlinedCount: inlinedCount,
|
| + code: code,
|
| + type: element.type.toString(),
|
| + outputUnit: _unitInfoForElement(element));
|
| + _elementToInfo[element] = info;
|
| +
|
| + List<FunctionInfo> nestedClosures = <FunctionInfo>[];
|
| if (element is MemberElement) {
|
| MemberElement member = element as MemberElement;
|
| for (Element closure in member.nestedClosures) {
|
| - Map<String, dynamic> child = this.process(closure);
|
| + Info child = this.process(closure);
|
| if (child != null) {
|
| - child['kind'] = 'closure';
|
| - children.add(child['id']);
|
| - size += child['size'];
|
| + BasicInfo parent = this.process(closure.enclosingElement);
|
| + if (parent != null) {
|
| + child.name = "${parent.name}.${child.name}";
|
| + }
|
| + nestedClosures.add(child);
|
| + size += child.size;
|
| }
|
| }
|
| }
|
| + info.closures = nestedClosures;
|
| + result.functions.add(info);
|
| + return info;
|
| + }
|
|
|
| - if (size == 0 && !shouldKeep(element)) {
|
| - return null;
|
| - }
|
| -
|
| - int inlinedCount = compiler.dumpInfoTask.inlineCount[element];
|
| - if (inlinedCount == null) {
|
| - inlinedCount = 0;
|
| - }
|
| -
|
| + OutputUnitInfo _unitInfoForElement(Element element) {
|
| OutputUnit outputUnit =
|
| - compiler.deferredLoadTask.outputUnitForElement(element);
|
| -
|
| - return {
|
| - 'kind': kind,
|
| - 'name': name,
|
| - 'id': id,
|
| - 'modifiers': modifiers,
|
| - 'children': children,
|
| - 'size': size,
|
| - 'returnType': returnType,
|
| - 'inferredReturnType': inferredReturnType,
|
| - 'parameters': parameters,
|
| - 'sideEffects': sideEffects,
|
| - 'inlinedCount': inlinedCount,
|
| - 'code': emittedCode == null ? null : '$emittedCode',
|
| - 'type': element.type.toString(),
|
| - 'outputUnit': mapper._outputUnit.add(outputUnit)
|
| - };
|
| + compiler.deferredLoadTask.outputUnitForElement(element);
|
| + return _outputToInfo.putIfAbsent(outputUnit, () {
|
| + // Dump-info currently only works with the full emitter. If another
|
| + // emitter is used it will fail here.
|
| + JavaScriptBackend backend = compiler.backend;
|
| + full.Emitter emitter = backend.emitter.emitter;
|
| + OutputUnitInfo info = new OutputUnitInfo(
|
| + outputUnit.name, emitter.outputBuffers[outputUnit].length);
|
| + result.outputUnits.add(info);
|
| + return info;
|
| + });
|
| }
|
| }
|
|
|
| @@ -395,12 +306,11 @@ class Selection {
|
| }
|
|
|
| class DumpInfoTask extends CompilerTask {
|
| - DumpInfoTask(Compiler compiler)
|
| - : super(compiler);
|
| + DumpInfoTask(Compiler compiler) : super(compiler);
|
|
|
| String get name => "Dump Info";
|
|
|
| - ElementToJsonVisitor infoCollector;
|
| + ElementInfoCollector infoCollector;
|
|
|
| /// The size of the generated output.
|
| int _programSize;
|
| @@ -411,7 +321,7 @@ class DumpInfoTask extends CompilerTask {
|
| final Set<jsAst.Node> _tracking = new Set<jsAst.Node>();
|
| // A mapping from Dart Elements to Javascript AST Nodes.
|
| final Map<Element, List<jsAst.Node>> _elementToNodes =
|
| - <Element, List<jsAst.Node>>{};
|
| + <Element, List<jsAst.Node>>{};
|
| // A mapping from Javascript AST Nodes to the size of their
|
| // pretty-printed contents.
|
| final Map<jsAst.Node, int> _nodeToSize = <jsAst.Node, int>{};
|
| @@ -455,14 +365,13 @@ class DumpInfoTask extends CompilerTask {
|
| if (!selectorsFromElement.containsKey(element)) {
|
| return const <Selection>[];
|
| } else {
|
| - return selectorsFromElement[element].expand(
|
| - (UniverseSelector selector) {
|
| - return compiler.world.allFunctions.filter(
|
| - selector.selector, selector.mask)
|
| - .map((element) {
|
| - return new Selection(element, selector.mask);
|
| - });
|
| + return selectorsFromElement[element].expand((UniverseSelector selector) {
|
| + return compiler.world.allFunctions
|
| + .filter(selector.selector, selector.mask)
|
| + .map((element) {
|
| + return new Selection(element, selector.mask);
|
| });
|
| + });
|
| }
|
| }
|
|
|
| @@ -481,8 +390,8 @@ class DumpInfoTask extends CompilerTask {
|
| void registerElementAst(Element element, jsAst.Node code) {
|
| if (compiler.dumpInfo) {
|
| _elementToNodes
|
| - .putIfAbsent(element, () => new List<jsAst.Node>())
|
| - .add(code);
|
| + .putIfAbsent(element, () => new List<jsAst.Node>())
|
| + .add(code);
|
| _tracking.add(code);
|
| }
|
| }
|
| @@ -501,9 +410,7 @@ class DumpInfoTask extends CompilerTask {
|
| // code was produced, return 0.
|
| int sizeOf(Element element) {
|
| if (_elementToNodes.containsKey(element)) {
|
| - return _elementToNodes[element]
|
| - .map(sizeOfNode)
|
| - .fold(0, (a, b) => a + b);
|
| + return _elementToNodes[element].map(sizeOfNode).fold(0, (a, b) => a + b);
|
| } else {
|
| return 0;
|
| }
|
| @@ -529,7 +436,7 @@ class DumpInfoTask extends CompilerTask {
|
| }
|
|
|
| void collectInfo() {
|
| - infoCollector = new ElementToJsonVisitor(compiler)..run();
|
| + infoCollector = new ElementInfoCollector(compiler)..run();
|
| }
|
|
|
| void dumpInfo() {
|
| @@ -546,103 +453,56 @@ class DumpInfoTask extends CompilerTask {
|
| });
|
| }
|
|
|
| -
|
| void dumpInfoJson(StringSink buffer) {
|
| JsonEncoder encoder = const JsonEncoder.withIndent(' ');
|
| Stopwatch stopwatch = new Stopwatch();
|
| stopwatch.start();
|
|
|
| - Map<String, List<Map<String, String>>> holding =
|
| - <String, List<Map<String, String>>>{};
|
| - for (Element fn in infoCollector.mapper.functions) {
|
| - Iterable<Selection> pulling = getRetaining(fn);
|
| + // Recursively build links to function uses
|
| + Iterable<FunctionElement> functionElements =
|
| + infoCollector._elementToInfo.keys.where((k) => k is FunctionElement);
|
| + for (FunctionElement element in functionElements) {
|
| + FunctionInfo info = infoCollector._elementToInfo[element];
|
| + Iterable<Selection> uses = getRetaining(element);
|
| // Don't bother recording an empty list of dependencies.
|
| - if (pulling.length > 0) {
|
| - String fnId = infoCollector.idOf(fn);
|
| - // Some dart2js builtin functions are not
|
| - // recorded. Don't register these.
|
| - if (fnId != null) {
|
| - holding[fnId] = pulling
|
| - .map((selection) {
|
| - return <String, String>{
|
| - "id": infoCollector.idOf(selection.selectedElement),
|
| - "mask": selection.mask.toString()
|
| - };
|
| - })
|
| - // Filter non-null ids for the same reason as above.
|
| - .where((a) => a['id'] != null)
|
| - .toList();
|
| - }
|
| + for (Selection selection in uses) {
|
| + // Don't register dart2js builtin functions that are not recorded.
|
| + Info useInfo = infoCollector._elementToInfo[selection.selectedElement];
|
| + if (useInfo == null) continue;
|
| + info.uses.add(new DependencyInfo(useInfo, '${selection.mask}'));
|
| }
|
| }
|
|
|
| // Track dependencies that come from inlining.
|
| for (Element element in inlineMap.keys) {
|
| - String keyId = infoCollector.idOf(element);
|
| - if (keyId != null) {
|
| - for (Element held in inlineMap[element]) {
|
| - String valueId = infoCollector.idOf(held);
|
| - if (valueId != null) {
|
| - holding.putIfAbsent(keyId, () => new List<Map<String, String>>())
|
| - .add(<String, String>{
|
| - "id": valueId,
|
| - "mask": "inlined"
|
| - });
|
| - }
|
| - }
|
| + FunctionInfo functionInfo = infoCollector._elementToInfo[element];
|
| + if (functionInfo == null) continue;
|
| + for (Element held in inlineMap[element]) {
|
| + Info heldInfo = infoCollector._elementToInfo[held];
|
| + if (heldInfo == null) continue;
|
| + functionInfo.uses.add(new DependencyInfo(heldInfo, 'inlined'));
|
| }
|
| }
|
|
|
| - List<Map<String, dynamic>> outputUnits =
|
| - new List<Map<String, dynamic>>();
|
| -
|
| - JavaScriptBackend backend = compiler.backend;
|
| - // Dump-info currently only works with the full emitter. If another
|
| - // emitter is used it will fail here.
|
| - full.Emitter fullEmitter = backend.emitter.emitter;
|
| -
|
| - for (OutputUnit outputUnit in
|
| - infoCollector.mapper._outputUnit._elementToId.keys) {
|
| - String id = infoCollector.mapper._outputUnit.add(outputUnit);
|
| - outputUnits.add(<String, dynamic> {
|
| - 'id': id,
|
| - 'name': outputUnit.name,
|
| - 'size': fullEmitter.outputBuffers[outputUnit].length,
|
| - });
|
| - }
|
| -
|
| - Map<String, dynamic> outJson = {
|
| - 'elements': infoCollector.toJson(),
|
| - 'holding': holding,
|
| - 'outputUnits': outputUnits,
|
| - 'dump_version': 3,
|
| - 'deferredFiles': compiler.deferredLoadTask.computeDeferredMap(),
|
| - // This increases when new information is added to the map, but the viewer
|
| - // still is compatible.
|
| - 'dump_minor_version': '2'
|
| - };
|
| -
|
| - Map<String, dynamic> generalProgramInfo = <String, dynamic> {
|
| - 'size': _programSize,
|
| - 'dart2jsVersion': infoCollector.dart2jsVersion,
|
| - 'compilationMoment': new DateTime.now().toString(),
|
| - 'compilationDuration': compiler.totalCompileTime.elapsed.toString(),
|
| - 'toJsonDuration': stopwatch.elapsedMilliseconds,
|
| - 'dumpInfoDuration': this.timing.toString(),
|
| - 'noSuchMethodEnabled': backend.enabledNoSuchMethod,
|
| - 'minified': compiler.enableMinification
|
| - };
|
| -
|
| - outJson['program'] = generalProgramInfo;
|
| -
|
| - ChunkedConversionSink<Object> sink =
|
| - encoder.startChunkedConversion(
|
| - new StringConversionSink.fromStringSink(buffer));
|
| - sink.add(outJson);
|
| - compiler.reportInfo(
|
| - NO_LOCATION_SPANNABLE,
|
| - MessageKind.GENERIC,
|
| - {'text': "View the dumped .info.json file at "
|
| - "https://dart-lang.github.io/dump-info-visualizer"});
|
| + AllInfo result = infoCollector.result;
|
| + result.deferredFiles = compiler.deferredLoadTask.computeDeferredMap();
|
| + stopwatch.stop();
|
| + result.program = new ProgramInfo(
|
| + size: _programSize,
|
| + dart2jsVersion: compiler.hasBuildId ? compiler.buildId : null,
|
| + compilationMoment: new DateTime.now(),
|
| + compilationDuration: compiler.totalCompileTime.elapsed,
|
| + toJsonDuration: stopwatch.elapsedMilliseconds,
|
| + dumpInfoDuration: this.timing,
|
| + noSuchMethodEnabled: compiler.backend.enabledNoSuchMethod,
|
| + minified: compiler.enableMinification);
|
| +
|
| + ChunkedConversionSink<Object> sink = encoder.startChunkedConversion(
|
| + new StringConversionSink.fromStringSink(buffer));
|
| + sink.add(result.toJson());
|
| + compiler.reportInfo(NO_LOCATION_SPANNABLE, MessageKind.GENERIC, {
|
| + 'text': "View the dumped .info.json file at "
|
| + "https://dart-lang.github.io/dump-info-visualizer"
|
| + });
|
| }
|
| }
|
|
|