• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2011 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 * @interface
33 */
34WebInspector.HeapSnapshotItem = function() { }
35
36WebInspector.HeapSnapshotItem.prototype = {
37    /**
38     * @return {number}
39     */
40    itemIndex: function() { },
41
42    /**
43     * @return {!Object}
44     */
45    serialize: function() { }
46};
47
48/**
49 * @constructor
50 * @implements {WebInspector.HeapSnapshotItem}
51 * @param {!WebInspector.HeapSnapshot} snapshot
52 * @param {number=} edgeIndex
53 */
54WebInspector.HeapSnapshotEdge = function(snapshot, edgeIndex)
55{
56    this._snapshot = snapshot;
57    this._edges = snapshot._containmentEdges;
58    this.edgeIndex = edgeIndex || 0;
59}
60
61WebInspector.HeapSnapshotEdge.prototype = {
62    /**
63     * @return {!WebInspector.HeapSnapshotEdge}
64     */
65    clone: function()
66    {
67        return new WebInspector.HeapSnapshotEdge(this._snapshot, this.edgeIndex);
68    },
69
70    /**
71     * @return {boolean}
72     */
73    hasStringName: function()
74    {
75        throw new Error("Not implemented");
76    },
77
78    /**
79     * @return {string}
80     */
81    name: function()
82    {
83        throw new Error("Not implemented");
84    },
85
86    /**
87     * @return {!WebInspector.HeapSnapshotNode}
88     */
89    node: function()
90    {
91        return this._snapshot.createNode(this.nodeIndex());
92    },
93
94    /**
95     * @return {number}
96     */
97    nodeIndex: function()
98    {
99        return this._edges[this.edgeIndex + this._snapshot._edgeToNodeOffset];
100    },
101
102    /**
103     * @return {string}
104     */
105    toString: function()
106    {
107        return "HeapSnapshotEdge: " + this.name();
108    },
109
110    /**
111     * @return {string}
112     */
113    type: function()
114    {
115        return this._snapshot._edgeTypes[this._type()];
116    },
117
118    /**
119     * @override
120     * @return {number}
121     */
122    itemIndex: function()
123    {
124        return this.edgeIndex;
125    },
126
127    /**
128     * @override
129     * @return {!WebInspector.HeapSnapshotCommon.Edge}
130     */
131    serialize: function()
132    {
133        return new WebInspector.HeapSnapshotCommon.Edge(this.name(), this.node().serialize(), this.type(), this.edgeIndex);
134    },
135
136    _type: function()
137    {
138        return this._edges[this.edgeIndex + this._snapshot._edgeTypeOffset];
139    }
140};
141
142
143/**
144 * @interface
145 */
146WebInspector.HeapSnapshotItemIterator = function() { }
147
148WebInspector.HeapSnapshotItemIterator.prototype = {
149    /**
150     * @return {boolean}
151     */
152    hasNext: function() { },
153
154    /**
155     * @return {!WebInspector.HeapSnapshotItem}
156     */
157    item: function() { },
158
159    next: function() { }
160};
161
162
163/**
164 * @interface
165 */
166WebInspector.HeapSnapshotItemIndexProvider = function() { }
167
168WebInspector.HeapSnapshotItemIndexProvider.prototype = {
169    /**
170     * @param {number} newIndex
171     * @return {!WebInspector.HeapSnapshotItem}
172     */
173    itemForIndex: function(newIndex) { },
174};
175
176/**
177 * @constructor
178 * @implements {WebInspector.HeapSnapshotItemIndexProvider}
179 * @param {!WebInspector.HeapSnapshot} snapshot
180 */
181WebInspector.HeapSnapshotNodeIndexProvider = function(snapshot)
182{
183    this._node = snapshot.createNode();
184}
185
186WebInspector.HeapSnapshotNodeIndexProvider.prototype = {
187    /**
188     * @param {number} index
189     * @return {!WebInspector.HeapSnapshotNode}
190     */
191    itemForIndex: function(index)
192    {
193        this._node.nodeIndex = index;
194        return this._node;
195    }
196};
197
198
199/**
200 * @constructor
201 * @implements {WebInspector.HeapSnapshotItemIndexProvider}
202 * @param {!WebInspector.HeapSnapshot} snapshot
203 */
204WebInspector.HeapSnapshotEdgeIndexProvider = function(snapshot)
205{
206    this._edge = snapshot.createEdge(0);
207}
208
209WebInspector.HeapSnapshotEdgeIndexProvider.prototype = {
210    /**
211     * @param {number} index
212     * @return {!WebInspector.HeapSnapshotEdge}
213     */
214    itemForIndex: function(index)
215    {
216        this._edge.edgeIndex = index;
217        return this._edge;
218    }
219};
220
221
222/**
223 * @constructor
224 * @implements {WebInspector.HeapSnapshotItemIndexProvider}
225 * @param {!WebInspector.HeapSnapshot} snapshot
226 */
227WebInspector.HeapSnapshotRetainerEdgeIndexProvider = function(snapshot)
228{
229    this._retainerEdge = snapshot.createRetainingEdge(0);
230}
231
232WebInspector.HeapSnapshotRetainerEdgeIndexProvider.prototype = {
233    /**
234     * @param {number} index
235     * @return {!WebInspector.HeapSnapshotRetainerEdge}
236     */
237    itemForIndex: function(index)
238    {
239        this._retainerEdge.setRetainerIndex(index);
240        return this._retainerEdge;
241    }
242};
243
244
245/**
246 * @constructor
247 * @implements {WebInspector.HeapSnapshotItemIterator}
248 * @param {!WebInspector.HeapSnapshotNode} node
249 */
250WebInspector.HeapSnapshotEdgeIterator = function(node)
251{
252    this._sourceNode = node;
253    this.edge = node._snapshot.createEdge(node._edgeIndexesStart());
254}
255
256WebInspector.HeapSnapshotEdgeIterator.prototype = {
257    /**
258     * @return {boolean}
259     */
260    hasNext: function()
261    {
262        return this.edge.edgeIndex < this._sourceNode._edgeIndexesEnd();
263    },
264
265    /**
266     * @return {!WebInspector.HeapSnapshotEdge}
267     */
268    item: function()
269    {
270        return this.edge;
271    },
272
273    next: function()
274    {
275        this.edge.edgeIndex += this.edge._snapshot._edgeFieldsCount;
276    }
277};
278
279/**
280 * @constructor
281 * @implements {WebInspector.HeapSnapshotItem}
282 * @param {!WebInspector.HeapSnapshot} snapshot
283 * @param {number} retainerIndex
284 */
285WebInspector.HeapSnapshotRetainerEdge = function(snapshot, retainerIndex)
286{
287    this._snapshot = snapshot;
288    this.setRetainerIndex(retainerIndex);
289}
290
291WebInspector.HeapSnapshotRetainerEdge.prototype = {
292    /**
293     * @return {!WebInspector.HeapSnapshotRetainerEdge}
294     */
295    clone: function()
296    {
297        return new WebInspector.HeapSnapshotRetainerEdge(this._snapshot, this.retainerIndex());
298    },
299
300    /**
301     * @return {boolean}
302     */
303    hasStringName: function()
304    {
305        return this._edge().hasStringName();
306    },
307
308    /**
309     * @return {string}
310     */
311    name: function()
312    {
313        return this._edge().name();
314    },
315
316    /**
317     * @return {!WebInspector.HeapSnapshotNode}
318     */
319    node: function()
320    {
321        return this._node();
322    },
323
324    /**
325     * @return {number}
326     */
327    nodeIndex: function()
328    {
329        return this._retainingNodeIndex;
330    },
331
332    /**
333     * @return {number}
334     */
335    retainerIndex: function()
336    {
337        return this._retainerIndex;
338    },
339
340    /**
341     * @param {number} retainerIndex
342     */
343    setRetainerIndex: function(retainerIndex)
344    {
345        if (retainerIndex === this._retainerIndex)
346            return;
347        this._retainerIndex = retainerIndex;
348        this._globalEdgeIndex = this._snapshot._retainingEdges[retainerIndex];
349        this._retainingNodeIndex = this._snapshot._retainingNodes[retainerIndex];
350        this._edgeInstance = null;
351        this._nodeInstance = null;
352    },
353
354    /**
355     * @param {number} edgeIndex
356     */
357    set edgeIndex(edgeIndex)
358    {
359        this.setRetainerIndex(edgeIndex);
360    },
361
362    _node: function()
363    {
364        if (!this._nodeInstance)
365            this._nodeInstance = this._snapshot.createNode(this._retainingNodeIndex);
366        return this._nodeInstance;
367    },
368
369    _edge: function()
370    {
371        if (!this._edgeInstance)
372            this._edgeInstance = this._snapshot.createEdge(this._globalEdgeIndex);
373        return this._edgeInstance;
374    },
375
376    /**
377     * @return {string}
378     */
379    toString: function()
380    {
381        return this._edge().toString();
382    },
383
384    /**
385     * @override
386     * @return {number}
387     */
388    itemIndex: function()
389    {
390        return this._retainerIndex;
391    },
392
393    /**
394     * @override
395     * @return {!WebInspector.HeapSnapshotCommon.Edge}
396     */
397    serialize: function()
398    {
399        return new WebInspector.HeapSnapshotCommon.Edge(this.name(), this.node().serialize(), this.type(), this._globalEdgeIndex);
400    },
401
402    /**
403     * @return {string}
404     */
405    type: function()
406    {
407        return this._edge().type();
408    }
409}
410
411/**
412 * @constructor
413 * @implements {WebInspector.HeapSnapshotItemIterator}
414 * @param {!WebInspector.HeapSnapshotNode} retainedNode
415 */
416WebInspector.HeapSnapshotRetainerEdgeIterator = function(retainedNode)
417{
418    var snapshot = retainedNode._snapshot;
419    var retainedNodeOrdinal = retainedNode._ordinal();
420    var retainerIndex = snapshot._firstRetainerIndex[retainedNodeOrdinal];
421    this._retainersEnd = snapshot._firstRetainerIndex[retainedNodeOrdinal + 1];
422    this.retainer = snapshot.createRetainingEdge(retainerIndex);
423}
424
425WebInspector.HeapSnapshotRetainerEdgeIterator.prototype = {
426    /**
427     * @return {boolean}
428     */
429    hasNext: function()
430    {
431        return this.retainer.retainerIndex() < this._retainersEnd;
432    },
433
434    /**
435     * @return {!WebInspector.HeapSnapshotRetainerEdge}
436     */
437    item: function()
438    {
439        return this.retainer;
440    },
441
442    next: function()
443    {
444        this.retainer.setRetainerIndex(this.retainer.retainerIndex() + 1);
445    }
446};
447
448/**
449 * @constructor
450 * @implements {WebInspector.HeapSnapshotItem}
451 * @param {!WebInspector.HeapSnapshot} snapshot
452 * @param {number=} nodeIndex
453 */
454WebInspector.HeapSnapshotNode = function(snapshot, nodeIndex)
455{
456    this._snapshot = snapshot;
457    this.nodeIndex = nodeIndex || 0;
458}
459
460WebInspector.HeapSnapshotNode.prototype = {
461    /**
462     * @return {number}
463     */
464    distance: function()
465    {
466        return this._snapshot._nodeDistances[this.nodeIndex / this._snapshot._nodeFieldCount];
467    },
468
469    /**
470     * @return {string}
471     */
472    className: function()
473    {
474        throw new Error("Not implemented");
475    },
476
477    /**
478     * @return {number}
479     */
480    classIndex: function()
481    {
482        throw new Error("Not implemented");
483    },
484
485    /**
486     * @return {number}
487     */
488    dominatorIndex: function()
489    {
490        var nodeFieldCount = this._snapshot._nodeFieldCount;
491        return this._snapshot._dominatorsTree[this.nodeIndex / this._snapshot._nodeFieldCount] * nodeFieldCount;
492    },
493
494    /**
495     * @return {!WebInspector.HeapSnapshotEdgeIterator}
496     */
497    edges: function()
498    {
499        return new WebInspector.HeapSnapshotEdgeIterator(this);
500    },
501
502    /**
503     * @return {number}
504     */
505    edgesCount: function()
506    {
507        return (this._edgeIndexesEnd() - this._edgeIndexesStart()) / this._snapshot._edgeFieldsCount;
508    },
509
510    /**
511     * @return {number}
512     */
513    id: function()
514    {
515        throw new Error("Not implemented");
516    },
517
518    /**
519     * @return {boolean}
520     */
521    isRoot: function()
522    {
523        return this.nodeIndex === this._snapshot._rootNodeIndex;
524    },
525
526    /**
527     * @return {string}
528     */
529    name: function()
530    {
531        return this._snapshot._strings[this._name()];
532    },
533
534    /**
535     * @return {number}
536     */
537    retainedSize: function()
538    {
539        return this._snapshot._retainedSizes[this._ordinal()];
540    },
541
542    /**
543     * @return {!WebInspector.HeapSnapshotRetainerEdgeIterator}
544     */
545    retainers: function()
546    {
547        return new WebInspector.HeapSnapshotRetainerEdgeIterator(this);
548    },
549
550    /**
551     * @return {number}
552     */
553    retainersCount: function()
554    {
555        var snapshot = this._snapshot;
556        var ordinal = this._ordinal();
557        return snapshot._firstRetainerIndex[ordinal + 1] - snapshot._firstRetainerIndex[ordinal];
558    },
559
560    /**
561     * @return {number}
562     */
563    selfSize: function()
564    {
565        var snapshot = this._snapshot;
566        return snapshot._nodes[this.nodeIndex + snapshot._nodeSelfSizeOffset];
567    },
568
569    /**
570     * @return {string}
571     */
572    type: function()
573    {
574        return this._snapshot._nodeTypes[this._type()];
575    },
576
577    /**
578     * @return {number}
579     */
580    traceNodeId: function()
581    {
582        var snapshot = this._snapshot;
583        return snapshot._nodes[this.nodeIndex + snapshot._nodeTraceNodeIdOffset];
584    },
585
586    /**
587     * @override
588     * @return {number}
589     */
590    itemIndex: function()
591    {
592        return this.nodeIndex;
593    },
594
595    /**
596     * @override
597     * @return {!WebInspector.HeapSnapshotCommon.Node}
598     */
599    serialize: function()
600    {
601        return new WebInspector.HeapSnapshotCommon.Node(this.id(), this.name(), this.distance(), this.nodeIndex, this.retainedSize(), this.selfSize(), this.type());
602    },
603
604    /**
605     * @return {number}
606     */
607    _name: function()
608    {
609        var snapshot = this._snapshot;
610        return snapshot._nodes[this.nodeIndex + snapshot._nodeNameOffset];
611    },
612
613    /**
614     * @return {number}
615     */
616    _edgeIndexesStart: function()
617    {
618        return this._snapshot._firstEdgeIndexes[this._ordinal()];
619    },
620
621    /**
622     * @return {number}
623     */
624    _edgeIndexesEnd: function()
625    {
626        return this._snapshot._firstEdgeIndexes[this._ordinal() + 1];
627    },
628
629    /**
630     * @return {number}
631     */
632    _ordinal: function()
633    {
634        return this.nodeIndex / this._snapshot._nodeFieldCount;
635    },
636
637    /**
638     * @return {number}
639     */
640    _nextNodeIndex: function()
641    {
642        return this.nodeIndex + this._snapshot._nodeFieldCount;
643    },
644
645    /**
646     * @return {number}
647     */
648    _type: function()
649    {
650        var snapshot = this._snapshot;
651        return snapshot._nodes[this.nodeIndex + snapshot._nodeTypeOffset];
652    }
653};
654
655/**
656 * @constructor
657 * @implements {WebInspector.HeapSnapshotItemIterator}
658 * @param {!WebInspector.HeapSnapshotNode} node
659 */
660WebInspector.HeapSnapshotNodeIterator = function(node)
661{
662    this.node = node;
663    this._nodesLength = node._snapshot._nodes.length;
664}
665
666WebInspector.HeapSnapshotNodeIterator.prototype = {
667    /**
668     * @return {boolean}
669     */
670    hasNext: function()
671    {
672        return this.node.nodeIndex < this._nodesLength;
673    },
674
675    /**
676     * @return {!WebInspector.HeapSnapshotNode}
677     */
678    item: function()
679    {
680        return this.node;
681    },
682
683    next: function()
684    {
685        this.node.nodeIndex = this.node._nextNodeIndex();
686    }
687}
688
689
690/**
691 * @constructor
692 * @implements {WebInspector.HeapSnapshotItemIterator}
693 * @param {!WebInspector.HeapSnapshotItemIndexProvider} itemProvider
694 * @param {!Array.<number>|!Uint32Array} indexes
695 */
696WebInspector.HeapSnapshotIndexRangeIterator = function(itemProvider, indexes)
697{
698    this._itemProvider = itemProvider;
699    this._indexes = indexes;
700    this._position = 0;
701}
702
703WebInspector.HeapSnapshotIndexRangeIterator.prototype = {
704    /**
705     * @return {boolean}
706     */
707    hasNext: function()
708    {
709        return this._position < this._indexes.length
710    },
711
712    /**
713     * @return {!WebInspector.HeapSnapshotItem}
714     */
715    item: function()
716    {
717        var index = this._indexes[this._position];
718        return this._itemProvider.itemForIndex(index);
719    },
720
721    next: function()
722    {
723        ++this._position;
724    }
725}
726
727
728/**
729 * @constructor
730 * @implements {WebInspector.HeapSnapshotItemIterator}
731 * @param {!WebInspector.HeapSnapshotItemIterator} iterator
732 * @param {function(!WebInspector.HeapSnapshotItem):boolean=} filter
733 */
734WebInspector.HeapSnapshotFilteredIterator = function(iterator, filter)
735{
736    this._iterator = iterator;
737    this._filter = filter;
738    this._skipFilteredItems();
739}
740
741WebInspector.HeapSnapshotFilteredIterator.prototype = {
742    /**
743     * @return {boolean}
744     */
745    hasNext: function()
746    {
747        return this._iterator.hasNext();
748    },
749
750    /**
751     * @return {!WebInspector.HeapSnapshotItem}
752     */
753    item: function()
754    {
755        return this._iterator.item();
756    },
757
758    next: function()
759    {
760        this._iterator.next();
761        this._skipFilteredItems();
762    },
763
764    _skipFilteredItems: function()
765    {
766        while (this._iterator.hasNext() && !this._filter(this._iterator.item())) {
767            this._iterator.next();
768        }
769    }
770}
771
772
773/**
774 * @param {!WebInspector.HeapSnapshotWorkerDispatcher=} dispatcher
775 * @constructor
776 */
777WebInspector.HeapSnapshotProgress = function(dispatcher)
778{
779    this._dispatcher = dispatcher;
780}
781
782WebInspector.HeapSnapshotProgress.prototype = {
783    /**
784     * @param {string} status
785     */
786    updateStatus: function(status)
787    {
788        this._sendUpdateEvent(WebInspector.UIString(status));
789    },
790
791    /**
792     * @param {string} title
793     * @param {number} value
794     * @param {number} total
795     */
796    updateProgress: function(title, value, total)
797    {
798        var percentValue = ((total ? (value / total) : 0) * 100).toFixed(0);
799        this._sendUpdateEvent(WebInspector.UIString(title, percentValue));
800    },
801
802    /**
803     * @param {string} text
804     */
805    _sendUpdateEvent: function(text)
806    {
807        // May be undefined in tests.
808        if (this._dispatcher)
809            this._dispatcher.sendEvent(WebInspector.HeapSnapshotProgressEvent.Update, text);
810    }
811}
812
813
814/**
815 * @param {!Object} profile
816 * @param {!WebInspector.HeapSnapshotProgress} progress
817 * @param {boolean} showHiddenData
818 * @constructor
819 */
820WebInspector.HeapSnapshot = function(profile, progress, showHiddenData)
821{
822    this._nodes = profile.nodes;
823    this._containmentEdges = profile.edges;
824    /** @type {!HeapSnapshotMetainfo} */
825    this._metaNode = profile.snapshot.meta;
826    this._strings = profile.strings;
827    this._progress = progress;
828
829    this._noDistance = -5;
830    this._rootNodeIndex = 0;
831    if (profile.snapshot.root_index)
832        this._rootNodeIndex = profile.snapshot.root_index;
833
834    this._snapshotDiffs = {};
835    this._aggregatesForDiff = null;
836    this._aggregates = {};
837    this._aggregatesSortedFlags = {};
838    this._showHiddenData = showHiddenData;
839
840    this._init();
841
842    if (profile.snapshot.trace_function_count) {
843        this._progress.updateStatus("Buiding allocation statistics\u2026");
844        var nodes = this._nodes;
845        var nodesLength = nodes.length;
846        var nodeFieldCount = this._nodeFieldCount;
847        var node = this.rootNode();
848        var liveObjects = {};
849        for (var nodeIndex = 0; nodeIndex < nodesLength; nodeIndex += nodeFieldCount) {
850            node.nodeIndex = nodeIndex;
851            var traceNodeId = node.traceNodeId();
852            var stats = liveObjects[traceNodeId];
853            if (!stats) {
854                liveObjects[traceNodeId] = stats = { count: 0, size: 0, ids: []};
855            }
856            stats.count++;
857            stats.size += node.selfSize();
858            stats.ids.push(node.id());
859        }
860        this._allocationProfile = new WebInspector.AllocationProfile(profile, liveObjects);
861        this._progress.updateStatus("Done");
862    }
863}
864
865/**
866 * @constructor
867 */
868function HeapSnapshotMetainfo()
869{
870    // New format.
871    this.node_fields = [];
872    this.node_types = [];
873    this.edge_fields = [];
874    this.edge_types = [];
875    this.trace_function_info_fields = [];
876    this.trace_node_fields = [];
877    this.type_strings = {};
878}
879
880/**
881 * @constructor
882 */
883function HeapSnapshotHeader()
884{
885    // New format.
886    this.title = "";
887    this.meta = new HeapSnapshotMetainfo();
888    this.node_count = 0;
889    this.edge_count = 0;
890}
891
892WebInspector.HeapSnapshot.prototype = {
893    _init: function()
894    {
895        var meta = this._metaNode;
896
897        this._nodeTypeOffset = meta.node_fields.indexOf("type");
898        this._nodeNameOffset = meta.node_fields.indexOf("name");
899        this._nodeIdOffset = meta.node_fields.indexOf("id");
900        this._nodeSelfSizeOffset = meta.node_fields.indexOf("self_size");
901        this._nodeEdgeCountOffset = meta.node_fields.indexOf("edge_count");
902        this._nodeTraceNodeIdOffset = meta.node_fields.indexOf("trace_node_id");
903        this._nodeFieldCount = meta.node_fields.length;
904
905        this._nodeTypes = meta.node_types[this._nodeTypeOffset];
906        this._nodeHiddenType = this._nodeTypes.indexOf("hidden");
907        this._nodeObjectType = this._nodeTypes.indexOf("object");
908        this._nodeNativeType = this._nodeTypes.indexOf("native");
909        this._nodeConsStringType = this._nodeTypes.indexOf("concatenated string");
910        this._nodeSlicedStringType = this._nodeTypes.indexOf("sliced string");
911        this._nodeCodeType = this._nodeTypes.indexOf("code");
912        this._nodeSyntheticType = this._nodeTypes.indexOf("synthetic");
913
914        this._edgeFieldsCount = meta.edge_fields.length;
915        this._edgeTypeOffset = meta.edge_fields.indexOf("type");
916        this._edgeNameOffset = meta.edge_fields.indexOf("name_or_index");
917        this._edgeToNodeOffset = meta.edge_fields.indexOf("to_node");
918
919        this._edgeTypes = meta.edge_types[this._edgeTypeOffset];
920        this._edgeTypes.push("invisible");
921        this._edgeElementType = this._edgeTypes.indexOf("element");
922        this._edgeHiddenType = this._edgeTypes.indexOf("hidden");
923        this._edgeInternalType = this._edgeTypes.indexOf("internal");
924        this._edgeShortcutType = this._edgeTypes.indexOf("shortcut");
925        this._edgeWeakType = this._edgeTypes.indexOf("weak");
926        this._edgeInvisibleType = this._edgeTypes.indexOf("invisible");
927
928        this.nodeCount = this._nodes.length / this._nodeFieldCount;
929        this._edgeCount = this._containmentEdges.length / this._edgeFieldsCount;
930
931        this._progress.updateStatus("Building edge indexes\u2026");
932        this._buildEdgeIndexes();
933        this._progress.updateStatus("Building retainers\u2026");
934        this._buildRetainers();
935        this._progress.updateStatus("Calculating node flags\u2026");
936        this._calculateFlags();
937        this._progress.updateStatus("Calculating distances\u2026");
938        this._calculateDistances();
939        this._progress.updateStatus("Building postorder index\u2026");
940        var result = this._buildPostOrderIndex();
941        // Actually it is array that maps node ordinal number to dominator node ordinal number.
942        this._progress.updateStatus("Building dominator tree\u2026");
943        this._dominatorsTree = this._buildDominatorTree(result.postOrderIndex2NodeOrdinal, result.nodeOrdinal2PostOrderIndex);
944        this._progress.updateStatus("Calculating retained sizes\u2026");
945        this._calculateRetainedSizes(result.postOrderIndex2NodeOrdinal);
946        this._progress.updateStatus("Buiding dominated nodes\u2026");
947        this._buildDominatedNodes();
948        this._progress.updateStatus("Calculating statistics\u2026");
949        this._calculateStatistics();
950        this._progress.updateStatus("Finished processing.");
951    },
952
953    _buildEdgeIndexes: function()
954    {
955        var nodes = this._nodes;
956        var nodeCount = this.nodeCount;
957        var firstEdgeIndexes = this._firstEdgeIndexes = new Uint32Array(nodeCount + 1);
958        var nodeFieldCount = this._nodeFieldCount;
959        var edgeFieldsCount = this._edgeFieldsCount;
960        var nodeEdgeCountOffset = this._nodeEdgeCountOffset;
961        firstEdgeIndexes[nodeCount] = this._containmentEdges.length;
962        for (var nodeOrdinal = 0, edgeIndex = 0; nodeOrdinal < nodeCount; ++nodeOrdinal) {
963            firstEdgeIndexes[nodeOrdinal] = edgeIndex;
964            edgeIndex += nodes[nodeOrdinal * nodeFieldCount + nodeEdgeCountOffset] * edgeFieldsCount;
965        }
966    },
967
968    _buildRetainers: function()
969    {
970        var retainingNodes = this._retainingNodes = new Uint32Array(this._edgeCount);
971        var retainingEdges = this._retainingEdges = new Uint32Array(this._edgeCount);
972        // Index of the first retainer in the _retainingNodes and _retainingEdges
973        // arrays. Addressed by retained node index.
974        var firstRetainerIndex = this._firstRetainerIndex = new Uint32Array(this.nodeCount + 1);
975
976        var containmentEdges = this._containmentEdges;
977        var edgeFieldsCount = this._edgeFieldsCount;
978        var nodeFieldCount = this._nodeFieldCount;
979        var edgeToNodeOffset = this._edgeToNodeOffset;
980        var firstEdgeIndexes = this._firstEdgeIndexes;
981        var nodeCount = this.nodeCount;
982
983        for (var toNodeFieldIndex = edgeToNodeOffset, l = containmentEdges.length; toNodeFieldIndex < l; toNodeFieldIndex += edgeFieldsCount) {
984            var toNodeIndex = containmentEdges[toNodeFieldIndex];
985            if (toNodeIndex % nodeFieldCount)
986                throw new Error("Invalid toNodeIndex " + toNodeIndex);
987            ++firstRetainerIndex[toNodeIndex / nodeFieldCount];
988        }
989        for (var i = 0, firstUnusedRetainerSlot = 0; i < nodeCount; i++) {
990            var retainersCount = firstRetainerIndex[i];
991            firstRetainerIndex[i] = firstUnusedRetainerSlot;
992            retainingNodes[firstUnusedRetainerSlot] = retainersCount;
993            firstUnusedRetainerSlot += retainersCount;
994        }
995        firstRetainerIndex[nodeCount] = retainingNodes.length;
996
997        var nextNodeFirstEdgeIndex = firstEdgeIndexes[0];
998        for (var srcNodeOrdinal = 0; srcNodeOrdinal < nodeCount; ++srcNodeOrdinal) {
999            var firstEdgeIndex = nextNodeFirstEdgeIndex;
1000            nextNodeFirstEdgeIndex = firstEdgeIndexes[srcNodeOrdinal + 1];
1001            var srcNodeIndex = srcNodeOrdinal * nodeFieldCount;
1002            for (var edgeIndex = firstEdgeIndex; edgeIndex < nextNodeFirstEdgeIndex; edgeIndex += edgeFieldsCount) {
1003                var toNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
1004                if (toNodeIndex % nodeFieldCount)
1005                    throw new Error("Invalid toNodeIndex " + toNodeIndex);
1006                var firstRetainerSlotIndex = firstRetainerIndex[toNodeIndex / nodeFieldCount];
1007                var nextUnusedRetainerSlotIndex = firstRetainerSlotIndex + (--retainingNodes[firstRetainerSlotIndex]);
1008                retainingNodes[nextUnusedRetainerSlotIndex] = srcNodeIndex;
1009                retainingEdges[nextUnusedRetainerSlotIndex] = edgeIndex;
1010            }
1011        }
1012    },
1013
1014    /**
1015     * @param {number=} nodeIndex
1016     */
1017    createNode: function(nodeIndex)
1018    {
1019        throw new Error("Not implemented");
1020    },
1021
1022    /**
1023     * @param {number} edgeIndex
1024     * @return {!WebInspector.JSHeapSnapshotEdge}
1025     */
1026    createEdge: function(edgeIndex)
1027    {
1028        throw new Error("Not implemented");
1029    },
1030
1031    /**
1032     * @param {number} retainerIndex
1033     * @return {!WebInspector.JSHeapSnapshotRetainerEdge}
1034     */
1035    createRetainingEdge: function(retainerIndex)
1036    {
1037        throw new Error("Not implemented");
1038    },
1039
1040    dispose: function()
1041    {
1042        delete this._nodes;
1043        delete this._strings;
1044        delete this._retainingEdges;
1045        delete this._retainingNodes;
1046        delete this._firstRetainerIndex;
1047        delete this._aggregates;
1048        delete this._aggregatesSortedFlags;
1049        delete this._dominatedNodes;
1050        delete this._firstDominatedNodeIndex;
1051        delete this._nodeDistances;
1052        delete this._dominatorsTree;
1053    },
1054
1055    _allNodes: function()
1056    {
1057        return new WebInspector.HeapSnapshotNodeIterator(this.rootNode());
1058    },
1059
1060    /**
1061     * @return {!WebInspector.HeapSnapshotNode}
1062     */
1063    rootNode: function()
1064    {
1065        return this.createNode(this._rootNodeIndex);
1066    },
1067
1068    get rootNodeIndex()
1069    {
1070        return this._rootNodeIndex;
1071    },
1072
1073    get totalSize()
1074    {
1075        return this.rootNode().retainedSize();
1076    },
1077
1078    _getDominatedIndex: function(nodeIndex)
1079    {
1080        if (nodeIndex % this._nodeFieldCount)
1081            throw new Error("Invalid nodeIndex: " + nodeIndex);
1082        return this._firstDominatedNodeIndex[nodeIndex / this._nodeFieldCount];
1083    },
1084
1085    /**
1086     * @param {!WebInspector.HeapSnapshotNode} node
1087     * @return {!Uint32Array}
1088     */
1089    _dominatedNodesOfNode: function(node)
1090    {
1091        var dominatedIndexFrom = this._getDominatedIndex(node.nodeIndex);
1092        var dominatedIndexTo = this._getDominatedIndex(node._nextNodeIndex());
1093        return this._dominatedNodes.subarray(dominatedIndexFrom, dominatedIndexTo);
1094    },
1095
1096    /**
1097     * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} nodeFilter
1098     * @return {!Object.<string, !WebInspector.HeapSnapshotCommon.Aggregate>}
1099     */
1100    aggregatesWithFilter: function(nodeFilter)
1101    {
1102        var minNodeId = nodeFilter.minNodeId;
1103        var maxNodeId = nodeFilter.maxNodeId;
1104        var allocationNodeId = nodeFilter.allocationNodeId;
1105        var key;
1106        var filter;
1107        if (typeof allocationNodeId === "number") {
1108            filter = this._createAllocationStackFilter(allocationNodeId);
1109        } else if (typeof minNodeId === "number" && typeof maxNodeId === "number") {
1110            key = minNodeId + ".." + maxNodeId;
1111            filter = this._createNodeIdFilter(minNodeId, maxNodeId);
1112        } else {
1113            key = "allObjects";
1114        }
1115        return this.aggregates(false, key, filter);
1116    },
1117
1118    /**
1119     * @param {number} minNodeId
1120     * @param {number} maxNodeId
1121     * @return {function(!WebInspector.HeapSnapshotNode):boolean}
1122     */
1123    _createNodeIdFilter: function(minNodeId, maxNodeId)
1124    {
1125        /**
1126         * @param {!WebInspector.HeapSnapshotNode} node
1127         * @return {boolean}
1128         */
1129        function nodeIdFilter(node)
1130        {
1131            var id = node.id();
1132            return id > minNodeId && id <= maxNodeId;
1133        }
1134        return nodeIdFilter;
1135    },
1136
1137    /**
1138     * @param {number} bottomUpAllocationNodeId
1139     * @return {function(!WebInspector.HeapSnapshotNode):boolean|undefined}
1140     */
1141    _createAllocationStackFilter: function(bottomUpAllocationNodeId)
1142    {
1143        var traceIds = this._allocationProfile.traceIds(bottomUpAllocationNodeId);
1144        if (!traceIds.length)
1145            return undefined;
1146        var set = {};
1147        for (var i = 0; i < traceIds.length; i++)
1148            set[traceIds[i]] = true;
1149        /**
1150         * @param {!WebInspector.HeapSnapshotNode} node
1151         * @return {boolean}
1152         */
1153        function traceIdFilter(node)
1154        {
1155            return !!set[node.traceNodeId()];
1156        };
1157        return traceIdFilter;
1158    },
1159
1160    /**
1161     * @param {boolean} sortedIndexes
1162     * @param {string=} key
1163     * @param {function(!WebInspector.HeapSnapshotNode):boolean=} filter
1164     * @return {!Object.<string, !WebInspector.HeapSnapshotCommon.Aggregate>}
1165     */
1166    aggregates: function(sortedIndexes, key, filter)
1167    {
1168        var aggregatesByClassName = key && this._aggregates[key];
1169        if (!aggregatesByClassName) {
1170            var aggregates = this._buildAggregates(filter);
1171            this._calculateClassesRetainedSize(aggregates.aggregatesByClassIndex, filter);
1172            aggregatesByClassName = aggregates.aggregatesByClassName;
1173            if (key)
1174                this._aggregates[key] = aggregatesByClassName;
1175        }
1176
1177        if (sortedIndexes && (!key || !this._aggregatesSortedFlags[key])) {
1178            this._sortAggregateIndexes(aggregatesByClassName);
1179            if (key)
1180                this._aggregatesSortedFlags[key] = sortedIndexes;
1181        }
1182        return aggregatesByClassName;
1183    },
1184
1185    /**
1186     * @return {!Array.<!WebInspector.HeapSnapshotCommon.SerializedAllocationNode>}
1187     */
1188    allocationTracesTops: function()
1189    {
1190        return this._allocationProfile.serializeTraceTops();
1191    },
1192
1193    /**
1194     * @param {number} nodeId
1195     * @return {!WebInspector.HeapSnapshotCommon.AllocationNodeCallers}
1196     */
1197    allocationNodeCallers: function(nodeId)
1198    {
1199        return this._allocationProfile.serializeCallers(nodeId);
1200    },
1201
1202    /**
1203     * @param {number} nodeIndex
1204     * @return {?Array.<!WebInspector.HeapSnapshotCommon.AllocationStackFrame>}
1205     */
1206    allocationStack: function(nodeIndex)
1207    {
1208        var node = this.createNode(nodeIndex);
1209        var allocationNodeId = node.traceNodeId();
1210        if (!allocationNodeId)
1211            return null;
1212        return this._allocationProfile.serializeAllocationStack(allocationNodeId);
1213    },
1214
1215    /**
1216     * @return {!Object.<string, !WebInspector.HeapSnapshotCommon.AggregateForDiff>}
1217     */
1218    aggregatesForDiff: function()
1219    {
1220        if (this._aggregatesForDiff)
1221            return this._aggregatesForDiff;
1222
1223        var aggregatesByClassName = this.aggregates(true, "allObjects");
1224        this._aggregatesForDiff  = {};
1225
1226        var node = this.createNode();
1227        for (var className in aggregatesByClassName) {
1228            var aggregate = aggregatesByClassName[className];
1229            var indexes = aggregate.idxs;
1230            var ids = new Array(indexes.length);
1231            var selfSizes = new Array(indexes.length);
1232            for (var i = 0; i < indexes.length; i++) {
1233                node.nodeIndex = indexes[i];
1234                ids[i] = node.id();
1235                selfSizes[i] = node.selfSize();
1236            }
1237
1238            this._aggregatesForDiff[className] = {
1239                indexes: indexes,
1240                ids: ids,
1241                selfSizes: selfSizes
1242            };
1243        }
1244        return this._aggregatesForDiff;
1245    },
1246
1247    /**
1248     * @param {!WebInspector.HeapSnapshotNode} node
1249     * @return {!boolean}
1250     */
1251    _isUserRoot: function(node)
1252    {
1253        return true;
1254    },
1255
1256    /**
1257     * @param {function(!WebInspector.HeapSnapshotNode)} action
1258     * @param {boolean=} userRootsOnly
1259     */
1260    forEachRoot: function(action, userRootsOnly)
1261    {
1262        for (var iter = this.rootNode().edges(); iter.hasNext(); iter.next()) {
1263            var node = iter.edge.node();
1264            if (!userRootsOnly || this._isUserRoot(node))
1265                action(node);
1266        }
1267    },
1268
1269    _calculateDistances: function()
1270    {
1271        var nodeFieldCount = this._nodeFieldCount;
1272        var nodeCount = this.nodeCount;
1273        var distances = this._nodeDistances = new Int32Array(nodeCount);
1274        var noDistance = this._noDistance;
1275        for (var i = 0; i < nodeCount; ++i)
1276            distances[i] = noDistance;
1277
1278        var nodesToVisit = new Uint32Array(this.nodeCount);
1279        var nodesToVisitLength = 0;
1280
1281        /**
1282         * @param {number} distance
1283         * @param {!WebInspector.HeapSnapshotNode} node
1284         */
1285        function enqueueNode(distance, node)
1286        {
1287            var ordinal = node._ordinal();
1288            if (distances[ordinal] !== noDistance)
1289                return;
1290            distances[ordinal] = distance;
1291            nodesToVisit[nodesToVisitLength++] = node.nodeIndex;
1292        }
1293
1294        this.forEachRoot(enqueueNode.bind(null, 1), true);
1295        this._bfs(nodesToVisit, nodesToVisitLength, distances);
1296
1297        // bfs for the rest of objects
1298        nodesToVisitLength = 0;
1299        this.forEachRoot(enqueueNode.bind(null, WebInspector.HeapSnapshotCommon.baseSystemDistance), false);
1300        this._bfs(nodesToVisit, nodesToVisitLength, distances);
1301    },
1302
1303    /**
1304     * @param {!Uint32Array} nodesToVisit
1305     * @param {!number} nodesToVisitLength
1306     * @param {!Int32Array} distances
1307     */
1308    _bfs: function(nodesToVisit, nodesToVisitLength, distances)
1309    {
1310        // Preload fields into local variables for better performance.
1311        var edgeFieldsCount = this._edgeFieldsCount;
1312        var nodeFieldCount = this._nodeFieldCount;
1313        var containmentEdges = this._containmentEdges;
1314        var firstEdgeIndexes = this._firstEdgeIndexes;
1315        var edgeToNodeOffset = this._edgeToNodeOffset;
1316        var edgeTypeOffset = this._edgeTypeOffset;
1317        var nodeCount = this.nodeCount;
1318        var containmentEdgesLength = containmentEdges.length;
1319        var edgeWeakType = this._edgeWeakType;
1320        var noDistance = this._noDistance;
1321
1322        var index = 0;
1323        while (index < nodesToVisitLength) {
1324            var nodeIndex = nodesToVisit[index++]; // shift generates too much garbage.
1325            var nodeOrdinal = nodeIndex / nodeFieldCount;
1326            var distance = distances[nodeOrdinal] + 1;
1327            var firstEdgeIndex = firstEdgeIndexes[nodeOrdinal];
1328            var edgesEnd = firstEdgeIndexes[nodeOrdinal + 1];
1329            for (var edgeIndex = firstEdgeIndex; edgeIndex < edgesEnd; edgeIndex += edgeFieldsCount) {
1330                var edgeType = containmentEdges[edgeIndex + edgeTypeOffset];
1331                if (edgeType == edgeWeakType)
1332                    continue;
1333                var childNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
1334                var childNodeOrdinal = childNodeIndex / nodeFieldCount;
1335                if (distances[childNodeOrdinal] !== noDistance)
1336                    continue;
1337                distances[childNodeOrdinal] = distance;
1338                nodesToVisit[nodesToVisitLength++] = childNodeIndex;
1339            }
1340        }
1341        if (nodesToVisitLength > nodeCount)
1342            throw new Error("BFS failed. Nodes to visit (" + nodesToVisitLength + ") is more than nodes count (" + nodeCount + ")");
1343    },
1344
1345    _buildAggregates: function(filter)
1346    {
1347        var aggregates = {};
1348        var aggregatesByClassName = {};
1349        var classIndexes = [];
1350        var nodes = this._nodes;
1351        var mapAndFlag = this.userObjectsMapAndFlag();
1352        var flags = mapAndFlag ? mapAndFlag.map : null;
1353        var flag = mapAndFlag ? mapAndFlag.flag : 0;
1354        var nodesLength = nodes.length;
1355        var nodeNativeType = this._nodeNativeType;
1356        var nodeFieldCount = this._nodeFieldCount;
1357        var selfSizeOffset = this._nodeSelfSizeOffset;
1358        var nodeTypeOffset = this._nodeTypeOffset;
1359        var node = this.rootNode();
1360        var nodeDistances = this._nodeDistances;
1361
1362        for (var nodeIndex = 0; nodeIndex < nodesLength; nodeIndex += nodeFieldCount) {
1363            var nodeOrdinal = nodeIndex / nodeFieldCount;
1364            if (flags && !(flags[nodeOrdinal] & flag))
1365                continue;
1366            node.nodeIndex = nodeIndex;
1367            if (filter && !filter(node))
1368                continue;
1369            var selfSize = nodes[nodeIndex + selfSizeOffset];
1370            if (!selfSize && nodes[nodeIndex + nodeTypeOffset] !== nodeNativeType)
1371                continue;
1372            var classIndex = node.classIndex();
1373            if (!(classIndex in aggregates)) {
1374                var nodeType = node.type();
1375                var nameMatters = nodeType === "object" || nodeType === "native";
1376                var value = {
1377                    count: 1,
1378                    distance: nodeDistances[nodeOrdinal],
1379                    self: selfSize,
1380                    maxRet: 0,
1381                    type: nodeType,
1382                    name: nameMatters ? node.name() : null,
1383                    idxs: [nodeIndex]
1384                };
1385                aggregates[classIndex] = value;
1386                classIndexes.push(classIndex);
1387                aggregatesByClassName[node.className()] = value;
1388            } else {
1389                var clss = aggregates[classIndex];
1390                clss.distance = Math.min(clss.distance, nodeDistances[nodeOrdinal]);
1391                ++clss.count;
1392                clss.self += selfSize;
1393                clss.idxs.push(nodeIndex);
1394            }
1395        }
1396
1397        // Shave off provisionally allocated space.
1398        for (var i = 0, l = classIndexes.length; i < l; ++i) {
1399            var classIndex = classIndexes[i];
1400            aggregates[classIndex].idxs = aggregates[classIndex].idxs.slice();
1401        }
1402        return {aggregatesByClassName: aggregatesByClassName, aggregatesByClassIndex: aggregates};
1403    },
1404
1405    _calculateClassesRetainedSize: function(aggregates, filter)
1406    {
1407        var rootNodeIndex = this._rootNodeIndex;
1408        var node = this.createNode(rootNodeIndex);
1409        var list = [rootNodeIndex];
1410        var sizes = [-1];
1411        var classes = [];
1412        var seenClassNameIndexes = {};
1413        var nodeFieldCount = this._nodeFieldCount;
1414        var nodeTypeOffset = this._nodeTypeOffset;
1415        var nodeNativeType = this._nodeNativeType;
1416        var dominatedNodes = this._dominatedNodes;
1417        var nodes = this._nodes;
1418        var mapAndFlag = this.userObjectsMapAndFlag();
1419        var flags = mapAndFlag ? mapAndFlag.map : null;
1420        var flag = mapAndFlag ? mapAndFlag.flag : 0;
1421        var firstDominatedNodeIndex = this._firstDominatedNodeIndex;
1422
1423        while (list.length) {
1424            var nodeIndex = list.pop();
1425            node.nodeIndex = nodeIndex;
1426            var classIndex = node.classIndex();
1427            var seen = !!seenClassNameIndexes[classIndex];
1428            var nodeOrdinal = nodeIndex / nodeFieldCount;
1429            var dominatedIndexFrom = firstDominatedNodeIndex[nodeOrdinal];
1430            var dominatedIndexTo = firstDominatedNodeIndex[nodeOrdinal + 1];
1431
1432            if (!seen &&
1433                (!flags || (flags[nodeOrdinal] & flag)) &&
1434                (!filter || filter(node)) &&
1435                (node.selfSize() || nodes[nodeIndex + nodeTypeOffset] === nodeNativeType)
1436               ) {
1437                aggregates[classIndex].maxRet += node.retainedSize();
1438                if (dominatedIndexFrom !== dominatedIndexTo) {
1439                    seenClassNameIndexes[classIndex] = true;
1440                    sizes.push(list.length);
1441                    classes.push(classIndex);
1442                }
1443            }
1444            for (var i = dominatedIndexFrom; i < dominatedIndexTo; i++)
1445                list.push(dominatedNodes[i]);
1446
1447            var l = list.length;
1448            while (sizes[sizes.length - 1] === l) {
1449                sizes.pop();
1450                classIndex = classes.pop();
1451                seenClassNameIndexes[classIndex] = false;
1452            }
1453        }
1454    },
1455
1456    _sortAggregateIndexes: function(aggregates)
1457    {
1458        var nodeA = this.createNode();
1459        var nodeB = this.createNode();
1460        for (var clss in aggregates)
1461            aggregates[clss].idxs.sort(
1462                function(idxA, idxB) {
1463                    nodeA.nodeIndex = idxA;
1464                    nodeB.nodeIndex = idxB;
1465                    return nodeA.id() < nodeB.id() ? -1 : 1;
1466                });
1467    },
1468
1469    _buildPostOrderIndex: function()
1470    {
1471        var nodeFieldCount = this._nodeFieldCount;
1472        var nodes = this._nodes;
1473        var nodeCount = this.nodeCount;
1474        var rootNodeOrdinal = this._rootNodeIndex / nodeFieldCount;
1475
1476        var edgeFieldsCount = this._edgeFieldsCount;
1477        var edgeTypeOffset = this._edgeTypeOffset;
1478        var edgeToNodeOffset = this._edgeToNodeOffset;
1479        var edgeShortcutType = this._edgeShortcutType;
1480        var firstEdgeIndexes = this._firstEdgeIndexes;
1481        var containmentEdges = this._containmentEdges;
1482
1483        var mapAndFlag = this.userObjectsMapAndFlag();
1484        var flags = mapAndFlag ? mapAndFlag.map : null;
1485        var flag = mapAndFlag ? mapAndFlag.flag : 0;
1486
1487        var stackNodes = new Uint32Array(nodeCount);
1488        var stackCurrentEdge = new Uint32Array(nodeCount);
1489        var postOrderIndex2NodeOrdinal = new Uint32Array(nodeCount);
1490        var nodeOrdinal2PostOrderIndex = new Uint32Array(nodeCount);
1491        var visited = new Uint8Array(nodeCount);
1492        var postOrderIndex = 0;
1493
1494        var stackTop = 0;
1495        stackNodes[0] = rootNodeOrdinal;
1496        stackCurrentEdge[0] = firstEdgeIndexes[rootNodeOrdinal];
1497        visited[rootNodeOrdinal] = 1;
1498
1499        while (stackTop >= 0) {
1500            var nodeOrdinal = stackNodes[stackTop];
1501            var edgeIndex = stackCurrentEdge[stackTop];
1502            var edgesEnd = firstEdgeIndexes[nodeOrdinal + 1];
1503
1504            if (edgeIndex < edgesEnd) {
1505                stackCurrentEdge[stackTop] += edgeFieldsCount;
1506                if (nodeOrdinal !== rootNodeOrdinal && containmentEdges[edgeIndex + edgeTypeOffset] === edgeShortcutType)
1507                    continue;
1508                var childNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
1509                var childNodeOrdinal = childNodeIndex / nodeFieldCount;
1510                if (visited[childNodeOrdinal])
1511                    continue;
1512                var nodeFlag = !flags || (flags[nodeOrdinal] & flag);
1513                var childNodeFlag = !flags || (flags[childNodeOrdinal] & flag);
1514                // We are skipping the edges from non-page-owned nodes to page-owned nodes.
1515                // Otherwise the dominators for the objects that also were retained by debugger would be affected.
1516                if (nodeOrdinal !== rootNodeOrdinal && childNodeFlag && !nodeFlag)
1517                    continue;
1518                ++stackTop;
1519                stackNodes[stackTop] = childNodeOrdinal;
1520                stackCurrentEdge[stackTop] = firstEdgeIndexes[childNodeOrdinal];
1521                visited[childNodeOrdinal] = 1;
1522            } else {
1523                // Done with all the node children
1524                nodeOrdinal2PostOrderIndex[nodeOrdinal] = postOrderIndex;
1525                postOrderIndex2NodeOrdinal[postOrderIndex++] = nodeOrdinal;
1526                --stackTop;
1527            }
1528        }
1529
1530        if (postOrderIndex !== nodeCount) {
1531            console.log("Error: Corrupted snapshot. " + (nodeCount - postOrderIndex) + " nodes are unreachable from the root:");
1532            var dumpNode = this.rootNode();
1533            for (var i = 0; i < nodeCount; ++i) {
1534                if (!visited[i]) {
1535                    // Fix it by giving the node a postorder index anyway.
1536                    nodeOrdinal2PostOrderIndex[i] = postOrderIndex;
1537                    postOrderIndex2NodeOrdinal[postOrderIndex++] = i;
1538                    dumpNode.nodeIndex = i * nodeFieldCount;
1539                    console.log(JSON.stringify(dumpNode.serialize()));
1540                    for (var retainers = dumpNode.retainers(); retainers.hasNext(); retainers = retainers.item().node().retainers())
1541                        console.log("  edgeName: " + retainers.item().name() + " nodeClassName: " + retainers.item().node().className());
1542                }
1543            }
1544        }
1545
1546        return {postOrderIndex2NodeOrdinal: postOrderIndex2NodeOrdinal, nodeOrdinal2PostOrderIndex: nodeOrdinal2PostOrderIndex};
1547    },
1548
1549    // The algorithm is based on the article:
1550    // K. Cooper, T. Harvey and K. Kennedy "A Simple, Fast Dominance Algorithm"
1551    // Softw. Pract. Exper. 4 (2001), pp. 1-10.
1552    /**
1553     * @param {!Array.<number>} postOrderIndex2NodeOrdinal
1554     * @param {!Array.<number>} nodeOrdinal2PostOrderIndex
1555     */
1556    _buildDominatorTree: function(postOrderIndex2NodeOrdinal, nodeOrdinal2PostOrderIndex)
1557    {
1558        var nodeFieldCount = this._nodeFieldCount;
1559        var nodes = this._nodes;
1560        var firstRetainerIndex = this._firstRetainerIndex;
1561        var retainingNodes = this._retainingNodes;
1562        var retainingEdges = this._retainingEdges;
1563        var edgeFieldsCount = this._edgeFieldsCount;
1564        var edgeTypeOffset = this._edgeTypeOffset;
1565        var edgeToNodeOffset = this._edgeToNodeOffset;
1566        var edgeShortcutType = this._edgeShortcutType;
1567        var firstEdgeIndexes = this._firstEdgeIndexes;
1568        var containmentEdges = this._containmentEdges;
1569        var containmentEdgesLength = this._containmentEdges.length;
1570        var rootNodeIndex = this._rootNodeIndex;
1571
1572        var mapAndFlag = this.userObjectsMapAndFlag();
1573        var flags = mapAndFlag ? mapAndFlag.map : null;
1574        var flag = mapAndFlag ? mapAndFlag.flag : 0;
1575
1576        var nodesCount = postOrderIndex2NodeOrdinal.length;
1577        var rootPostOrderedIndex = nodesCount - 1;
1578        var noEntry = nodesCount;
1579        var dominators = new Uint32Array(nodesCount);
1580        for (var i = 0; i < rootPostOrderedIndex; ++i)
1581            dominators[i] = noEntry;
1582        dominators[rootPostOrderedIndex] = rootPostOrderedIndex;
1583
1584        // The affected array is used to mark entries which dominators
1585        // have to be racalculated because of changes in their retainers.
1586        var affected = new Uint8Array(nodesCount);
1587        var nodeOrdinal;
1588
1589        { // Mark the root direct children as affected.
1590            nodeOrdinal = this._rootNodeIndex / nodeFieldCount;
1591            var beginEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal] + edgeToNodeOffset;
1592            var endEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal + 1];
1593            for (var toNodeFieldIndex = beginEdgeToNodeFieldIndex;
1594                 toNodeFieldIndex < endEdgeToNodeFieldIndex;
1595                 toNodeFieldIndex += edgeFieldsCount) {
1596                var childNodeOrdinal = containmentEdges[toNodeFieldIndex] / nodeFieldCount;
1597                affected[nodeOrdinal2PostOrderIndex[childNodeOrdinal]] = 1;
1598            }
1599        }
1600
1601        var changed = true;
1602        while (changed) {
1603            changed = false;
1604            for (var postOrderIndex = rootPostOrderedIndex - 1; postOrderIndex >= 0; --postOrderIndex) {
1605                if (affected[postOrderIndex] === 0)
1606                    continue;
1607                affected[postOrderIndex] = 0;
1608                // If dominator of the entry has already been set to root,
1609                // then it can't propagate any further.
1610                if (dominators[postOrderIndex] === rootPostOrderedIndex)
1611                    continue;
1612                nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex];
1613                var nodeFlag = !flags || (flags[nodeOrdinal] & flag);
1614                var newDominatorIndex = noEntry;
1615                var beginRetainerIndex = firstRetainerIndex[nodeOrdinal];
1616                var endRetainerIndex = firstRetainerIndex[nodeOrdinal + 1];
1617                for (var retainerIndex = beginRetainerIndex; retainerIndex < endRetainerIndex; ++retainerIndex) {
1618                    var retainerEdgeIndex = retainingEdges[retainerIndex];
1619                    var retainerEdgeType = containmentEdges[retainerEdgeIndex + edgeTypeOffset];
1620                    var retainerNodeIndex = retainingNodes[retainerIndex];
1621                    if (retainerNodeIndex !== rootNodeIndex && retainerEdgeType === edgeShortcutType)
1622                        continue;
1623                    var retainerNodeOrdinal = retainerNodeIndex / nodeFieldCount;
1624                    var retainerNodeFlag = !flags || (flags[retainerNodeOrdinal] & flag);
1625                    // We are skipping the edges from non-page-owned nodes to page-owned nodes.
1626                    // Otherwise the dominators for the objects that also were retained by debugger would be affected.
1627                    if (retainerNodeIndex !== rootNodeIndex && nodeFlag && !retainerNodeFlag)
1628                        continue;
1629                    var retanerPostOrderIndex = nodeOrdinal2PostOrderIndex[retainerNodeOrdinal];
1630                    if (dominators[retanerPostOrderIndex] !== noEntry) {
1631                        if (newDominatorIndex === noEntry)
1632                            newDominatorIndex = retanerPostOrderIndex;
1633                        else {
1634                            while (retanerPostOrderIndex !== newDominatorIndex) {
1635                                while (retanerPostOrderIndex < newDominatorIndex)
1636                                    retanerPostOrderIndex = dominators[retanerPostOrderIndex];
1637                                while (newDominatorIndex < retanerPostOrderIndex)
1638                                    newDominatorIndex = dominators[newDominatorIndex];
1639                            }
1640                        }
1641                        // If idom has already reached the root, it doesn't make sense
1642                        // to check other retainers.
1643                        if (newDominatorIndex === rootPostOrderedIndex)
1644                            break;
1645                    }
1646                }
1647                if (newDominatorIndex !== noEntry && dominators[postOrderIndex] !== newDominatorIndex) {
1648                    dominators[postOrderIndex] = newDominatorIndex;
1649                    changed = true;
1650                    nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex];
1651                    beginEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal] + edgeToNodeOffset;
1652                    endEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal + 1];
1653                    for (var toNodeFieldIndex = beginEdgeToNodeFieldIndex;
1654                         toNodeFieldIndex < endEdgeToNodeFieldIndex;
1655                         toNodeFieldIndex += edgeFieldsCount) {
1656                        var childNodeOrdinal = containmentEdges[toNodeFieldIndex] / nodeFieldCount;
1657                        affected[nodeOrdinal2PostOrderIndex[childNodeOrdinal]] = 1;
1658                    }
1659                }
1660            }
1661        }
1662
1663        var dominatorsTree = new Uint32Array(nodesCount);
1664        for (var postOrderIndex = 0, l = dominators.length; postOrderIndex < l; ++postOrderIndex) {
1665            nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex];
1666            dominatorsTree[nodeOrdinal] = postOrderIndex2NodeOrdinal[dominators[postOrderIndex]];
1667        }
1668        return dominatorsTree;
1669    },
1670
1671    _calculateRetainedSizes: function(postOrderIndex2NodeOrdinal)
1672    {
1673        var nodeCount = this.nodeCount;
1674        var nodes = this._nodes;
1675        var nodeSelfSizeOffset = this._nodeSelfSizeOffset;
1676        var nodeFieldCount = this._nodeFieldCount;
1677        var dominatorsTree = this._dominatorsTree;
1678        var retainedSizes = this._retainedSizes = new Float64Array(nodeCount);
1679
1680        for (var nodeOrdinal = 0; nodeOrdinal < nodeCount; ++nodeOrdinal)
1681            retainedSizes[nodeOrdinal] = nodes[nodeOrdinal * nodeFieldCount + nodeSelfSizeOffset];
1682
1683        // Propagate retained sizes for each node excluding root.
1684        for (var postOrderIndex = 0; postOrderIndex < nodeCount - 1; ++postOrderIndex) {
1685            var nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex];
1686            var dominatorOrdinal = dominatorsTree[nodeOrdinal];
1687            retainedSizes[dominatorOrdinal] += retainedSizes[nodeOrdinal];
1688        }
1689    },
1690
1691    _buildDominatedNodes: function()
1692    {
1693        // Builds up two arrays:
1694        //  - "dominatedNodes" is a continuous array, where each node owns an
1695        //    interval (can be empty) with corresponding dominated nodes.
1696        //  - "indexArray" is an array of indexes in the "dominatedNodes"
1697        //    with the same positions as in the _nodeIndex.
1698        var indexArray = this._firstDominatedNodeIndex = new Uint32Array(this.nodeCount + 1);
1699        // All nodes except the root have dominators.
1700        var dominatedNodes = this._dominatedNodes = new Uint32Array(this.nodeCount - 1);
1701
1702        // Count the number of dominated nodes for each node. Skip the root (node at
1703        // index 0) as it is the only node that dominates itself.
1704        var nodeFieldCount = this._nodeFieldCount;
1705        var dominatorsTree = this._dominatorsTree;
1706
1707        var fromNodeOrdinal = 0;
1708        var toNodeOrdinal = this.nodeCount;
1709        var rootNodeOrdinal = this._rootNodeIndex / nodeFieldCount;
1710        if (rootNodeOrdinal === fromNodeOrdinal)
1711            fromNodeOrdinal = 1;
1712        else if (rootNodeOrdinal === toNodeOrdinal - 1)
1713            toNodeOrdinal = toNodeOrdinal - 1;
1714        else
1715            throw new Error("Root node is expected to be either first or last");
1716        for (var nodeOrdinal = fromNodeOrdinal; nodeOrdinal < toNodeOrdinal; ++nodeOrdinal)
1717            ++indexArray[dominatorsTree[nodeOrdinal]];
1718        // Put in the first slot of each dominatedNodes slice the count of entries
1719        // that will be filled.
1720        var firstDominatedNodeIndex = 0;
1721        for (var i = 0, l = this.nodeCount; i < l; ++i) {
1722            var dominatedCount = dominatedNodes[firstDominatedNodeIndex] = indexArray[i];
1723            indexArray[i] = firstDominatedNodeIndex;
1724            firstDominatedNodeIndex += dominatedCount;
1725        }
1726        indexArray[this.nodeCount] = dominatedNodes.length;
1727        // Fill up the dominatedNodes array with indexes of dominated nodes. Skip the root (node at
1728        // index 0) as it is the only node that dominates itself.
1729        for (var nodeOrdinal = fromNodeOrdinal; nodeOrdinal < toNodeOrdinal; ++nodeOrdinal) {
1730            var dominatorOrdinal = dominatorsTree[nodeOrdinal];
1731            var dominatedRefIndex = indexArray[dominatorOrdinal];
1732            dominatedRefIndex += (--dominatedNodes[dominatedRefIndex]);
1733            dominatedNodes[dominatedRefIndex] = nodeOrdinal * nodeFieldCount;
1734        }
1735    },
1736
1737    _calculateFlags: function()
1738    {
1739        throw new Error("Not implemented");
1740    },
1741
1742    _calculateStatistics: function()
1743    {
1744        throw new Error("Not implemented");
1745    },
1746
1747    userObjectsMapAndFlag: function()
1748    {
1749        throw new Error("Not implemented");
1750    },
1751
1752    /**
1753     * @param {string} baseSnapshotId
1754     * @param {!Object.<string, !WebInspector.HeapSnapshotCommon.AggregateForDiff>} baseSnapshotAggregates
1755     * @return {!Object.<string, !WebInspector.HeapSnapshotCommon.Diff>}
1756     */
1757    calculateSnapshotDiff: function(baseSnapshotId, baseSnapshotAggregates)
1758    {
1759        var snapshotDiff = this._snapshotDiffs[baseSnapshotId];
1760        if (snapshotDiff)
1761            return snapshotDiff;
1762        snapshotDiff = {};
1763
1764        var aggregates = this.aggregates(true, "allObjects");
1765        for (var className in baseSnapshotAggregates) {
1766            var baseAggregate = baseSnapshotAggregates[className];
1767            var diff = this._calculateDiffForClass(baseAggregate, aggregates[className]);
1768            if (diff)
1769                snapshotDiff[className] = diff;
1770        }
1771        var emptyBaseAggregate = new WebInspector.HeapSnapshotCommon.AggregateForDiff();
1772        for (var className in aggregates) {
1773            if (className in baseSnapshotAggregates)
1774                continue;
1775            snapshotDiff[className] = this._calculateDiffForClass(emptyBaseAggregate, aggregates[className]);
1776        }
1777
1778        this._snapshotDiffs[baseSnapshotId] = snapshotDiff;
1779        return snapshotDiff;
1780    },
1781
1782    /**
1783     * @param {!WebInspector.HeapSnapshotCommon.AggregateForDiff} baseAggregate
1784     * @param {!WebInspector.HeapSnapshotCommon.Aggregate} aggregate
1785     * @return {?WebInspector.HeapSnapshotCommon.Diff}
1786     */
1787    _calculateDiffForClass: function(baseAggregate, aggregate)
1788    {
1789        var baseIds = baseAggregate.ids;
1790        var baseIndexes = baseAggregate.indexes;
1791        var baseSelfSizes = baseAggregate.selfSizes;
1792
1793        var indexes = aggregate ? aggregate.idxs : [];
1794
1795        var i = 0, l = baseIds.length;
1796        var j = 0, m = indexes.length;
1797        var diff = new WebInspector.HeapSnapshotCommon.Diff();
1798
1799        var nodeB = this.createNode(indexes[j]);
1800        while (i < l && j < m) {
1801            var nodeAId = baseIds[i];
1802            if (nodeAId < nodeB.id()) {
1803                diff.deletedIndexes.push(baseIndexes[i]);
1804                diff.removedCount++;
1805                diff.removedSize += baseSelfSizes[i];
1806                ++i;
1807            } else if (nodeAId > nodeB.id()) { // Native nodes(e.g. dom groups) may have ids less than max JS object id in the base snapshot
1808                diff.addedIndexes.push(indexes[j]);
1809                diff.addedCount++;
1810                diff.addedSize += nodeB.selfSize();
1811                nodeB.nodeIndex = indexes[++j];
1812            } else { // nodeAId === nodeB.id()
1813                ++i;
1814                nodeB.nodeIndex = indexes[++j];
1815            }
1816        }
1817        while (i < l) {
1818            diff.deletedIndexes.push(baseIndexes[i]);
1819            diff.removedCount++;
1820            diff.removedSize += baseSelfSizes[i];
1821            ++i;
1822        }
1823        while (j < m) {
1824            diff.addedIndexes.push(indexes[j]);
1825            diff.addedCount++;
1826            diff.addedSize += nodeB.selfSize();
1827            nodeB.nodeIndex = indexes[++j];
1828        }
1829        diff.countDelta = diff.addedCount - diff.removedCount;
1830        diff.sizeDelta = diff.addedSize - diff.removedSize;
1831        if (!diff.addedCount && !diff.removedCount)
1832            return null;
1833        return diff;
1834    },
1835
1836    _nodeForSnapshotObjectId: function(snapshotObjectId)
1837    {
1838        for (var it = this._allNodes(); it.hasNext(); it.next()) {
1839            if (it.node.id() === snapshotObjectId)
1840                return it.node;
1841        }
1842        return null;
1843    },
1844
1845    /**
1846     * @param {string} snapshotObjectId
1847     * @return {?string}
1848     */
1849    nodeClassName: function(snapshotObjectId)
1850    {
1851        var node = this._nodeForSnapshotObjectId(snapshotObjectId);
1852        if (node)
1853            return node.className();
1854        return null;
1855    },
1856
1857    /**
1858     * @param {string} name
1859     * @return {!Array.<number>}
1860     */
1861    idsOfObjectsWithName: function(name)
1862    {
1863        var ids = [];
1864        for (var it = this._allNodes(); it.hasNext(); it.next()) {
1865            if (it.item().name() === name)
1866                ids.push(it.item().id());
1867        }
1868        return ids;
1869    },
1870
1871    /**
1872     * @param {string} snapshotObjectId
1873     * @return {?Array.<string>}
1874     */
1875    dominatorIdsForNode: function(snapshotObjectId)
1876    {
1877        var node = this._nodeForSnapshotObjectId(snapshotObjectId);
1878        if (!node)
1879            return null;
1880        var result = [];
1881        while (!node.isRoot()) {
1882            result.push(node.id());
1883            node.nodeIndex = node.dominatorIndex();
1884        }
1885        return result;
1886    },
1887
1888    /**
1889     * @param {number} nodeIndex
1890     * @return {!WebInspector.HeapSnapshotEdgesProvider}
1891     */
1892    createEdgesProvider: function(nodeIndex)
1893    {
1894        var node = this.createNode(nodeIndex);
1895        var filter = this.containmentEdgesFilter();
1896        var indexProvider = new WebInspector.HeapSnapshotEdgeIndexProvider(this);
1897        return new WebInspector.HeapSnapshotEdgesProvider(this, filter, node.edges(), indexProvider);
1898    },
1899
1900    /**
1901     * @param {number} nodeIndex
1902     * @param {?function(!WebInspector.HeapSnapshotEdge):boolean} filter
1903     * @return {!WebInspector.HeapSnapshotEdgesProvider}
1904     */
1905    createEdgesProviderForTest: function(nodeIndex, filter)
1906    {
1907        var node = this.createNode(nodeIndex);
1908        var indexProvider = new WebInspector.HeapSnapshotEdgeIndexProvider(this);
1909        return new WebInspector.HeapSnapshotEdgesProvider(this, filter, node.edges(), indexProvider);
1910    },
1911
1912    /**
1913     * @return {?function(!WebInspector.HeapSnapshotEdge):boolean}
1914     */
1915    retainingEdgesFilter: function()
1916    {
1917        return null;
1918    },
1919
1920    /**
1921     * @return {?function(!WebInspector.HeapSnapshotEdge):boolean}
1922     */
1923    containmentEdgesFilter: function()
1924    {
1925        return null;
1926    },
1927
1928    /**
1929     * @param {number} nodeIndex
1930     * @return {!WebInspector.HeapSnapshotEdgesProvider}
1931     */
1932    createRetainingEdgesProvider: function(nodeIndex)
1933    {
1934        var node = this.createNode(nodeIndex);
1935        var filter = this.retainingEdgesFilter();
1936        var indexProvider = new WebInspector.HeapSnapshotRetainerEdgeIndexProvider(this);
1937        return new WebInspector.HeapSnapshotEdgesProvider(this, filter, node.retainers(), indexProvider);
1938    },
1939
1940    /**
1941     * @param {string} baseSnapshotId
1942     * @param {string} className
1943     * @return {!WebInspector.HeapSnapshotNodesProvider}
1944     */
1945    createAddedNodesProvider: function(baseSnapshotId, className)
1946    {
1947        var snapshotDiff = this._snapshotDiffs[baseSnapshotId];
1948        var diffForClass = snapshotDiff[className];
1949        return new WebInspector.HeapSnapshotNodesProvider(this, null, diffForClass.addedIndexes);
1950    },
1951
1952    /**
1953     * @param {!Array.<number>} nodeIndexes
1954     * @return {!WebInspector.HeapSnapshotNodesProvider}
1955     */
1956    createDeletedNodesProvider: function(nodeIndexes)
1957    {
1958        return new WebInspector.HeapSnapshotNodesProvider(this, null, nodeIndexes);
1959    },
1960
1961    /**
1962     * @return {?function(!WebInspector.HeapSnapshotNode):boolean}
1963     */
1964    classNodesFilter: function()
1965    {
1966        return null;
1967    },
1968
1969    /**
1970     * @param {string} className
1971     * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} nodeFilter
1972     * @return {!WebInspector.HeapSnapshotNodesProvider}
1973     */
1974    createNodesProviderForClass: function(className, nodeFilter)
1975    {
1976        return new WebInspector.HeapSnapshotNodesProvider(this, this.classNodesFilter(), this.aggregatesWithFilter(nodeFilter)[className].idxs);
1977    },
1978
1979    /**
1980     * @param {number} nodeIndex
1981     * @return {!WebInspector.HeapSnapshotNodesProvider}
1982     */
1983    createNodesProviderForDominator: function(nodeIndex)
1984    {
1985        var node = this.createNode(nodeIndex);
1986        return new WebInspector.HeapSnapshotNodesProvider(this, null, this._dominatedNodesOfNode(node));
1987    },
1988
1989    /**
1990     * @return {number}
1991     */
1992    _maxJsNodeId: function()
1993    {
1994        var nodeFieldCount = this._nodeFieldCount;
1995        var nodes = this._nodes;
1996        var nodesLength = nodes.length;
1997        var id = 0;
1998        for (var nodeIndex = this._nodeIdOffset; nodeIndex < nodesLength; nodeIndex += nodeFieldCount) {
1999            var nextId = nodes[nodeIndex];
2000            // JS objects have odd ids, skip native objects.
2001            if (nextId % 2 === 0)
2002                continue;
2003            if (id < nextId)
2004                id = nextId;
2005        }
2006        return id;
2007    },
2008
2009    /**
2010     * @return {!WebInspector.HeapSnapshotCommon.StaticData}
2011     */
2012    updateStaticData: function()
2013    {
2014        return new WebInspector.HeapSnapshotCommon.StaticData(this.nodeCount, this._rootNodeIndex, this.totalSize, this._maxJsNodeId());
2015    }
2016};
2017
2018/**
2019 * @constructor
2020 * @param {!WebInspector.HeapSnapshotItemIterator} iterator
2021 * @param {!WebInspector.HeapSnapshotItemIndexProvider} indexProvider
2022 */
2023WebInspector.HeapSnapshotItemProvider = function(iterator, indexProvider)
2024{
2025    this._iterator = iterator;
2026    this._indexProvider = indexProvider;
2027    this._isEmpty = !iterator.hasNext();
2028    /** @type {?Array.<number>} */
2029    this._iterationOrder = null;
2030    this._currentComparator = null;
2031    this._sortedPrefixLength = 0;
2032    this._sortedSuffixLength = 0;
2033}
2034
2035WebInspector.HeapSnapshotItemProvider.prototype = {
2036    _createIterationOrder: function()
2037    {
2038        if (this._iterationOrder)
2039            return;
2040        this._iterationOrder = [];
2041        for (var iterator = this._iterator; iterator.hasNext(); iterator.next())
2042            this._iterationOrder.push(iterator.item().itemIndex());
2043    },
2044
2045    /**
2046     * @return {boolean}
2047     */
2048    isEmpty: function()
2049    {
2050        return this._isEmpty;
2051    },
2052
2053    /**
2054     * @param {number} begin
2055     * @param {number} end
2056     * @return {!WebInspector.HeapSnapshotCommon.ItemsRange}
2057     */
2058    serializeItemsRange: function(begin, end)
2059    {
2060        this._createIterationOrder();
2061        if (begin > end)
2062            throw new Error("Start position > end position: " + begin + " > " + end);
2063        if (end > this._iterationOrder.length)
2064            end = this._iterationOrder.length;
2065        if (this._sortedPrefixLength < end && begin < this._iterationOrder.length - this._sortedSuffixLength) {
2066            this.sort(this._currentComparator, this._sortedPrefixLength, this._iterationOrder.length - 1 - this._sortedSuffixLength, begin, end - 1);
2067            if (begin <= this._sortedPrefixLength)
2068                this._sortedPrefixLength = end;
2069            if (end >= this._iterationOrder.length - this._sortedSuffixLength)
2070                this._sortedSuffixLength = this._iterationOrder.length - begin;
2071        }
2072        var position = begin;
2073        var count = end - begin;
2074        var result = new Array(count);
2075        var iterator = this._iterator;
2076        for (var i = 0 ; i < count; ++i) {
2077            var itemIndex = this._iterationOrder[position++];
2078            var item = this._indexProvider.itemForIndex(itemIndex);
2079            result[i] = item.serialize();
2080        }
2081        return new WebInspector.HeapSnapshotCommon.ItemsRange(begin, end, this._iterationOrder.length, result);
2082    },
2083
2084    sortAndRewind: function(comparator)
2085    {
2086        this._currentComparator = comparator;
2087        this._sortedPrefixLength = 0;
2088        this._sortedSuffixLength = 0;
2089    }
2090}
2091
2092/**
2093 * @constructor
2094 * @extends {WebInspector.HeapSnapshotItemProvider}
2095 * @param {!WebInspector.HeapSnapshot} snapshot
2096 * @param {?function(!WebInspector.HeapSnapshotEdge):boolean} filter
2097 * @param {!WebInspector.HeapSnapshotEdgeIterator} edgesIter
2098 * @param {!WebInspector.HeapSnapshotItemIndexProvider} indexProvider
2099 */
2100WebInspector.HeapSnapshotEdgesProvider = function(snapshot, filter, edgesIter, indexProvider)
2101{
2102    this.snapshot = snapshot;
2103    var iter = filter ? new WebInspector.HeapSnapshotFilteredIterator(edgesIter, /** @type {function(!WebInspector.HeapSnapshotItem):boolean} */ (filter)) : edgesIter;
2104    WebInspector.HeapSnapshotItemProvider.call(this, iter, indexProvider);
2105}
2106
2107WebInspector.HeapSnapshotEdgesProvider.prototype = {
2108    /**
2109     * @param {!WebInspector.HeapSnapshotCommon.ComparatorConfig} comparator
2110     * @param {number} leftBound
2111     * @param {number} rightBound
2112     * @param {number} windowLeft
2113     * @param {number} windowRight
2114     */
2115    sort: function(comparator, leftBound, rightBound, windowLeft, windowRight)
2116    {
2117        var fieldName1 = comparator.fieldName1;
2118        var fieldName2 = comparator.fieldName2;
2119        var ascending1 = comparator.ascending1;
2120        var ascending2 = comparator.ascending2;
2121
2122        var edgeA = this._iterator.item().clone();
2123        var edgeB = edgeA.clone();
2124        var nodeA = this.snapshot.createNode();
2125        var nodeB = this.snapshot.createNode();
2126
2127        function compareEdgeFieldName(ascending, indexA, indexB)
2128        {
2129            edgeA.edgeIndex = indexA;
2130            edgeB.edgeIndex = indexB;
2131            if (edgeB.name() === "__proto__") return -1;
2132            if (edgeA.name() === "__proto__") return 1;
2133            var result =
2134                edgeA.hasStringName() === edgeB.hasStringName() ?
2135                (edgeA.name() < edgeB.name() ? -1 : (edgeA.name() > edgeB.name() ? 1 : 0)) :
2136                (edgeA.hasStringName() ? -1 : 1);
2137            return ascending ? result : -result;
2138        }
2139
2140        function compareNodeField(fieldName, ascending, indexA, indexB)
2141        {
2142            edgeA.edgeIndex = indexA;
2143            nodeA.nodeIndex = edgeA.nodeIndex();
2144            var valueA = nodeA[fieldName]();
2145
2146            edgeB.edgeIndex = indexB;
2147            nodeB.nodeIndex = edgeB.nodeIndex();
2148            var valueB = nodeB[fieldName]();
2149
2150            var result = valueA < valueB ? -1 : (valueA > valueB ? 1 : 0);
2151            return ascending ? result : -result;
2152        }
2153
2154        function compareEdgeAndNode(indexA, indexB) {
2155            var result = compareEdgeFieldName(ascending1, indexA, indexB);
2156            if (result === 0)
2157                result = compareNodeField(fieldName2, ascending2, indexA, indexB);
2158            if (result === 0)
2159                return indexA - indexB;
2160            return result;
2161        }
2162
2163        function compareNodeAndEdge(indexA, indexB) {
2164            var result = compareNodeField(fieldName1, ascending1, indexA, indexB);
2165            if (result === 0)
2166                result = compareEdgeFieldName(ascending2, indexA, indexB);
2167            if (result === 0)
2168                return indexA - indexB;
2169            return result;
2170        }
2171
2172        function compareNodeAndNode(indexA, indexB) {
2173            var result = compareNodeField(fieldName1, ascending1, indexA, indexB);
2174            if (result === 0)
2175                result = compareNodeField(fieldName2, ascending2, indexA, indexB);
2176            if (result === 0)
2177                return indexA - indexB;
2178            return result;
2179        }
2180
2181        if (fieldName1 === "!edgeName")
2182            this._iterationOrder.sortRange(compareEdgeAndNode, leftBound, rightBound, windowLeft, windowRight);
2183        else if (fieldName2 === "!edgeName")
2184            this._iterationOrder.sortRange(compareNodeAndEdge, leftBound, rightBound, windowLeft, windowRight);
2185        else
2186            this._iterationOrder.sortRange(compareNodeAndNode, leftBound, rightBound, windowLeft, windowRight);
2187    },
2188
2189    __proto__: WebInspector.HeapSnapshotItemProvider.prototype
2190}
2191
2192
2193/**
2194 * @constructor
2195 * @extends {WebInspector.HeapSnapshotItemProvider}
2196 * @param {!WebInspector.HeapSnapshot} snapshot
2197 * @param {?function(!WebInspector.HeapSnapshotNode):boolean} filter
2198 * @param {(!Array.<number>|!Uint32Array)} nodeIndexes
2199 */
2200WebInspector.HeapSnapshotNodesProvider = function(snapshot, filter, nodeIndexes)
2201{
2202    this.snapshot = snapshot;
2203    var indexProvider = new WebInspector.HeapSnapshotNodeIndexProvider(snapshot);
2204    var it = new WebInspector.HeapSnapshotIndexRangeIterator(indexProvider, nodeIndexes);
2205
2206    if (filter)
2207        it = new WebInspector.HeapSnapshotFilteredIterator(it, /** @type {function(!WebInspector.HeapSnapshotItem):boolean} */ (filter));
2208    WebInspector.HeapSnapshotItemProvider.call(this, it, indexProvider);
2209}
2210
2211WebInspector.HeapSnapshotNodesProvider.prototype = {
2212    /**
2213     * @param {string} snapshotObjectId
2214     * @return {number}
2215     */
2216    nodePosition: function(snapshotObjectId)
2217    {
2218        this._createIterationOrder();
2219        var node = this.snapshot.createNode();
2220        for (var i = 0; i < this._iterationOrder.length; i++) {
2221            node.nodeIndex = this._iterationOrder[i];
2222            if (node.id() === snapshotObjectId)
2223                break;
2224        }
2225        if (i === this._iterationOrder.length)
2226            return -1;
2227        var targetNodeIndex = this._iterationOrder[i];
2228        var smallerCount = 0;
2229        var compare = this._buildCompareFunction(this._currentComparator);
2230        for (var i = 0; i < this._iterationOrder.length; i++) {
2231            if (compare(this._iterationOrder[i], targetNodeIndex) < 0)
2232                ++smallerCount;
2233        }
2234        return smallerCount;
2235    },
2236
2237    /**
2238     * @return {function(number,number):number}
2239     */
2240    _buildCompareFunction: function(comparator)
2241    {
2242        var nodeA = this.snapshot.createNode();
2243        var nodeB = this.snapshot.createNode();
2244        var fieldAccessor1 = nodeA[comparator.fieldName1];
2245        var fieldAccessor2 = nodeA[comparator.fieldName2];
2246        var ascending1 = comparator.ascending1 ? 1 : -1;
2247        var ascending2 = comparator.ascending2 ? 1 : -1;
2248
2249        /**
2250         * @param {function():*} fieldAccessor
2251         * @param {number} ascending
2252         * @return {number}
2253         */
2254        function sortByNodeField(fieldAccessor, ascending)
2255        {
2256            var valueA = fieldAccessor.call(nodeA);
2257            var valueB = fieldAccessor.call(nodeB);
2258            return valueA < valueB ? -ascending : (valueA > valueB ? ascending : 0);
2259        }
2260
2261        /**
2262         * @param {number} indexA
2263         * @param {number} indexB
2264         * @return {number}
2265         */
2266        function sortByComparator(indexA, indexB)
2267        {
2268            nodeA.nodeIndex = indexA;
2269            nodeB.nodeIndex = indexB;
2270            var result = sortByNodeField(fieldAccessor1, ascending1);
2271            if (result === 0)
2272                result = sortByNodeField(fieldAccessor2, ascending2);
2273            return result || indexA - indexB;
2274        }
2275
2276        return sortByComparator;
2277    },
2278
2279    /**
2280     * @param {!WebInspector.HeapSnapshotCommon.ComparatorConfig} comparator
2281     * @param {number} leftBound
2282     * @param {number} rightBound
2283     * @param {number} windowLeft
2284     * @param {number} windowRight
2285     */
2286    sort: function(comparator, leftBound, rightBound, windowLeft, windowRight)
2287    {
2288        this._iterationOrder.sortRange(this._buildCompareFunction(comparator), leftBound, rightBound, windowLeft, windowRight);
2289    },
2290
2291    __proto__: WebInspector.HeapSnapshotItemProvider.prototype
2292}
2293
2294