1 /* 2 * Copyright (C) 2012 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 /** 32 * @constructor 33 * @extends {WebInspector.HeapSnapshot} 34 * @param {!Object} profile 35 * @param {!WebInspector.HeapSnapshotProgress} progress 36 * @param {boolean} showHiddenData 37 */ 38 WebInspector.JSHeapSnapshot = function(profile, progress, showHiddenData) 39 { 40 this._nodeFlags = { // bit flags 41 canBeQueried: 1, 42 detachedDOMTreeNode: 2, 43 pageObject: 4, // The idea is to track separately the objects owned by the page and the objects owned by debugger. 44 45 visitedMarkerMask: 0x0ffff, 46 visitedMarker: 0x10000 47 }; 48 this._lazyStringCache = { }; 49 this._showHiddenData = showHiddenData; 50 WebInspector.HeapSnapshot.call(this, profile, progress); 51 } 52 53 WebInspector.JSHeapSnapshot.prototype = { 54 /** 55 * @param {number} nodeIndex 56 * @return {!WebInspector.JSHeapSnapshotNode} 57 */ 58 createNode: function(nodeIndex) 59 { 60 return new WebInspector.JSHeapSnapshotNode(this, nodeIndex); 61 }, 62 63 /** 64 * @override 65 * @param {number} edgeIndex 66 * @return {!WebInspector.JSHeapSnapshotEdge} 67 */ 68 createEdge: function(edgeIndex) 69 { 70 return new WebInspector.JSHeapSnapshotEdge(this, edgeIndex); 71 }, 72 73 /** 74 * @override 75 * @param {number} retainerIndex 76 * @return {!WebInspector.JSHeapSnapshotRetainerEdge} 77 */ 78 createRetainingEdge: function(retainerIndex) 79 { 80 return new WebInspector.JSHeapSnapshotRetainerEdge(this, retainerIndex); 81 }, 82 83 /** 84 * @override 85 * @return {?function(!WebInspector.JSHeapSnapshotNode):boolean} 86 */ 87 classNodesFilter: function() 88 { 89 var mapAndFlag = this.userObjectsMapAndFlag(); 90 if (!mapAndFlag) 91 return null; 92 var map = mapAndFlag.map; 93 var flag = mapAndFlag.flag; 94 /** 95 * @param {!WebInspector.JSHeapSnapshotNode} node 96 * @return {boolean} 97 */ 98 function filter(node) 99 { 100 return !!(map[node.ordinal()] & flag); 101 } 102 return filter; 103 }, 104 105 /** 106 * @return {function(!WebInspector.HeapSnapshotEdge):boolean} 107 */ 108 containmentEdgesFilter: function() 109 { 110 var showHiddenData = this._showHiddenData; 111 function filter(edge) 112 { 113 if (edge.isInvisible()) 114 return false; 115 if (showHiddenData) 116 return true; 117 return !edge.isHidden() && !edge.node().isHidden(); 118 } 119 return filter; 120 }, 121 122 /** 123 * @return {function(!WebInspector.HeapSnapshotEdge):boolean} 124 */ 125 retainingEdgesFilter: function() 126 { 127 var containmentEdgesFilter = this.containmentEdgesFilter(); 128 function filter(edge) 129 { 130 return containmentEdgesFilter(edge) && !edge.node().isRoot() && !edge.isWeak(); 131 } 132 return filter; 133 }, 134 135 _calculateFlags: function() 136 { 137 this._flags = new Uint32Array(this.nodeCount); 138 this._markDetachedDOMTreeNodes(); 139 this._markQueriableHeapObjects(); 140 this._markPageOwnedNodes(); 141 }, 142 143 calculateDistances: function() 144 { 145 /** 146 * @param {!WebInspector.HeapSnapshotNode} node 147 * @param {!WebInspector.HeapSnapshotEdge} edge 148 * @return {boolean} 149 */ 150 function filter(node, edge) 151 { 152 if (node.isHidden()) 153 return edge.name() !== "sloppy_function_map" || node.rawName() !== "system / NativeContext"; 154 if (node.isArray()) { 155 // DescriptorArrays are fixed arrays used to hold instance descriptors. 156 // The format of the these objects is: 157 // [0]: Number of descriptors 158 // [1]: Either Smi(0) if uninitialized, or a pointer to small fixed array: 159 // [0]: pointer to fixed array with enum cache 160 // [1]: either Smi(0) or pointer to fixed array with indices 161 // [i*3+2]: i-th key 162 // [i*3+3]: i-th type 163 // [i*3+4]: i-th descriptor 164 // As long as maps may share descriptor arrays some of the descriptor 165 // links may not be valid for all the maps. We just skip 166 // all the descriptor links when calculating distances. 167 // For more details see http://crbug.com/413608 168 if (node.rawName() !== "(map descriptors)") 169 return true; 170 var index = edge.name(); 171 return index < 2 || (index % 3) !== 1; 172 } 173 return true; 174 } 175 WebInspector.HeapSnapshot.prototype.calculateDistances.call(this, filter); 176 }, 177 178 /** 179 * @param {!WebInspector.HeapSnapshotNode} node 180 * @return {!boolean} 181 */ 182 _isUserRoot: function(node) 183 { 184 return node.isUserRoot() || node.isDocumentDOMTreesRoot(); 185 }, 186 187 /** 188 * @param {function(!WebInspector.HeapSnapshotNode)} action 189 * @param {boolean=} userRootsOnly 190 */ 191 forEachRoot: function(action, userRootsOnly) 192 { 193 /** 194 * @param {!WebInspector.HeapSnapshotNode} node 195 * @param {string} name 196 * @return {?WebInspector.HeapSnapshotNode} 197 */ 198 function getChildNodeByName(node, name) 199 { 200 for (var iter = node.edges(); iter.hasNext(); iter.next()) { 201 var child = iter.edge.node(); 202 if (child.name() === name) 203 return child; 204 } 205 return null; 206 } 207 208 var visitedNodes = {}; 209 /** 210 * @param {!WebInspector.HeapSnapshotNode} node 211 */ 212 function doAction(node) 213 { 214 var ordinal = node.ordinal(); 215 if (!visitedNodes[ordinal]) { 216 action(node); 217 visitedNodes[ordinal] = true; 218 } 219 } 220 221 var gcRoots = getChildNodeByName(this.rootNode(), "(GC roots)"); 222 if (!gcRoots) 223 return; 224 225 if (userRootsOnly) { 226 for (var iter = this.rootNode().edges(); iter.hasNext(); iter.next()) { 227 var node = iter.edge.node(); 228 if (this._isUserRoot(node)) 229 doAction(node); 230 } 231 } else { 232 for (var iter = gcRoots.edges(); iter.hasNext(); iter.next()) { 233 var subRoot = iter.edge.node(); 234 for (var iter2 = subRoot.edges(); iter2.hasNext(); iter2.next()) 235 doAction(iter2.edge.node()); 236 doAction(subRoot); 237 } 238 for (var iter = this.rootNode().edges(); iter.hasNext(); iter.next()) 239 doAction(iter.edge.node()) 240 } 241 }, 242 243 /** 244 * @return {?{map: !Uint32Array, flag: number}} 245 */ 246 userObjectsMapAndFlag: function() 247 { 248 return this._showHiddenData ? null : { 249 map: this._flags, 250 flag: this._nodeFlags.pageObject 251 }; 252 }, 253 254 _flagsOfNode: function(node) 255 { 256 return this._flags[node.nodeIndex / this._nodeFieldCount]; 257 }, 258 259 _markDetachedDOMTreeNodes: function() 260 { 261 var flag = this._nodeFlags.detachedDOMTreeNode; 262 var detachedDOMTreesRoot; 263 for (var iter = this.rootNode().edges(); iter.hasNext(); iter.next()) { 264 var node = iter.edge.node(); 265 if (node.name() === "(Detached DOM trees)") { 266 detachedDOMTreesRoot = node; 267 break; 268 } 269 } 270 271 if (!detachedDOMTreesRoot) 272 return; 273 274 var detachedDOMTreeRE = /^Detached DOM tree/; 275 for (var iter = detachedDOMTreesRoot.edges(); iter.hasNext(); iter.next()) { 276 var node = iter.edge.node(); 277 if (detachedDOMTreeRE.test(node.className())) { 278 for (var edgesIter = node.edges(); edgesIter.hasNext(); edgesIter.next()) 279 this._flags[edgesIter.edge.node().nodeIndex / this._nodeFieldCount] |= flag; 280 } 281 } 282 }, 283 284 _markQueriableHeapObjects: function() 285 { 286 // Allow runtime properties query for objects accessible from Window objects 287 // via regular properties, and for DOM wrappers. Trying to access random objects 288 // can cause a crash due to insonsistent state of internal properties of wrappers. 289 var flag = this._nodeFlags.canBeQueried; 290 var hiddenEdgeType = this._edgeHiddenType; 291 var internalEdgeType = this._edgeInternalType; 292 var invisibleEdgeType = this._edgeInvisibleType; 293 var weakEdgeType = this._edgeWeakType; 294 var edgeToNodeOffset = this._edgeToNodeOffset; 295 var edgeTypeOffset = this._edgeTypeOffset; 296 var edgeFieldsCount = this._edgeFieldsCount; 297 var containmentEdges = this.containmentEdges; 298 var nodes = this.nodes; 299 var nodeCount = this.nodeCount; 300 var nodeFieldCount = this._nodeFieldCount; 301 var firstEdgeIndexes = this._firstEdgeIndexes; 302 303 var flags = this._flags; 304 var list = []; 305 306 for (var iter = this.rootNode().edges(); iter.hasNext(); iter.next()) { 307 if (iter.edge.node().isUserRoot()) 308 list.push(iter.edge.node().nodeIndex / nodeFieldCount); 309 } 310 311 while (list.length) { 312 var nodeOrdinal = list.pop(); 313 if (flags[nodeOrdinal] & flag) 314 continue; 315 flags[nodeOrdinal] |= flag; 316 var beginEdgeIndex = firstEdgeIndexes[nodeOrdinal]; 317 var endEdgeIndex = firstEdgeIndexes[nodeOrdinal + 1]; 318 for (var edgeIndex = beginEdgeIndex; edgeIndex < endEdgeIndex; edgeIndex += edgeFieldsCount) { 319 var childNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset]; 320 var childNodeOrdinal = childNodeIndex / nodeFieldCount; 321 if (flags[childNodeOrdinal] & flag) 322 continue; 323 var type = containmentEdges[edgeIndex + edgeTypeOffset]; 324 if (type === hiddenEdgeType || type === invisibleEdgeType || type === internalEdgeType || type === weakEdgeType) 325 continue; 326 list.push(childNodeOrdinal); 327 } 328 } 329 }, 330 331 _markPageOwnedNodes: function() 332 { 333 var edgeShortcutType = this._edgeShortcutType; 334 var edgeElementType = this._edgeElementType; 335 var edgeToNodeOffset = this._edgeToNodeOffset; 336 var edgeTypeOffset = this._edgeTypeOffset; 337 var edgeFieldsCount = this._edgeFieldsCount; 338 var edgeWeakType = this._edgeWeakType; 339 var firstEdgeIndexes = this._firstEdgeIndexes; 340 var containmentEdges = this.containmentEdges; 341 var containmentEdgesLength = containmentEdges.length; 342 var nodes = this.nodes; 343 var nodeFieldCount = this._nodeFieldCount; 344 var nodesCount = this.nodeCount; 345 346 var flags = this._flags; 347 var flag = this._nodeFlags.pageObject; 348 var visitedMarker = this._nodeFlags.visitedMarker; 349 var visitedMarkerMask = this._nodeFlags.visitedMarkerMask; 350 var markerAndFlag = visitedMarker | flag; 351 352 var nodesToVisit = new Uint32Array(nodesCount); 353 var nodesToVisitLength = 0; 354 355 var rootNodeOrdinal = this._rootNodeIndex / nodeFieldCount; 356 var node = this.rootNode(); 357 for (var edgeIndex = firstEdgeIndexes[rootNodeOrdinal], endEdgeIndex = firstEdgeIndexes[rootNodeOrdinal + 1]; 358 edgeIndex < endEdgeIndex; 359 edgeIndex += edgeFieldsCount) { 360 var edgeType = containmentEdges[edgeIndex + edgeTypeOffset]; 361 var nodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset]; 362 if (edgeType === edgeElementType) { 363 node.nodeIndex = nodeIndex; 364 if (!node.isDocumentDOMTreesRoot()) 365 continue; 366 } else if (edgeType !== edgeShortcutType) 367 continue; 368 var nodeOrdinal = nodeIndex / nodeFieldCount; 369 nodesToVisit[nodesToVisitLength++] = nodeOrdinal; 370 flags[nodeOrdinal] |= visitedMarker; 371 } 372 373 while (nodesToVisitLength) { 374 var nodeOrdinal = nodesToVisit[--nodesToVisitLength]; 375 flags[nodeOrdinal] |= flag; 376 flags[nodeOrdinal] &= visitedMarkerMask; 377 var beginEdgeIndex = firstEdgeIndexes[nodeOrdinal]; 378 var endEdgeIndex = firstEdgeIndexes[nodeOrdinal + 1]; 379 for (var edgeIndex = beginEdgeIndex; edgeIndex < endEdgeIndex; edgeIndex += edgeFieldsCount) { 380 var childNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset]; 381 var childNodeOrdinal = childNodeIndex / nodeFieldCount; 382 if (flags[childNodeOrdinal] & markerAndFlag) 383 continue; 384 var type = containmentEdges[edgeIndex + edgeTypeOffset]; 385 if (type === edgeWeakType) 386 continue; 387 nodesToVisit[nodesToVisitLength++] = childNodeOrdinal; 388 flags[childNodeOrdinal] |= visitedMarker; 389 } 390 } 391 }, 392 393 _calculateStatistics: function() 394 { 395 var nodeFieldCount = this._nodeFieldCount; 396 var nodes = this.nodes; 397 var nodesLength = nodes.length; 398 var nodeTypeOffset = this._nodeTypeOffset; 399 var nodeSizeOffset = this._nodeSelfSizeOffset;; 400 var nodeNativeType = this._nodeNativeType; 401 var nodeCodeType = this._nodeCodeType; 402 var nodeConsStringType = this._nodeConsStringType; 403 var nodeSlicedStringType = this._nodeSlicedStringType; 404 var sizeNative = 0; 405 var sizeCode = 0; 406 var sizeStrings = 0; 407 var sizeJSArrays = 0; 408 var node = this.rootNode(); 409 for (var nodeIndex = 0; nodeIndex < nodesLength; nodeIndex += nodeFieldCount) { 410 node.nodeIndex = nodeIndex; 411 var nodeType = nodes[nodeIndex + nodeTypeOffset]; 412 var nodeSize = nodes[nodeIndex + nodeSizeOffset]; 413 if (nodeType === nodeNativeType) 414 sizeNative += nodeSize; 415 else if (nodeType === nodeCodeType) 416 sizeCode += nodeSize; 417 else if (nodeType === nodeConsStringType || nodeType === nodeSlicedStringType || node.type() === "string") 418 sizeStrings += nodeSize; 419 else if (node.name() === "Array") 420 sizeJSArrays += this._calculateArraySize(node); 421 } 422 this._statistics = new WebInspector.HeapSnapshotCommon.Statistics(); 423 this._statistics.total = this.totalSize; 424 this._statistics.v8heap = this.totalSize - sizeNative; 425 this._statistics.native = sizeNative; 426 this._statistics.code = sizeCode; 427 this._statistics.jsArrays = sizeJSArrays; 428 this._statistics.strings = sizeStrings; 429 }, 430 431 /** 432 * @param {!WebInspector.HeapSnapshotNode} node 433 * @return {number} 434 */ 435 _calculateArraySize: function(node) 436 { 437 var size = node.selfSize(); 438 var beginEdgeIndex = node.edgeIndexesStart(); 439 var endEdgeIndex = node.edgeIndexesEnd(); 440 var containmentEdges = this.containmentEdges; 441 var strings = this.strings; 442 var edgeToNodeOffset = this._edgeToNodeOffset; 443 var edgeTypeOffset = this._edgeTypeOffset; 444 var edgeNameOffset = this._edgeNameOffset; 445 var edgeFieldsCount = this._edgeFieldsCount; 446 var edgeInternalType = this._edgeInternalType; 447 for (var edgeIndex = beginEdgeIndex; edgeIndex < endEdgeIndex; edgeIndex += edgeFieldsCount) { 448 var edgeType = containmentEdges[edgeIndex + edgeTypeOffset]; 449 if (edgeType !== edgeInternalType) 450 continue; 451 var edgeName = strings[containmentEdges[edgeIndex + edgeNameOffset]]; 452 if (edgeName !== "elements") 453 continue; 454 var elementsNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset]; 455 node.nodeIndex = elementsNodeIndex; 456 if (node.retainersCount() === 1) 457 size += node.selfSize(); 458 break; 459 } 460 return size; 461 }, 462 463 /** 464 * @return {!WebInspector.HeapSnapshotCommon.Statistics} 465 */ 466 getStatistics: function() 467 { 468 return this._statistics; 469 }, 470 471 __proto__: WebInspector.HeapSnapshot.prototype 472 }; 473 474 /** 475 * @constructor 476 * @extends {WebInspector.HeapSnapshotNode} 477 * @param {!WebInspector.JSHeapSnapshot} snapshot 478 * @param {number=} nodeIndex 479 */ 480 WebInspector.JSHeapSnapshotNode = function(snapshot, nodeIndex) 481 { 482 WebInspector.HeapSnapshotNode.call(this, snapshot, nodeIndex) 483 } 484 485 WebInspector.JSHeapSnapshotNode.prototype = { 486 /** 487 * @return {boolean} 488 */ 489 canBeQueried: function() 490 { 491 var flags = this._snapshot._flagsOfNode(this); 492 return !!(flags & this._snapshot._nodeFlags.canBeQueried); 493 }, 494 495 /** 496 * @return {string} 497 */ 498 rawName: WebInspector.HeapSnapshotNode.prototype.name, 499 500 /** 501 * @return {string} 502 */ 503 name: function() 504 { 505 var snapshot = this._snapshot; 506 if (this._type() === snapshot._nodeConsStringType) { 507 var string = snapshot._lazyStringCache[this.nodeIndex]; 508 if (typeof string === "undefined") { 509 string = this._consStringName(); 510 snapshot._lazyStringCache[this.nodeIndex] = string; 511 } 512 return string; 513 } 514 return this.rawName(); 515 }, 516 517 /** 518 * @return {string} 519 */ 520 _consStringName: function() 521 { 522 var snapshot = this._snapshot; 523 var consStringType = snapshot._nodeConsStringType; 524 var edgeInternalType = snapshot._edgeInternalType; 525 var edgeFieldsCount = snapshot._edgeFieldsCount; 526 var edgeToNodeOffset = snapshot._edgeToNodeOffset; 527 var edgeTypeOffset = snapshot._edgeTypeOffset; 528 var edgeNameOffset = snapshot._edgeNameOffset; 529 var strings = snapshot.strings; 530 var edges = snapshot.containmentEdges; 531 var firstEdgeIndexes = snapshot._firstEdgeIndexes; 532 var nodeFieldCount = snapshot._nodeFieldCount; 533 var nodeTypeOffset = snapshot._nodeTypeOffset; 534 var nodeNameOffset = snapshot._nodeNameOffset; 535 var nodes = snapshot.nodes; 536 var nodesStack = []; 537 nodesStack.push(this.nodeIndex); 538 var name = ""; 539 540 while (nodesStack.length && name.length < 1024) { 541 var nodeIndex = nodesStack.pop(); 542 if (nodes[nodeIndex + nodeTypeOffset] !== consStringType) { 543 name += strings[nodes[nodeIndex + nodeNameOffset]]; 544 continue; 545 } 546 var nodeOrdinal = nodeIndex / nodeFieldCount; 547 var beginEdgeIndex = firstEdgeIndexes[nodeOrdinal]; 548 var endEdgeIndex = firstEdgeIndexes[nodeOrdinal + 1]; 549 var firstNodeIndex = 0; 550 var secondNodeIndex = 0; 551 for (var edgeIndex = beginEdgeIndex; edgeIndex < endEdgeIndex && (!firstNodeIndex || !secondNodeIndex); edgeIndex += edgeFieldsCount) { 552 var edgeType = edges[edgeIndex + edgeTypeOffset]; 553 if (edgeType === edgeInternalType) { 554 var edgeName = strings[edges[edgeIndex + edgeNameOffset]]; 555 if (edgeName === "first") 556 firstNodeIndex = edges[edgeIndex + edgeToNodeOffset]; 557 else if (edgeName === "second") 558 secondNodeIndex = edges[edgeIndex + edgeToNodeOffset]; 559 } 560 } 561 nodesStack.push(secondNodeIndex); 562 nodesStack.push(firstNodeIndex); 563 } 564 return name; 565 }, 566 567 /** 568 * @return {string} 569 */ 570 className: function() 571 { 572 var type = this.type(); 573 switch (type) { 574 case "hidden": 575 return "(system)"; 576 case "object": 577 case "native": 578 return this.name(); 579 case "code": 580 return "(compiled code)"; 581 default: 582 return "(" + type + ")"; 583 } 584 }, 585 586 /** 587 * @return {number} 588 */ 589 classIndex: function() 590 { 591 var snapshot = this._snapshot; 592 var nodes = snapshot.nodes; 593 var type = nodes[this.nodeIndex + snapshot._nodeTypeOffset];; 594 if (type === snapshot._nodeObjectType || type === snapshot._nodeNativeType) 595 return nodes[this.nodeIndex + snapshot._nodeNameOffset]; 596 return -1 - type; 597 }, 598 599 /** 600 * @return {number} 601 */ 602 id: function() 603 { 604 var snapshot = this._snapshot; 605 return snapshot.nodes[this.nodeIndex + snapshot._nodeIdOffset]; 606 }, 607 608 /** 609 * @return {boolean} 610 */ 611 isHidden: function() 612 { 613 return this._type() === this._snapshot._nodeHiddenType; 614 }, 615 616 /** 617 * @return {boolean} 618 */ 619 isArray: function() 620 { 621 return this._type() === this._snapshot._nodeArrayType; 622 }, 623 624 /** 625 * @return {boolean} 626 */ 627 isSynthetic: function() 628 { 629 return this._type() === this._snapshot._nodeSyntheticType; 630 }, 631 632 /** 633 * @return {!boolean} 634 */ 635 isUserRoot: function() 636 { 637 return !this.isSynthetic(); 638 }, 639 640 /** 641 * @return {!boolean} 642 */ 643 isDocumentDOMTreesRoot: function() 644 { 645 return this.isSynthetic() && this.name() === "(Document DOM trees)"; 646 }, 647 648 /** 649 * @return {!WebInspector.HeapSnapshotCommon.Node} 650 */ 651 serialize: function() 652 { 653 var result = WebInspector.HeapSnapshotNode.prototype.serialize.call(this); 654 var flags = this._snapshot._flagsOfNode(this); 655 if (flags & this._snapshot._nodeFlags.canBeQueried) 656 result.canBeQueried = true; 657 if (flags & this._snapshot._nodeFlags.detachedDOMTreeNode) 658 result.detachedDOMTreeNode = true; 659 return result; 660 }, 661 662 __proto__: WebInspector.HeapSnapshotNode.prototype 663 }; 664 665 /** 666 * @constructor 667 * @extends {WebInspector.HeapSnapshotEdge} 668 * @param {!WebInspector.JSHeapSnapshot} snapshot 669 * @param {number=} edgeIndex 670 */ 671 WebInspector.JSHeapSnapshotEdge = function(snapshot, edgeIndex) 672 { 673 WebInspector.HeapSnapshotEdge.call(this, snapshot, edgeIndex); 674 } 675 676 WebInspector.JSHeapSnapshotEdge.prototype = { 677 /** 678 * @return {!WebInspector.JSHeapSnapshotEdge} 679 */ 680 clone: function() 681 { 682 var snapshot = /** @type {!WebInspector.JSHeapSnapshot} */ (this._snapshot); 683 return new WebInspector.JSHeapSnapshotEdge(snapshot, this.edgeIndex); 684 }, 685 686 /** 687 * @return {boolean} 688 */ 689 hasStringName: function() 690 { 691 if (!this.isShortcut()) 692 return this._hasStringName(); 693 return isNaN(parseInt(this._name(), 10)); 694 }, 695 696 /** 697 * @return {boolean} 698 */ 699 isElement: function() 700 { 701 return this._type() === this._snapshot._edgeElementType; 702 }, 703 704 /** 705 * @return {boolean} 706 */ 707 isHidden: function() 708 { 709 return this._type() === this._snapshot._edgeHiddenType; 710 }, 711 712 /** 713 * @return {boolean} 714 */ 715 isWeak: function() 716 { 717 return this._type() === this._snapshot._edgeWeakType; 718 }, 719 720 /** 721 * @return {boolean} 722 */ 723 isInternal: function() 724 { 725 return this._type() === this._snapshot._edgeInternalType; 726 }, 727 728 /** 729 * @return {boolean} 730 */ 731 isInvisible: function() 732 { 733 return this._type() === this._snapshot._edgeInvisibleType; 734 }, 735 736 /** 737 * @return {boolean} 738 */ 739 isShortcut: function() 740 { 741 return this._type() === this._snapshot._edgeShortcutType; 742 }, 743 744 /** 745 * @return {string|number} 746 */ 747 name: function() 748 { 749 if (!this.isShortcut()) 750 return this._name(); 751 var numName = parseInt(this._name(), 10); 752 return isNaN(numName) ? this._name() : numName; 753 }, 754 755 /** 756 * @return {string} 757 */ 758 toString: function() 759 { 760 var name = this.name(); 761 switch (this.type()) { 762 case "context": return "->" + name; 763 case "element": return "[" + name + "]"; 764 case "weak": return "[[" + name + "]]"; 765 case "property": 766 return name.indexOf(" ") === -1 ? "." + name : "[\"" + name + "\"]"; 767 case "shortcut": 768 if (typeof name === "string") 769 return name.indexOf(" ") === -1 ? "." + name : "[\"" + name + "\"]"; 770 else 771 return "[" + name + "]"; 772 case "internal": 773 case "hidden": 774 case "invisible": 775 return "{" + name + "}"; 776 }; 777 return "?" + name + "?"; 778 }, 779 780 /** 781 * @return {boolean} 782 */ 783 _hasStringName: function() 784 { 785 var type = this._type(); 786 var snapshot = this._snapshot; 787 return type !== snapshot._edgeElementType && type !== snapshot._edgeHiddenType; 788 }, 789 790 /** 791 * @return {string|number} 792 */ 793 _name: function() 794 { 795 return this._hasStringName() ? this._snapshot.strings[this._nameOrIndex()] : this._nameOrIndex(); 796 }, 797 798 /** 799 * @return {number} 800 */ 801 _nameOrIndex: function() 802 { 803 return this._edges[this.edgeIndex + this._snapshot._edgeNameOffset]; 804 }, 805 806 /** 807 * @return {number} 808 */ 809 _type: function() 810 { 811 return this._edges[this.edgeIndex + this._snapshot._edgeTypeOffset]; 812 }, 813 814 __proto__: WebInspector.HeapSnapshotEdge.prototype 815 }; 816 817 818 /** 819 * @constructor 820 * @extends {WebInspector.HeapSnapshotRetainerEdge} 821 * @param {!WebInspector.JSHeapSnapshot} snapshot 822 * @param {number} retainerIndex 823 */ 824 WebInspector.JSHeapSnapshotRetainerEdge = function(snapshot, retainerIndex) 825 { 826 WebInspector.HeapSnapshotRetainerEdge.call(this, snapshot, retainerIndex); 827 } 828 829 WebInspector.JSHeapSnapshotRetainerEdge.prototype = { 830 /** 831 * @return {!WebInspector.JSHeapSnapshotRetainerEdge} 832 */ 833 clone: function() 834 { 835 var snapshot = /** @type {!WebInspector.JSHeapSnapshot} */ (this._snapshot); 836 return new WebInspector.JSHeapSnapshotRetainerEdge(snapshot, this.retainerIndex()); 837 }, 838 839 /** 840 * @return {boolean} 841 */ 842 isHidden: function() 843 { 844 return this._edge().isHidden(); 845 }, 846 847 /** 848 * @return {boolean} 849 */ 850 isInternal: function() 851 { 852 return this._edge().isInternal(); 853 }, 854 855 /** 856 * @return {boolean} 857 */ 858 isInvisible: function() 859 { 860 return this._edge().isInvisible(); 861 }, 862 863 /** 864 * @return {boolean} 865 */ 866 isShortcut: function() 867 { 868 return this._edge().isShortcut(); 869 }, 870 871 /** 872 * @return {boolean} 873 */ 874 isWeak: function() 875 { 876 return this._edge().isWeak(); 877 }, 878 879 __proto__: WebInspector.HeapSnapshotRetainerEdge.prototype 880 } 881 882