Home | History | Annotate | Download | only in heap_snapshot_worker
      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  */
     34 WebInspector.HeapSnapshotItem = function() { }
     35 
     36 WebInspector.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  */
     54 WebInspector.HeapSnapshotEdge = function(snapshot, edgeIndex)
     55 {
     56     this._snapshot = snapshot;
     57     this._edges = snapshot.containmentEdges;
     58     this.edgeIndex = edgeIndex || 0;
     59 }
     60 
     61 WebInspector.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  */
    146 WebInspector.HeapSnapshotItemIterator = function() { }
    147 
    148 WebInspector.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  */
    166 WebInspector.HeapSnapshotItemIndexProvider = function() { }
    167 
    168 WebInspector.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  */
    181 WebInspector.HeapSnapshotNodeIndexProvider = function(snapshot)
    182 {
    183     this._node = snapshot.createNode();
    184 }
    185 
    186 WebInspector.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  */
    204 WebInspector.HeapSnapshotEdgeIndexProvider = function(snapshot)
    205 {
    206     this._edge = snapshot.createEdge(0);
    207 }
    208 
    209 WebInspector.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  */
    227 WebInspector.HeapSnapshotRetainerEdgeIndexProvider = function(snapshot)
    228 {
    229     this._retainerEdge = snapshot.createRetainingEdge(0);
    230 }
    231 
    232 WebInspector.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  */
    250 WebInspector.HeapSnapshotEdgeIterator = function(node)
    251 {
    252     this._sourceNode = node;
    253     this.edge = node._snapshot.createEdge(node.edgeIndexesStart());
    254 }
    255 
    256 WebInspector.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  */
    285 WebInspector.HeapSnapshotRetainerEdge = function(snapshot, retainerIndex)
    286 {
    287     this._snapshot = snapshot;
    288     this.setRetainerIndex(retainerIndex);
    289 }
    290 
    291 WebInspector.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  */
    416 WebInspector.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 
    425 WebInspector.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  */
    454 WebInspector.HeapSnapshotNode = function(snapshot, nodeIndex)
    455 {
    456     this._snapshot = snapshot;
    457     this.nodeIndex = nodeIndex || 0;
    458 }
    459 
    460 WebInspector.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  */
    660 WebInspector.HeapSnapshotNodeIterator = function(node)
    661 {
    662     this.node = node;
    663     this._nodesLength = node._snapshot.nodes.length;
    664 }
    665 
    666 WebInspector.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  */
    696 WebInspector.HeapSnapshotIndexRangeIterator = function(itemProvider, indexes)
    697 {
    698     this._itemProvider = itemProvider;
    699     this._indexes = indexes;
    700     this._position = 0;
    701 }
    702 
    703 WebInspector.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  */
    734 WebInspector.HeapSnapshotFilteredIterator = function(iterator, filter)
    735 {
    736     this._iterator = iterator;
    737     this._filter = filter;
    738     this._skipFilteredItems();
    739 }
    740 
    741 WebInspector.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  */
    777 WebInspector.HeapSnapshotProgress = function(dispatcher)
    778 {
    779     this._dispatcher = dispatcher;
    780 }
    781 
    782 WebInspector.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  * @constructor
    818  */
    819 WebInspector.HeapSnapshot = function(profile, progress)
    820 {
    821     /** @type {!Uint32Array} */
    822     this.nodes = profile.nodes;
    823     /** @type {!Uint32Array} */
    824     this.containmentEdges = profile.edges;
    825     /** @type {!HeapSnapshotMetainfo} */
    826     this._metaNode = profile.snapshot.meta;
    827     /** @type {!Array.<string>} */
    828     this.strings = profile.strings;
    829     this._progress = progress;
    830 
    831     this._noDistance = -5;
    832     this._rootNodeIndex = 0;
    833     if (profile.snapshot.root_index)
    834         this._rootNodeIndex = profile.snapshot.root_index;
    835 
    836     this._snapshotDiffs = {};
    837     this._aggregatesForDiff = null;
    838     this._aggregates = {};
    839     this._aggregatesSortedFlags = {};
    840 
    841     this._init();
    842 
    843     if (profile.snapshot.trace_function_count) {
    844         this._progress.updateStatus("Building allocation statistics\u2026");
    845         var nodes = this.nodes;
    846         var nodesLength = nodes.length;
    847         var nodeFieldCount = this._nodeFieldCount;
    848         var node = this.rootNode();
    849         var liveObjects = {};
    850         for (var nodeIndex = 0; nodeIndex < nodesLength; nodeIndex += nodeFieldCount) {
    851             node.nodeIndex = nodeIndex;
    852             var traceNodeId = node.traceNodeId();
    853             var stats = liveObjects[traceNodeId];
    854             if (!stats)
    855                 liveObjects[traceNodeId] = stats = { count: 0, size: 0, ids: [] };
    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  */
    868 function 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  */
    883 function 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 
    892 WebInspector.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._nodeArrayType = this._nodeTypes.indexOf("array");
    907         this._nodeHiddenType = this._nodeTypes.indexOf("hidden");
    908         this._nodeObjectType = this._nodeTypes.indexOf("object");
    909         this._nodeNativeType = this._nodeTypes.indexOf("native");
    910         this._nodeConsStringType = this._nodeTypes.indexOf("concatenated string");
    911         this._nodeSlicedStringType = this._nodeTypes.indexOf("sliced string");
    912         this._nodeCodeType = this._nodeTypes.indexOf("code");
    913         this._nodeSyntheticType = this._nodeTypes.indexOf("synthetic");
    914 
    915         this._edgeFieldsCount = meta.edge_fields.length;
    916         this._edgeTypeOffset = meta.edge_fields.indexOf("type");
    917         this._edgeNameOffset = meta.edge_fields.indexOf("name_or_index");
    918         this._edgeToNodeOffset = meta.edge_fields.indexOf("to_node");
    919 
    920         this._edgeTypes = meta.edge_types[this._edgeTypeOffset];
    921         this._edgeTypes.push("invisible");
    922         this._edgeElementType = this._edgeTypes.indexOf("element");
    923         this._edgeHiddenType = this._edgeTypes.indexOf("hidden");
    924         this._edgeInternalType = this._edgeTypes.indexOf("internal");
    925         this._edgeShortcutType = this._edgeTypes.indexOf("shortcut");
    926         this._edgeWeakType = this._edgeTypes.indexOf("weak");
    927         this._edgeInvisibleType = this._edgeTypes.indexOf("invisible");
    928 
    929         this.nodeCount = this.nodes.length / this._nodeFieldCount;
    930         this._edgeCount = this.containmentEdges.length / this._edgeFieldsCount;
    931 
    932         this._retainedSizes = new Float64Array(this.nodeCount);
    933         this._firstEdgeIndexes = new Uint32Array(this.nodeCount + 1);
    934         this._retainingNodes = new Uint32Array(this._edgeCount);
    935         this._retainingEdges = new Uint32Array(this._edgeCount);
    936         this._firstRetainerIndex = new Uint32Array(this.nodeCount + 1);
    937         this._nodeDistances = new Int32Array(this.nodeCount);
    938         this._firstDominatedNodeIndex = new Uint32Array(this.nodeCount + 1);
    939         this._dominatedNodes = new Uint32Array(this.nodeCount - 1);
    940 
    941         this._progress.updateStatus("Building edge indexes\u2026");
    942         this._buildEdgeIndexes();
    943         this._progress.updateStatus("Building retainers\u2026");
    944         this._buildRetainers();
    945         this._progress.updateStatus("Calculating node flags\u2026");
    946         this._calculateFlags();
    947         this._progress.updateStatus("Calculating distances\u2026");
    948         this.calculateDistances();
    949         this._progress.updateStatus("Building postorder index\u2026");
    950         var result = this._buildPostOrderIndex();
    951         // Actually it is array that maps node ordinal number to dominator node ordinal number.
    952         this._progress.updateStatus("Building dominator tree\u2026");
    953         this._dominatorsTree = this._buildDominatorTree(result.postOrderIndex2NodeOrdinal, result.nodeOrdinal2PostOrderIndex);
    954         this._progress.updateStatus("Calculating retained sizes\u2026");
    955         this._calculateRetainedSizes(result.postOrderIndex2NodeOrdinal);
    956         this._progress.updateStatus("Buiding dominated nodes\u2026");
    957         this._buildDominatedNodes();
    958         this._progress.updateStatus("Calculating statistics\u2026");
    959         this._calculateStatistics();
    960         this._progress.updateStatus("Finished processing.");
    961     },
    962 
    963     _buildEdgeIndexes: function()
    964     {
    965         var nodes = this.nodes;
    966         var nodeCount = this.nodeCount;
    967         var firstEdgeIndexes = this._firstEdgeIndexes;
    968         var nodeFieldCount = this._nodeFieldCount;
    969         var edgeFieldsCount = this._edgeFieldsCount;
    970         var nodeEdgeCountOffset = this._nodeEdgeCountOffset;
    971         firstEdgeIndexes[nodeCount] = this.containmentEdges.length;
    972         for (var nodeOrdinal = 0, edgeIndex = 0; nodeOrdinal < nodeCount; ++nodeOrdinal) {
    973             firstEdgeIndexes[nodeOrdinal] = edgeIndex;
    974             edgeIndex += nodes[nodeOrdinal * nodeFieldCount + nodeEdgeCountOffset] * edgeFieldsCount;
    975         }
    976     },
    977 
    978     _buildRetainers: function()
    979     {
    980         var retainingNodes = this._retainingNodes;
    981         var retainingEdges = this._retainingEdges;
    982         // Index of the first retainer in the _retainingNodes and _retainingEdges
    983         // arrays. Addressed by retained node index.
    984         var firstRetainerIndex = this._firstRetainerIndex;
    985 
    986         var containmentEdges = this.containmentEdges;
    987         var edgeFieldsCount = this._edgeFieldsCount;
    988         var nodeFieldCount = this._nodeFieldCount;
    989         var edgeToNodeOffset = this._edgeToNodeOffset;
    990         var firstEdgeIndexes = this._firstEdgeIndexes;
    991         var nodeCount = this.nodeCount;
    992 
    993         for (var toNodeFieldIndex = edgeToNodeOffset, l = containmentEdges.length; toNodeFieldIndex < l; toNodeFieldIndex += edgeFieldsCount) {
    994             var toNodeIndex = containmentEdges[toNodeFieldIndex];
    995             if (toNodeIndex % nodeFieldCount)
    996                 throw new Error("Invalid toNodeIndex " + toNodeIndex);
    997             ++firstRetainerIndex[toNodeIndex / nodeFieldCount];
    998         }
    999         for (var i = 0, firstUnusedRetainerSlot = 0; i < nodeCount; i++) {
   1000             var retainersCount = firstRetainerIndex[i];
   1001             firstRetainerIndex[i] = firstUnusedRetainerSlot;
   1002             retainingNodes[firstUnusedRetainerSlot] = retainersCount;
   1003             firstUnusedRetainerSlot += retainersCount;
   1004         }
   1005         firstRetainerIndex[nodeCount] = retainingNodes.length;
   1006 
   1007         var nextNodeFirstEdgeIndex = firstEdgeIndexes[0];
   1008         for (var srcNodeOrdinal = 0; srcNodeOrdinal < nodeCount; ++srcNodeOrdinal) {
   1009             var firstEdgeIndex = nextNodeFirstEdgeIndex;
   1010             nextNodeFirstEdgeIndex = firstEdgeIndexes[srcNodeOrdinal + 1];
   1011             var srcNodeIndex = srcNodeOrdinal * nodeFieldCount;
   1012             for (var edgeIndex = firstEdgeIndex; edgeIndex < nextNodeFirstEdgeIndex; edgeIndex += edgeFieldsCount) {
   1013                 var toNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
   1014                 if (toNodeIndex % nodeFieldCount)
   1015                     throw new Error("Invalid toNodeIndex " + toNodeIndex);
   1016                 var firstRetainerSlotIndex = firstRetainerIndex[toNodeIndex / nodeFieldCount];
   1017                 var nextUnusedRetainerSlotIndex = firstRetainerSlotIndex + (--retainingNodes[firstRetainerSlotIndex]);
   1018                 retainingNodes[nextUnusedRetainerSlotIndex] = srcNodeIndex;
   1019                 retainingEdges[nextUnusedRetainerSlotIndex] = edgeIndex;
   1020             }
   1021         }
   1022     },
   1023 
   1024     /**
   1025      * @param {number=} nodeIndex
   1026      */
   1027     createNode: function(nodeIndex)
   1028     {
   1029         throw new Error("Not implemented");
   1030     },
   1031 
   1032     /**
   1033      * @param {number} edgeIndex
   1034      * @return {!WebInspector.JSHeapSnapshotEdge}
   1035      */
   1036     createEdge: function(edgeIndex)
   1037     {
   1038         throw new Error("Not implemented");
   1039     },
   1040 
   1041     /**
   1042      * @param {number} retainerIndex
   1043      * @return {!WebInspector.JSHeapSnapshotRetainerEdge}
   1044      */
   1045     createRetainingEdge: function(retainerIndex)
   1046     {
   1047         throw new Error("Not implemented");
   1048     },
   1049 
   1050     _allNodes: function()
   1051     {
   1052         return new WebInspector.HeapSnapshotNodeIterator(this.rootNode());
   1053     },
   1054 
   1055     /**
   1056      * @return {!WebInspector.HeapSnapshotNode}
   1057      */
   1058     rootNode: function()
   1059     {
   1060         return this.createNode(this._rootNodeIndex);
   1061     },
   1062 
   1063     get rootNodeIndex()
   1064     {
   1065         return this._rootNodeIndex;
   1066     },
   1067 
   1068     get totalSize()
   1069     {
   1070         return this.rootNode().retainedSize();
   1071     },
   1072 
   1073     _getDominatedIndex: function(nodeIndex)
   1074     {
   1075         if (nodeIndex % this._nodeFieldCount)
   1076             throw new Error("Invalid nodeIndex: " + nodeIndex);
   1077         return this._firstDominatedNodeIndex[nodeIndex / this._nodeFieldCount];
   1078     },
   1079 
   1080     /**
   1081      * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} nodeFilter
   1082      * @return {!Object.<string, !WebInspector.HeapSnapshotCommon.Aggregate>}
   1083      */
   1084     aggregatesWithFilter: function(nodeFilter)
   1085     {
   1086         var minNodeId = nodeFilter.minNodeId;
   1087         var maxNodeId = nodeFilter.maxNodeId;
   1088         var allocationNodeId = nodeFilter.allocationNodeId;
   1089         var key;
   1090         var filter;
   1091         if (typeof allocationNodeId === "number") {
   1092             filter = this._createAllocationStackFilter(allocationNodeId);
   1093         } else if (typeof minNodeId === "number" && typeof maxNodeId === "number") {
   1094             key = minNodeId + ".." + maxNodeId;
   1095             filter = this._createNodeIdFilter(minNodeId, maxNodeId);
   1096         } else {
   1097             key = "allObjects";
   1098         }
   1099         return this.aggregates(false, key, filter);
   1100     },
   1101 
   1102     /**
   1103      * @param {number} minNodeId
   1104      * @param {number} maxNodeId
   1105      * @return {function(!WebInspector.HeapSnapshotNode):boolean}
   1106      */
   1107     _createNodeIdFilter: function(minNodeId, maxNodeId)
   1108     {
   1109         /**
   1110          * @param {!WebInspector.HeapSnapshotNode} node
   1111          * @return {boolean}
   1112          */
   1113         function nodeIdFilter(node)
   1114         {
   1115             var id = node.id();
   1116             return id > minNodeId && id <= maxNodeId;
   1117         }
   1118         return nodeIdFilter;
   1119     },
   1120 
   1121     /**
   1122      * @param {number} bottomUpAllocationNodeId
   1123      * @return {function(!WebInspector.HeapSnapshotNode):boolean|undefined}
   1124      */
   1125     _createAllocationStackFilter: function(bottomUpAllocationNodeId)
   1126     {
   1127         var traceIds = this._allocationProfile.traceIds(bottomUpAllocationNodeId);
   1128         if (!traceIds.length)
   1129             return undefined;
   1130         var set = {};
   1131         for (var i = 0; i < traceIds.length; i++)
   1132             set[traceIds[i]] = true;
   1133         /**
   1134          * @param {!WebInspector.HeapSnapshotNode} node
   1135          * @return {boolean}
   1136          */
   1137         function traceIdFilter(node)
   1138         {
   1139             return !!set[node.traceNodeId()];
   1140         };
   1141         return traceIdFilter;
   1142     },
   1143 
   1144     /**
   1145      * @param {boolean} sortedIndexes
   1146      * @param {string=} key
   1147      * @param {function(!WebInspector.HeapSnapshotNode):boolean=} filter
   1148      * @return {!Object.<string, !WebInspector.HeapSnapshotCommon.Aggregate>}
   1149      */
   1150     aggregates: function(sortedIndexes, key, filter)
   1151     {
   1152         var aggregatesByClassName = key && this._aggregates[key];
   1153         if (!aggregatesByClassName) {
   1154             var aggregates = this._buildAggregates(filter);
   1155             this._calculateClassesRetainedSize(aggregates.aggregatesByClassIndex, filter);
   1156             aggregatesByClassName = aggregates.aggregatesByClassName;
   1157             if (key)
   1158                 this._aggregates[key] = aggregatesByClassName;
   1159         }
   1160 
   1161         if (sortedIndexes && (!key || !this._aggregatesSortedFlags[key])) {
   1162             this._sortAggregateIndexes(aggregatesByClassName);
   1163             if (key)
   1164                 this._aggregatesSortedFlags[key] = sortedIndexes;
   1165         }
   1166         return aggregatesByClassName;
   1167     },
   1168 
   1169     /**
   1170      * @return {!Array.<!WebInspector.HeapSnapshotCommon.SerializedAllocationNode>}
   1171      */
   1172     allocationTracesTops: function()
   1173     {
   1174         return this._allocationProfile.serializeTraceTops();
   1175     },
   1176 
   1177     /**
   1178      * @param {number} nodeId
   1179      * @return {!WebInspector.HeapSnapshotCommon.AllocationNodeCallers}
   1180      */
   1181     allocationNodeCallers: function(nodeId)
   1182     {
   1183         return this._allocationProfile.serializeCallers(nodeId);
   1184     },
   1185 
   1186     /**
   1187      * @param {number} nodeIndex
   1188      * @return {?Array.<!WebInspector.HeapSnapshotCommon.AllocationStackFrame>}
   1189      */
   1190     allocationStack: function(nodeIndex)
   1191     {
   1192         var node = this.createNode(nodeIndex);
   1193         var allocationNodeId = node.traceNodeId();
   1194         if (!allocationNodeId)
   1195             return null;
   1196         return this._allocationProfile.serializeAllocationStack(allocationNodeId);
   1197     },
   1198 
   1199     /**
   1200      * @return {!Object.<string, !WebInspector.HeapSnapshotCommon.AggregateForDiff>}
   1201      */
   1202     aggregatesForDiff: function()
   1203     {
   1204         if (this._aggregatesForDiff)
   1205             return this._aggregatesForDiff;
   1206 
   1207         var aggregatesByClassName = this.aggregates(true, "allObjects");
   1208         this._aggregatesForDiff  = {};
   1209 
   1210         var node = this.createNode();
   1211         for (var className in aggregatesByClassName) {
   1212             var aggregate = aggregatesByClassName[className];
   1213             var indexes = aggregate.idxs;
   1214             var ids = new Array(indexes.length);
   1215             var selfSizes = new Array(indexes.length);
   1216             for (var i = 0; i < indexes.length; i++) {
   1217                 node.nodeIndex = indexes[i];
   1218                 ids[i] = node.id();
   1219                 selfSizes[i] = node.selfSize();
   1220             }
   1221 
   1222             this._aggregatesForDiff[className] = {
   1223                 indexes: indexes,
   1224                 ids: ids,
   1225                 selfSizes: selfSizes
   1226             };
   1227         }
   1228         return this._aggregatesForDiff;
   1229     },
   1230 
   1231     /**
   1232      * @param {!WebInspector.HeapSnapshotNode} node
   1233      * @return {!boolean}
   1234      */
   1235     _isUserRoot: function(node)
   1236     {
   1237         return true;
   1238     },
   1239 
   1240     /**
   1241      * @param {function(!WebInspector.HeapSnapshotNode)} action
   1242      * @param {boolean=} userRootsOnly
   1243      */
   1244     forEachRoot: function(action, userRootsOnly)
   1245     {
   1246         for (var iter = this.rootNode().edges(); iter.hasNext(); iter.next()) {
   1247             var node = iter.edge.node();
   1248             if (!userRootsOnly || this._isUserRoot(node))
   1249                 action(node);
   1250         }
   1251     },
   1252 
   1253     /**
   1254      * @param {function(!WebInspector.HeapSnapshotNode,!WebInspector.HeapSnapshotEdge):boolean=} filter
   1255      */
   1256     calculateDistances: function(filter)
   1257     {
   1258         var nodeFieldCount = this._nodeFieldCount;
   1259         var nodeCount = this.nodeCount;
   1260         var distances = this._nodeDistances;
   1261         var noDistance = this._noDistance;
   1262         for (var i = 0; i < nodeCount; ++i)
   1263             distances[i] = noDistance;
   1264 
   1265         var nodesToVisit = new Uint32Array(this.nodeCount);
   1266         var nodesToVisitLength = 0;
   1267 
   1268         /**
   1269          * @param {number} distance
   1270          * @param {!WebInspector.HeapSnapshotNode} node
   1271          */
   1272         function enqueueNode(distance, node)
   1273         {
   1274             var ordinal = node.ordinal();
   1275             if (distances[ordinal] !== noDistance)
   1276                 return;
   1277             distances[ordinal] = distance;
   1278             nodesToVisit[nodesToVisitLength++] = node.nodeIndex;
   1279         }
   1280 
   1281         this.forEachRoot(enqueueNode.bind(null, 1), true);
   1282         this._bfs(nodesToVisit, nodesToVisitLength, distances, filter);
   1283 
   1284         // bfs for the rest of objects
   1285         nodesToVisitLength = 0;
   1286         this.forEachRoot(enqueueNode.bind(null, WebInspector.HeapSnapshotCommon.baseSystemDistance), false);
   1287         this._bfs(nodesToVisit, nodesToVisitLength, distances, filter);
   1288     },
   1289 
   1290     /**
   1291      * @param {!Uint32Array} nodesToVisit
   1292      * @param {!number} nodesToVisitLength
   1293      * @param {!Int32Array} distances
   1294      * @param {function(!WebInspector.HeapSnapshotNode,!WebInspector.HeapSnapshotEdge):boolean=} filter
   1295      */
   1296     _bfs: function(nodesToVisit, nodesToVisitLength, distances, filter)
   1297     {
   1298         // Preload fields into local variables for better performance.
   1299         var edgeFieldsCount = this._edgeFieldsCount;
   1300         var nodeFieldCount = this._nodeFieldCount;
   1301         var containmentEdges = this.containmentEdges;
   1302         var firstEdgeIndexes = this._firstEdgeIndexes;
   1303         var edgeToNodeOffset = this._edgeToNodeOffset;
   1304         var edgeTypeOffset = this._edgeTypeOffset;
   1305         var nodeCount = this.nodeCount;
   1306         var containmentEdgesLength = containmentEdges.length;
   1307         var edgeWeakType = this._edgeWeakType;
   1308         var noDistance = this._noDistance;
   1309 
   1310         var index = 0;
   1311         var edge = this.createEdge(0);
   1312         var node = this.createNode(0);
   1313         while (index < nodesToVisitLength) {
   1314             var nodeIndex = nodesToVisit[index++]; // shift generates too much garbage.
   1315             var nodeOrdinal = nodeIndex / nodeFieldCount;
   1316             var distance = distances[nodeOrdinal] + 1;
   1317             var firstEdgeIndex = firstEdgeIndexes[nodeOrdinal];
   1318             var edgesEnd = firstEdgeIndexes[nodeOrdinal + 1];
   1319             node.nodeIndex = nodeIndex;
   1320             for (var edgeIndex = firstEdgeIndex; edgeIndex < edgesEnd; edgeIndex += edgeFieldsCount) {
   1321                 var edgeType = containmentEdges[edgeIndex + edgeTypeOffset];
   1322                 if (edgeType === edgeWeakType)
   1323                     continue;
   1324                 var childNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
   1325                 var childNodeOrdinal = childNodeIndex / nodeFieldCount;
   1326                 if (distances[childNodeOrdinal] !== noDistance)
   1327                     continue;
   1328                 edge.edgeIndex = edgeIndex;
   1329                 if (filter && !filter(node, edge))
   1330                     continue;
   1331                 distances[childNodeOrdinal] = distance;
   1332                 nodesToVisit[nodesToVisitLength++] = childNodeIndex;
   1333             }
   1334         }
   1335         if (nodesToVisitLength > nodeCount)
   1336             throw new Error("BFS failed. Nodes to visit (" + nodesToVisitLength + ") is more than nodes count (" + nodeCount + ")");
   1337     },
   1338 
   1339     _buildAggregates: function(filter)
   1340     {
   1341         var aggregates = {};
   1342         var aggregatesByClassName = {};
   1343         var classIndexes = [];
   1344         var nodes = this.nodes;
   1345         var mapAndFlag = this.userObjectsMapAndFlag();
   1346         var flags = mapAndFlag ? mapAndFlag.map : null;
   1347         var flag = mapAndFlag ? mapAndFlag.flag : 0;
   1348         var nodesLength = nodes.length;
   1349         var nodeNativeType = this._nodeNativeType;
   1350         var nodeFieldCount = this._nodeFieldCount;
   1351         var selfSizeOffset = this._nodeSelfSizeOffset;
   1352         var nodeTypeOffset = this._nodeTypeOffset;
   1353         var node = this.rootNode();
   1354         var nodeDistances = this._nodeDistances;
   1355 
   1356         for (var nodeIndex = 0; nodeIndex < nodesLength; nodeIndex += nodeFieldCount) {
   1357             var nodeOrdinal = nodeIndex / nodeFieldCount;
   1358             if (flags && !(flags[nodeOrdinal] & flag))
   1359                 continue;
   1360             node.nodeIndex = nodeIndex;
   1361             if (filter && !filter(node))
   1362                 continue;
   1363             var selfSize = nodes[nodeIndex + selfSizeOffset];
   1364             if (!selfSize && nodes[nodeIndex + nodeTypeOffset] !== nodeNativeType)
   1365                 continue;
   1366             var classIndex = node.classIndex();
   1367             if (!(classIndex in aggregates)) {
   1368                 var nodeType = node.type();
   1369                 var nameMatters = nodeType === "object" || nodeType === "native";
   1370                 var value = {
   1371                     count: 1,
   1372                     distance: nodeDistances[nodeOrdinal],
   1373                     self: selfSize,
   1374                     maxRet: 0,
   1375                     type: nodeType,
   1376                     name: nameMatters ? node.name() : null,
   1377                     idxs: [nodeIndex]
   1378                 };
   1379                 aggregates[classIndex] = value;
   1380                 classIndexes.push(classIndex);
   1381                 aggregatesByClassName[node.className()] = value;
   1382             } else {
   1383                 var clss = aggregates[classIndex];
   1384                 clss.distance = Math.min(clss.distance, nodeDistances[nodeOrdinal]);
   1385                 ++clss.count;
   1386                 clss.self += selfSize;
   1387                 clss.idxs.push(nodeIndex);
   1388             }
   1389         }
   1390 
   1391         // Shave off provisionally allocated space.
   1392         for (var i = 0, l = classIndexes.length; i < l; ++i) {
   1393             var classIndex = classIndexes[i];
   1394             aggregates[classIndex].idxs = aggregates[classIndex].idxs.slice();
   1395         }
   1396         return {aggregatesByClassName: aggregatesByClassName, aggregatesByClassIndex: aggregates};
   1397     },
   1398 
   1399     _calculateClassesRetainedSize: function(aggregates, filter)
   1400     {
   1401         var rootNodeIndex = this._rootNodeIndex;
   1402         var node = this.createNode(rootNodeIndex);
   1403         var list = [rootNodeIndex];
   1404         var sizes = [-1];
   1405         var classes = [];
   1406         var seenClassNameIndexes = {};
   1407         var nodeFieldCount = this._nodeFieldCount;
   1408         var nodeTypeOffset = this._nodeTypeOffset;
   1409         var nodeNativeType = this._nodeNativeType;
   1410         var dominatedNodes = this._dominatedNodes;
   1411         var nodes = this.nodes;
   1412         var mapAndFlag = this.userObjectsMapAndFlag();
   1413         var flags = mapAndFlag ? mapAndFlag.map : null;
   1414         var flag = mapAndFlag ? mapAndFlag.flag : 0;
   1415         var firstDominatedNodeIndex = this._firstDominatedNodeIndex;
   1416 
   1417         while (list.length) {
   1418             var nodeIndex = list.pop();
   1419             node.nodeIndex = nodeIndex;
   1420             var classIndex = node.classIndex();
   1421             var seen = !!seenClassNameIndexes[classIndex];
   1422             var nodeOrdinal = nodeIndex / nodeFieldCount;
   1423             var dominatedIndexFrom = firstDominatedNodeIndex[nodeOrdinal];
   1424             var dominatedIndexTo = firstDominatedNodeIndex[nodeOrdinal + 1];
   1425 
   1426             if (!seen &&
   1427                 (!flags || (flags[nodeOrdinal] & flag)) &&
   1428                 (!filter || filter(node)) &&
   1429                 (node.selfSize() || nodes[nodeIndex + nodeTypeOffset] === nodeNativeType)
   1430                ) {
   1431                 aggregates[classIndex].maxRet += node.retainedSize();
   1432                 if (dominatedIndexFrom !== dominatedIndexTo) {
   1433                     seenClassNameIndexes[classIndex] = true;
   1434                     sizes.push(list.length);
   1435                     classes.push(classIndex);
   1436                 }
   1437             }
   1438             for (var i = dominatedIndexFrom; i < dominatedIndexTo; i++)
   1439                 list.push(dominatedNodes[i]);
   1440 
   1441             var l = list.length;
   1442             while (sizes[sizes.length - 1] === l) {
   1443                 sizes.pop();
   1444                 classIndex = classes.pop();
   1445                 seenClassNameIndexes[classIndex] = false;
   1446             }
   1447         }
   1448     },
   1449 
   1450     _sortAggregateIndexes: function(aggregates)
   1451     {
   1452         var nodeA = this.createNode();
   1453         var nodeB = this.createNode();
   1454         for (var clss in aggregates)
   1455             aggregates[clss].idxs.sort(
   1456                 function(idxA, idxB) {
   1457                     nodeA.nodeIndex = idxA;
   1458                     nodeB.nodeIndex = idxB;
   1459                     return nodeA.id() < nodeB.id() ? -1 : 1;
   1460                 });
   1461     },
   1462 
   1463     _buildPostOrderIndex: function()
   1464     {
   1465         var nodeFieldCount = this._nodeFieldCount;
   1466         var nodes = this.nodes;
   1467         var nodeCount = this.nodeCount;
   1468         var rootNodeOrdinal = this._rootNodeIndex / nodeFieldCount;
   1469 
   1470         var edgeFieldsCount = this._edgeFieldsCount;
   1471         var edgeTypeOffset = this._edgeTypeOffset;
   1472         var edgeToNodeOffset = this._edgeToNodeOffset;
   1473         var edgeShortcutType = this._edgeShortcutType;
   1474         var firstEdgeIndexes = this._firstEdgeIndexes;
   1475         var containmentEdges = this.containmentEdges;
   1476 
   1477         var mapAndFlag = this.userObjectsMapAndFlag();
   1478         var flags = mapAndFlag ? mapAndFlag.map : null;
   1479         var flag = mapAndFlag ? mapAndFlag.flag : 0;
   1480 
   1481         var stackNodes = new Uint32Array(nodeCount);
   1482         var stackCurrentEdge = new Uint32Array(nodeCount);
   1483         var postOrderIndex2NodeOrdinal = new Uint32Array(nodeCount);
   1484         var nodeOrdinal2PostOrderIndex = new Uint32Array(nodeCount);
   1485         var visited = new Uint8Array(nodeCount);
   1486         var postOrderIndex = 0;
   1487 
   1488         var stackTop = 0;
   1489         stackNodes[0] = rootNodeOrdinal;
   1490         stackCurrentEdge[0] = firstEdgeIndexes[rootNodeOrdinal];
   1491         visited[rootNodeOrdinal] = 1;
   1492 
   1493         while (stackTop >= 0) {
   1494             var nodeOrdinal = stackNodes[stackTop];
   1495             var edgeIndex = stackCurrentEdge[stackTop];
   1496             var edgesEnd = firstEdgeIndexes[nodeOrdinal + 1];
   1497 
   1498             if (edgeIndex < edgesEnd) {
   1499                 stackCurrentEdge[stackTop] += edgeFieldsCount;
   1500                 if (nodeOrdinal !== rootNodeOrdinal && containmentEdges[edgeIndex + edgeTypeOffset] === edgeShortcutType)
   1501                     continue;
   1502                 var childNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
   1503                 var childNodeOrdinal = childNodeIndex / nodeFieldCount;
   1504                 if (visited[childNodeOrdinal])
   1505                     continue;
   1506                 var nodeFlag = !flags || (flags[nodeOrdinal] & flag);
   1507                 var childNodeFlag = !flags || (flags[childNodeOrdinal] & flag);
   1508                 // We are skipping the edges from non-page-owned nodes to page-owned nodes.
   1509                 // Otherwise the dominators for the objects that also were retained by debugger would be affected.
   1510                 if (nodeOrdinal !== rootNodeOrdinal && childNodeFlag && !nodeFlag)
   1511                     continue;
   1512                 ++stackTop;
   1513                 stackNodes[stackTop] = childNodeOrdinal;
   1514                 stackCurrentEdge[stackTop] = firstEdgeIndexes[childNodeOrdinal];
   1515                 visited[childNodeOrdinal] = 1;
   1516             } else {
   1517                 // Done with all the node children
   1518                 nodeOrdinal2PostOrderIndex[nodeOrdinal] = postOrderIndex;
   1519                 postOrderIndex2NodeOrdinal[postOrderIndex++] = nodeOrdinal;
   1520                 --stackTop;
   1521             }
   1522         }
   1523 
   1524         if (postOrderIndex !== nodeCount) {
   1525             console.log("Error: Corrupted snapshot. " + (nodeCount - postOrderIndex) + " nodes are unreachable from the root:");
   1526             var dumpNode = this.rootNode();
   1527             for (var i = 0; i < nodeCount; ++i) {
   1528                 if (!visited[i]) {
   1529                     // Fix it by giving the node a postorder index anyway.
   1530                     nodeOrdinal2PostOrderIndex[i] = postOrderIndex;
   1531                     postOrderIndex2NodeOrdinal[postOrderIndex++] = i;
   1532                     dumpNode.nodeIndex = i * nodeFieldCount;
   1533                     console.log(JSON.stringify(dumpNode.serialize()));
   1534                     for (var retainers = dumpNode.retainers(); retainers.hasNext(); retainers = retainers.item().node().retainers())
   1535                         console.log("  edgeName: " + retainers.item().name() + " nodeClassName: " + retainers.item().node().className());
   1536                 }
   1537             }
   1538         }
   1539 
   1540         return {postOrderIndex2NodeOrdinal: postOrderIndex2NodeOrdinal, nodeOrdinal2PostOrderIndex: nodeOrdinal2PostOrderIndex};
   1541     },
   1542 
   1543     // The algorithm is based on the article:
   1544     // K. Cooper, T. Harvey and K. Kennedy "A Simple, Fast Dominance Algorithm"
   1545     // Softw. Pract. Exper. 4 (2001), pp. 1-10.
   1546     /**
   1547      * @param {!Array.<number>} postOrderIndex2NodeOrdinal
   1548      * @param {!Array.<number>} nodeOrdinal2PostOrderIndex
   1549      */
   1550     _buildDominatorTree: function(postOrderIndex2NodeOrdinal, nodeOrdinal2PostOrderIndex)
   1551     {
   1552         var nodeFieldCount = this._nodeFieldCount;
   1553         var nodes = this.nodes;
   1554         var firstRetainerIndex = this._firstRetainerIndex;
   1555         var retainingNodes = this._retainingNodes;
   1556         var retainingEdges = this._retainingEdges;
   1557         var edgeFieldsCount = this._edgeFieldsCount;
   1558         var edgeTypeOffset = this._edgeTypeOffset;
   1559         var edgeToNodeOffset = this._edgeToNodeOffset;
   1560         var edgeShortcutType = this._edgeShortcutType;
   1561         var firstEdgeIndexes = this._firstEdgeIndexes;
   1562         var containmentEdges = this.containmentEdges;
   1563         var containmentEdgesLength = this.containmentEdges.length;
   1564         var rootNodeIndex = this._rootNodeIndex;
   1565 
   1566         var mapAndFlag = this.userObjectsMapAndFlag();
   1567         var flags = mapAndFlag ? mapAndFlag.map : null;
   1568         var flag = mapAndFlag ? mapAndFlag.flag : 0;
   1569 
   1570         var nodesCount = postOrderIndex2NodeOrdinal.length;
   1571         var rootPostOrderedIndex = nodesCount - 1;
   1572         var noEntry = nodesCount;
   1573         var dominators = new Uint32Array(nodesCount);
   1574         for (var i = 0; i < rootPostOrderedIndex; ++i)
   1575             dominators[i] = noEntry;
   1576         dominators[rootPostOrderedIndex] = rootPostOrderedIndex;
   1577 
   1578         // The affected array is used to mark entries which dominators
   1579         // have to be racalculated because of changes in their retainers.
   1580         var affected = new Uint8Array(nodesCount);
   1581         var nodeOrdinal;
   1582 
   1583         { // Mark the root direct children as affected.
   1584             nodeOrdinal = this._rootNodeIndex / nodeFieldCount;
   1585             var beginEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal] + edgeToNodeOffset;
   1586             var endEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal + 1];
   1587             for (var toNodeFieldIndex = beginEdgeToNodeFieldIndex;
   1588                  toNodeFieldIndex < endEdgeToNodeFieldIndex;
   1589                  toNodeFieldIndex += edgeFieldsCount) {
   1590                 var childNodeOrdinal = containmentEdges[toNodeFieldIndex] / nodeFieldCount;
   1591                 affected[nodeOrdinal2PostOrderIndex[childNodeOrdinal]] = 1;
   1592             }
   1593         }
   1594 
   1595         var changed = true;
   1596         while (changed) {
   1597             changed = false;
   1598             for (var postOrderIndex = rootPostOrderedIndex - 1; postOrderIndex >= 0; --postOrderIndex) {
   1599                 if (affected[postOrderIndex] === 0)
   1600                     continue;
   1601                 affected[postOrderIndex] = 0;
   1602                 // If dominator of the entry has already been set to root,
   1603                 // then it can't propagate any further.
   1604                 if (dominators[postOrderIndex] === rootPostOrderedIndex)
   1605                     continue;
   1606                 nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex];
   1607                 var nodeFlag = !flags || (flags[nodeOrdinal] & flag);
   1608                 var newDominatorIndex = noEntry;
   1609                 var beginRetainerIndex = firstRetainerIndex[nodeOrdinal];
   1610                 var endRetainerIndex = firstRetainerIndex[nodeOrdinal + 1];
   1611                 for (var retainerIndex = beginRetainerIndex; retainerIndex < endRetainerIndex; ++retainerIndex) {
   1612                     var retainerEdgeIndex = retainingEdges[retainerIndex];
   1613                     var retainerEdgeType = containmentEdges[retainerEdgeIndex + edgeTypeOffset];
   1614                     var retainerNodeIndex = retainingNodes[retainerIndex];
   1615                     if (retainerNodeIndex !== rootNodeIndex && retainerEdgeType === edgeShortcutType)
   1616                         continue;
   1617                     var retainerNodeOrdinal = retainerNodeIndex / nodeFieldCount;
   1618                     var retainerNodeFlag = !flags || (flags[retainerNodeOrdinal] & flag);
   1619                     // We are skipping the edges from non-page-owned nodes to page-owned nodes.
   1620                     // Otherwise the dominators for the objects that also were retained by debugger would be affected.
   1621                     if (retainerNodeIndex !== rootNodeIndex && nodeFlag && !retainerNodeFlag)
   1622                         continue;
   1623                     var retanerPostOrderIndex = nodeOrdinal2PostOrderIndex[retainerNodeOrdinal];
   1624                     if (dominators[retanerPostOrderIndex] !== noEntry) {
   1625                         if (newDominatorIndex === noEntry)
   1626                             newDominatorIndex = retanerPostOrderIndex;
   1627                         else {
   1628                             while (retanerPostOrderIndex !== newDominatorIndex) {
   1629                                 while (retanerPostOrderIndex < newDominatorIndex)
   1630                                     retanerPostOrderIndex = dominators[retanerPostOrderIndex];
   1631                                 while (newDominatorIndex < retanerPostOrderIndex)
   1632                                     newDominatorIndex = dominators[newDominatorIndex];
   1633                             }
   1634                         }
   1635                         // If idom has already reached the root, it doesn't make sense
   1636                         // to check other retainers.
   1637                         if (newDominatorIndex === rootPostOrderedIndex)
   1638                             break;
   1639                     }
   1640                 }
   1641                 if (newDominatorIndex !== noEntry && dominators[postOrderIndex] !== newDominatorIndex) {
   1642                     dominators[postOrderIndex] = newDominatorIndex;
   1643                     changed = true;
   1644                     nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex];
   1645                     beginEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal] + edgeToNodeOffset;
   1646                     endEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal + 1];
   1647                     for (var toNodeFieldIndex = beginEdgeToNodeFieldIndex;
   1648                          toNodeFieldIndex < endEdgeToNodeFieldIndex;
   1649                          toNodeFieldIndex += edgeFieldsCount) {
   1650                         var childNodeOrdinal = containmentEdges[toNodeFieldIndex] / nodeFieldCount;
   1651                         affected[nodeOrdinal2PostOrderIndex[childNodeOrdinal]] = 1;
   1652                     }
   1653                 }
   1654             }
   1655         }
   1656 
   1657         var dominatorsTree = new Uint32Array(nodesCount);
   1658         for (var postOrderIndex = 0, l = dominators.length; postOrderIndex < l; ++postOrderIndex) {
   1659             nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex];
   1660             dominatorsTree[nodeOrdinal] = postOrderIndex2NodeOrdinal[dominators[postOrderIndex]];
   1661         }
   1662         return dominatorsTree;
   1663     },
   1664 
   1665     _calculateRetainedSizes: function(postOrderIndex2NodeOrdinal)
   1666     {
   1667         var nodeCount = this.nodeCount;
   1668         var nodes = this.nodes;
   1669         var nodeSelfSizeOffset = this._nodeSelfSizeOffset;
   1670         var nodeFieldCount = this._nodeFieldCount;
   1671         var dominatorsTree = this._dominatorsTree;
   1672         var retainedSizes = this._retainedSizes;
   1673 
   1674         for (var nodeOrdinal = 0; nodeOrdinal < nodeCount; ++nodeOrdinal)
   1675             retainedSizes[nodeOrdinal] = nodes[nodeOrdinal * nodeFieldCount + nodeSelfSizeOffset];
   1676 
   1677         // Propagate retained sizes for each node excluding root.
   1678         for (var postOrderIndex = 0; postOrderIndex < nodeCount - 1; ++postOrderIndex) {
   1679             var nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex];
   1680             var dominatorOrdinal = dominatorsTree[nodeOrdinal];
   1681             retainedSizes[dominatorOrdinal] += retainedSizes[nodeOrdinal];
   1682         }
   1683     },
   1684 
   1685     _buildDominatedNodes: function()
   1686     {
   1687         // Builds up two arrays:
   1688         //  - "dominatedNodes" is a continuous array, where each node owns an
   1689         //    interval (can be empty) with corresponding dominated nodes.
   1690         //  - "indexArray" is an array of indexes in the "dominatedNodes"
   1691         //    with the same positions as in the _nodeIndex.
   1692         var indexArray = this._firstDominatedNodeIndex;
   1693         // All nodes except the root have dominators.
   1694         var dominatedNodes = this._dominatedNodes;
   1695 
   1696         // Count the number of dominated nodes for each node. Skip the root (node at
   1697         // index 0) as it is the only node that dominates itself.
   1698         var nodeFieldCount = this._nodeFieldCount;
   1699         var dominatorsTree = this._dominatorsTree;
   1700 
   1701         var fromNodeOrdinal = 0;
   1702         var toNodeOrdinal = this.nodeCount;
   1703         var rootNodeOrdinal = this._rootNodeIndex / nodeFieldCount;
   1704         if (rootNodeOrdinal === fromNodeOrdinal)
   1705             fromNodeOrdinal = 1;
   1706         else if (rootNodeOrdinal === toNodeOrdinal - 1)
   1707             toNodeOrdinal = toNodeOrdinal - 1;
   1708         else
   1709             throw new Error("Root node is expected to be either first or last");
   1710         for (var nodeOrdinal = fromNodeOrdinal; nodeOrdinal < toNodeOrdinal; ++nodeOrdinal)
   1711             ++indexArray[dominatorsTree[nodeOrdinal]];
   1712         // Put in the first slot of each dominatedNodes slice the count of entries
   1713         // that will be filled.
   1714         var firstDominatedNodeIndex = 0;
   1715         for (var i = 0, l = this.nodeCount; i < l; ++i) {
   1716             var dominatedCount = dominatedNodes[firstDominatedNodeIndex] = indexArray[i];
   1717             indexArray[i] = firstDominatedNodeIndex;
   1718             firstDominatedNodeIndex += dominatedCount;
   1719         }
   1720         indexArray[this.nodeCount] = dominatedNodes.length;
   1721         // Fill up the dominatedNodes array with indexes of dominated nodes. Skip the root (node at
   1722         // index 0) as it is the only node that dominates itself.
   1723         for (var nodeOrdinal = fromNodeOrdinal; nodeOrdinal < toNodeOrdinal; ++nodeOrdinal) {
   1724             var dominatorOrdinal = dominatorsTree[nodeOrdinal];
   1725             var dominatedRefIndex = indexArray[dominatorOrdinal];
   1726             dominatedRefIndex += (--dominatedNodes[dominatedRefIndex]);
   1727             dominatedNodes[dominatedRefIndex] = nodeOrdinal * nodeFieldCount;
   1728         }
   1729     },
   1730 
   1731     _calculateFlags: function()
   1732     {
   1733         throw new Error("Not implemented");
   1734     },
   1735 
   1736     _calculateStatistics: function()
   1737     {
   1738         throw new Error("Not implemented");
   1739     },
   1740 
   1741     userObjectsMapAndFlag: function()
   1742     {
   1743         throw new Error("Not implemented");
   1744     },
   1745 
   1746     /**
   1747      * @param {string} baseSnapshotId
   1748      * @param {!Object.<string, !WebInspector.HeapSnapshotCommon.AggregateForDiff>} baseSnapshotAggregates
   1749      * @return {!Object.<string, !WebInspector.HeapSnapshotCommon.Diff>}
   1750      */
   1751     calculateSnapshotDiff: function(baseSnapshotId, baseSnapshotAggregates)
   1752     {
   1753         var snapshotDiff = this._snapshotDiffs[baseSnapshotId];
   1754         if (snapshotDiff)
   1755             return snapshotDiff;
   1756         snapshotDiff = {};
   1757 
   1758         var aggregates = this.aggregates(true, "allObjects");
   1759         for (var className in baseSnapshotAggregates) {
   1760             var baseAggregate = baseSnapshotAggregates[className];
   1761             var diff = this._calculateDiffForClass(baseAggregate, aggregates[className]);
   1762             if (diff)
   1763                 snapshotDiff[className] = diff;
   1764         }
   1765         var emptyBaseAggregate = new WebInspector.HeapSnapshotCommon.AggregateForDiff();
   1766         for (var className in aggregates) {
   1767             if (className in baseSnapshotAggregates)
   1768                 continue;
   1769             snapshotDiff[className] = this._calculateDiffForClass(emptyBaseAggregate, aggregates[className]);
   1770         }
   1771 
   1772         this._snapshotDiffs[baseSnapshotId] = snapshotDiff;
   1773         return snapshotDiff;
   1774     },
   1775 
   1776     /**
   1777      * @param {!WebInspector.HeapSnapshotCommon.AggregateForDiff} baseAggregate
   1778      * @param {!WebInspector.HeapSnapshotCommon.Aggregate} aggregate
   1779      * @return {?WebInspector.HeapSnapshotCommon.Diff}
   1780      */
   1781     _calculateDiffForClass: function(baseAggregate, aggregate)
   1782     {
   1783         var baseIds = baseAggregate.ids;
   1784         var baseIndexes = baseAggregate.indexes;
   1785         var baseSelfSizes = baseAggregate.selfSizes;
   1786 
   1787         var indexes = aggregate ? aggregate.idxs : [];
   1788 
   1789         var i = 0, l = baseIds.length;
   1790         var j = 0, m = indexes.length;
   1791         var diff = new WebInspector.HeapSnapshotCommon.Diff();
   1792 
   1793         var nodeB = this.createNode(indexes[j]);
   1794         while (i < l && j < m) {
   1795             var nodeAId = baseIds[i];
   1796             if (nodeAId < nodeB.id()) {
   1797                 diff.deletedIndexes.push(baseIndexes[i]);
   1798                 diff.removedCount++;
   1799                 diff.removedSize += baseSelfSizes[i];
   1800                 ++i;
   1801             } else if (nodeAId > nodeB.id()) { // Native nodes(e.g. dom groups) may have ids less than max JS object id in the base snapshot
   1802                 diff.addedIndexes.push(indexes[j]);
   1803                 diff.addedCount++;
   1804                 diff.addedSize += nodeB.selfSize();
   1805                 nodeB.nodeIndex = indexes[++j];
   1806             } else { // nodeAId === nodeB.id()
   1807                 ++i;
   1808                 nodeB.nodeIndex = indexes[++j];
   1809             }
   1810         }
   1811         while (i < l) {
   1812             diff.deletedIndexes.push(baseIndexes[i]);
   1813             diff.removedCount++;
   1814             diff.removedSize += baseSelfSizes[i];
   1815             ++i;
   1816         }
   1817         while (j < m) {
   1818             diff.addedIndexes.push(indexes[j]);
   1819             diff.addedCount++;
   1820             diff.addedSize += nodeB.selfSize();
   1821             nodeB.nodeIndex = indexes[++j];
   1822         }
   1823         diff.countDelta = diff.addedCount - diff.removedCount;
   1824         diff.sizeDelta = diff.addedSize - diff.removedSize;
   1825         if (!diff.addedCount && !diff.removedCount)
   1826             return null;
   1827         return diff;
   1828     },
   1829 
   1830     _nodeForSnapshotObjectId: function(snapshotObjectId)
   1831     {
   1832         for (var it = this._allNodes(); it.hasNext(); it.next()) {
   1833             if (it.node.id() === snapshotObjectId)
   1834                 return it.node;
   1835         }
   1836         return null;
   1837     },
   1838 
   1839     /**
   1840      * @param {string} snapshotObjectId
   1841      * @return {?string}
   1842      */
   1843     nodeClassName: function(snapshotObjectId)
   1844     {
   1845         var node = this._nodeForSnapshotObjectId(snapshotObjectId);
   1846         if (node)
   1847             return node.className();
   1848         return null;
   1849     },
   1850 
   1851     /**
   1852      * @param {string} name
   1853      * @return {!Array.<number>}
   1854      */
   1855     idsOfObjectsWithName: function(name)
   1856     {
   1857         var ids = [];
   1858         for (var it = this._allNodes(); it.hasNext(); it.next()) {
   1859             if (it.item().name() === name)
   1860                 ids.push(it.item().id());
   1861         }
   1862         return ids;
   1863     },
   1864 
   1865     /**
   1866      * @param {number} nodeIndex
   1867      * @return {!WebInspector.HeapSnapshotEdgesProvider}
   1868      */
   1869     createEdgesProvider: function(nodeIndex)
   1870     {
   1871         var node = this.createNode(nodeIndex);
   1872         var filter = this.containmentEdgesFilter();
   1873         var indexProvider = new WebInspector.HeapSnapshotEdgeIndexProvider(this);
   1874         return new WebInspector.HeapSnapshotEdgesProvider(this, filter, node.edges(), indexProvider);
   1875     },
   1876 
   1877     /**
   1878      * @param {number} nodeIndex
   1879      * @param {?function(!WebInspector.HeapSnapshotEdge):boolean} filter
   1880      * @return {!WebInspector.HeapSnapshotEdgesProvider}
   1881      */
   1882     createEdgesProviderForTest: function(nodeIndex, filter)
   1883     {
   1884         var node = this.createNode(nodeIndex);
   1885         var indexProvider = new WebInspector.HeapSnapshotEdgeIndexProvider(this);
   1886         return new WebInspector.HeapSnapshotEdgesProvider(this, filter, node.edges(), indexProvider);
   1887     },
   1888 
   1889     /**
   1890      * @return {?function(!WebInspector.HeapSnapshotEdge):boolean}
   1891      */
   1892     retainingEdgesFilter: function()
   1893     {
   1894         return null;
   1895     },
   1896 
   1897     /**
   1898      * @return {?function(!WebInspector.HeapSnapshotEdge):boolean}
   1899      */
   1900     containmentEdgesFilter: function()
   1901     {
   1902         return null;
   1903     },
   1904 
   1905     /**
   1906      * @param {number} nodeIndex
   1907      * @return {!WebInspector.HeapSnapshotEdgesProvider}
   1908      */
   1909     createRetainingEdgesProvider: function(nodeIndex)
   1910     {
   1911         var node = this.createNode(nodeIndex);
   1912         var filter = this.retainingEdgesFilter();
   1913         var indexProvider = new WebInspector.HeapSnapshotRetainerEdgeIndexProvider(this);
   1914         return new WebInspector.HeapSnapshotEdgesProvider(this, filter, node.retainers(), indexProvider);
   1915     },
   1916 
   1917     /**
   1918      * @param {string} baseSnapshotId
   1919      * @param {string} className
   1920      * @return {!WebInspector.HeapSnapshotNodesProvider}
   1921      */
   1922     createAddedNodesProvider: function(baseSnapshotId, className)
   1923     {
   1924         var snapshotDiff = this._snapshotDiffs[baseSnapshotId];
   1925         var diffForClass = snapshotDiff[className];
   1926         return new WebInspector.HeapSnapshotNodesProvider(this, null, diffForClass.addedIndexes);
   1927     },
   1928 
   1929     /**
   1930      * @param {!Array.<number>} nodeIndexes
   1931      * @return {!WebInspector.HeapSnapshotNodesProvider}
   1932      */
   1933     createDeletedNodesProvider: function(nodeIndexes)
   1934     {
   1935         return new WebInspector.HeapSnapshotNodesProvider(this, null, nodeIndexes);
   1936     },
   1937 
   1938     /**
   1939      * @return {?function(!WebInspector.HeapSnapshotNode):boolean}
   1940      */
   1941     classNodesFilter: function()
   1942     {
   1943         return null;
   1944     },
   1945 
   1946     /**
   1947      * @param {string} className
   1948      * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} nodeFilter
   1949      * @return {!WebInspector.HeapSnapshotNodesProvider}
   1950      */
   1951     createNodesProviderForClass: function(className, nodeFilter)
   1952     {
   1953         return new WebInspector.HeapSnapshotNodesProvider(this, this.classNodesFilter(), this.aggregatesWithFilter(nodeFilter)[className].idxs);
   1954     },
   1955 
   1956     /**
   1957      * @return {number}
   1958      */
   1959     _maxJsNodeId: function()
   1960     {
   1961         var nodeFieldCount = this._nodeFieldCount;
   1962         var nodes = this.nodes;
   1963         var nodesLength = nodes.length;
   1964         var id = 0;
   1965         for (var nodeIndex = this._nodeIdOffset; nodeIndex < nodesLength; nodeIndex += nodeFieldCount) {
   1966             var nextId = nodes[nodeIndex];
   1967             // JS objects have odd ids, skip native objects.
   1968             if (nextId % 2 === 0)
   1969                 continue;
   1970             if (id < nextId)
   1971                 id = nextId;
   1972         }
   1973         return id;
   1974     },
   1975 
   1976     /**
   1977      * @return {!WebInspector.HeapSnapshotCommon.StaticData}
   1978      */
   1979     updateStaticData: function()
   1980     {
   1981         return new WebInspector.HeapSnapshotCommon.StaticData(this.nodeCount, this._rootNodeIndex, this.totalSize, this._maxJsNodeId());
   1982     }
   1983 };
   1984 
   1985 /**
   1986  * @constructor
   1987  * @param {!WebInspector.HeapSnapshotItemIterator} iterator
   1988  * @param {!WebInspector.HeapSnapshotItemIndexProvider} indexProvider
   1989  */
   1990 WebInspector.HeapSnapshotItemProvider = function(iterator, indexProvider)
   1991 {
   1992     this._iterator = iterator;
   1993     this._indexProvider = indexProvider;
   1994     this._isEmpty = !iterator.hasNext();
   1995     /** @type {?Array.<number>} */
   1996     this._iterationOrder = null;
   1997     this._currentComparator = null;
   1998     this._sortedPrefixLength = 0;
   1999     this._sortedSuffixLength = 0;
   2000 }
   2001 
   2002 WebInspector.HeapSnapshotItemProvider.prototype = {
   2003     _createIterationOrder: function()
   2004     {
   2005         if (this._iterationOrder)
   2006             return;
   2007         this._iterationOrder = [];
   2008         for (var iterator = this._iterator; iterator.hasNext(); iterator.next())
   2009             this._iterationOrder.push(iterator.item().itemIndex());
   2010     },
   2011 
   2012     /**
   2013      * @return {boolean}
   2014      */
   2015     isEmpty: function()
   2016     {
   2017         return this._isEmpty;
   2018     },
   2019 
   2020     /**
   2021      * @param {number} begin
   2022      * @param {number} end
   2023      * @return {!WebInspector.HeapSnapshotCommon.ItemsRange}
   2024      */
   2025     serializeItemsRange: function(begin, end)
   2026     {
   2027         this._createIterationOrder();
   2028         if (begin > end)
   2029             throw new Error("Start position > end position: " + begin + " > " + end);
   2030         if (end > this._iterationOrder.length)
   2031             end = this._iterationOrder.length;
   2032         if (this._sortedPrefixLength < end && begin < this._iterationOrder.length - this._sortedSuffixLength) {
   2033             this.sort(this._currentComparator, this._sortedPrefixLength, this._iterationOrder.length - 1 - this._sortedSuffixLength, begin, end - 1);
   2034             if (begin <= this._sortedPrefixLength)
   2035                 this._sortedPrefixLength = end;
   2036             if (end >= this._iterationOrder.length - this._sortedSuffixLength)
   2037                 this._sortedSuffixLength = this._iterationOrder.length - begin;
   2038         }
   2039         var position = begin;
   2040         var count = end - begin;
   2041         var result = new Array(count);
   2042         var iterator = this._iterator;
   2043         for (var i = 0 ; i < count; ++i) {
   2044             var itemIndex = this._iterationOrder[position++];
   2045             var item = this._indexProvider.itemForIndex(itemIndex);
   2046             result[i] = item.serialize();
   2047         }
   2048         return new WebInspector.HeapSnapshotCommon.ItemsRange(begin, end, this._iterationOrder.length, result);
   2049     },
   2050 
   2051     sortAndRewind: function(comparator)
   2052     {
   2053         this._currentComparator = comparator;
   2054         this._sortedPrefixLength = 0;
   2055         this._sortedSuffixLength = 0;
   2056     }
   2057 }
   2058 
   2059 /**
   2060  * @constructor
   2061  * @extends {WebInspector.HeapSnapshotItemProvider}
   2062  * @param {!WebInspector.HeapSnapshot} snapshot
   2063  * @param {?function(!WebInspector.HeapSnapshotEdge):boolean} filter
   2064  * @param {!WebInspector.HeapSnapshotEdgeIterator} edgesIter
   2065  * @param {!WebInspector.HeapSnapshotItemIndexProvider} indexProvider
   2066  */
   2067 WebInspector.HeapSnapshotEdgesProvider = function(snapshot, filter, edgesIter, indexProvider)
   2068 {
   2069     this.snapshot = snapshot;
   2070     var iter = filter ? new WebInspector.HeapSnapshotFilteredIterator(edgesIter, /** @type {function(!WebInspector.HeapSnapshotItem):boolean} */ (filter)) : edgesIter;
   2071     WebInspector.HeapSnapshotItemProvider.call(this, iter, indexProvider);
   2072 }
   2073 
   2074 WebInspector.HeapSnapshotEdgesProvider.prototype = {
   2075     /**
   2076      * @param {!WebInspector.HeapSnapshotCommon.ComparatorConfig} comparator
   2077      * @param {number} leftBound
   2078      * @param {number} rightBound
   2079      * @param {number} windowLeft
   2080      * @param {number} windowRight
   2081      */
   2082     sort: function(comparator, leftBound, rightBound, windowLeft, windowRight)
   2083     {
   2084         var fieldName1 = comparator.fieldName1;
   2085         var fieldName2 = comparator.fieldName2;
   2086         var ascending1 = comparator.ascending1;
   2087         var ascending2 = comparator.ascending2;
   2088 
   2089         var edgeA = this._iterator.item().clone();
   2090         var edgeB = edgeA.clone();
   2091         var nodeA = this.snapshot.createNode();
   2092         var nodeB = this.snapshot.createNode();
   2093 
   2094         function compareEdgeFieldName(ascending, indexA, indexB)
   2095         {
   2096             edgeA.edgeIndex = indexA;
   2097             edgeB.edgeIndex = indexB;
   2098             if (edgeB.name() === "__proto__") return -1;
   2099             if (edgeA.name() === "__proto__") return 1;
   2100             var result =
   2101                 edgeA.hasStringName() === edgeB.hasStringName() ?
   2102                 (edgeA.name() < edgeB.name() ? -1 : (edgeA.name() > edgeB.name() ? 1 : 0)) :
   2103                 (edgeA.hasStringName() ? -1 : 1);
   2104             return ascending ? result : -result;
   2105         }
   2106 
   2107         function compareNodeField(fieldName, ascending, indexA, indexB)
   2108         {
   2109             edgeA.edgeIndex = indexA;
   2110             nodeA.nodeIndex = edgeA.nodeIndex();
   2111             var valueA = nodeA[fieldName]();
   2112 
   2113             edgeB.edgeIndex = indexB;
   2114             nodeB.nodeIndex = edgeB.nodeIndex();
   2115             var valueB = nodeB[fieldName]();
   2116 
   2117             var result = valueA < valueB ? -1 : (valueA > valueB ? 1 : 0);
   2118             return ascending ? result : -result;
   2119         }
   2120 
   2121         function compareEdgeAndNode(indexA, indexB) {
   2122             var result = compareEdgeFieldName(ascending1, indexA, indexB);
   2123             if (result === 0)
   2124                 result = compareNodeField(fieldName2, ascending2, indexA, indexB);
   2125             if (result === 0)
   2126                 return indexA - indexB;
   2127             return result;
   2128         }
   2129 
   2130         function compareNodeAndEdge(indexA, indexB) {
   2131             var result = compareNodeField(fieldName1, ascending1, indexA, indexB);
   2132             if (result === 0)
   2133                 result = compareEdgeFieldName(ascending2, indexA, indexB);
   2134             if (result === 0)
   2135                 return indexA - indexB;
   2136             return result;
   2137         }
   2138 
   2139         function compareNodeAndNode(indexA, indexB) {
   2140             var result = compareNodeField(fieldName1, ascending1, indexA, indexB);
   2141             if (result === 0)
   2142                 result = compareNodeField(fieldName2, ascending2, indexA, indexB);
   2143             if (result === 0)
   2144                 return indexA - indexB;
   2145             return result;
   2146         }
   2147 
   2148         if (fieldName1 === "!edgeName")
   2149             this._iterationOrder.sortRange(compareEdgeAndNode, leftBound, rightBound, windowLeft, windowRight);
   2150         else if (fieldName2 === "!edgeName")
   2151             this._iterationOrder.sortRange(compareNodeAndEdge, leftBound, rightBound, windowLeft, windowRight);
   2152         else
   2153             this._iterationOrder.sortRange(compareNodeAndNode, leftBound, rightBound, windowLeft, windowRight);
   2154     },
   2155 
   2156     __proto__: WebInspector.HeapSnapshotItemProvider.prototype
   2157 }
   2158 
   2159 
   2160 /**
   2161  * @constructor
   2162  * @extends {WebInspector.HeapSnapshotItemProvider}
   2163  * @param {!WebInspector.HeapSnapshot} snapshot
   2164  * @param {?function(!WebInspector.HeapSnapshotNode):boolean} filter
   2165  * @param {(!Array.<number>|!Uint32Array)} nodeIndexes
   2166  */
   2167 WebInspector.HeapSnapshotNodesProvider = function(snapshot, filter, nodeIndexes)
   2168 {
   2169     this.snapshot = snapshot;
   2170     var indexProvider = new WebInspector.HeapSnapshotNodeIndexProvider(snapshot);
   2171     var it = new WebInspector.HeapSnapshotIndexRangeIterator(indexProvider, nodeIndexes);
   2172 
   2173     if (filter)
   2174         it = new WebInspector.HeapSnapshotFilteredIterator(it, /** @type {function(!WebInspector.HeapSnapshotItem):boolean} */ (filter));
   2175     WebInspector.HeapSnapshotItemProvider.call(this, it, indexProvider);
   2176 }
   2177 
   2178 WebInspector.HeapSnapshotNodesProvider.prototype = {
   2179     /**
   2180      * @param {string} snapshotObjectId
   2181      * @return {number}
   2182      */
   2183     nodePosition: function(snapshotObjectId)
   2184     {
   2185         this._createIterationOrder();
   2186         var node = this.snapshot.createNode();
   2187         for (var i = 0; i < this._iterationOrder.length; i++) {
   2188             node.nodeIndex = this._iterationOrder[i];
   2189             if (node.id() === snapshotObjectId)
   2190                 break;
   2191         }
   2192         if (i === this._iterationOrder.length)
   2193             return -1;
   2194         var targetNodeIndex = this._iterationOrder[i];
   2195         var smallerCount = 0;
   2196         var compare = this._buildCompareFunction(this._currentComparator);
   2197         for (var i = 0; i < this._iterationOrder.length; i++) {
   2198             if (compare(this._iterationOrder[i], targetNodeIndex) < 0)
   2199                 ++smallerCount;
   2200         }
   2201         return smallerCount;
   2202     },
   2203 
   2204     /**
   2205      * @return {function(number,number):number}
   2206      */
   2207     _buildCompareFunction: function(comparator)
   2208     {
   2209         var nodeA = this.snapshot.createNode();
   2210         var nodeB = this.snapshot.createNode();
   2211         var fieldAccessor1 = nodeA[comparator.fieldName1];
   2212         var fieldAccessor2 = nodeA[comparator.fieldName2];
   2213         var ascending1 = comparator.ascending1 ? 1 : -1;
   2214         var ascending2 = comparator.ascending2 ? 1 : -1;
   2215 
   2216         /**
   2217          * @param {function():*} fieldAccessor
   2218          * @param {number} ascending
   2219          * @return {number}
   2220          */
   2221         function sortByNodeField(fieldAccessor, ascending)
   2222         {
   2223             var valueA = fieldAccessor.call(nodeA);
   2224             var valueB = fieldAccessor.call(nodeB);
   2225             return valueA < valueB ? -ascending : (valueA > valueB ? ascending : 0);
   2226         }
   2227 
   2228         /**
   2229          * @param {number} indexA
   2230          * @param {number} indexB
   2231          * @return {number}
   2232          */
   2233         function sortByComparator(indexA, indexB)
   2234         {
   2235             nodeA.nodeIndex = indexA;
   2236             nodeB.nodeIndex = indexB;
   2237             var result = sortByNodeField(fieldAccessor1, ascending1);
   2238             if (result === 0)
   2239                 result = sortByNodeField(fieldAccessor2, ascending2);
   2240             return result || indexA - indexB;
   2241         }
   2242 
   2243         return sortByComparator;
   2244     },
   2245 
   2246     /**
   2247      * @param {!WebInspector.HeapSnapshotCommon.ComparatorConfig} comparator
   2248      * @param {number} leftBound
   2249      * @param {number} rightBound
   2250      * @param {number} windowLeft
   2251      * @param {number} windowRight
   2252      */
   2253     sort: function(comparator, leftBound, rightBound, windowLeft, windowRight)
   2254     {
   2255         this._iterationOrder.sortRange(this._buildCompareFunction(comparator), leftBound, rightBound, windowLeft, windowRight);
   2256     },
   2257 
   2258     __proto__: WebInspector.HeapSnapshotItemProvider.prototype
   2259 }
   2260 
   2261