Linter Demo Errors: 15Warnings: 43File: /home/fstrocco/Dart/dart/benchmark/compiler/lib/src/dump_info.dart // Copyright (c) 2013, 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. library dump_info; 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 'types/types.dart' show TypeMask; import 'deferred_load.dart' show OutputUnit; import 'js_backend/js_backend.dart' show JavaScriptBackend; import 'js/js.dart' as jsAst; import 'universe/universe.dart' show Selector; import 'util/util.dart' show NO_LOCATION_SPANNABLE; /// Maps objects to an id. Supports lookups in /// both directions. class IdMapper{ Map _idToElement = {}; Map _elementToId = {}; int _idCounter = 0; String name; IdMapper(this.name); Iterable 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 _library = new IdMapper('library'); IdMapper _typedef = new IdMapper('typedef'); IdMapper _field = new IdMapper('field'); IdMapper _class = new IdMapper('class'); IdMapper _function = new IdMapper('function'); IdMapper _outputUnit = new IdMapper('outputUnit'); Iterable get functions => _function.elements; // Convert this database of elements into JSON for rendering Map _toJson(ElementToJsonVisitor elementToJson) { Map json = {}; var m = [_library, _typedef, _field, _class, _function]; for (IdMapper mapper in m) { Map 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 ElementVisitor> { final GroupedIdMapper mapper = new GroupedIdMapper(); final Compiler compiler; final Map> jsonCache = {}; String dart2jsVersion; ElementToJsonVisitor(this.compiler); void run() { Backend backend = compiler.backend; dart2jsVersion = compiler.hasBuildId ? compiler.buildId : null; for (var library in compiler.libraryLoader.libraries.toList()) { library.accept(this); } } // 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. bool shouldKeep(Element element) { return compiler.dumpInfoTask.selectorsFromElement.containsKey(element) || compiler.dumpInfoTask.inlineCount.containsKey(element); } Map toJson() { return mapper._toJson(this); } // Memoization of the JSON creating process. Map process(Element element) { return jsonCache.putIfAbsent(element, () => element.accept(this)); } // 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 visitElement(Element element) { return null; } Map visitConstructorBodyElement(ConstructorBodyElement e) { return visitFunctionElement(e.constructor); } Map visitLibraryElement(LibraryElement element) { var id = mapper._library.add(element); List children = []; String libname = element.getLibraryName(); libname = libname == "" ? "" : libname; int size = compiler.dumpInfoTask.sizeOf(element); LibraryElement contentsOfLibrary = element.isPatched ? element.patch : element; contentsOfLibrary.forEachLocalMember((Element member) { Map childJson = this.process(member); if (childJson == null) return; children.add(childJson['id']); }); if (children.length == 0 && !shouldKeep(element)) { return null; } return { 'kind': 'library', 'name': libname, 'size': size, 'id': id, 'children': children }; } Map 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 }; } Map visitFieldElement(FieldElement element) { String id = mapper._field.add(element); List children = []; StringBuffer emittedCode = compiler.dumpInfoTask.codeOf(element); TypeMask inferredType = compiler.typesTask.getGuaranteedTypeOfElement(element); // If a field has an empty inferred type it is never used. if (inferredType == null || inferredType.isEmpty || element.isConst) { return null; } int size = compiler.dumpInfoTask.sizeOf(element); String code; if (emittedCode != null) { size += emittedCode.length; code = emittedCode.toString(); } 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']; } } } 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) }; } Map visitClassElement(ClassElement element) { String id = mapper._class.add(element); List children = []; int size = compiler.dumpInfoTask.sizeOf(element); JavaScriptBackend backend = compiler.backend; Map modifiers = { 'abstract': element.isAbstract }; element.forEachLocalMember((Element member) { Map 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 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 processedParent = this.process(parent); if (processedParent != null) { child['name'] = "${processedParent['name']}.${child['name']}"; } if (child != null) { size += child['size']; } } } } }); // Omit element if it is not needed. if (!backend.emitter.neededClasses.contains(element) && children.length == 0) { 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) }; } Map visitFunctionElement(FunctionElement element) { String id = mapper._function.add(element); String name = element.name; String kind = "function"; List children = []; List> parameters = []; String inferredReturnType = null; String returnType = null; String sideEffects = null; String code = ""; StringBuffer emittedCode = compiler.dumpInfoTask.codeOf(element); int size = compiler.dumpInfoTask.sizeOf(element); Map modifiers = { 'static': element.isStatic, 'const': element.isConst, 'factory': element.isFactoryConstructor, 'external': element.isPatched }; var enclosingElement = element.enclosingElement; if (enclosingElement.isField || enclosingElement.isFunction || element.isClosure || enclosingElement.isConstructor) { kind = "closure"; name = ""; } else if (modifiers['static']) { kind = 'function'; } else if (enclosingElement.isClass) { kind = 'method'; } if (element.isConstructor) { name == "" ? "${element.enclosingElement.name}" : "${element.enclosingElement.name}.${element.name}"; kind = "constructor"; } if (emittedCode != null) { FunctionSignature signature = element.functionSignature; returnType = signature.type.returnType.toString(); signature.forEachParameter((parameter) { parameters.add({ 'name': parameter.name, 'type': compiler.typesTask .getGuaranteedTypeOfElement(parameter).toString(), 'declaredType': parameter.node.type.toString() }); }); inferredReturnType = compiler.typesTask .getGuaranteedReturnTypeOfElement(element).toString(); sideEffects = compiler.world.getSideEffectsOfElement(element).toString(); code = emittedCode.toString(); } if (element is MemberElement) { MemberElement member = element as MemberElement; for (Element closure in member.nestedClosures) { Map child = this.process(closure); if (child != null) { child['kind'] = 'closure'; children.add(child['id']); size += child['size']; } } } if (size == 0 && !shouldKeep(element)) { return null; } int inlinedCount = compiler.dumpInfoTask.inlineCount[element]; if (inlinedCount == null) { inlinedCount = 0; } 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': code, 'type': element.type.toString(), 'outputUnit': mapper._outputUnit.add(outputUnit) }; } } class Selection { final Element selectedElement; final Selector selector; Selection(this.selectedElement, this.selector); } class DumpInfoTask extends CompilerTask { DumpInfoTask(Compiler compiler) : super(compiler); String name = "Dump Info"; ElementToJsonVisitor infoCollector; /// The size of the generated output. int _programSize; // A set of javascript AST nodes that we care about the size of. // This set is automatically populated when registerElementAst() // is called. final Set _tracking = new Set(); // A mapping from Dart Elements to Javascript AST Nodes. final Map> _elementToNodes = >{}; // A mapping from Javascript AST Nodes to the size of their // pretty-printed contents. final Map _nodeToSize = {}; final Map _nodeBeforeSize = {}; final Map _fieldNameToSize = {}; final Map> selectorsFromElement = {}; final Map inlineCount = {}; // A mapping from an element to a list of elements that are // inlined inside of it. final Map> inlineMap = >{}; /// Register the size of the generated output. void reportSize(int programSize) { _programSize = programSize; } void registerInlined(Element element, Element inlinedFrom) { inlineCount.putIfAbsent(element, () => 0); inlineCount[element] += 1; inlineMap.putIfAbsent(inlinedFrom, () => new List()); inlineMap[inlinedFrom].add(element); } /** * Registers that a function uses a selector in the * function body */ void elementUsesSelector(Element element, Selector selector) { if (compiler.dumpInfo) { selectorsFromElement .putIfAbsent(element, () => new Set()) .add(selector); } } /** * Returns an iterable of [Selection]s that are used by * [element]. Each [Selection] contains an element that is * used and the selector that selected the element. */ Iterable getRetaining(Element element) { if (!selectorsFromElement.containsKey(element)) { return const []; } else { return selectorsFromElement[element].expand( (selector) { return compiler.world.allFunctions.filter(selector).map((element) { return new Selection(element, selector); }); }); } } /** * A callback that can be called before a jsAst [node] is * pretty-printed. The size of the code buffer ([aftersize]) * is also passed. */ void enteringAst(jsAst.Node node, int beforeSize) { if (isTracking(node)) { _nodeBeforeSize[node] = beforeSize; } } /** * A callback that can be called after a jsAst [node] is * pretty-printed. The size of the code buffer ([aftersize]) * is also passed. */ void exitingAst(jsAst.Node node, int afterSize) { if (isTracking(node)) { int diff = afterSize - _nodeBeforeSize[node]; recordAstSize(node, diff); } } // Returns true if we care about tracking the size of // this node. bool isTracking(jsAst.Node code) { if (compiler.dumpInfo) { return _tracking.contains(code); } else { return false; } } // Registers that a javascript AST node `code` was produced by the // dart Element `element`. void registerElementAst(Element element, jsAst.Node code) { if (compiler.dumpInfo) { _elementToNodes .putIfAbsent(element, () => new List()) .add(code); _tracking.add(code); } } // Records the size of a dart AST node after it has been // pretty-printed into the output buffer. void recordAstSize(jsAst.Node code, int size) { if (compiler.dumpInfo) { //TODO: should I be incrementing here instead? _nodeToSize[code] = size; } } // Field names are treated differently by the dart compiler // so they must be recorded seperately. void recordFieldNameSize(Element element, int size) { _fieldNameToSize[element] = size; } // Returns the size of the source code that // was generated for an element. If no source // code was produced, return 0. int sizeOf(Element element) { if (_fieldNameToSize.containsKey(element)) { return _fieldNameToSize[element]; } if (_elementToNodes.containsKey(element)) { return _elementToNodes[element] .map(sizeOfNode) .fold(0, (a, b) => a + b); } else { return 0; } } int sizeOfNode(jsAst.Node node) { if (_nodeToSize.containsKey(node)) { return _nodeToSize[node]; } else { return 0; } } StringBuffer codeOf(Element element) { List code = _elementToNodes[element]; if (code == null) return null; // Concatenate rendered ASTs. StringBuffer sb = new StringBuffer(); for (jsAst.Node ast in code) { sb.writeln(jsAst.prettyPrint(ast, compiler).getText()); } return sb; } void collectInfo() { infoCollector = new ElementToJsonVisitor(compiler)..run(); } void dumpInfo() { measure(() { if (infoCollector == null) { collectInfo(); } StringBuffer jsonBuffer = new StringBuffer(); dumpInfoJson(jsonBuffer); compiler.outputProvider('', 'info.json') ..add(jsonBuffer.toString()) ..close(); }); } void dumpInfoJson(StringSink buffer) { JsonEncoder encoder = const JsonEncoder(); DateTime startToJsonTime = new DateTime.now(); Map>> holding = >>{}; for (Element fn in infoCollector.mapper.functions) { Iterable pulling = getRetaining(fn); // 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 { "id": infoCollector.idOf(selection.selectedElement), "mask": selection.selector.mask.toString() }; }) // Filter non-null ids for the same reason as above. .where((a) => a['id'] != null) .toList(); } } } // 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>()) .add({ "id": valueId, "mask": "inlined" }); } } } } List> outputUnits = new List>(); JavaScriptBackend backend = compiler.backend; for (OutputUnit outputUnit in infoCollector.mapper._outputUnit._elementToId.keys) { String id = infoCollector.mapper._outputUnit.add(outputUnit); outputUnits.add( { 'id': id, 'name': outputUnit.name, 'size': backend.emitter.oldEmitter.outputBuffers[outputUnit].length, }); } Map 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': '1' }; Duration toJsonDuration = new DateTime.now().difference(startToJsonTime); Map generalProgramInfo = { 'size': _programSize, 'dart2jsVersion': infoCollector.dart2jsVersion, 'compilationMoment': new DateTime.now().toString(), 'compilationDuration': compiler.totalCompileTime.elapsed.toString(), 'toJsonDuration': 0, 'dumpInfoDuration': this.timing.toString(), 'noSuchMethodEnabled': backend.enabledNoSuchMethod }; outJson['program'] = generalProgramInfo; ChunkedConversionSink sink = encoder.startChunkedConversion( new StringConversionSink.fromStringSink(buffer)); sink.add(outJson); compiler.reportInfo(NO_LOCATION_SPANNABLE, const MessageKind( "View the dumped .info.json file at " "https://dart-lang.github.io/dump-info-visualizer")); } }