HeapSnapshot.js 63 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824
  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. * @constructor
  32. */
  33. WebInspector.HeapSnapshotArraySlice = function(array, start, end)
  34. {
  35. this._array = array;
  36. this._start = start;
  37. this.length = end - start;
  38. }
  39. WebInspector.HeapSnapshotArraySlice.prototype = {
  40. item: function(index)
  41. {
  42. return this._array[this._start + index];
  43. },
  44. slice: function(start, end)
  45. {
  46. if (typeof end === "undefined")
  47. end = this.length;
  48. return this._array.subarray(this._start + start, this._start + end);
  49. }
  50. }
  51. /**
  52. * @constructor
  53. * @param {number=} edgeIndex
  54. */
  55. WebInspector.HeapSnapshotEdge = function(snapshot, edges, edgeIndex)
  56. {
  57. this._snapshot = snapshot;
  58. this._edges = edges;
  59. this.edgeIndex = edgeIndex || 0;
  60. }
  61. WebInspector.HeapSnapshotEdge.prototype = {
  62. clone: function()
  63. {
  64. return new WebInspector.HeapSnapshotEdge(this._snapshot, this._edges, this.edgeIndex);
  65. },
  66. hasStringName: function()
  67. {
  68. throw new Error("Not implemented");
  69. },
  70. name: function()
  71. {
  72. throw new Error("Not implemented");
  73. },
  74. node: function()
  75. {
  76. return this._snapshot.createNode(this.nodeIndex());
  77. },
  78. nodeIndex: function()
  79. {
  80. return this._edges.item(this.edgeIndex + this._snapshot._edgeToNodeOffset);
  81. },
  82. rawEdges: function()
  83. {
  84. return this._edges;
  85. },
  86. toString: function()
  87. {
  88. return "HeapSnapshotEdge: " + this.name();
  89. },
  90. type: function()
  91. {
  92. return this._snapshot._edgeTypes[this._type()];
  93. },
  94. serialize: function()
  95. {
  96. var node = this.node();
  97. return {
  98. name: this.name(),
  99. node: node.serialize(),
  100. nodeIndex: this.nodeIndex(),
  101. type: this.type(),
  102. distance: node.distance()
  103. };
  104. },
  105. _type: function()
  106. {
  107. return this._edges.item(this.edgeIndex + this._snapshot._edgeTypeOffset);
  108. }
  109. };
  110. /**
  111. * @constructor
  112. */
  113. WebInspector.HeapSnapshotEdgeIterator = function(edge)
  114. {
  115. this.edge = edge;
  116. }
  117. WebInspector.HeapSnapshotEdgeIterator.prototype = {
  118. rewind: function()
  119. {
  120. this.edge.edgeIndex = 0;
  121. },
  122. hasNext: function()
  123. {
  124. return this.edge.edgeIndex < this.edge._edges.length;
  125. },
  126. index: function()
  127. {
  128. return this.edge.edgeIndex;
  129. },
  130. setIndex: function(newIndex)
  131. {
  132. this.edge.edgeIndex = newIndex;
  133. },
  134. item: function()
  135. {
  136. return this.edge;
  137. },
  138. next: function()
  139. {
  140. this.edge.edgeIndex += this.edge._snapshot._edgeFieldsCount;
  141. }
  142. };
  143. /**
  144. * @constructor
  145. */
  146. WebInspector.HeapSnapshotRetainerEdge = function(snapshot, retainedNodeIndex, retainerIndex)
  147. {
  148. this._snapshot = snapshot;
  149. this._retainedNodeIndex = retainedNodeIndex;
  150. var retainedNodeOrdinal = retainedNodeIndex / snapshot._nodeFieldCount;
  151. this._firstRetainer = snapshot._firstRetainerIndex[retainedNodeOrdinal];
  152. this._retainersCount = snapshot._firstRetainerIndex[retainedNodeOrdinal + 1] - this._firstRetainer;
  153. this.setRetainerIndex(retainerIndex);
  154. }
  155. WebInspector.HeapSnapshotRetainerEdge.prototype = {
  156. clone: function()
  157. {
  158. return new WebInspector.HeapSnapshotRetainerEdge(this._snapshot, this._retainedNodeIndex, this.retainerIndex());
  159. },
  160. hasStringName: function()
  161. {
  162. return this._edge().hasStringName();
  163. },
  164. name: function()
  165. {
  166. return this._edge().name();
  167. },
  168. node: function()
  169. {
  170. return this._node();
  171. },
  172. nodeIndex: function()
  173. {
  174. return this._nodeIndex;
  175. },
  176. retainerIndex: function()
  177. {
  178. return this._retainerIndex;
  179. },
  180. setRetainerIndex: function(newIndex)
  181. {
  182. if (newIndex !== this._retainerIndex) {
  183. this._retainerIndex = newIndex;
  184. this.edgeIndex = newIndex;
  185. }
  186. },
  187. set edgeIndex(edgeIndex)
  188. {
  189. var retainerIndex = this._firstRetainer + edgeIndex;
  190. this._globalEdgeIndex = this._snapshot._retainingEdges[retainerIndex];
  191. this._nodeIndex = this._snapshot._retainingNodes[retainerIndex];
  192. delete this._edgeInstance;
  193. delete this._nodeInstance;
  194. },
  195. _node: function()
  196. {
  197. if (!this._nodeInstance)
  198. this._nodeInstance = this._snapshot.createNode(this._nodeIndex);
  199. return this._nodeInstance;
  200. },
  201. _edge: function()
  202. {
  203. if (!this._edgeInstance) {
  204. var edgeIndex = this._globalEdgeIndex - this._node()._edgeIndexesStart();
  205. this._edgeInstance = this._snapshot.createEdge(this._node().rawEdges(), edgeIndex);
  206. }
  207. return this._edgeInstance;
  208. },
  209. toString: function()
  210. {
  211. return this._edge().toString();
  212. },
  213. serialize: function()
  214. {
  215. var node = this.node();
  216. return {
  217. name: this.name(),
  218. node: node.serialize(),
  219. nodeIndex: this.nodeIndex(),
  220. type: this.type(),
  221. distance: node.distance()
  222. };
  223. },
  224. type: function()
  225. {
  226. return this._edge().type();
  227. }
  228. }
  229. /**
  230. * @constructor
  231. */
  232. WebInspector.HeapSnapshotRetainerEdgeIterator = function(retainer)
  233. {
  234. this.retainer = retainer;
  235. }
  236. WebInspector.HeapSnapshotRetainerEdgeIterator.prototype = {
  237. rewind: function()
  238. {
  239. this.retainer.setRetainerIndex(0);
  240. },
  241. hasNext: function()
  242. {
  243. return this.retainer.retainerIndex() < this.retainer._retainersCount;
  244. },
  245. index: function()
  246. {
  247. return this.retainer.retainerIndex();
  248. },
  249. setIndex: function(newIndex)
  250. {
  251. this.retainer.setRetainerIndex(newIndex);
  252. },
  253. item: function()
  254. {
  255. return this.retainer;
  256. },
  257. next: function()
  258. {
  259. this.retainer.setRetainerIndex(this.retainer.retainerIndex() + 1);
  260. }
  261. };
  262. /**
  263. * @constructor
  264. * @param {number=} nodeIndex
  265. */
  266. WebInspector.HeapSnapshotNode = function(snapshot, nodeIndex)
  267. {
  268. this._snapshot = snapshot;
  269. this._firstNodeIndex = nodeIndex;
  270. this.nodeIndex = nodeIndex;
  271. }
  272. WebInspector.HeapSnapshotNode.prototype = {
  273. distance: function()
  274. {
  275. return this._snapshot._nodeDistances[this.nodeIndex / this._snapshot._nodeFieldCount];
  276. },
  277. className: function()
  278. {
  279. throw new Error("Not implemented");
  280. },
  281. classIndex: function()
  282. {
  283. throw new Error("Not implemented");
  284. },
  285. dominatorIndex: function()
  286. {
  287. var nodeFieldCount = this._snapshot._nodeFieldCount;
  288. return this._snapshot._dominatorsTree[this.nodeIndex / this._snapshot._nodeFieldCount] * nodeFieldCount;
  289. },
  290. edges: function()
  291. {
  292. return new WebInspector.HeapSnapshotEdgeIterator(this._snapshot.createEdge(this.rawEdges(), 0));
  293. },
  294. edgesCount: function()
  295. {
  296. return (this._edgeIndexesEnd() - this._edgeIndexesStart()) / this._snapshot._edgeFieldsCount;
  297. },
  298. id: function()
  299. {
  300. throw new Error("Not implemented");
  301. },
  302. isRoot: function()
  303. {
  304. return this.nodeIndex === this._snapshot._rootNodeIndex;
  305. },
  306. name: function()
  307. {
  308. return this._snapshot._strings[this._name()];
  309. },
  310. rawEdges: function()
  311. {
  312. return new WebInspector.HeapSnapshotArraySlice(this._snapshot._containmentEdges, this._edgeIndexesStart(), this._edgeIndexesEnd());
  313. },
  314. retainedSize: function()
  315. {
  316. var snapshot = this._snapshot;
  317. return snapshot._nodes[this.nodeIndex + snapshot._nodeRetainedSizeOffset];
  318. },
  319. retainers: function()
  320. {
  321. return new WebInspector.HeapSnapshotRetainerEdgeIterator(this._snapshot.createRetainingEdge(this.nodeIndex, 0));
  322. },
  323. selfSize: function()
  324. {
  325. var snapshot = this._snapshot;
  326. return snapshot._nodes[this.nodeIndex + snapshot._nodeSelfSizeOffset];
  327. },
  328. type: function()
  329. {
  330. return this._snapshot._nodeTypes[this._type()];
  331. },
  332. serialize: function()
  333. {
  334. return {
  335. id: this.id(),
  336. name: this.name(),
  337. distance: this.distance(),
  338. nodeIndex: this.nodeIndex,
  339. retainedSize: this.retainedSize(),
  340. selfSize: this.selfSize(),
  341. type: this.type(),
  342. };
  343. },
  344. _name: function()
  345. {
  346. var snapshot = this._snapshot;
  347. return snapshot._nodes[this.nodeIndex + snapshot._nodeNameOffset];
  348. },
  349. _edgeIndexesStart: function()
  350. {
  351. return this._snapshot._firstEdgeIndexes[this._ordinal()];
  352. },
  353. _edgeIndexesEnd: function()
  354. {
  355. return this._snapshot._firstEdgeIndexes[this._ordinal() + 1];
  356. },
  357. _ordinal: function()
  358. {
  359. return this.nodeIndex / this._snapshot._nodeFieldCount;
  360. },
  361. _nextNodeIndex: function()
  362. {
  363. return this.nodeIndex + this._snapshot._nodeFieldCount;
  364. },
  365. _type: function()
  366. {
  367. var snapshot = this._snapshot;
  368. return snapshot._nodes[this.nodeIndex + snapshot._nodeTypeOffset];
  369. }
  370. };
  371. /**
  372. * @constructor
  373. */
  374. WebInspector.HeapSnapshotNodeIterator = function(node)
  375. {
  376. this.node = node;
  377. this._nodesLength = node._snapshot._nodes.length;
  378. }
  379. WebInspector.HeapSnapshotNodeIterator.prototype = {
  380. rewind: function()
  381. {
  382. this.node.nodeIndex = this.node._firstNodeIndex;
  383. },
  384. hasNext: function()
  385. {
  386. return this.node.nodeIndex < this._nodesLength;
  387. },
  388. index: function()
  389. {
  390. return this.node.nodeIndex;
  391. },
  392. setIndex: function(newIndex)
  393. {
  394. this.node.nodeIndex = newIndex;
  395. },
  396. item: function()
  397. {
  398. return this.node;
  399. },
  400. next: function()
  401. {
  402. this.node.nodeIndex = this.node._nextNodeIndex();
  403. }
  404. }
  405. /**
  406. * @param{WebInspector.HeapSnapshotWorkerDispatcher=} dispatcher
  407. * @constructor
  408. */
  409. WebInspector.HeapSnapshotProgress = function(dispatcher)
  410. {
  411. this._dispatcher = dispatcher;
  412. }
  413. WebInspector.HeapSnapshotProgress.Event = {
  414. Update: "ProgressUpdate"
  415. };
  416. WebInspector.HeapSnapshotProgress.prototype = {
  417. /**
  418. * @param{string} status
  419. */
  420. updateStatus: function(status)
  421. {
  422. this._sendUpdateEvent(WebInspector.UIString(status));
  423. },
  424. /**
  425. * @param{string} title
  426. * @param{number} value
  427. * @param{number} total
  428. */
  429. updateProgress: function(title, value, total)
  430. {
  431. var percentValue = ((total ? (value / total) : 0) * 100).toFixed(0);
  432. this._sendUpdateEvent(WebInspector.UIString(title, percentValue));
  433. },
  434. /**
  435. * @param{string} text
  436. */
  437. _sendUpdateEvent: function(text)
  438. {
  439. // May be undefined in tests.
  440. if (this._dispatcher)
  441. this._dispatcher.sendEvent(WebInspector.HeapSnapshotProgress.Event.Update, text);
  442. }
  443. }
  444. /**
  445. * @param{WebInspector.HeapSnapshotProgress} progress
  446. * @constructor
  447. */
  448. WebInspector.HeapSnapshot = function(profile, progress)
  449. {
  450. this.uid = profile.snapshot.uid;
  451. this._nodes = profile.nodes;
  452. this._containmentEdges = profile.edges;
  453. /** @type{HeapSnapshotMetainfo} */
  454. this._metaNode = profile.snapshot.meta;
  455. this._strings = profile.strings;
  456. this._progress = progress;
  457. this._noDistance = -5;
  458. this._rootNodeIndex = 0;
  459. if (profile.snapshot.root_index)
  460. this._rootNodeIndex = profile.snapshot.root_index;
  461. this._snapshotDiffs = {};
  462. this._aggregatesForDiff = null;
  463. this._init();
  464. }
  465. /**
  466. * @constructor
  467. */
  468. function HeapSnapshotMetainfo()
  469. {
  470. // New format.
  471. this.node_fields = [];
  472. this.node_types = [];
  473. this.edge_fields = [];
  474. this.edge_types = [];
  475. this.type_strings = {};
  476. // Old format.
  477. this.fields = [];
  478. this.types = [];
  479. }
  480. /**
  481. * @constructor
  482. */
  483. function HeapSnapshotHeader()
  484. {
  485. // New format.
  486. this.title = "";
  487. this.uid = 0;
  488. this.meta = new HeapSnapshotMetainfo();
  489. this.node_count = 0;
  490. this.edge_count = 0;
  491. }
  492. WebInspector.HeapSnapshot.prototype = {
  493. _init: function()
  494. {
  495. var meta = this._metaNode;
  496. this._nodeTypeOffset = meta.node_fields.indexOf("type");
  497. this._nodeNameOffset = meta.node_fields.indexOf("name");
  498. this._nodeIdOffset = meta.node_fields.indexOf("id");
  499. this._nodeSelfSizeOffset = meta.node_fields.indexOf("self_size");
  500. this._nodeEdgeCountOffset = meta.node_fields.indexOf("edge_count");
  501. this._nodeFieldCount = meta.node_fields.length;
  502. this._nodeTypes = meta.node_types[this._nodeTypeOffset];
  503. this._nodeHiddenType = this._nodeTypes.indexOf("hidden");
  504. this._nodeObjectType = this._nodeTypes.indexOf("object");
  505. this._nodeNativeType = this._nodeTypes.indexOf("native");
  506. this._nodeCodeType = this._nodeTypes.indexOf("code");
  507. this._nodeSyntheticType = this._nodeTypes.indexOf("synthetic");
  508. this._edgeFieldsCount = meta.edge_fields.length;
  509. this._edgeTypeOffset = meta.edge_fields.indexOf("type");
  510. this._edgeNameOffset = meta.edge_fields.indexOf("name_or_index");
  511. this._edgeToNodeOffset = meta.edge_fields.indexOf("to_node");
  512. this._edgeTypes = meta.edge_types[this._edgeTypeOffset];
  513. this._edgeTypes.push("invisible");
  514. this._edgeElementType = this._edgeTypes.indexOf("element");
  515. this._edgeHiddenType = this._edgeTypes.indexOf("hidden");
  516. this._edgeInternalType = this._edgeTypes.indexOf("internal");
  517. this._edgeShortcutType = this._edgeTypes.indexOf("shortcut");
  518. this._edgeWeakType = this._edgeTypes.indexOf("weak");
  519. this._edgeInvisibleType = this._edgeTypes.indexOf("invisible");
  520. this.nodeCount = this._nodes.length / this._nodeFieldCount;
  521. this._edgeCount = this._containmentEdges.length / this._edgeFieldsCount;
  522. this._progress.updateStatus("Building edge indexes\u2026");
  523. this._buildEdgeIndexes();
  524. this._progress.updateStatus("Marking invisible edges\u2026");
  525. this._markInvisibleEdges();
  526. this._progress.updateStatus("Building retainers\u2026");
  527. this._buildRetainers();
  528. this._progress.updateStatus("Calculating node flags\u2026");
  529. this._calculateFlags();
  530. this._progress.updateStatus("Calculating distances\u2026");
  531. this._calculateDistances();
  532. this._progress.updateStatus("Building postorder index\u2026");
  533. var result = this._buildPostOrderIndex();
  534. // Actually it is array that maps node ordinal number to dominator node ordinal number.
  535. this._progress.updateStatus("Building dominator tree\u2026");
  536. this._dominatorsTree = this._buildDominatorTree(result.postOrderIndex2NodeOrdinal, result.nodeOrdinal2PostOrderIndex);
  537. this._progress.updateStatus("Calculating retained sizes\u2026");
  538. this._calculateRetainedSizes(result.postOrderIndex2NodeOrdinal);
  539. this._progress.updateStatus("Buiding dominated nodes\u2026");
  540. this._buildDominatedNodes();
  541. this._progress.updateStatus("Finished processing.");
  542. },
  543. _buildEdgeIndexes: function()
  544. {
  545. var nodes = this._nodes;
  546. var nodeCount = this.nodeCount;
  547. var firstEdgeIndexes = this._firstEdgeIndexes = new Uint32Array(nodeCount + 1);
  548. var nodeFieldCount = this._nodeFieldCount;
  549. var edgeFieldsCount = this._edgeFieldsCount;
  550. var nodeEdgeCountOffset = this._nodeEdgeCountOffset;
  551. firstEdgeIndexes[nodeCount] = this._containmentEdges.length;
  552. for (var nodeOrdinal = 0, edgeIndex = 0; nodeOrdinal < nodeCount; ++nodeOrdinal) {
  553. firstEdgeIndexes[nodeOrdinal] = edgeIndex;
  554. edgeIndex += nodes[nodeOrdinal * nodeFieldCount + nodeEdgeCountOffset] * edgeFieldsCount;
  555. }
  556. },
  557. _buildRetainers: function()
  558. {
  559. var retainingNodes = this._retainingNodes = new Uint32Array(this._edgeCount);
  560. var retainingEdges = this._retainingEdges = new Uint32Array(this._edgeCount);
  561. // Index of the first retainer in the _retainingNodes and _retainingEdges
  562. // arrays. Addressed by retained node index.
  563. var firstRetainerIndex = this._firstRetainerIndex = new Uint32Array(this.nodeCount + 1);
  564. var containmentEdges = this._containmentEdges;
  565. var edgeFieldsCount = this._edgeFieldsCount;
  566. var nodeFieldCount = this._nodeFieldCount;
  567. var edgeToNodeOffset = this._edgeToNodeOffset;
  568. var nodes = this._nodes;
  569. var firstEdgeIndexes = this._firstEdgeIndexes;
  570. var nodeCount = this.nodeCount;
  571. for (var toNodeFieldIndex = edgeToNodeOffset, l = containmentEdges.length; toNodeFieldIndex < l; toNodeFieldIndex += edgeFieldsCount) {
  572. var toNodeIndex = containmentEdges[toNodeFieldIndex];
  573. if (toNodeIndex % nodeFieldCount)
  574. throw new Error("Invalid toNodeIndex " + toNodeIndex);
  575. ++firstRetainerIndex[toNodeIndex / nodeFieldCount];
  576. }
  577. for (var i = 0, firstUnusedRetainerSlot = 0; i < nodeCount; i++) {
  578. var retainersCount = firstRetainerIndex[i];
  579. firstRetainerIndex[i] = firstUnusedRetainerSlot;
  580. retainingNodes[firstUnusedRetainerSlot] = retainersCount;
  581. firstUnusedRetainerSlot += retainersCount;
  582. }
  583. firstRetainerIndex[nodeCount] = retainingNodes.length;
  584. var nextNodeFirstEdgeIndex = firstEdgeIndexes[0];
  585. for (var srcNodeOrdinal = 0; srcNodeOrdinal < nodeCount; ++srcNodeOrdinal) {
  586. var firstEdgeIndex = nextNodeFirstEdgeIndex;
  587. nextNodeFirstEdgeIndex = firstEdgeIndexes[srcNodeOrdinal + 1];
  588. var srcNodeIndex = srcNodeOrdinal * nodeFieldCount;
  589. for (var edgeIndex = firstEdgeIndex; edgeIndex < nextNodeFirstEdgeIndex; edgeIndex += edgeFieldsCount) {
  590. var toNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
  591. if (toNodeIndex % nodeFieldCount)
  592. throw new Error("Invalid toNodeIndex " + toNodeIndex);
  593. var firstRetainerSlotIndex = firstRetainerIndex[toNodeIndex / nodeFieldCount];
  594. var nextUnusedRetainerSlotIndex = firstRetainerSlotIndex + (--retainingNodes[firstRetainerSlotIndex]);
  595. retainingNodes[nextUnusedRetainerSlotIndex] = srcNodeIndex;
  596. retainingEdges[nextUnusedRetainerSlotIndex] = edgeIndex;
  597. }
  598. }
  599. },
  600. /**
  601. * @param {number=} nodeIndex
  602. */
  603. createNode: function(nodeIndex)
  604. {
  605. throw new Error("Not implemented");
  606. },
  607. createEdge: function(edges, edgeIndex)
  608. {
  609. throw new Error("Not implemented");
  610. },
  611. createRetainingEdge: function(retainedNodeIndex, retainerIndex)
  612. {
  613. throw new Error("Not implemented");
  614. },
  615. dispose: function()
  616. {
  617. delete this._nodes;
  618. delete this._strings;
  619. delete this._retainingEdges;
  620. delete this._retainingNodes;
  621. delete this._firstRetainerIndex;
  622. if (this._aggregates) {
  623. delete this._aggregates;
  624. delete this._aggregatesSortedFlags;
  625. }
  626. delete this._dominatedNodes;
  627. delete this._firstDominatedNodeIndex;
  628. delete this._nodeDistances;
  629. delete this._dominatorsTree;
  630. },
  631. _allNodes: function()
  632. {
  633. return new WebInspector.HeapSnapshotNodeIterator(this.rootNode());
  634. },
  635. rootNode: function()
  636. {
  637. return this.createNode(this._rootNodeIndex);
  638. },
  639. get rootNodeIndex()
  640. {
  641. return this._rootNodeIndex;
  642. },
  643. get totalSize()
  644. {
  645. return this.rootNode().retainedSize();
  646. },
  647. _getDominatedIndex: function(nodeIndex)
  648. {
  649. if (nodeIndex % this._nodeFieldCount)
  650. throw new Error("Invalid nodeIndex: " + nodeIndex);
  651. return this._firstDominatedNodeIndex[nodeIndex / this._nodeFieldCount];
  652. },
  653. _dominatedNodesOfNode: function(node)
  654. {
  655. var dominatedIndexFrom = this._getDominatedIndex(node.nodeIndex);
  656. var dominatedIndexTo = this._getDominatedIndex(node._nextNodeIndex());
  657. return new WebInspector.HeapSnapshotArraySlice(this._dominatedNodes, dominatedIndexFrom, dominatedIndexTo);
  658. },
  659. /**
  660. * @param {boolean} sortedIndexes
  661. * @param {string} key
  662. * @param {string=} filterString
  663. */
  664. aggregates: function(sortedIndexes, key, filterString)
  665. {
  666. if (!this._aggregates) {
  667. this._aggregates = {};
  668. this._aggregatesSortedFlags = {};
  669. }
  670. var aggregatesByClassName = this._aggregates[key];
  671. if (aggregatesByClassName) {
  672. if (sortedIndexes && !this._aggregatesSortedFlags[key]) {
  673. this._sortAggregateIndexes(aggregatesByClassName);
  674. this._aggregatesSortedFlags[key] = sortedIndexes;
  675. }
  676. return aggregatesByClassName;
  677. }
  678. var filter;
  679. if (filterString)
  680. filter = this._parseFilter(filterString);
  681. var aggregates = this._buildAggregates(filter);
  682. this._calculateClassesRetainedSize(aggregates.aggregatesByClassIndex, filter);
  683. aggregatesByClassName = aggregates.aggregatesByClassName;
  684. if (sortedIndexes)
  685. this._sortAggregateIndexes(aggregatesByClassName);
  686. this._aggregatesSortedFlags[key] = sortedIndexes;
  687. this._aggregates[key] = aggregatesByClassName;
  688. return aggregatesByClassName;
  689. },
  690. aggregatesForDiff: function()
  691. {
  692. if (this._aggregatesForDiff)
  693. return this._aggregatesForDiff;
  694. var aggregatesByClassName = this.aggregates(true, "allObjects");
  695. this._aggregatesForDiff = {};
  696. var node = this.createNode();
  697. for (var className in aggregatesByClassName) {
  698. var aggregate = aggregatesByClassName[className];
  699. var indexes = aggregate.idxs;
  700. var ids = new Array(indexes.length);
  701. var selfSizes = new Array(indexes.length);
  702. for (var i = 0; i < indexes.length; i++) {
  703. node.nodeIndex = indexes[i];
  704. ids[i] = node.id();
  705. selfSizes[i] = node.selfSize();
  706. }
  707. this._aggregatesForDiff[className] = {
  708. indexes: indexes,
  709. ids: ids,
  710. selfSizes: selfSizes
  711. };
  712. }
  713. return this._aggregatesForDiff;
  714. },
  715. /**
  716. * @param {!WebInspector.HeapSnapshotNode} node
  717. * @return {!boolean}
  718. */
  719. _isUserRoot: function(node)
  720. {
  721. return true;
  722. },
  723. /**
  724. * @param {function(!WebInspector.HeapSnapshotNode)} action
  725. * @param {boolean=} userRootsOnly
  726. */
  727. forEachRoot: function(action, userRootsOnly)
  728. {
  729. for (var iter = this.rootNode().edges(); iter.hasNext(); iter.next()) {
  730. var node = iter.edge.node();
  731. if (!userRootsOnly || this._isUserRoot(node))
  732. action(node);
  733. }
  734. },
  735. _calculateDistances: function()
  736. {
  737. var nodeFieldCount = this._nodeFieldCount;
  738. var nodeCount = this.nodeCount;
  739. var distances = new Int32Array(nodeCount);
  740. var noDistance = this._noDistance;
  741. for (var i = 0; i < nodeCount; ++i)
  742. distances[i] = noDistance;
  743. var nodesToVisit = new Uint32Array(this.nodeCount);
  744. var nodesToVisitLength = 0;
  745. /**
  746. * @param {!WebInspector.HeapSnapshotNode} node
  747. */
  748. function enqueueNode(node)
  749. {
  750. var ordinal = node._ordinal();
  751. if (distances[ordinal] !== noDistance)
  752. return;
  753. distances[ordinal] = 0;
  754. nodesToVisit[nodesToVisitLength++] = node.nodeIndex;
  755. }
  756. this.forEachRoot(enqueueNode, true);
  757. this._bfs(nodesToVisit, nodesToVisitLength, distances);
  758. // bfs for the rest of objects
  759. nodesToVisitLength = 0;
  760. this.forEachRoot(enqueueNode);
  761. this._bfs(nodesToVisit, nodesToVisitLength, distances);
  762. this._nodeDistances = distances;
  763. },
  764. /**
  765. * @param {!Uint32Array} nodesToVisit
  766. * @param {!number} nodesToVisitLength
  767. * @param {!Int32Array} distances
  768. */
  769. _bfs: function(nodesToVisit, nodesToVisitLength, distances)
  770. {
  771. // Preload fields into local variables for better performance.
  772. var edgeFieldsCount = this._edgeFieldsCount;
  773. var nodeFieldCount = this._nodeFieldCount;
  774. var containmentEdges = this._containmentEdges;
  775. var firstEdgeIndexes = this._firstEdgeIndexes;
  776. var edgeToNodeOffset = this._edgeToNodeOffset;
  777. var edgeTypeOffset = this._edgeTypeOffset;
  778. var nodes = this._nodes;
  779. var nodeCount = this.nodeCount;
  780. var containmentEdgesLength = containmentEdges.length;
  781. var edgeWeakType = this._edgeWeakType;
  782. var noDistance = this._noDistance;
  783. var index = 0;
  784. while (index < nodesToVisitLength) {
  785. var nodeIndex = nodesToVisit[index++]; // shift generates too much garbage.
  786. var nodeOrdinal = nodeIndex / nodeFieldCount;
  787. var distance = distances[nodeOrdinal] + 1;
  788. var firstEdgeIndex = firstEdgeIndexes[nodeOrdinal];
  789. var edgesEnd = firstEdgeIndexes[nodeOrdinal + 1];
  790. for (var edgeIndex = firstEdgeIndex; edgeIndex < edgesEnd; edgeIndex += edgeFieldsCount) {
  791. var edgeType = containmentEdges[edgeIndex + edgeTypeOffset];
  792. if (edgeType == edgeWeakType)
  793. continue;
  794. var childNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
  795. var childNodeOrdinal = childNodeIndex / nodeFieldCount;
  796. if (distances[childNodeOrdinal] !== noDistance)
  797. continue;
  798. distances[childNodeOrdinal] = distance;
  799. nodesToVisit[nodesToVisitLength++] = childNodeIndex;
  800. }
  801. }
  802. if (nodesToVisitLength > nodeCount)
  803. throw new Error("BFS failed. Nodes to visit (" + nodesToVisitLength + ") is more than nodes count (" + nodeCount + ")");
  804. },
  805. _buildAggregates: function(filter)
  806. {
  807. var aggregates = {};
  808. var aggregatesByClassName = {};
  809. var classIndexes = [];
  810. var nodes = this._nodes;
  811. var mapAndFlag = this.userObjectsMapAndFlag();
  812. var flags = mapAndFlag ? mapAndFlag.map : null;
  813. var flag = mapAndFlag ? mapAndFlag.flag : 0;
  814. var nodesLength = nodes.length;
  815. var nodeNativeType = this._nodeNativeType;
  816. var nodeFieldCount = this._nodeFieldCount;
  817. var selfSizeOffset = this._nodeSelfSizeOffset;
  818. var nodeTypeOffset = this._nodeTypeOffset;
  819. var node = this.rootNode();
  820. var nodeDistances = this._nodeDistances;
  821. for (var nodeIndex = 0; nodeIndex < nodesLength; nodeIndex += nodeFieldCount) {
  822. var nodeOrdinal = nodeIndex / nodeFieldCount;
  823. if (flags && !(flags[nodeOrdinal] & flag))
  824. continue;
  825. node.nodeIndex = nodeIndex;
  826. if (filter && !filter(node))
  827. continue;
  828. var selfSize = nodes[nodeIndex + selfSizeOffset];
  829. if (!selfSize && nodes[nodeIndex + nodeTypeOffset] !== nodeNativeType)
  830. continue;
  831. var classIndex = node.classIndex();
  832. if (!(classIndex in aggregates)) {
  833. var nodeType = node.type();
  834. var nameMatters = nodeType === "object" || nodeType === "native";
  835. var value = {
  836. count: 1,
  837. distance: nodeDistances[nodeOrdinal],
  838. self: selfSize,
  839. maxRet: 0,
  840. type: nodeType,
  841. name: nameMatters ? node.name() : null,
  842. idxs: [nodeIndex]
  843. };
  844. aggregates[classIndex] = value;
  845. classIndexes.push(classIndex);
  846. aggregatesByClassName[node.className()] = value;
  847. } else {
  848. var clss = aggregates[classIndex];
  849. clss.distance = Math.min(clss.distance, nodeDistances[nodeOrdinal]);
  850. ++clss.count;
  851. clss.self += selfSize;
  852. clss.idxs.push(nodeIndex);
  853. }
  854. }
  855. // Shave off provisionally allocated space.
  856. for (var i = 0, l = classIndexes.length; i < l; ++i) {
  857. var classIndex = classIndexes[i];
  858. aggregates[classIndex].idxs = aggregates[classIndex].idxs.slice();
  859. }
  860. return {aggregatesByClassName: aggregatesByClassName, aggregatesByClassIndex: aggregates};
  861. },
  862. _calculateClassesRetainedSize: function(aggregates, filter)
  863. {
  864. var rootNodeIndex = this._rootNodeIndex;
  865. var node = this.createNode(rootNodeIndex);
  866. var list = [rootNodeIndex];
  867. var sizes = [-1];
  868. var classes = [];
  869. var seenClassNameIndexes = {};
  870. var nodeFieldCount = this._nodeFieldCount;
  871. var nodeTypeOffset = this._nodeTypeOffset;
  872. var nodeNativeType = this._nodeNativeType;
  873. var dominatedNodes = this._dominatedNodes;
  874. var nodes = this._nodes;
  875. var mapAndFlag = this.userObjectsMapAndFlag();
  876. var flags = mapAndFlag ? mapAndFlag.map : null;
  877. var flag = mapAndFlag ? mapAndFlag.flag : 0;
  878. var firstDominatedNodeIndex = this._firstDominatedNodeIndex;
  879. while (list.length) {
  880. var nodeIndex = list.pop();
  881. node.nodeIndex = nodeIndex;
  882. var classIndex = node.classIndex();
  883. var seen = !!seenClassNameIndexes[classIndex];
  884. var nodeOrdinal = nodeIndex / nodeFieldCount;
  885. var dominatedIndexFrom = firstDominatedNodeIndex[nodeOrdinal];
  886. var dominatedIndexTo = firstDominatedNodeIndex[nodeOrdinal + 1];
  887. if (!seen &&
  888. (!flags || (flags[nodeOrdinal] & flag)) &&
  889. (!filter || filter(node)) &&
  890. (node.selfSize() || nodes[nodeIndex + nodeTypeOffset] === nodeNativeType)
  891. ) {
  892. aggregates[classIndex].maxRet += node.retainedSize();
  893. if (dominatedIndexFrom !== dominatedIndexTo) {
  894. seenClassNameIndexes[classIndex] = true;
  895. sizes.push(list.length);
  896. classes.push(classIndex);
  897. }
  898. }
  899. for (var i = dominatedIndexFrom; i < dominatedIndexTo; i++)
  900. list.push(dominatedNodes[i]);
  901. var l = list.length;
  902. while (sizes[sizes.length - 1] === l) {
  903. sizes.pop();
  904. classIndex = classes.pop();
  905. seenClassNameIndexes[classIndex] = false;
  906. }
  907. }
  908. },
  909. _sortAggregateIndexes: function(aggregates)
  910. {
  911. var nodeA = this.createNode();
  912. var nodeB = this.createNode();
  913. for (var clss in aggregates)
  914. aggregates[clss].idxs.sort(
  915. function(idxA, idxB) {
  916. nodeA.nodeIndex = idxA;
  917. nodeB.nodeIndex = idxB;
  918. return nodeA.id() < nodeB.id() ? -1 : 1;
  919. });
  920. },
  921. _buildPostOrderIndex: function()
  922. {
  923. var nodeFieldCount = this._nodeFieldCount;
  924. var nodes = this._nodes;
  925. var nodeCount = this.nodeCount;
  926. var rootNodeOrdinal = this._rootNodeIndex / nodeFieldCount;
  927. var edgeFieldsCount = this._edgeFieldsCount;
  928. var edgeTypeOffset = this._edgeTypeOffset;
  929. var edgeToNodeOffset = this._edgeToNodeOffset;
  930. var edgeShortcutType = this._edgeShortcutType;
  931. var firstEdgeIndexes = this._firstEdgeIndexes;
  932. var containmentEdges = this._containmentEdges;
  933. var containmentEdgesLength = this._containmentEdges.length;
  934. var mapAndFlag = this.userObjectsMapAndFlag();
  935. var flags = mapAndFlag ? mapAndFlag.map : null;
  936. var flag = mapAndFlag ? mapAndFlag.flag : 0;
  937. var nodesToVisit = new Uint32Array(nodeCount);
  938. var postOrderIndex2NodeOrdinal = new Uint32Array(nodeCount);
  939. var nodeOrdinal2PostOrderIndex = new Uint32Array(nodeCount);
  940. var painted = new Uint8Array(nodeCount);
  941. var nodesToVisitLength = 0;
  942. var postOrderIndex = 0;
  943. var grey = 1;
  944. var black = 2;
  945. nodesToVisit[nodesToVisitLength++] = rootNodeOrdinal;
  946. painted[rootNodeOrdinal] = grey;
  947. while (nodesToVisitLength) {
  948. var nodeOrdinal = nodesToVisit[nodesToVisitLength - 1];
  949. if (painted[nodeOrdinal] === grey) {
  950. painted[nodeOrdinal] = black;
  951. var nodeFlag = !flags || (flags[nodeOrdinal] & flag);
  952. var beginEdgeIndex = firstEdgeIndexes[nodeOrdinal];
  953. var endEdgeIndex = firstEdgeIndexes[nodeOrdinal + 1];
  954. for (var edgeIndex = beginEdgeIndex; edgeIndex < endEdgeIndex; edgeIndex += edgeFieldsCount) {
  955. if (nodeOrdinal !== rootNodeOrdinal && containmentEdges[edgeIndex + edgeTypeOffset] === edgeShortcutType)
  956. continue;
  957. var childNodeIndex = containmentEdges[edgeIndex + edgeToNodeOffset];
  958. var childNodeOrdinal = childNodeIndex / nodeFieldCount;
  959. var childNodeFlag = !flags || (flags[childNodeOrdinal] & flag);
  960. // We are skipping the edges from non-page-owned nodes to page-owned nodes.
  961. // Otherwise the dominators for the objects that also were retained by debugger would be affected.
  962. if (nodeOrdinal !== rootNodeOrdinal && childNodeFlag && !nodeFlag)
  963. continue;
  964. if (!painted[childNodeOrdinal]) {
  965. painted[childNodeOrdinal] = grey;
  966. nodesToVisit[nodesToVisitLength++] = childNodeOrdinal;
  967. }
  968. }
  969. } else {
  970. nodeOrdinal2PostOrderIndex[nodeOrdinal] = postOrderIndex;
  971. postOrderIndex2NodeOrdinal[postOrderIndex++] = nodeOrdinal;
  972. --nodesToVisitLength;
  973. }
  974. }
  975. if (postOrderIndex !== nodeCount) {
  976. console.log("Error: Corrupted snapshot. " + (nodeCount - postOrderIndex) + " nodes are unreachable from the root:");
  977. var dumpNode = this.rootNode();
  978. for (var i = 0; i < nodeCount; ++i) {
  979. if (painted[i] !== black) {
  980. // Fix it by giving the node a postorder index anyway.
  981. nodeOrdinal2PostOrderIndex[i] = postOrderIndex;
  982. postOrderIndex2NodeOrdinal[postOrderIndex++] = i;
  983. dumpNode.nodeIndex = i * nodeFieldCount;
  984. console.log(JSON.stringify(dumpNode.serialize()));
  985. for (var retainers = dumpNode.retainers(); retainers.hasNext(); retainers = retainers.item().node().retainers())
  986. console.log(" edgeName: " + retainers.item().name() + " nodeClassName: " + retainers.item().node().className());
  987. }
  988. }
  989. }
  990. return {postOrderIndex2NodeOrdinal: postOrderIndex2NodeOrdinal, nodeOrdinal2PostOrderIndex: nodeOrdinal2PostOrderIndex};
  991. },
  992. // The algorithm is based on the article:
  993. // K. Cooper, T. Harvey and K. Kennedy "A Simple, Fast Dominance Algorithm"
  994. // Softw. Pract. Exper. 4 (2001), pp. 1-10.
  995. /**
  996. * @param {Array.<number>} postOrderIndex2NodeOrdinal
  997. * @param {Array.<number>} nodeOrdinal2PostOrderIndex
  998. */
  999. _buildDominatorTree: function(postOrderIndex2NodeOrdinal, nodeOrdinal2PostOrderIndex)
  1000. {
  1001. var nodeFieldCount = this._nodeFieldCount;
  1002. var nodes = this._nodes;
  1003. var firstRetainerIndex = this._firstRetainerIndex;
  1004. var retainingNodes = this._retainingNodes;
  1005. var retainingEdges = this._retainingEdges;
  1006. var edgeFieldsCount = this._edgeFieldsCount;
  1007. var edgeTypeOffset = this._edgeTypeOffset;
  1008. var edgeToNodeOffset = this._edgeToNodeOffset;
  1009. var edgeShortcutType = this._edgeShortcutType;
  1010. var firstEdgeIndexes = this._firstEdgeIndexes;
  1011. var containmentEdges = this._containmentEdges;
  1012. var containmentEdgesLength = this._containmentEdges.length;
  1013. var rootNodeIndex = this._rootNodeIndex;
  1014. var mapAndFlag = this.userObjectsMapAndFlag();
  1015. var flags = mapAndFlag ? mapAndFlag.map : null;
  1016. var flag = mapAndFlag ? mapAndFlag.flag : 0;
  1017. var nodesCount = postOrderIndex2NodeOrdinal.length;
  1018. var rootPostOrderedIndex = nodesCount - 1;
  1019. var noEntry = nodesCount;
  1020. var dominators = new Uint32Array(nodesCount);
  1021. for (var i = 0; i < rootPostOrderedIndex; ++i)
  1022. dominators[i] = noEntry;
  1023. dominators[rootPostOrderedIndex] = rootPostOrderedIndex;
  1024. // The affected array is used to mark entries which dominators
  1025. // have to be racalculated because of changes in their retainers.
  1026. var affected = new Uint8Array(nodesCount);
  1027. var nodeOrdinal;
  1028. { // Mark the root direct children as affected.
  1029. nodeOrdinal = this._rootNodeIndex / nodeFieldCount;
  1030. var beginEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal] + edgeToNodeOffset;
  1031. var endEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal + 1];
  1032. for (var toNodeFieldIndex = beginEdgeToNodeFieldIndex;
  1033. toNodeFieldIndex < endEdgeToNodeFieldIndex;
  1034. toNodeFieldIndex += edgeFieldsCount) {
  1035. var childNodeOrdinal = containmentEdges[toNodeFieldIndex] / nodeFieldCount;
  1036. affected[nodeOrdinal2PostOrderIndex[childNodeOrdinal]] = 1;
  1037. }
  1038. }
  1039. var changed = true;
  1040. while (changed) {
  1041. changed = false;
  1042. for (var postOrderIndex = rootPostOrderedIndex - 1; postOrderIndex >= 0; --postOrderIndex) {
  1043. if (affected[postOrderIndex] === 0)
  1044. continue;
  1045. affected[postOrderIndex] = 0;
  1046. // If dominator of the entry has already been set to root,
  1047. // then it can't propagate any further.
  1048. if (dominators[postOrderIndex] === rootPostOrderedIndex)
  1049. continue;
  1050. nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex];
  1051. var nodeFlag = !flags || (flags[nodeOrdinal] & flag);
  1052. var newDominatorIndex = noEntry;
  1053. var beginRetainerIndex = firstRetainerIndex[nodeOrdinal];
  1054. var endRetainerIndex = firstRetainerIndex[nodeOrdinal + 1];
  1055. for (var retainerIndex = beginRetainerIndex; retainerIndex < endRetainerIndex; ++retainerIndex) {
  1056. var retainerEdgeIndex = retainingEdges[retainerIndex];
  1057. var retainerEdgeType = containmentEdges[retainerEdgeIndex + edgeTypeOffset];
  1058. var retainerNodeIndex = retainingNodes[retainerIndex];
  1059. if (retainerNodeIndex !== rootNodeIndex && retainerEdgeType === edgeShortcutType)
  1060. continue;
  1061. var retainerNodeOrdinal = retainerNodeIndex / nodeFieldCount;
  1062. var retainerNodeFlag = !flags || (flags[retainerNodeOrdinal] & flag);
  1063. // We are skipping the edges from non-page-owned nodes to page-owned nodes.
  1064. // Otherwise the dominators for the objects that also were retained by debugger would be affected.
  1065. if (retainerNodeIndex !== rootNodeIndex && nodeFlag && !retainerNodeFlag)
  1066. continue;
  1067. var retanerPostOrderIndex = nodeOrdinal2PostOrderIndex[retainerNodeOrdinal];
  1068. if (dominators[retanerPostOrderIndex] !== noEntry) {
  1069. if (newDominatorIndex === noEntry)
  1070. newDominatorIndex = retanerPostOrderIndex;
  1071. else {
  1072. while (retanerPostOrderIndex !== newDominatorIndex) {
  1073. while (retanerPostOrderIndex < newDominatorIndex)
  1074. retanerPostOrderIndex = dominators[retanerPostOrderIndex];
  1075. while (newDominatorIndex < retanerPostOrderIndex)
  1076. newDominatorIndex = dominators[newDominatorIndex];
  1077. }
  1078. }
  1079. // If idom has already reached the root, it doesn't make sense
  1080. // to check other retainers.
  1081. if (newDominatorIndex === rootPostOrderedIndex)
  1082. break;
  1083. }
  1084. }
  1085. if (newDominatorIndex !== noEntry && dominators[postOrderIndex] !== newDominatorIndex) {
  1086. dominators[postOrderIndex] = newDominatorIndex;
  1087. changed = true;
  1088. nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex];
  1089. beginEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal] + edgeToNodeOffset;
  1090. endEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal + 1];
  1091. for (var toNodeFieldIndex = beginEdgeToNodeFieldIndex;
  1092. toNodeFieldIndex < endEdgeToNodeFieldIndex;
  1093. toNodeFieldIndex += edgeFieldsCount) {
  1094. var childNodeOrdinal = containmentEdges[toNodeFieldIndex] / nodeFieldCount;
  1095. affected[nodeOrdinal2PostOrderIndex[childNodeOrdinal]] = 1;
  1096. }
  1097. }
  1098. }
  1099. }
  1100. var dominatorsTree = new Uint32Array(nodesCount);
  1101. for (var postOrderIndex = 0, l = dominators.length; postOrderIndex < l; ++postOrderIndex) {
  1102. nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex];
  1103. dominatorsTree[nodeOrdinal] = postOrderIndex2NodeOrdinal[dominators[postOrderIndex]];
  1104. }
  1105. return dominatorsTree;
  1106. },
  1107. _calculateRetainedSizes: function(postOrderIndex2NodeOrdinal)
  1108. {
  1109. var nodeCount = this.nodeCount;
  1110. var nodes = this._nodes;
  1111. var nodeSelfSizeOffset = this._nodeSelfSizeOffset;
  1112. var nodeFieldCount = this._nodeFieldCount;
  1113. var dominatorsTree = this._dominatorsTree;
  1114. // Reuse now unused edge_count field to store retained size.
  1115. var nodeRetainedSizeOffset = this._nodeRetainedSizeOffset = this._nodeEdgeCountOffset;
  1116. delete this._nodeEdgeCountOffset;
  1117. for (var nodeIndex = 0, l = nodes.length; nodeIndex < l; nodeIndex += nodeFieldCount)
  1118. nodes[nodeIndex + nodeRetainedSizeOffset] = nodes[nodeIndex + nodeSelfSizeOffset];
  1119. // Propagate retained sizes for each node excluding root.
  1120. for (var postOrderIndex = 0; postOrderIndex < nodeCount - 1; ++postOrderIndex) {
  1121. var nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex];
  1122. var nodeIndex = nodeOrdinal * nodeFieldCount;
  1123. var dominatorIndex = dominatorsTree[nodeOrdinal] * nodeFieldCount;
  1124. nodes[dominatorIndex + nodeRetainedSizeOffset] += nodes[nodeIndex + nodeRetainedSizeOffset];
  1125. }
  1126. },
  1127. _buildDominatedNodes: function()
  1128. {
  1129. // Builds up two arrays:
  1130. // - "dominatedNodes" is a continuous array, where each node owns an
  1131. // interval (can be empty) with corresponding dominated nodes.
  1132. // - "indexArray" is an array of indexes in the "dominatedNodes"
  1133. // with the same positions as in the _nodeIndex.
  1134. var indexArray = this._firstDominatedNodeIndex = new Uint32Array(this.nodeCount + 1);
  1135. // All nodes except the root have dominators.
  1136. var dominatedNodes = this._dominatedNodes = new Uint32Array(this.nodeCount - 1);
  1137. // Count the number of dominated nodes for each node. Skip the root (node at
  1138. // index 0) as it is the only node that dominates itself.
  1139. var nodeFieldCount = this._nodeFieldCount;
  1140. var dominatorsTree = this._dominatorsTree;
  1141. var fromNodeOrdinal = 0;
  1142. var toNodeOrdinal = this.nodeCount;
  1143. var rootNodeOrdinal = this._rootNodeIndex / nodeFieldCount;
  1144. if (rootNodeOrdinal === fromNodeOrdinal)
  1145. fromNodeOrdinal = 1;
  1146. else if (rootNodeOrdinal === toNodeOrdinal - 1)
  1147. toNodeOrdinal = toNodeOrdinal - 1;
  1148. else
  1149. throw new Error("Root node is expected to be either first or last");
  1150. for (var nodeOrdinal = fromNodeOrdinal; nodeOrdinal < toNodeOrdinal; ++nodeOrdinal)
  1151. ++indexArray[dominatorsTree[nodeOrdinal]];
  1152. // Put in the first slot of each dominatedNodes slice the count of entries
  1153. // that will be filled.
  1154. var firstDominatedNodeIndex = 0;
  1155. for (var i = 0, l = this.nodeCount; i < l; ++i) {
  1156. var dominatedCount = dominatedNodes[firstDominatedNodeIndex] = indexArray[i];
  1157. indexArray[i] = firstDominatedNodeIndex;
  1158. firstDominatedNodeIndex += dominatedCount;
  1159. }
  1160. indexArray[this.nodeCount] = dominatedNodes.length;
  1161. // Fill up the dominatedNodes array with indexes of dominated nodes. Skip the root (node at
  1162. // index 0) as it is the only node that dominates itself.
  1163. for (var nodeOrdinal = fromNodeOrdinal; nodeOrdinal < toNodeOrdinal; ++nodeOrdinal) {
  1164. var dominatorOrdinal = dominatorsTree[nodeOrdinal];
  1165. var dominatedRefIndex = indexArray[dominatorOrdinal];
  1166. dominatedRefIndex += (--dominatedNodes[dominatedRefIndex]);
  1167. dominatedNodes[dominatedRefIndex] = nodeOrdinal * nodeFieldCount;
  1168. }
  1169. },
  1170. _markInvisibleEdges: function()
  1171. {
  1172. throw new Error("Not implemented");
  1173. },
  1174. _calculateFlags: function()
  1175. {
  1176. throw new Error("Not implemented");
  1177. },
  1178. userObjectsMapAndFlag: function()
  1179. {
  1180. throw new Error("Not implemented");
  1181. },
  1182. calculateSnapshotDiff: function(baseSnapshotId, baseSnapshotAggregates)
  1183. {
  1184. var snapshotDiff = this._snapshotDiffs[baseSnapshotId];
  1185. if (snapshotDiff)
  1186. return snapshotDiff;
  1187. snapshotDiff = {};
  1188. var aggregates = this.aggregates(true, "allObjects");
  1189. for (var className in baseSnapshotAggregates) {
  1190. var baseAggregate = baseSnapshotAggregates[className];
  1191. var diff = this._calculateDiffForClass(baseAggregate, aggregates[className]);
  1192. if (diff)
  1193. snapshotDiff[className] = diff;
  1194. }
  1195. var emptyBaseAggregate = { ids: [], indexes: [], selfSizes: [] };
  1196. for (var className in aggregates) {
  1197. if (className in baseSnapshotAggregates)
  1198. continue;
  1199. snapshotDiff[className] = this._calculateDiffForClass(emptyBaseAggregate, aggregates[className]);
  1200. }
  1201. this._snapshotDiffs[baseSnapshotId] = snapshotDiff;
  1202. return snapshotDiff;
  1203. },
  1204. _calculateDiffForClass: function(baseAggregate, aggregate)
  1205. {
  1206. var baseIds = baseAggregate.ids;
  1207. var baseIndexes = baseAggregate.indexes;
  1208. var baseSelfSizes = baseAggregate.selfSizes;
  1209. var indexes = aggregate ? aggregate.idxs : [];
  1210. var i = 0, l = baseIds.length;
  1211. var j = 0, m = indexes.length;
  1212. var diff = { addedCount: 0,
  1213. removedCount: 0,
  1214. addedSize: 0,
  1215. removedSize: 0,
  1216. deletedIndexes: [],
  1217. addedIndexes: [] };
  1218. var nodeB = this.createNode(indexes[j]);
  1219. while (i < l && j < m) {
  1220. var nodeAId = baseIds[i];
  1221. if (nodeAId < nodeB.id()) {
  1222. diff.deletedIndexes.push(baseIndexes[i]);
  1223. diff.removedCount++;
  1224. diff.removedSize += baseSelfSizes[i];
  1225. ++i;
  1226. } else if (nodeAId > nodeB.id()) { // Native nodes(e.g. dom groups) may have ids less than max JS object id in the base snapshot
  1227. diff.addedIndexes.push(indexes[j]);
  1228. diff.addedCount++;
  1229. diff.addedSize += nodeB.selfSize();
  1230. nodeB.nodeIndex = indexes[++j];
  1231. } else { // nodeAId === nodeB.id()
  1232. ++i;
  1233. nodeB.nodeIndex = indexes[++j];
  1234. }
  1235. }
  1236. while (i < l) {
  1237. diff.deletedIndexes.push(baseIndexes[i]);
  1238. diff.removedCount++;
  1239. diff.removedSize += baseSelfSizes[i];
  1240. ++i;
  1241. }
  1242. while (j < m) {
  1243. diff.addedIndexes.push(indexes[j]);
  1244. diff.addedCount++;
  1245. diff.addedSize += nodeB.selfSize();
  1246. nodeB.nodeIndex = indexes[++j];
  1247. }
  1248. diff.countDelta = diff.addedCount - diff.removedCount;
  1249. diff.sizeDelta = diff.addedSize - diff.removedSize;
  1250. if (!diff.addedCount && !diff.removedCount)
  1251. return null;
  1252. return diff;
  1253. },
  1254. _nodeForSnapshotObjectId: function(snapshotObjectId)
  1255. {
  1256. for (var it = this._allNodes(); it.hasNext(); it.next()) {
  1257. if (it.node.id() === snapshotObjectId)
  1258. return it.node;
  1259. }
  1260. return null;
  1261. },
  1262. nodeClassName: function(snapshotObjectId)
  1263. {
  1264. var node = this._nodeForSnapshotObjectId(snapshotObjectId);
  1265. if (node)
  1266. return node.className();
  1267. return null;
  1268. },
  1269. dominatorIdsForNode: function(snapshotObjectId)
  1270. {
  1271. var node = this._nodeForSnapshotObjectId(snapshotObjectId);
  1272. if (!node)
  1273. return null;
  1274. var result = [];
  1275. while (!node.isRoot()) {
  1276. result.push(node.id());
  1277. node.nodeIndex = node.dominatorIndex();
  1278. }
  1279. return result;
  1280. },
  1281. _parseFilter: function(filter)
  1282. {
  1283. if (!filter)
  1284. return null;
  1285. var parsedFilter = eval("(function(){return " + filter + "})()");
  1286. return parsedFilter.bind(this);
  1287. },
  1288. createEdgesProvider: function(nodeIndex, showHiddenData)
  1289. {
  1290. var node = this.createNode(nodeIndex);
  1291. var filter = this.containmentEdgesFilter(showHiddenData);
  1292. return new WebInspector.HeapSnapshotEdgesProvider(this, filter, node.edges());
  1293. },
  1294. createEdgesProviderForTest: function(nodeIndex, filter)
  1295. {
  1296. var node = this.createNode(nodeIndex);
  1297. return new WebInspector.HeapSnapshotEdgesProvider(this, filter, node.edges());
  1298. },
  1299. retainingEdgesFilter: function(showHiddenData)
  1300. {
  1301. return null;
  1302. },
  1303. containmentEdgesFilter: function(showHiddenData)
  1304. {
  1305. return null;
  1306. },
  1307. createRetainingEdgesProvider: function(nodeIndex, showHiddenData)
  1308. {
  1309. var node = this.createNode(nodeIndex);
  1310. var filter = this.retainingEdgesFilter(showHiddenData);
  1311. return new WebInspector.HeapSnapshotEdgesProvider(this, filter, node.retainers());
  1312. },
  1313. createAddedNodesProvider: function(baseSnapshotId, className)
  1314. {
  1315. var snapshotDiff = this._snapshotDiffs[baseSnapshotId];
  1316. var diffForClass = snapshotDiff[className];
  1317. return new WebInspector.HeapSnapshotNodesProvider(this, null, diffForClass.addedIndexes);
  1318. },
  1319. createDeletedNodesProvider: function(nodeIndexes)
  1320. {
  1321. return new WebInspector.HeapSnapshotNodesProvider(this, null, nodeIndexes);
  1322. },
  1323. classNodesFilter: function()
  1324. {
  1325. return null;
  1326. },
  1327. createNodesProviderForClass: function(className, aggregatesKey)
  1328. {
  1329. return new WebInspector.HeapSnapshotNodesProvider(this, this.classNodesFilter(), this.aggregates(false, aggregatesKey)[className].idxs);
  1330. },
  1331. createNodesProviderForDominator: function(nodeIndex)
  1332. {
  1333. var node = this.createNode(nodeIndex);
  1334. return new WebInspector.HeapSnapshotNodesProvider(this, null, this._dominatedNodesOfNode(node));
  1335. },
  1336. updateStaticData: function()
  1337. {
  1338. return {nodeCount: this.nodeCount, rootNodeIndex: this._rootNodeIndex, totalSize: this.totalSize, uid: this.uid};
  1339. }
  1340. };
  1341. /**
  1342. * @constructor
  1343. * @param {Array.<number>=} unfilteredIterationOrder
  1344. */
  1345. WebInspector.HeapSnapshotFilteredOrderedIterator = function(iterator, filter, unfilteredIterationOrder)
  1346. {
  1347. this._filter = filter;
  1348. this._iterator = iterator;
  1349. this._unfilteredIterationOrder = unfilteredIterationOrder;
  1350. this._iterationOrder = null;
  1351. this._position = 0;
  1352. this._currentComparator = null;
  1353. this._sortedPrefixLength = 0;
  1354. }
  1355. WebInspector.HeapSnapshotFilteredOrderedIterator.prototype = {
  1356. _createIterationOrder: function()
  1357. {
  1358. if (this._iterationOrder)
  1359. return;
  1360. if (this._unfilteredIterationOrder && !this._filter) {
  1361. this._iterationOrder = this._unfilteredIterationOrder.slice(0);
  1362. this._unfilteredIterationOrder = null;
  1363. return;
  1364. }
  1365. this._iterationOrder = [];
  1366. var iterator = this._iterator;
  1367. if (!this._unfilteredIterationOrder && !this._filter) {
  1368. for (iterator.rewind(); iterator.hasNext(); iterator.next())
  1369. this._iterationOrder.push(iterator.index());
  1370. } else if (!this._unfilteredIterationOrder) {
  1371. for (iterator.rewind(); iterator.hasNext(); iterator.next()) {
  1372. if (this._filter(iterator.item()))
  1373. this._iterationOrder.push(iterator.index());
  1374. }
  1375. } else {
  1376. var order = this._unfilteredIterationOrder.constructor === Array ?
  1377. this._unfilteredIterationOrder : this._unfilteredIterationOrder.slice(0);
  1378. for (var i = 0, l = order.length; i < l; ++i) {
  1379. iterator.setIndex(order[i]);
  1380. if (this._filter(iterator.item()))
  1381. this._iterationOrder.push(iterator.index());
  1382. }
  1383. this._unfilteredIterationOrder = null;
  1384. }
  1385. },
  1386. rewind: function()
  1387. {
  1388. this._position = 0;
  1389. },
  1390. hasNext: function()
  1391. {
  1392. return this._position < this._iterationOrder.length;
  1393. },
  1394. isEmpty: function()
  1395. {
  1396. if (this._iterationOrder)
  1397. return !this._iterationOrder.length;
  1398. if (this._unfilteredIterationOrder && !this._filter)
  1399. return !this._unfilteredIterationOrder.length;
  1400. var iterator = this._iterator;
  1401. if (!this._unfilteredIterationOrder && !this._filter) {
  1402. iterator.rewind();
  1403. return !iterator.hasNext();
  1404. } else if (!this._unfilteredIterationOrder) {
  1405. for (iterator.rewind(); iterator.hasNext(); iterator.next())
  1406. if (this._filter(iterator.item()))
  1407. return false;
  1408. } else {
  1409. var order = this._unfilteredIterationOrder.constructor === Array ?
  1410. this._unfilteredIterationOrder : this._unfilteredIterationOrder.slice(0);
  1411. for (var i = 0, l = order.length; i < l; ++i) {
  1412. iterator.setIndex(order[i]);
  1413. if (this._filter(iterator.item()))
  1414. return false;
  1415. }
  1416. }
  1417. return true;
  1418. },
  1419. item: function()
  1420. {
  1421. this._iterator.setIndex(this._iterationOrder[this._position]);
  1422. return this._iterator.item();
  1423. },
  1424. get length()
  1425. {
  1426. this._createIterationOrder();
  1427. return this._iterationOrder.length;
  1428. },
  1429. next: function()
  1430. {
  1431. ++this._position;
  1432. },
  1433. /**
  1434. * @param {number} begin
  1435. * @param {number} end
  1436. */
  1437. serializeItemsRange: function(begin, end)
  1438. {
  1439. this._createIterationOrder();
  1440. if (begin > end)
  1441. throw new Error("Start position > end position: " + begin + " > " + end);
  1442. if (end >= this._iterationOrder.length)
  1443. end = this._iterationOrder.length;
  1444. if (this._sortedPrefixLength < end) {
  1445. this.sort(this._currentComparator, this._sortedPrefixLength, this._iterationOrder.length - 1, end - this._sortedPrefixLength);
  1446. this._sortedPrefixLength = end;
  1447. }
  1448. this._position = begin;
  1449. var startPosition = this._position;
  1450. var count = end - begin;
  1451. var result = new Array(count);
  1452. for (var i = 0 ; i < count && this.hasNext(); ++i, this.next())
  1453. result[i] = this.item().serialize();
  1454. result.length = i;
  1455. result.totalLength = this._iterationOrder.length;
  1456. result.startPosition = startPosition;
  1457. result.endPosition = this._position;
  1458. return result;
  1459. },
  1460. sortAll: function()
  1461. {
  1462. this._createIterationOrder();
  1463. if (this._sortedPrefixLength === this._iterationOrder.length)
  1464. return;
  1465. this.sort(this._currentComparator, this._sortedPrefixLength, this._iterationOrder.length - 1, this._iterationOrder.length);
  1466. this._sortedPrefixLength = this._iterationOrder.length;
  1467. },
  1468. sortAndRewind: function(comparator)
  1469. {
  1470. this._currentComparator = comparator;
  1471. this._sortedPrefixLength = 0;
  1472. this.rewind();
  1473. }
  1474. }
  1475. WebInspector.HeapSnapshotFilteredOrderedIterator.prototype.createComparator = function(fieldNames)
  1476. {
  1477. return {fieldName1: fieldNames[0], ascending1: fieldNames[1], fieldName2: fieldNames[2], ascending2: fieldNames[3]};
  1478. }
  1479. /**
  1480. * @constructor
  1481. * @extends {WebInspector.HeapSnapshotFilteredOrderedIterator}
  1482. */
  1483. WebInspector.HeapSnapshotEdgesProvider = function(snapshot, filter, edgesIter)
  1484. {
  1485. this.snapshot = snapshot;
  1486. WebInspector.HeapSnapshotFilteredOrderedIterator.call(this, edgesIter, filter);
  1487. }
  1488. WebInspector.HeapSnapshotEdgesProvider.prototype = {
  1489. sort: function(comparator, leftBound, rightBound, count)
  1490. {
  1491. var fieldName1 = comparator.fieldName1;
  1492. var fieldName2 = comparator.fieldName2;
  1493. var ascending1 = comparator.ascending1;
  1494. var ascending2 = comparator.ascending2;
  1495. var edgeA = this._iterator.item().clone();
  1496. var edgeB = edgeA.clone();
  1497. var nodeA = this.snapshot.createNode();
  1498. var nodeB = this.snapshot.createNode();
  1499. function compareEdgeFieldName(ascending, indexA, indexB)
  1500. {
  1501. edgeA.edgeIndex = indexA;
  1502. edgeB.edgeIndex = indexB;
  1503. if (edgeB.name() === "__proto__") return -1;
  1504. if (edgeA.name() === "__proto__") return 1;
  1505. var result =
  1506. edgeA.hasStringName() === edgeB.hasStringName() ?
  1507. (edgeA.name() < edgeB.name() ? -1 : (edgeA.name() > edgeB.name() ? 1 : 0)) :
  1508. (edgeA.hasStringName() ? -1 : 1);
  1509. return ascending ? result : -result;
  1510. }
  1511. function compareNodeField(fieldName, ascending, indexA, indexB)
  1512. {
  1513. edgeA.edgeIndex = indexA;
  1514. nodeA.nodeIndex = edgeA.nodeIndex();
  1515. var valueA = nodeA[fieldName]();
  1516. edgeB.edgeIndex = indexB;
  1517. nodeB.nodeIndex = edgeB.nodeIndex();
  1518. var valueB = nodeB[fieldName]();
  1519. var result = valueA < valueB ? -1 : (valueA > valueB ? 1 : 0);
  1520. return ascending ? result : -result;
  1521. }
  1522. function compareEdgeAndNode(indexA, indexB) {
  1523. var result = compareEdgeFieldName(ascending1, indexA, indexB);
  1524. if (result === 0)
  1525. result = compareNodeField(fieldName2, ascending2, indexA, indexB);
  1526. return result;
  1527. }
  1528. function compareNodeAndEdge(indexA, indexB) {
  1529. var result = compareNodeField(fieldName1, ascending1, indexA, indexB);
  1530. if (result === 0)
  1531. result = compareEdgeFieldName(ascending2, indexA, indexB);
  1532. return result;
  1533. }
  1534. function compareNodeAndNode(indexA, indexB) {
  1535. var result = compareNodeField(fieldName1, ascending1, indexA, indexB);
  1536. if (result === 0)
  1537. result = compareNodeField(fieldName2, ascending2, indexA, indexB);
  1538. return result;
  1539. }
  1540. if (fieldName1 === "!edgeName")
  1541. this._iterationOrder.sortRange(compareEdgeAndNode, leftBound, rightBound, count);
  1542. else if (fieldName2 === "!edgeName")
  1543. this._iterationOrder.sortRange(compareNodeAndEdge, leftBound, rightBound, count);
  1544. else
  1545. this._iterationOrder.sortRange(compareNodeAndNode, leftBound, rightBound, count);
  1546. },
  1547. __proto__: WebInspector.HeapSnapshotFilteredOrderedIterator.prototype
  1548. }
  1549. /**
  1550. * @constructor
  1551. * @extends {WebInspector.HeapSnapshotFilteredOrderedIterator}
  1552. * @param {Array.<number>=} nodeIndexes
  1553. */
  1554. WebInspector.HeapSnapshotNodesProvider = function(snapshot, filter, nodeIndexes)
  1555. {
  1556. this.snapshot = snapshot;
  1557. WebInspector.HeapSnapshotFilteredOrderedIterator.call(this, snapshot._allNodes(), filter, nodeIndexes);
  1558. }
  1559. WebInspector.HeapSnapshotNodesProvider.prototype = {
  1560. nodePosition: function(snapshotObjectId)
  1561. {
  1562. this._createIterationOrder();
  1563. if (this.isEmpty())
  1564. return -1;
  1565. this.sortAll();
  1566. var node = this.snapshot.createNode();
  1567. for (var i = 0; i < this._iterationOrder.length; i++) {
  1568. node.nodeIndex = this._iterationOrder[i];
  1569. if (node.id() === snapshotObjectId)
  1570. return i;
  1571. }
  1572. return -1;
  1573. },
  1574. sort: function(comparator, leftBound, rightBound, count)
  1575. {
  1576. var fieldName1 = comparator.fieldName1;
  1577. var fieldName2 = comparator.fieldName2;
  1578. var ascending1 = comparator.ascending1;
  1579. var ascending2 = comparator.ascending2;
  1580. var nodeA = this.snapshot.createNode();
  1581. var nodeB = this.snapshot.createNode();
  1582. function sortByNodeField(fieldName, ascending)
  1583. {
  1584. var valueOrFunctionA = nodeA[fieldName];
  1585. var valueA = typeof valueOrFunctionA !== "function" ? valueOrFunctionA : valueOrFunctionA.call(nodeA);
  1586. var valueOrFunctionB = nodeB[fieldName];
  1587. var valueB = typeof valueOrFunctionB !== "function" ? valueOrFunctionB : valueOrFunctionB.call(nodeB);
  1588. var result = valueA < valueB ? -1 : (valueA > valueB ? 1 : 0);
  1589. return ascending ? result : -result;
  1590. }
  1591. function sortByComparator(indexA, indexB) {
  1592. nodeA.nodeIndex = indexA;
  1593. nodeB.nodeIndex = indexB;
  1594. var result = sortByNodeField(fieldName1, ascending1);
  1595. if (result === 0)
  1596. result = sortByNodeField(fieldName2, ascending2);
  1597. return result;
  1598. }
  1599. this._iterationOrder.sortRange(sortByComparator, leftBound, rightBound, count);
  1600. },
  1601. __proto__: WebInspector.HeapSnapshotFilteredOrderedIterator.prototype
  1602. }