| OLD | NEW | 
|---|
|  | (Empty) | 
| 1 // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file |  | 
| 2 // for details. All rights reserved. Use of this source code is governed by a |  | 
| 3 // BSD-style license that can be found in the LICENSE file. |  | 
| 4 |  | 
| 5 library dump_info; |  | 
| 6 |  | 
| 7 import 'dart:convert' show |  | 
| 8     HtmlEscape, |  | 
| 9     JsonEncoder, |  | 
| 10     StringConversionSink, |  | 
| 11     ChunkedConversionSink; |  | 
| 12 |  | 
| 13 import 'elements/elements.dart'; |  | 
| 14 import 'elements/visitor.dart'; |  | 
| 15 import 'dart2jslib.dart' show |  | 
| 16     Backend, |  | 
| 17     CodeBuffer, |  | 
| 18     Compiler, |  | 
| 19     CompilerTask, |  | 
| 20     MessageKind; |  | 
| 21 import 'types/types.dart' show TypeMask; |  | 
| 22 import 'deferred_load.dart' show OutputUnit; |  | 
| 23 import 'js_backend/js_backend.dart' show JavaScriptBackend; |  | 
| 24 import 'js/js.dart' as jsAst; |  | 
| 25 import 'universe/universe.dart' show Selector; |  | 
| 26 import 'util/util.dart' show NO_LOCATION_SPANNABLE; |  | 
| 27 |  | 
| 28 /// Maps objects to an id.  Supports lookups in |  | 
| 29 /// both directions. |  | 
| 30 class IdMapper<T>{ |  | 
| 31   Map<int, T> _idToElement = {}; |  | 
| 32   Map<T, int> _elementToId = {}; |  | 
| 33   int _idCounter = 0; |  | 
| 34   String name; |  | 
| 35 |  | 
| 36   IdMapper(this.name); |  | 
| 37 |  | 
| 38   Iterable<T> get elements => _elementToId.keys; |  | 
| 39 |  | 
| 40   String add(T e) { |  | 
| 41     if (_elementToId.containsKey(e)) { |  | 
| 42       return name + "/${_elementToId[e]}"; |  | 
| 43     } |  | 
| 44 |  | 
| 45     _idToElement[_idCounter] = e; |  | 
| 46     _elementToId[e] = _idCounter; |  | 
| 47     _idCounter += 1; |  | 
| 48     return name + "/${_idCounter - 1}"; |  | 
| 49   } |  | 
| 50 } |  | 
| 51 |  | 
| 52 class GroupedIdMapper { |  | 
| 53   // Mappers for specific kinds of elements. |  | 
| 54   IdMapper<LibraryElement> _library = new IdMapper('library'); |  | 
| 55   IdMapper<TypedefElement> _typedef = new IdMapper('typedef'); |  | 
| 56   IdMapper<FieldElement> _field = new IdMapper('field'); |  | 
| 57   IdMapper<ClassElement> _class = new IdMapper('class'); |  | 
| 58   IdMapper<FunctionElement> _function = new IdMapper('function'); |  | 
| 59   IdMapper<OutputUnit> _outputUnit = new IdMapper('outputUnit'); |  | 
| 60 |  | 
| 61   Iterable<Element> get functions => _function.elements; |  | 
| 62 |  | 
| 63   // Convert this database of elements into JSON for rendering |  | 
| 64   Map<String, dynamic> _toJson(ElementToJsonVisitor elementToJson) { |  | 
| 65     Map<String, dynamic> json = {}; |  | 
| 66     var m = [_library, _typedef, _field, _class, _function]; |  | 
| 67     for (IdMapper mapper in m) { |  | 
| 68       Map<String, dynamic> innerMapper = {}; |  | 
| 69       mapper._idToElement.forEach((k, v) { |  | 
| 70         // All these elements are already cached in the |  | 
| 71         // jsonCache, so this is just an access. |  | 
| 72         var elementJson = elementToJson.process(v); |  | 
| 73         if (elementJson != null) { |  | 
| 74           innerMapper["$k"] = elementJson; |  | 
| 75         } |  | 
| 76       }); |  | 
| 77       json[mapper.name] = innerMapper; |  | 
| 78     } |  | 
| 79     return json; |  | 
| 80   } |  | 
| 81 } |  | 
| 82 |  | 
| 83 class ElementToJsonVisitor extends ElementVisitor<Map<String, dynamic>> { |  | 
| 84   final GroupedIdMapper mapper = new GroupedIdMapper(); |  | 
| 85   final Compiler compiler; |  | 
| 86 |  | 
| 87   final Map<Element, Map<String, dynamic>> jsonCache = {}; |  | 
| 88 |  | 
| 89   int programSize; |  | 
| 90   String dart2jsVersion; |  | 
| 91 |  | 
| 92   ElementToJsonVisitor(this.compiler); |  | 
| 93 |  | 
| 94   void run() { |  | 
| 95     Backend backend = compiler.backend; |  | 
| 96     if (backend is JavaScriptBackend) { |  | 
| 97       // Add up the sizes of all output-buffers. |  | 
| 98       programSize = backend.emitter.oldEmitter.outputBuffers.values.fold(0, |  | 
| 99           (a, b) => a + b.length); |  | 
| 100     } else { |  | 
| 101       programSize = compiler.assembledCode.length; |  | 
| 102     } |  | 
| 103 |  | 
| 104     dart2jsVersion = compiler.hasBuildId ? compiler.buildId : null; |  | 
| 105 |  | 
| 106     for (var library in compiler.libraryLoader.libraries.toList()) { |  | 
| 107       library.accept(this); |  | 
| 108     } |  | 
| 109   } |  | 
| 110 |  | 
| 111   // If keeping the element is in question (like if a function has a size |  | 
| 112   // of zero), only keep it if it holds dependencies to elsewhere. |  | 
| 113   bool shouldKeep(Element element) { |  | 
| 114     return compiler.dumpInfoTask.selectorsFromElement.containsKey(element) |  | 
| 115         || compiler.dumpInfoTask.inlineCount.containsKey(element); |  | 
| 116   } |  | 
| 117 |  | 
| 118   Map<String, dynamic> toJson() { |  | 
| 119     return mapper._toJson(this); |  | 
| 120   } |  | 
| 121 |  | 
| 122   // Memoization of the JSON creating process. |  | 
| 123   Map<String, dynamic> process(Element element) { |  | 
| 124     return jsonCache.putIfAbsent(element, () => element.accept(this)); |  | 
| 125   } |  | 
| 126 |  | 
| 127   // Returns the id of an [element] if it has already been processed. |  | 
| 128   // If the element has not been processed, this function does not |  | 
| 129   // process it, and simply returns null instead. |  | 
| 130   String idOf(Element element) { |  | 
| 131     if (jsonCache.containsKey(element) && jsonCache[element] != null) { |  | 
| 132       return jsonCache[element]['id']; |  | 
| 133     } else { |  | 
| 134       return null; |  | 
| 135     } |  | 
| 136   } |  | 
| 137 |  | 
| 138   Map<String, dynamic> visitElement(Element element) { |  | 
| 139     return null; |  | 
| 140   } |  | 
| 141 |  | 
| 142   Map<String, dynamic> visitConstructorBodyElement(ConstructorBodyElement e) { |  | 
| 143     return visitFunctionElement(e.constructor); |  | 
| 144   } |  | 
| 145 |  | 
| 146   Map<String, dynamic> visitLibraryElement(LibraryElement element) { |  | 
| 147     var id = mapper._library.add(element); |  | 
| 148     List<String> children = <String>[]; |  | 
| 149 |  | 
| 150     String libname = element.getLibraryName(); |  | 
| 151     libname = libname == "" ? "<unnamed>" : libname; |  | 
| 152 |  | 
| 153     int size = compiler.dumpInfoTask.sizeOf(element); |  | 
| 154 |  | 
| 155     LibraryElement contentsOfLibrary = element.isPatched |  | 
| 156       ? element.patch : element; |  | 
| 157     contentsOfLibrary.forEachLocalMember((Element member) { |  | 
| 158       Map<String, dynamic> childJson = this.process(member); |  | 
| 159       if (childJson == null) return; |  | 
| 160       children.add(childJson['id']); |  | 
| 161     }); |  | 
| 162 |  | 
| 163     if (children.length == 0 && !shouldKeep(element)) { |  | 
| 164       return null; |  | 
| 165     } |  | 
| 166 |  | 
| 167     return { |  | 
| 168       'kind': 'library', |  | 
| 169       'name': libname, |  | 
| 170       'size': size, |  | 
| 171       'id': id, |  | 
| 172       'children': children |  | 
| 173     }; |  | 
| 174   } |  | 
| 175 |  | 
| 176   Map<String, dynamic> visitTypedefElement(TypedefElement element) { |  | 
| 177     String id = mapper._typedef.add(element); |  | 
| 178     return element.alias == null |  | 
| 179       ? null |  | 
| 180       : { |  | 
| 181         'id': id, |  | 
| 182         'type': element.alias.toString(), |  | 
| 183         'kind': 'typedef', |  | 
| 184         'name': element.name |  | 
| 185       }; |  | 
| 186   } |  | 
| 187 |  | 
| 188   Map<String, dynamic> visitFieldElement(FieldElement element) { |  | 
| 189     String id = mapper._field.add(element); |  | 
| 190     List<String> children = []; |  | 
| 191     StringBuffer emittedCode = compiler.dumpInfoTask.codeOf(element); |  | 
| 192 |  | 
| 193     TypeMask inferredType = |  | 
| 194         compiler.typesTask.getGuaranteedTypeOfElement(element); |  | 
| 195     // If a field has an empty inferred type it is never used. |  | 
| 196     if (inferredType == null || inferredType.isEmpty || element.isConst) { |  | 
| 197       return null; |  | 
| 198     } |  | 
| 199 |  | 
| 200     int size = compiler.dumpInfoTask.sizeOf(element); |  | 
| 201     String code; |  | 
| 202 |  | 
| 203     if (emittedCode != null) { |  | 
| 204       size += emittedCode.length; |  | 
| 205       code = emittedCode.toString(); |  | 
| 206     } |  | 
| 207 |  | 
| 208     for (Element closure in element.nestedClosures) { |  | 
| 209       var childJson = this.process(closure); |  | 
| 210       if (childJson != null) { |  | 
| 211         children.add(childJson['id']); |  | 
| 212         if (childJson.containsKey('size')) { |  | 
| 213           size += childJson['size']; |  | 
| 214         } |  | 
| 215       } |  | 
| 216     } |  | 
| 217 |  | 
| 218     OutputUnit outputUnit = |  | 
| 219         compiler.deferredLoadTask.outputUnitForElement(element); |  | 
| 220 |  | 
| 221     return { |  | 
| 222       'id': id, |  | 
| 223       'kind': 'field', |  | 
| 224       'type': element.type.toString(), |  | 
| 225       'inferredType': inferredType.toString(), |  | 
| 226       'name': element.name, |  | 
| 227       'children': children, |  | 
| 228       'size': size, |  | 
| 229       'code': code, |  | 
| 230       'outputUnit': mapper._outputUnit.add(outputUnit) |  | 
| 231     }; |  | 
| 232   } |  | 
| 233 |  | 
| 234   Map<String, dynamic> visitClassElement(ClassElement element) { |  | 
| 235     String id = mapper._class.add(element); |  | 
| 236     List<String> children = []; |  | 
| 237 |  | 
| 238     int size = compiler.dumpInfoTask.sizeOf(element); |  | 
| 239     JavaScriptBackend backend = compiler.backend; |  | 
| 240 |  | 
| 241     Map<String, dynamic> modifiers = { 'abstract': element.isAbstract }; |  | 
| 242 |  | 
| 243     element.forEachLocalMember((Element member) { |  | 
| 244       Map<String, dynamic> childJson = this.process(member); |  | 
| 245       if (childJson != null) { |  | 
| 246         children.add(childJson['id']); |  | 
| 247 |  | 
| 248         // Closures are placed in the library namespace, but |  | 
| 249         // we want to attribute them to a function, and by |  | 
| 250         // extension, this class.  Process and add the sizes |  | 
| 251         // here. |  | 
| 252         if (member is MemberElement) { |  | 
| 253           for (Element closure in member.nestedClosures) { |  | 
| 254             Map<String, dynamic> child = this.process(closure); |  | 
| 255 |  | 
| 256             // Look for the parent element of this closure which should |  | 
| 257             // be a class.  If it exists, set the display name to |  | 
| 258             // the name of the class + the name of the closure function. |  | 
| 259             Element parent = closure.enclosingElement; |  | 
| 260             Map<String, dynamic> processedParent = this.process(parent); |  | 
| 261             if (processedParent != null) { |  | 
| 262               child['name'] = "${processedParent['name']}.${child['name']}"; |  | 
| 263             } |  | 
| 264 |  | 
| 265             if (child != null) { |  | 
| 266               size += child['size']; |  | 
| 267             } |  | 
| 268           } |  | 
| 269         } |  | 
| 270       } |  | 
| 271     }); |  | 
| 272 |  | 
| 273     // Omit element if it is not needed. |  | 
| 274     if (!backend.emitter.neededClasses.contains(element) && |  | 
| 275         children.length == 0) { |  | 
| 276       return null; |  | 
| 277     } |  | 
| 278 |  | 
| 279     OutputUnit outputUnit = |  | 
| 280         compiler.deferredLoadTask.outputUnitForElement(element); |  | 
| 281 |  | 
| 282     return { |  | 
| 283       'name': element.name, |  | 
| 284       'size': size, |  | 
| 285       'kind': 'class', |  | 
| 286       'modifiers': modifiers, |  | 
| 287       'children': children, |  | 
| 288       'id': id, |  | 
| 289       'outputUnit': mapper._outputUnit.add(outputUnit) |  | 
| 290     }; |  | 
| 291   } |  | 
| 292 |  | 
| 293   Map<String, dynamic> visitFunctionElement(FunctionElement element) { |  | 
| 294     String id = mapper._function.add(element); |  | 
| 295     String name = element.name; |  | 
| 296     String kind = "function"; |  | 
| 297     List<String> children = []; |  | 
| 298     List<Map<String, dynamic>> parameters = []; |  | 
| 299     String inferredReturnType = null; |  | 
| 300     String returnType = null; |  | 
| 301     String sideEffects = null; |  | 
| 302     String code = ""; |  | 
| 303 |  | 
| 304     StringBuffer emittedCode = compiler.dumpInfoTask.codeOf(element); |  | 
| 305     int size = compiler.dumpInfoTask.sizeOf(element); |  | 
| 306 |  | 
| 307     Map<String, dynamic> modifiers = { |  | 
| 308       'static': element.isStatic, |  | 
| 309       'const': element.isConst, |  | 
| 310       'factory': element.isFactoryConstructor, |  | 
| 311       'external': element.isPatched |  | 
| 312     }; |  | 
| 313 |  | 
| 314     var enclosingElement = element.enclosingElement; |  | 
| 315     if (enclosingElement.isField || |  | 
| 316                enclosingElement.isFunction || |  | 
| 317                element.isClosure || |  | 
| 318                enclosingElement.isConstructor) { |  | 
| 319       kind = "closure"; |  | 
| 320       name = "<unnamed>"; |  | 
| 321     } else if (modifiers['static']) { |  | 
| 322       kind = 'function'; |  | 
| 323     } else if (enclosingElement.isClass) { |  | 
| 324       kind = 'method'; |  | 
| 325     } |  | 
| 326 |  | 
| 327     if (element.isConstructor) { |  | 
| 328       name == "" |  | 
| 329         ? "${element.enclosingElement.name}" |  | 
| 330         : "${element.enclosingElement.name}.${element.name}"; |  | 
| 331       kind = "constructor"; |  | 
| 332     } |  | 
| 333 |  | 
| 334     if (emittedCode != null) { |  | 
| 335       FunctionSignature signature = element.functionSignature; |  | 
| 336       returnType = signature.type.returnType.toString(); |  | 
| 337       signature.forEachParameter((parameter) { |  | 
| 338         parameters.add({ |  | 
| 339           'name': parameter.name, |  | 
| 340           'type': compiler.typesTask |  | 
| 341             .getGuaranteedTypeOfElement(parameter).toString(), |  | 
| 342           'declaredType': parameter.node.type.toString() |  | 
| 343         }); |  | 
| 344       }); |  | 
| 345       inferredReturnType = compiler.typesTask |  | 
| 346         .getGuaranteedReturnTypeOfElement(element).toString(); |  | 
| 347       sideEffects = compiler.world.getSideEffectsOfElement(element).toString(); |  | 
| 348       code = emittedCode.toString(); |  | 
| 349     } |  | 
| 350 |  | 
| 351     if (element is MemberElement) { |  | 
| 352       MemberElement member = element as MemberElement; |  | 
| 353       for (Element closure in member.nestedClosures) { |  | 
| 354         Map<String, dynamic> child = this.process(closure); |  | 
| 355         if (child != null) { |  | 
| 356           child['kind'] = 'closure'; |  | 
| 357           children.add(child['id']); |  | 
| 358           size += child['size']; |  | 
| 359         } |  | 
| 360       } |  | 
| 361     } |  | 
| 362 |  | 
| 363     if (size == 0 && !shouldKeep(element)) { |  | 
| 364       return null; |  | 
| 365     } |  | 
| 366 |  | 
| 367     int inlinedCount = compiler.dumpInfoTask.inlineCount[element]; |  | 
| 368     if (inlinedCount == null) { |  | 
| 369       inlinedCount = 0; |  | 
| 370     } |  | 
| 371 |  | 
| 372     OutputUnit outputUnit = |  | 
| 373         compiler.deferredLoadTask.outputUnitForElement(element); |  | 
| 374 |  | 
| 375     return { |  | 
| 376       'kind': kind, |  | 
| 377       'name': name, |  | 
| 378       'id': id, |  | 
| 379       'modifiers': modifiers, |  | 
| 380       'children': children, |  | 
| 381       'size': size, |  | 
| 382       'returnType': returnType, |  | 
| 383       'inferredReturnType': inferredReturnType, |  | 
| 384       'parameters': parameters, |  | 
| 385       'sideEffects': sideEffects, |  | 
| 386       'inlinedCount': inlinedCount, |  | 
| 387       'code': code, |  | 
| 388       'type': element.type.toString(), |  | 
| 389       'outputUnit': mapper._outputUnit.add(outputUnit) |  | 
| 390     }; |  | 
| 391   } |  | 
| 392 } |  | 
| 393 |  | 
| 394 class Selection { |  | 
| 395   final Element selectedElement; |  | 
| 396   final Selector selector; |  | 
| 397   Selection(this.selectedElement, this.selector); |  | 
| 398 } |  | 
| 399 |  | 
| 400 class DumpInfoTask extends CompilerTask { |  | 
| 401   DumpInfoTask(Compiler compiler) |  | 
| 402       : super(compiler); |  | 
| 403 |  | 
| 404   String name = "Dump Info"; |  | 
| 405 |  | 
| 406   ElementToJsonVisitor infoCollector; |  | 
| 407 |  | 
| 408   // A set of javascript AST nodes that we care about the size of. |  | 
| 409   // This set is automatically populated when registerElementAst() |  | 
| 410   // is called. |  | 
| 411   final Set<jsAst.Node> _tracking = new Set<jsAst.Node>(); |  | 
| 412   // A mapping from Dart Elements to Javascript AST Nodes. |  | 
| 413   final Map<Element, List<jsAst.Node>> _elementToNodes = |  | 
| 414     <Element, List<jsAst.Node>>{}; |  | 
| 415   // A mapping from Javascript AST Nodes to the size of their |  | 
| 416   // pretty-printed contents. |  | 
| 417   final Map<jsAst.Node, int> _nodeToSize = <jsAst.Node, int>{}; |  | 
| 418   final Map<jsAst.Node, int> _nodeBeforeSize = <jsAst.Node, int>{}; |  | 
| 419   final Map<Element, int> _fieldNameToSize = <Element, int>{}; |  | 
| 420 |  | 
| 421   final Map<Element, Set<Selector>> selectorsFromElement = {}; |  | 
| 422   final Map<Element, int> inlineCount = <Element, int>{}; |  | 
| 423   // A mapping from an element to a list of elements that are |  | 
| 424   // inlined inside of it. |  | 
| 425   final Map<Element, List<Element>> inlineMap = <Element, List<Element>>{}; |  | 
| 426 |  | 
| 427   void registerInlined(Element element, Element inlinedFrom) { |  | 
| 428     inlineCount.putIfAbsent(element, () => 0); |  | 
| 429     inlineCount[element] += 1; |  | 
| 430     inlineMap.putIfAbsent(inlinedFrom, () => new List<Element>()); |  | 
| 431     inlineMap[inlinedFrom].add(element); |  | 
| 432   } |  | 
| 433 |  | 
| 434   /** |  | 
| 435    * Registers that a function uses a selector in the |  | 
| 436    * function body |  | 
| 437    */ |  | 
| 438   void elementUsesSelector(Element element, Selector selector) { |  | 
| 439     if (compiler.dumpInfo) { |  | 
| 440       selectorsFromElement |  | 
| 441           .putIfAbsent(element, () => new Set<Selector>()) |  | 
| 442           .add(selector); |  | 
| 443     } |  | 
| 444   } |  | 
| 445 |  | 
| 446   /** |  | 
| 447    * Returns an iterable of [Selection]s that are used by |  | 
| 448    * [element].  Each [Selection] contains an element that is |  | 
| 449    * used and the selector that selected the element. |  | 
| 450    */ |  | 
| 451   Iterable<Selection> getRetaining(Element element) { |  | 
| 452     if (!selectorsFromElement.containsKey(element)) { |  | 
| 453       return const <Selection>[]; |  | 
| 454     } else { |  | 
| 455       return selectorsFromElement[element].expand( |  | 
| 456         (selector) { |  | 
| 457           return compiler.world.allFunctions.filter(selector).map((element) { |  | 
| 458             return new Selection(element, selector); |  | 
| 459           }); |  | 
| 460         }); |  | 
| 461     } |  | 
| 462   } |  | 
| 463 |  | 
| 464   /** |  | 
| 465    * A callback that can be called before a jsAst [node] is |  | 
| 466    * pretty-printed. The size of the code buffer ([aftersize]) |  | 
| 467    * is also passed. |  | 
| 468    */ |  | 
| 469   void enteringAst(jsAst.Node node, int beforeSize) { |  | 
| 470     if (isTracking(node)) { |  | 
| 471       _nodeBeforeSize[node] = beforeSize; |  | 
| 472     } |  | 
| 473   } |  | 
| 474 |  | 
| 475   /** |  | 
| 476    * A callback that can be called after a jsAst [node] is |  | 
| 477    * pretty-printed. The size of the code buffer ([aftersize]) |  | 
| 478    * is also passed. |  | 
| 479    */ |  | 
| 480   void exitingAst(jsAst.Node node, int afterSize) { |  | 
| 481     if (isTracking(node)) { |  | 
| 482       int diff = afterSize - _nodeBeforeSize[node]; |  | 
| 483       recordAstSize(node, diff); |  | 
| 484     } |  | 
| 485   } |  | 
| 486 |  | 
| 487   // Returns true if we care about tracking the size of |  | 
| 488   // this node. |  | 
| 489   bool isTracking(jsAst.Node code) { |  | 
| 490     if (compiler.dumpInfo) { |  | 
| 491       return _tracking.contains(code); |  | 
| 492     } else { |  | 
| 493       return false; |  | 
| 494     } |  | 
| 495   } |  | 
| 496 |  | 
| 497   // Registers that a javascript AST node `code` was produced by the |  | 
| 498   // dart Element `element`. |  | 
| 499   void registerElementAst(Element element, jsAst.Node code) { |  | 
| 500     if (compiler.dumpInfo) { |  | 
| 501       _elementToNodes |  | 
| 502         .putIfAbsent(element, () => new List<jsAst.Node>()) |  | 
| 503         .add(code); |  | 
| 504       _tracking.add(code); |  | 
| 505     } |  | 
| 506   } |  | 
| 507 |  | 
| 508   // Records the size of a dart AST node after it has been |  | 
| 509   // pretty-printed into the output buffer. |  | 
| 510   void recordAstSize(jsAst.Node code, int size) { |  | 
| 511     if (compiler.dumpInfo) { |  | 
| 512       //TODO: should I be incrementing here instead? |  | 
| 513       _nodeToSize[code] = size; |  | 
| 514     } |  | 
| 515   } |  | 
| 516 |  | 
| 517   // Field names are treated differently by the dart compiler |  | 
| 518   // so they must be recorded seperately. |  | 
| 519   void recordFieldNameSize(Element element, int size) { |  | 
| 520     _fieldNameToSize[element] = size; |  | 
| 521   } |  | 
| 522 |  | 
| 523   // Returns the size of the source code that |  | 
| 524   // was generated for an element.  If no source |  | 
| 525   // code was produced, return 0. |  | 
| 526   int sizeOf(Element element) { |  | 
| 527     if (_fieldNameToSize.containsKey(element)) { |  | 
| 528       return _fieldNameToSize[element]; |  | 
| 529     } |  | 
| 530     if (_elementToNodes.containsKey(element)) { |  | 
| 531       return _elementToNodes[element] |  | 
| 532         .map(sizeOfNode) |  | 
| 533         .fold(0, (a, b) => a + b); |  | 
| 534     } else { |  | 
| 535       return 0; |  | 
| 536     } |  | 
| 537   } |  | 
| 538 |  | 
| 539   int sizeOfNode(jsAst.Node node) { |  | 
| 540     if (_nodeToSize.containsKey(node)) { |  | 
| 541       return _nodeToSize[node]; |  | 
| 542     } else { |  | 
| 543       return 0; |  | 
| 544     } |  | 
| 545   } |  | 
| 546 |  | 
| 547   StringBuffer codeOf(Element element) { |  | 
| 548     List<jsAst.Node> code = _elementToNodes[element]; |  | 
| 549     if (code == null) return null; |  | 
| 550     // Concatenate rendered ASTs. |  | 
| 551     StringBuffer sb = new StringBuffer(); |  | 
| 552     for (jsAst.Node ast in code) { |  | 
| 553       sb.writeln(jsAst.prettyPrint(ast, compiler).getText()); |  | 
| 554     } |  | 
| 555     return sb; |  | 
| 556   } |  | 
| 557 |  | 
| 558   void collectInfo() { |  | 
| 559     infoCollector = new ElementToJsonVisitor(compiler)..run(); |  | 
| 560   } |  | 
| 561 |  | 
| 562   void dumpInfo() { |  | 
| 563     measure(() { |  | 
| 564       if (infoCollector == null) { |  | 
| 565         collectInfo(); |  | 
| 566       } |  | 
| 567 |  | 
| 568       StringBuffer jsonBuffer = new StringBuffer(); |  | 
| 569       dumpInfoJson(jsonBuffer); |  | 
| 570       compiler.outputProvider('', 'info.json') |  | 
| 571         ..add(jsonBuffer.toString()) |  | 
| 572         ..close(); |  | 
| 573     }); |  | 
| 574   } |  | 
| 575 |  | 
| 576 |  | 
| 577   void dumpInfoJson(StringSink buffer) { |  | 
| 578     JsonEncoder encoder = const JsonEncoder(); |  | 
| 579     DateTime startToJsonTime = new DateTime.now(); |  | 
| 580 |  | 
| 581     Map<String, List<Map<String, String>>> holding = |  | 
| 582         <String, List<Map<String, String>>>{}; |  | 
| 583     for (Element fn in infoCollector.mapper.functions) { |  | 
| 584       Iterable<Selection> pulling = getRetaining(fn); |  | 
| 585       // Don't bother recording an empty list of dependencies. |  | 
| 586       if (pulling.length > 0) { |  | 
| 587         String fnId = infoCollector.idOf(fn); |  | 
| 588         // Some dart2js builtin functions are not |  | 
| 589         // recorded.  Don't register these. |  | 
| 590         if (fnId != null) { |  | 
| 591           holding[fnId] = pulling |  | 
| 592             .map((selection) { |  | 
| 593               return <String, String>{ |  | 
| 594                 "id": infoCollector.idOf(selection.selectedElement), |  | 
| 595                 "mask": selection.selector.mask.toString() |  | 
| 596               }; |  | 
| 597             }) |  | 
| 598             // Filter non-null ids for the same reason as above. |  | 
| 599             .where((a) => a['id'] != null) |  | 
| 600             .toList(); |  | 
| 601         } |  | 
| 602       } |  | 
| 603     } |  | 
| 604 |  | 
| 605     // Track dependencies that come from inlining. |  | 
| 606     for (Element element in inlineMap.keys) { |  | 
| 607       String keyId = infoCollector.idOf(element); |  | 
| 608       if (keyId != null) { |  | 
| 609         for (Element held in inlineMap[element]) { |  | 
| 610           String valueId = infoCollector.idOf(held); |  | 
| 611           if (valueId != null) { |  | 
| 612             holding.putIfAbsent(keyId, () => new List<Map<String, String>>()) |  | 
| 613               .add(<String, String>{ |  | 
| 614                 "id": valueId, |  | 
| 615                 "mask": "inlined" |  | 
| 616               }); |  | 
| 617           } |  | 
| 618         } |  | 
| 619       } |  | 
| 620     } |  | 
| 621 |  | 
| 622     List<Map<String, dynamic>> outputUnits = |  | 
| 623         new List<Map<String, dynamic>>(); |  | 
| 624 |  | 
| 625     JavaScriptBackend backend = compiler.backend; |  | 
| 626 |  | 
| 627     for (OutputUnit outputUnit in |  | 
| 628         infoCollector.mapper._outputUnit._elementToId.keys) { |  | 
| 629       String id = infoCollector.mapper._outputUnit.add(outputUnit); |  | 
| 630       outputUnits.add(<String, dynamic> { |  | 
| 631         'id': id, |  | 
| 632         'name': outputUnit.name, |  | 
| 633         'size': backend.emitter.oldEmitter.outputBuffers[outputUnit].length, |  | 
| 634       }); |  | 
| 635     } |  | 
| 636 |  | 
| 637     Map<String, dynamic> outJson = { |  | 
| 638       'elements': infoCollector.toJson(), |  | 
| 639       'holding': holding, |  | 
| 640       'outputUnits': outputUnits, |  | 
| 641       'dump_version': 3, |  | 
| 642     }; |  | 
| 643 |  | 
| 644     Duration toJsonDuration = new DateTime.now().difference(startToJsonTime); |  | 
| 645 |  | 
| 646     Map<String, dynamic> generalProgramInfo = <String, dynamic> { |  | 
| 647       'size': infoCollector.programSize, |  | 
| 648       'dart2jsVersion': infoCollector.dart2jsVersion, |  | 
| 649       'compilationMoment': new DateTime.now().toString(), |  | 
| 650       'compilationDuration': compiler.totalCompileTime.elapsed.toString(), |  | 
| 651       'toJsonDuration': 0, |  | 
| 652       'dumpInfoDuration': this.timing.toString(), |  | 
| 653       'noSuchMethodEnabled': compiler.enabledNoSuchMethod |  | 
| 654     }; |  | 
| 655 |  | 
| 656     outJson['program'] = generalProgramInfo; |  | 
| 657 |  | 
| 658     ChunkedConversionSink<Object> sink = |  | 
| 659       encoder.startChunkedConversion( |  | 
| 660           new StringConversionSink.fromStringSink(buffer)); |  | 
| 661     sink.add(outJson); |  | 
| 662     compiler.reportInfo(NO_LOCATION_SPANNABLE, |  | 
| 663         const MessageKind( |  | 
| 664             "View the dumped .info.json file at " |  | 
| 665             "https://dart-lang.github.io/dump-info-visualizer")); |  | 
| 666   } |  | 
| 667 } |  | 
| OLD | NEW | 
|---|