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