| 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 |