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, // bits: 0,1111,1111,1111,1111 46 visitedMarker: 0x10000 // bits: 1,0000,0000,0000,0000 47 }; 48 this._lazyStringCache = { }; 49 WebInspector.HeapSnapshot.call(this, profile, progress, showHiddenData); 50 } 51 52 WebInspector.JSHeapSnapshot.prototype = { 53 /** 54 * @param {number} nodeIndex 55 * @return {!WebInspector.JSHeapSnapshotNode} 56 */ 57 createNode: function(nodeIndex) 58 { 59 return new WebInspector.JSHeapSnapshotNode(this, nodeIndex); 60 }, 61 62 /** 63 * @override 64 * @param {number} edgeIndex 65 * @return {!WebInspector.JSHeapSnapshotEdge} 66 */ 67 createEdge: function(edgeIndex) 68 { 69 return new WebInspector.JSHeapSnapshotEdge(this, edgeIndex); 70 }, 71 72 /** 73 * @override 74 * @param {number} retainerIndex 75 * @return {!WebInspector.JSHeapSnapshotRetainerEdge} 76 */ 77 createRetainingEdge: function(retainerIndex) 78 { 79 return new WebInspector.JSHeapSnapshotRetainerEdge(this, retainerIndex); 80 }, 81 82 /** 83 * @override 84 * @return {?function(!WebInspector.JSHeapSnapshotNode):boolean} 85 */ 86 classNodesFilter: function() 87 { 88 /** 89 * @param {!WebInspector.JSHeapSnapshotNode} node 90 * @return {boolean} 91 */ 92 function filter(node) 93 { 94 return node.isUserObject(); 95 } 96 return this._showHiddenData ? null : filter; 97 }, 98 99 /** 100 * @return {function(!WebInspector.HeapSnapshotEdge):boolean} 101 */ 102 containmentEdgesFilter: function() 103 { 104 var showHiddenData = this._showHiddenData; 105 function filter(edge) { 106 if (edge.isInvisible()) 107 return false; 108 if (showHiddenData) 109 return true; 110 return !edge.isHidden() && !edge.node().isHidden(); 111 } 112 return filter; 113 }, 114 115 /** 116 * @return {function(!WebInspector.HeapSnapshotEdge):boolean} 117 */ 118 retainingEdgesFilter: function() 119 { 120 var containmentEdgesFilter = this.containmentEdgesFilter(); 121 function filter(edge) 122 { 123 return containmentEdgesFilter(edge) && !edge.node().isRoot() && !edge.isWeak(); 124 } 125 return filter; 126 }, 127 128 dispose: function() 129 { 130 WebInspector.HeapSnapshot.prototype.dispose.call(this); 131 delete this._flags; 132 }, 133 134 _calculateFlags: function() 135 { 136 this._flags = new Uint32Array(this.nodeCount); 137 this._markDetachedDOMTreeNodes(); 138 this._markQueriableHeapObjects(); 139 this._markPageOwnedNodes(); 140 }, 141 142 /** 143 * @param {!WebInspector.HeapSnapshotNode} node 144 * @return {!boolean} 145 */ 146 _isUserRoot: function(node) 147 { 148 return node.isUserRoot() || node.isDocumentDOMTreesRoot(); 149 }, 150 151 /** 152 * @param {function(!WebInspector.HeapSnapshotNode)} action 153 * @param {boolean=} userRootsOnly 154 */ 155 forEachRoot: function(action, userRootsOnly) 156 { 157 /** 158 * @param {!WebInspector.HeapSnapshotNode} node 159 * @param {string} name 160 * @return {?WebInspector.HeapSnapshotNode} 161 */ 162 function getChildNodeByName(node, name) 163 { 164 for (var iter = node.edges(); iter.hasNext(); iter.next()) { 165 var child = iter.edge.node(); 166 if (child.name() === name) 167 return child; 168 } 169 return null; 170 } 171 172 var visitedNodes = {}; 173 /** 174 * @param {!WebInspector.HeapSnapshotNode} node 175 */ 176 function doAction(node) 177 { 178 var ordinal = node._ordinal(); 179 if (!visitedNodes[ordinal]) { 180 action(node); 181 visitedNodes[ordinal] = true; 182 } 183 } 184 185 var gcRoots = getChildNodeByName(this.rootNode(), "(GC roots)"); 186 if (!gcRoots) 187 return; 188 189 if (userRootsOnly) { 190 for (var iter = this.rootNode().edges(); iter.hasNext(); iter.next()) { 191 var node = iter.edge.node(); 192 if (this._isUserRoot(node)) 193 doAction(node); 194 } 195 } else { 196 for (var iter = gcRoots.edges(); iter.hasNext(); iter.next()) { 197 var subRoot = iter.edge.node(); 198 for (var iter2 = subRoot.edges(); iter2.hasNext(); iter2.next()) 199 doAction(iter2.edge.node()); 200 doAction(subRoot); 201 } 202 for (var iter = this.rootNode().edges(); iter.hasNext(); iter.next()) 203 doAction(iter.edge.node()) 204 } 205 }, 206 207 /** 208 * @return {?{map: !Uint32Array, flag: number}} 209 */ 210 userObjectsMapAndFlag: function() 211 { 212 return this._showHiddenData ? null : { 213 map: this._flags, 214 flag: this._nodeFlags.pageObject 215 }; 216 }, 217 218 _flagsOfNode: function(node) 219 { 220 return this._flags[node.nodeIndex / this._nodeFieldCount]; 221 }, 222 223 _markDetachedDOMTreeNodes: function() 224 { 225 var flag = this._nodeFlags.detachedDOMTreeNode; 226 var detachedDOMTreesRoot; 227 for (var iter = this.rootNode().edges(); iter.hasNext(); iter.next()) { 228 var node = iter.edge.node(); 229 if (node.name() === "(Detached DOM trees)") { 230 detachedDOMTreesRoot = node; 231 break; 232 } 233 } 234 235 if (!detachedDOMTreesRoot) 236 return; 237 238 var detachedDOMTreeRE = /^Detached DOM tree/; 239 for (var iter = detachedDOMTreesRoot.edges(); iter.hasNext(); iter.next()) { 240 var node = iter.edge.node(); 241 if (detachedDOMTreeRE.test(node.className())) { 242 for (var edgesIter = node.edges(); edgesIter.hasNext(); edgesIter.next()) 243 this._flags[edgesIter.edge.node().nodeIndex / this._nodeFieldCount] |= flag; 244 } 245 } 246 }, 247 248 _markQueriableHeapObjects: function() 249 { 250 // Allow runtime properties query for objects accessible from Window objects 251 // via regular properties, and for DOM wrappers. Trying to access random objects 252 // can cause a crash due to insonsistent state of internal properties of wrappers. 253 var flag = this._nodeFlags.canBeQueried; 254 var hiddenEdgeType = this._edgeHiddenType; 255 var internalEdgeType = this._edgeInternalType; 256 var invisibleEdgeType = this._edgeInvisibleType; 257 var weakEdgeType = this._edgeWeakType; 258 var edgeToNodeOffset = this._edgeToNodeOffset; 259 var edgeTypeOffset = this._edgeTypeOffset; 260 var edgeFieldsCount = this._edgeFieldsCount; 261 var containmentEdges = this._containmentEdges; 262 var nodes = this._nodes; 263 var nodeCount = this.nodeCount; 264 var nodeFieldCount = this._nodeFieldCount; 265 var firstEdgeIndexes = this._firstEdgeIndexes; 266 267 var flags = this._flags; 268 var list = []; 269 270 for (var iter = this.rootNode().edges(); iter.hasNext(); iter.next()) { 271 if (iter.edge.node().isUserRoot()) 272 list.push(iter.edge.node().nodeIndex / nodeFieldCount); 273 } 274 275 while (list.length) { 276 var nodeOrdinal = list.pop(); 277 if (flags[nodeOrdinal] & flag) 278 continue; 279 flags[nodeOrdinal] |= flag; 280 var beginEdgeIndex = firstEdgeIndexes[nodeOrdinal]; 281 var endEdgeIndex = firstEdgeIndexes[nodeOrdinal + 1]; 282 for (var edgeIndex = beginEdgeIndex; edgeIndex < endEdgeIndex; edgeIndex += edgeFieldsCount) { 283 var childNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset]; 284 var childNodeOrdinal = childNodeIndex / nodeFieldCount; 285 if (flags[childNodeOrdinal] & flag) 286 continue; 287 var type = containmentEdges[edgeIndex + edgeTypeOffset]; 288 if (type === hiddenEdgeType || type === invisibleEdgeType || type === internalEdgeType || type === weakEdgeType) 289 continue; 290 list.push(childNodeOrdinal); 291 } 292 } 293 }, 294 295 _markPageOwnedNodes: function() 296 { 297 var edgeShortcutType = this._edgeShortcutType; 298 var edgeElementType = this._edgeElementType; 299 var edgeToNodeOffset = this._edgeToNodeOffset; 300 var edgeTypeOffset = this._edgeTypeOffset; 301 var edgeFieldsCount = this._edgeFieldsCount; 302 var edgeWeakType = this._edgeWeakType; 303 var firstEdgeIndexes = this._firstEdgeIndexes; 304 var containmentEdges = this._containmentEdges; 305 var containmentEdgesLength = containmentEdges.length; 306 var nodes = this._nodes; 307 var nodeFieldCount = this._nodeFieldCount; 308 var nodesCount = this.nodeCount; 309 310 var flags = this._flags; 311 var flag = this._nodeFlags.pageObject; 312 var visitedMarker = this._nodeFlags.visitedMarker; 313 var visitedMarkerMask = this._nodeFlags.visitedMarkerMask; 314 var markerAndFlag = visitedMarker | flag; 315 316 var nodesToVisit = new Uint32Array(nodesCount); 317 var nodesToVisitLength = 0; 318 319 var rootNodeOrdinal = this._rootNodeIndex / nodeFieldCount; 320 var node = this.rootNode(); 321 for (var edgeIndex = firstEdgeIndexes[rootNodeOrdinal], endEdgeIndex = firstEdgeIndexes[rootNodeOrdinal + 1]; 322 edgeIndex < endEdgeIndex; 323 edgeIndex += edgeFieldsCount) { 324 var edgeType = containmentEdges[edgeIndex + edgeTypeOffset]; 325 var nodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset]; 326 if (edgeType === edgeElementType) { 327 node.nodeIndex = nodeIndex; 328 if (!node.isDocumentDOMTreesRoot()) 329 continue; 330 } else if (edgeType !== edgeShortcutType) 331 continue; 332 var nodeOrdinal = nodeIndex / nodeFieldCount; 333 nodesToVisit[nodesToVisitLength++] = nodeOrdinal; 334 flags[nodeOrdinal] |= visitedMarker; 335 } 336 337 while (nodesToVisitLength) { 338 var nodeOrdinal = nodesToVisit[--nodesToVisitLength]; 339 flags[nodeOrdinal] |= flag; 340 flags[nodeOrdinal] &= visitedMarkerMask; 341 var beginEdgeIndex = firstEdgeIndexes[nodeOrdinal]; 342 var endEdgeIndex = firstEdgeIndexes[nodeOrdinal + 1]; 343 for (var edgeIndex = beginEdgeIndex; edgeIndex < endEdgeIndex; edgeIndex += edgeFieldsCount) { 344 var childNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset]; 345 var childNodeOrdinal = childNodeIndex / nodeFieldCount; 346 if (flags[childNodeOrdinal] & markerAndFlag) 347 continue; 348 var type = containmentEdges[edgeIndex + edgeTypeOffset]; 349 if (type === edgeWeakType) 350 continue; 351 nodesToVisit[nodesToVisitLength++] = childNodeOrdinal; 352 flags[childNodeOrdinal] |= visitedMarker; 353 } 354 } 355 }, 356 357 _calculateStatistics: function() 358 { 359 var nodeFieldCount = this._nodeFieldCount; 360 var nodes = this._nodes; 361 var nodesLength = nodes.length; 362 var nodeTypeOffset = this._nodeTypeOffset; 363 var nodeSizeOffset = this._nodeSelfSizeOffset;; 364 var nodeNativeType = this._nodeNativeType; 365 var nodeCodeType = this._nodeCodeType; 366 var nodeConsStringType = this._nodeConsStringType; 367 var nodeSlicedStringType = this._nodeSlicedStringType; 368 var sizeNative = 0; 369 var sizeCode = 0; 370 var sizeStrings = 0; 371 var sizeJSArrays = 0; 372 var node = this.rootNode(); 373 for (var nodeIndex = 0; nodeIndex < nodesLength; nodeIndex += nodeFieldCount) { 374 node.nodeIndex = nodeIndex; 375 var nodeType = nodes[nodeIndex + nodeTypeOffset]; 376 var nodeSize = nodes[nodeIndex + nodeSizeOffset]; 377 if (nodeType === nodeNativeType) 378 sizeNative += nodeSize; 379 else if (nodeType === nodeCodeType) 380 sizeCode += nodeSize; 381 else if (nodeType === nodeConsStringType || nodeType === nodeSlicedStringType || node.type() === "string") 382 sizeStrings += nodeSize; 383 else if (node.name() === "Array") 384 sizeJSArrays += this._calculateArraySize(node); 385 } 386 this._statistics = new WebInspector.HeapSnapshotCommon.Statistics(); 387 this._statistics.total = this.totalSize; 388 this._statistics.v8heap = this.totalSize - sizeNative; 389 this._statistics.native = sizeNative; 390 this._statistics.code = sizeCode; 391 this._statistics.jsArrays = sizeJSArrays; 392 this._statistics.strings = sizeStrings; 393 }, 394 395 /** 396 * @param {!WebInspector.HeapSnapshotNode} node 397 * @return {number} 398 */ 399 _calculateArraySize: function(node) 400 { 401 var size = node.selfSize(); 402 var beginEdgeIndex = node._edgeIndexesStart(); 403 var endEdgeIndex = node._edgeIndexesEnd(); 404 var containmentEdges = this._containmentEdges; 405 var strings = this._strings; 406 var edgeToNodeOffset = this._edgeToNodeOffset; 407 var edgeTypeOffset = this._edgeTypeOffset; 408 var edgeNameOffset = this._edgeNameOffset; 409 var edgeFieldsCount = this._edgeFieldsCount; 410 var edgeInternalType = this._edgeInternalType; 411 for (var edgeIndex = beginEdgeIndex; edgeIndex < endEdgeIndex; edgeIndex += edgeFieldsCount) { 412 var edgeType = containmentEdges[edgeIndex + edgeTypeOffset]; 413 if (edgeType !== edgeInternalType) 414 continue; 415 var edgeName = strings[containmentEdges[edgeIndex + edgeNameOffset]]; 416 if (edgeName !== "elements") 417 continue; 418 var elementsNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset]; 419 node.nodeIndex = elementsNodeIndex; 420 if (node.retainersCount() === 1) 421 size += node.selfSize(); 422 break; 423 } 424 return size; 425 }, 426 427 /** 428 * @return {!WebInspector.HeapSnapshotCommon.Statistics} 429 */ 430 getStatistics: function() 431 { 432 return this._statistics; 433 }, 434 435 __proto__: WebInspector.HeapSnapshot.prototype 436 }; 437 438 /** 439 * @constructor 440 * @extends {WebInspector.HeapSnapshotNode} 441 * @param {!WebInspector.JSHeapSnapshot} snapshot 442 * @param {number=} nodeIndex 443 */ 444 WebInspector.JSHeapSnapshotNode = function(snapshot, nodeIndex) 445 { 446 WebInspector.HeapSnapshotNode.call(this, snapshot, nodeIndex) 447 } 448 449 WebInspector.JSHeapSnapshotNode.prototype = { 450 /** 451 * @return {boolean} 452 */ 453 canBeQueried: function() 454 { 455 var flags = this._snapshot._flagsOfNode(this); 456 return !!(flags & this._snapshot._nodeFlags.canBeQueried); 457 }, 458 459 /** 460 * @return {boolean} 461 */ 462 isUserObject: function() 463 { 464 var flags = this._snapshot._flagsOfNode(this); 465 return !!(flags & this._snapshot._nodeFlags.pageObject); 466 }, 467 468 469 /** 470 * @return {string} 471 */ 472 name: function() { 473 var snapshot = this._snapshot; 474 if (this._type() === snapshot._nodeConsStringType) { 475 var string = snapshot._lazyStringCache[this.nodeIndex]; 476 if (typeof string === "undefined") { 477 string = this._consStringName(); 478 snapshot._lazyStringCache[this.nodeIndex] = string; 479 } 480 return string; 481 } 482 return WebInspector.HeapSnapshotNode.prototype.name.call(this); 483 }, 484 485 _consStringName: function() 486 { 487 var snapshot = this._snapshot; 488 var consStringType = snapshot._nodeConsStringType; 489 var edgeInternalType = snapshot._edgeInternalType; 490 var edgeFieldsCount = snapshot._edgeFieldsCount; 491 var edgeToNodeOffset = snapshot._edgeToNodeOffset; 492 var edgeTypeOffset = snapshot._edgeTypeOffset; 493 var edgeNameOffset = snapshot._edgeNameOffset; 494 var strings = snapshot._strings; 495 var edges = snapshot._containmentEdges; 496 var firstEdgeIndexes = snapshot._firstEdgeIndexes; 497 var nodeFieldCount = snapshot._nodeFieldCount; 498 var nodeTypeOffset = snapshot._nodeTypeOffset; 499 var nodeNameOffset = snapshot._nodeNameOffset; 500 var nodes = snapshot._nodes; 501 var nodesStack = []; 502 nodesStack.push(this.nodeIndex); 503 var name = ""; 504 505 while (nodesStack.length && name.length < 1024) { 506 var nodeIndex = nodesStack.pop(); 507 if (nodes[nodeIndex + nodeTypeOffset] !== consStringType) { 508 name += strings[nodes[nodeIndex + nodeNameOffset]]; 509 continue; 510 } 511 var nodeOrdinal = nodeIndex / nodeFieldCount; 512 var beginEdgeIndex = firstEdgeIndexes[nodeOrdinal]; 513 var endEdgeIndex = firstEdgeIndexes[nodeOrdinal + 1]; 514 var firstNodeIndex = 0; 515 var secondNodeIndex = 0; 516 for (var edgeIndex = beginEdgeIndex; edgeIndex < endEdgeIndex && (!firstNodeIndex || !secondNodeIndex); edgeIndex += edgeFieldsCount) { 517 var edgeType = edges[edgeIndex + edgeTypeOffset]; 518 if (edgeType === edgeInternalType) { 519 var edgeName = strings[edges[edgeIndex + edgeNameOffset]]; 520 if (edgeName === "first") 521 firstNodeIndex = edges[edgeIndex + edgeToNodeOffset]; 522 else if (edgeName === "second") 523 secondNodeIndex = edges[edgeIndex + edgeToNodeOffset]; 524 } 525 } 526 nodesStack.push(secondNodeIndex); 527 nodesStack.push(firstNodeIndex); 528 } 529 return name; 530 }, 531 532 /** 533 * @return {string} 534 */ 535 className: function() 536 { 537 var type = this.type(); 538 switch (type) { 539 case "hidden": 540 return "(system)"; 541 case "object": 542 case "native": 543 return this.name(); 544 case "code": 545 return "(compiled code)"; 546 default: 547 return "(" + type + ")"; 548 } 549 }, 550 551 /** 552 * @return {number} 553 */ 554 classIndex: function() 555 { 556 var snapshot = this._snapshot; 557 var nodes = snapshot._nodes; 558 var type = nodes[this.nodeIndex + snapshot._nodeTypeOffset];; 559 if (type === snapshot._nodeObjectType || type === snapshot._nodeNativeType) 560 return nodes[this.nodeIndex + snapshot._nodeNameOffset]; 561 return -1 - type; 562 }, 563 564 /** 565 * @return {number} 566 */ 567 id: function() 568 { 569 var snapshot = this._snapshot; 570 return snapshot._nodes[this.nodeIndex + snapshot._nodeIdOffset]; 571 }, 572 573 /** 574 * @return {boolean} 575 */ 576 isHidden: function() 577 { 578 return this._type() === this._snapshot._nodeHiddenType; 579 }, 580 581 /** 582 * @return {boolean} 583 */ 584 isSynthetic: function() 585 { 586 return this._type() === this._snapshot._nodeSyntheticType; 587 }, 588 589 /** 590 * @return {!boolean} 591 */ 592 isUserRoot: function() 593 { 594 return !this.isSynthetic(); 595 }, 596 597 /** 598 * @return {!boolean} 599 */ 600 isDocumentDOMTreesRoot: function() 601 { 602 return this.isSynthetic() && this.name() === "(Document DOM trees)"; 603 }, 604 605 /** 606 * @return {!WebInspector.HeapSnapshotCommon.Node} 607 */ 608 serialize: function() 609 { 610 var result = WebInspector.HeapSnapshotNode.prototype.serialize.call(this); 611 var flags = this._snapshot._flagsOfNode(this); 612 if (flags & this._snapshot._nodeFlags.canBeQueried) 613 result.canBeQueried = true; 614 if (flags & this._snapshot._nodeFlags.detachedDOMTreeNode) 615 result.detachedDOMTreeNode = true; 616 return result; 617 }, 618 619 __proto__: WebInspector.HeapSnapshotNode.prototype 620 }; 621 622 /** 623 * @constructor 624 * @extends {WebInspector.HeapSnapshotEdge} 625 * @param {!WebInspector.JSHeapSnapshot} snapshot 626 * @param {number=} edgeIndex 627 */ 628 WebInspector.JSHeapSnapshotEdge = function(snapshot, edgeIndex) 629 { 630 WebInspector.HeapSnapshotEdge.call(this, snapshot, edgeIndex); 631 } 632 633 WebInspector.JSHeapSnapshotEdge.prototype = { 634 /** 635 * @return {!WebInspector.JSHeapSnapshotEdge} 636 */ 637 clone: function() 638 { 639 var snapshot = /** @type {!WebInspector.JSHeapSnapshot} */ (this._snapshot); 640 return new WebInspector.JSHeapSnapshotEdge(snapshot, this.edgeIndex); 641 }, 642 643 /** 644 * @return {boolean} 645 */ 646 hasStringName: function() 647 { 648 if (!this.isShortcut()) 649 return this._hasStringName(); 650 return isNaN(parseInt(this._name(), 10)); 651 }, 652 653 /** 654 * @return {boolean} 655 */ 656 isElement: function() 657 { 658 return this._type() === this._snapshot._edgeElementType; 659 }, 660 661 /** 662 * @return {boolean} 663 */ 664 isHidden: function() 665 { 666 return this._type() === this._snapshot._edgeHiddenType; 667 }, 668 669 /** 670 * @return {boolean} 671 */ 672 isWeak: function() 673 { 674 return this._type() === this._snapshot._edgeWeakType; 675 }, 676 677 /** 678 * @return {boolean} 679 */ 680 isInternal: function() 681 { 682 return this._type() === this._snapshot._edgeInternalType; 683 }, 684 685 /** 686 * @return {boolean} 687 */ 688 isInvisible: function() 689 { 690 return this._type() === this._snapshot._edgeInvisibleType; 691 }, 692 693 /** 694 * @return {boolean} 695 */ 696 isShortcut: function() 697 { 698 return this._type() === this._snapshot._edgeShortcutType; 699 }, 700 701 /** 702 * @return {string} 703 */ 704 name: function() 705 { 706 if (!this.isShortcut()) 707 return this._name(); 708 var numName = parseInt(this._name(), 10); 709 return isNaN(numName) ? this._name() : numName; 710 }, 711 712 /** 713 * @return {string} 714 */ 715 toString: function() 716 { 717 var name = this.name(); 718 switch (this.type()) { 719 case "context": return "->" + name; 720 case "element": return "[" + name + "]"; 721 case "weak": return "[[" + name + "]]"; 722 case "property": 723 return name.indexOf(" ") === -1 ? "." + name : "[\"" + name + "\"]"; 724 case "shortcut": 725 if (typeof name === "string") 726 return name.indexOf(" ") === -1 ? "." + name : "[\"" + name + "\"]"; 727 else 728 return "[" + name + "]"; 729 case "internal": 730 case "hidden": 731 case "invisible": 732 return "{" + name + "}"; 733 }; 734 return "?" + name + "?"; 735 }, 736 737 _hasStringName: function() 738 { 739 return !this.isElement() && !this.isHidden(); 740 }, 741 742 _name: function() 743 { 744 return this._hasStringName() ? this._snapshot._strings[this._nameOrIndex()] : this._nameOrIndex(); 745 }, 746 747 _nameOrIndex: function() 748 { 749 return this._edges[this.edgeIndex + this._snapshot._edgeNameOffset]; 750 }, 751 752 _type: function() 753 { 754 return this._edges[this.edgeIndex + this._snapshot._edgeTypeOffset]; 755 }, 756 757 __proto__: WebInspector.HeapSnapshotEdge.prototype 758 }; 759 760 761 /** 762 * @constructor 763 * @extends {WebInspector.HeapSnapshotRetainerEdge} 764 * @param {!WebInspector.JSHeapSnapshot} snapshot 765 * @param {number} retainerIndex 766 */ 767 WebInspector.JSHeapSnapshotRetainerEdge = function(snapshot, retainerIndex) 768 { 769 WebInspector.HeapSnapshotRetainerEdge.call(this, snapshot, retainerIndex); 770 } 771 772 WebInspector.JSHeapSnapshotRetainerEdge.prototype = { 773 /** 774 * @return {!WebInspector.JSHeapSnapshotRetainerEdge} 775 */ 776 clone: function() 777 { 778 var snapshot = /** @type {!WebInspector.JSHeapSnapshot} */ (this._snapshot); 779 return new WebInspector.JSHeapSnapshotRetainerEdge(snapshot, this.retainerIndex()); 780 }, 781 782 /** 783 * @return {boolean} 784 */ 785 isHidden: function() 786 { 787 return this._edge().isHidden(); 788 }, 789 790 /** 791 * @return {boolean} 792 */ 793 isInternal: function() 794 { 795 return this._edge().isInternal(); 796 }, 797 798 /** 799 * @return {boolean} 800 */ 801 isInvisible: function() 802 { 803 return this._edge().isInvisible(); 804 }, 805 806 /** 807 * @return {boolean} 808 */ 809 isShortcut: function() 810 { 811 return this._edge().isShortcut(); 812 }, 813 814 /** 815 * @return {boolean} 816 */ 817 isWeak: function() 818 { 819 return this._edge().isWeak(); 820 }, 821 822 __proto__: WebInspector.HeapSnapshotRetainerEdge.prototype 823 } 824 825