Upstream version 8.37.180.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / profiler / heap_snapshot_worker / JSHeapSnapshot.js
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