Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library object_graph; | 5 library object_graph; |
| 6 | 6 |
| 7 import 'dart:async'; | |
| 7 import 'dart:typed_data'; | 8 import 'dart:typed_data'; |
| 9 import 'dart:collection'; | |
|
koda
2015/05/21 02:05:10
Sort imports.
| |
| 8 | 10 |
| 9 import 'dominator_tree.dart'; | 11 import 'package:logging/logging.dart'; |
| 10 | 12 |
| 11 // Port of dart::ReadStream from vm/datastream.h. | 13 // Port of dart::ReadStream from vm/datastream.h. |
| 12 class ReadStream { | 14 class _ReadStream { |
| 13 int _cur = 0; | 15 int position = 0; |
| 14 final ByteData _data; | 16 int _size = 0; |
| 15 | 17 final List<ByteData> _chunks; |
| 16 ReadStream(this._data); | 18 |
| 17 | 19 _ReadStream(this._chunks) { |
| 18 int get pendingBytes => _data.lengthInBytes - _cur; | 20 int n = _chunks.length; |
| 19 | 21 for (var i = 0; i < n; i++) { |
| 22 var chunk = _chunks[i]; | |
| 23 if (i + 1 != n) { | |
| 24 assert(chunk.lengthInBytes == (1 << 20)); | |
| 25 } | |
| 26 _size += chunk.lengthInBytes; | |
| 27 } | |
| 28 } | |
| 29 | |
| 30 int get pendingBytes => _size - position; | |
| 31 | |
| 32 int getUint8(i) { | |
| 33 return _chunks[i >> 20].getUint8(i & 0xFFFFF); | |
| 34 } | |
| 35 | |
| 20 int readUnsigned() { | 36 int readUnsigned() { |
| 21 int result = 0; | 37 int result = 0; |
| 22 int shift = 0; | 38 int shift = 0; |
| 23 while (_data.getUint8(_cur) <= maxUnsignedDataPerByte) { | 39 while (getUint8(position) <= maxUnsignedDataPerByte) { |
| 24 result |= _data.getUint8(_cur) << shift; | 40 result |= getUint8(position) << shift; |
| 25 shift += dataBitsPerByte; | 41 shift += dataBitsPerByte; |
| 26 ++_cur; | 42 ++position; |
| 27 } | 43 } |
| 28 result |= (_data.getUint8(_cur) & byteMask) << shift; | 44 result |= (getUint8(position) & byteMask) << shift; |
| 29 ++_cur; | 45 ++position; |
| 30 return result; | 46 return result; |
| 31 } | 47 } |
| 32 | 48 |
| 33 static const int dataBitsPerByte = 7; | 49 static const int dataBitsPerByte = 7; |
| 34 static const int byteMask = (1 << dataBitsPerByte) - 1; | 50 static const int byteMask = (1 << dataBitsPerByte) - 1; |
| 35 static const int maxUnsignedDataPerByte = byteMask; | 51 static const int maxUnsignedDataPerByte = byteMask; |
| 36 } | 52 } |
| 37 | 53 |
| 38 class ObjectVertex { | 54 class ObjectVertex { |
| 39 // Never null. The isolate root has id 0. | 55 // 0 represents invalid/uninitialized, 1 is the root. |
| 40 final int _id; | 56 final int _id; |
| 41 bool get isRoot => _id == 0; | 57 final ObjectGraph _graph; |
| 42 // TODO(koda): Include units in object graph metadata. | 58 |
| 43 int addressForWordSize(int bytesPerWord) => _id * 2 * bytesPerWord; | 59 ObjectVertex._(this._id, this._graph); |
| 44 // null for VM-heap objects. | 60 |
| 45 int _shallowSize; | 61 bool operator ==(other) => _id == other._id && _graph == other._graph; |
| 46 int get shallowSize => _shallowSize; | 62 int get hashCode => _id; |
| 47 int _retainedSize; | 63 |
| 48 int get retainedSize => _retainedSize; | 64 int get retainedSize => _graph._retainedSizes[_id]; |
| 49 // null for VM-heap objects. | 65 ObjectVertex get dominator => new ObjectVertex._(_graph._doms[_id], _graph); |
| 50 int _classId; | 66 |
| 51 int get classId => _classId; | 67 int get shallowSize { |
| 52 final List<ObjectVertex> succ = new List<ObjectVertex>(); | 68 var stream = new _ReadStream(_graph._chunks); |
| 53 ObjectVertex(this._id) : _retainedSize = 0; | 69 stream.position = _graph._positions[_id]; |
| 54 String toString() => '$_id,$_shallowSize,$succ'; | 70 stream.readUnsigned(); // addr |
| 71 return stream.readUnsigned(); // shallowSize | |
| 72 } | |
| 73 | |
| 74 int get vmCid { | |
| 75 var stream = new _ReadStream(_graph._chunks); | |
| 76 stream.position = _graph._positions[_id]; | |
| 77 stream.readUnsigned(); // addr | |
| 78 stream.readUnsigned(); // shallowSize | |
| 79 return stream.readUnsigned(); // cid | |
| 80 } | |
| 81 | |
| 82 get successors => new _SuccessorsIterable(_graph, _id); | |
| 83 | |
| 84 int get address { | |
| 85 // Note that everywhere else in this file, "address" really means an address | |
| 86 // scaled down by kObjectAlignment. They were scaled down so they would fit | |
| 87 // into Smis on client. | |
| 88 var stream = new _ReadStream(_graph._chunks); | |
| 89 stream.position = _graph._positions[_id]; | |
| 90 var scaledAddr = stream.readUnsigned(); | |
| 91 return scaledAddr * _graph._kObjectAlignment; | |
| 92 } | |
| 93 | |
| 94 List<ObjectVertex> dominatorTreeChildren() { | |
| 95 var N = _graph._N; | |
| 96 var doms = _graph._doms; | |
| 97 | |
| 98 var parentId = _id; | |
| 99 var domChildren = []; | |
| 100 | |
| 101 for (var childId = 1; childId <= N; childId++) { | |
| 102 if (doms[childId] == parentId) { | |
| 103 domChildren.add(new ObjectVertex._(childId, _graph)); | |
| 104 } | |
| 105 } | |
| 106 | |
| 107 return domChildren; | |
| 108 } | |
| 55 } | 109 } |
| 56 | 110 |
| 57 // See implementation of ObjectGraph::Serialize for format. | 111 class _SuccessorsIterable extends IterableBase<ObjectVertex> { |
| 58 class ObjectGraph { | 112 final ObjectGraph _graph; |
| 59 final Map<int, ObjectVertex> _idToVertex = new Map<int, ObjectVertex>(); | 113 final int _id; |
| 60 | 114 |
| 61 ObjectVertex _asVertex(int id) { | 115 _SuccessorsIterable(this._graph, this._id); |
| 62 return _idToVertex.putIfAbsent(id, () => new ObjectVertex(id)); | 116 |
| 117 Iterator<ObjectVertex> get iterator => new _SuccessorsIterator(_graph, _id); | |
| 118 } | |
| 119 | |
| 120 class _SuccessorsIterator implements Iterator<ObjectVertex> { | |
| 121 final ObjectGraph _graph; | |
| 122 _ReadStream _stream; | |
| 123 | |
| 124 ObjectVertex current; | |
| 125 | |
| 126 _SuccessorsIterator(this._graph, int id) { | |
| 127 _stream = new _ReadStream(this._graph._chunks); | |
| 128 _stream.position = _graph._positions[id]; | |
| 129 _stream.readUnsigned(); // addr | |
| 130 _stream.readUnsigned(); // shallowSize | |
| 131 _stream.readUnsigned(); // cid | |
|
koda
2015/05/21 02:05:09
Consider having a sanity check on 'cid' here to ca
| |
| 63 } | 132 } |
| 64 | 133 |
| 65 void _addFrom(ReadStream stream) { | 134 bool moveNext() { |
| 66 ObjectVertex obj = _asVertex(stream.readUnsigned()); | 135 while (true) { |
| 67 obj._shallowSize = stream.readUnsigned(); | 136 var nextAddr = _stream.readUnsigned(); |
| 68 obj._classId = stream.readUnsigned(); | 137 if (nextAddr == 0) return false; |
| 69 int last = stream.readUnsigned(); | 138 var nextId = _graph._addrToId[nextAddr]; |
| 70 while (last != 0) { | 139 if (nextId == null) continue; // Reference to VM isolate's heap. |
| 71 obj.succ.add(_asVertex(last)); | 140 current = new ObjectVertex._(nextId, _graph); |
| 72 last = stream.readUnsigned(); | 141 return true; |
| 73 } | 142 } |
| 74 } | 143 } |
| 144 } | |
| 75 | 145 |
| 76 ObjectGraph(ReadStream reader) { | 146 class _VerticesIterable extends IterableBase<ObjectVertex> { |
| 77 while (reader.pendingBytes > 0) { | 147 final ObjectGraph _graph; |
| 78 _addFrom(reader); | 148 |
| 79 } | 149 _VerticesIterable(this._graph); |
| 80 _computeRetainedSizes(); | 150 |
| 81 _mostRetained = new List<ObjectVertex>.from( | 151 Iterator<ObjectVertex> get iterator => new _VerticesIterator(_graph); |
| 82 vertices.where((u) => !u.isRoot)); | 152 } |
| 153 | |
| 154 class _VerticesIterator implements Iterator<ObjectVertex> { | |
| 155 final ObjectGraph _graph; | |
| 156 | |
| 157 int _nextId = 0; | |
| 158 ObjectVertex current; | |
| 159 | |
| 160 _VerticesIterator(this._graph); | |
| 161 | |
| 162 bool moveNext() { | |
| 163 if (_nextId == _graph._N) return false; | |
| 164 current = new ObjectVertex._(_nextId++, _graph); | |
| 165 return true; | |
| 166 } | |
| 167 } | |
| 168 | |
| 169 class ObjectGraph { | |
| 170 ObjectGraph(List<ByteData> chunks, int nodeCount) | |
| 171 : this._chunks = chunks | |
| 172 , this._N = nodeCount; | |
| 173 | |
| 174 int get size => _size; | |
| 175 int get vertexCount => _N; | |
| 176 int get edgeCount => _E; | |
| 177 | |
| 178 ObjectVertex get root => new ObjectVertex._(1, this); | |
| 179 Iterable<ObjectVertex> get vertices => new _VerticesIterable(this); | |
| 180 | |
| 181 Iterable<ObjectVertex> getMostRetained({int classId, int limit}) { | |
| 182 List<ObjectVertex> _mostRetained = | |
| 183 new List<ObjectVertex>.from(vertices.where((u) => !u.isRoot)); | |
| 83 _mostRetained.sort((u, v) => v.retainedSize - u.retainedSize); | 184 _mostRetained.sort((u, v) => v.retainedSize - u.retainedSize); |
| 84 } | |
| 85 | 185 |
| 86 Iterable<ObjectVertex> get vertices => _idToVertex.values; | |
| 87 List<ObjectVertex> _mostRetained; | |
| 88 | |
| 89 ObjectVertex get root => _asVertex(0); | |
| 90 | |
| 91 Iterable<ObjectVertex> getMostRetained({int classId, int limit}) { | |
| 92 var result = _mostRetained; | 186 var result = _mostRetained; |
| 93 if (classId != null) { | 187 if (classId != null) { |
| 94 result = result.where((u) => u.classId == classId); | 188 result = result.where((u) => u.classId == classId); |
| 95 } | 189 } |
| 96 if (limit != null) { | 190 if (limit != null) { |
| 97 result = result.take(limit); | 191 result = result.take(limit); |
| 98 } | 192 } |
| 99 return result; | 193 return result; |
| 100 } | 194 } |
| 101 | 195 |
| 102 void _computeRetainedSizes() { | 196 Future process(statusReporter) async { |
| 103 // The retained size for an object is the sum of the shallow sizes of | 197 // We build futures here instead of marking the steps as async to avoid the |
| 104 // all its descendants in the dominator tree (including itself). | 198 // heavy lifting being inside a tranformed method. |
|
koda
2015/05/21 02:05:09
tranformed -> transformed
| |
| 105 var d = new Dominator(); | 199 |
| 106 for (ObjectVertex u in vertices) { | 200 statusReporter.add("Finding node positions..."); |
| 107 if (u.shallowSize != null) { | 201 await new Future(() => _buildPositions()); |
| 108 u._retainedSize = u.shallowSize; | 202 |
| 109 d.addEdges(u, u.succ.where((ObjectVertex v) => v.shallowSize != null)); | 203 statusReporter.add("Finding post order..."); |
| 110 } | 204 await new Future(() => _buildPostOrder()); |
| 111 } | 205 |
| 112 d.computeDominatorTree(root); | 206 statusReporter.add("Finding predecessors..."); |
| 113 // Compute all retained sizes "bottom up", starting from the leaves. | 207 await new Future(() => _buildPredecessors()); |
| 114 // Keep track of number of remaining children of each vertex. | 208 |
| 115 var degree = new Map<ObjectVertex, int>(); | 209 statusReporter.add("Finding dominators..."); |
| 116 for (ObjectVertex u in vertices) { | 210 await new Future(() => _buildDominators()); |
| 117 var v = d.dominator(u); | 211 |
| 118 if (v != null) { | 212 _firstPreds = null; |
| 119 degree[v] = 1 + degree.putIfAbsent(v, () => 0); | 213 _preds = null; |
| 120 } | 214 _postOrderIndices = null; |
| 121 } | 215 |
| 122 var leaves = new List<ObjectVertex>(); | 216 statusReporter.add("Finding retained sizes..."); |
| 123 for (ObjectVertex u in vertices) { | 217 await new Future(() => _calculateRetainedSizes()); |
| 124 if (!degree.containsKey(u)) { | 218 |
| 125 leaves.add(u); | 219 _postOrderOrdinals = null; |
| 126 } | 220 |
| 127 } | 221 statusReporter.add("Done"); |
| 128 while (!leaves.isEmpty) { | 222 return this; |
| 129 var v = leaves.removeLast(); | 223 } |
| 130 var u = d.dominator(v); | 224 |
| 131 if (u == null) continue; | 225 final List<ByteData> _chunks; |
| 132 u._retainedSize += v._retainedSize; | 226 |
| 133 if (--degree[u] == 0) { | 227 int _kObjectAlignment; |
| 134 leaves.add(u); | 228 int _N; |
| 135 } | 229 int _E; |
| 136 } | 230 int _size; |
| 137 } | 231 |
| 138 } | 232 Map<int, int> _addrToId = new Map<int, int>(); |
| 233 | |
| 234 // Indexed by node id, with id 0 representing invalid/uninitialized. | |
| 235 Uint32List _positions; // Position of the node in the snapshot. | |
| 236 Uint32List _postOrderOrdinals; // post-order index -> id | |
| 237 Uint32List _postOrderIndices; // id -> post-order index | |
| 238 Uint32List _firstPreds; // Offset into preds. | |
| 239 Uint32List _preds; | |
| 240 Uint32List _doms; | |
| 241 Uint32List _retainedSizes; | |
| 242 | |
| 243 void _buildPositions() { | |
| 244 var N = _N; | |
| 245 var addrToId = _addrToId; | |
|
koda
2015/05/21 02:05:10
Can you make the field final and get rid of this m
| |
| 246 | |
| 247 var positions = new Uint32List(N + 1); | |
| 248 | |
| 249 var stream = new _ReadStream(_chunks); | |
| 250 _kObjectAlignment = stream.readUnsigned(); | |
| 251 | |
| 252 var id = 1; | |
| 253 while (stream.pendingBytes > 0) { | |
| 254 positions[id] = stream.position; | |
| 255 var addr = stream.readUnsigned(); | |
| 256 var shallowSize = stream.readUnsigned(); | |
| 257 var cid = stream.readUnsigned(); | |
| 258 addrToId[addr] = id; | |
| 259 | |
| 260 var succAddr = stream.readUnsigned(); | |
| 261 while (succAddr != 0) { | |
| 262 succAddr = stream.readUnsigned(); | |
| 263 } | |
| 264 id++; | |
| 265 } | |
| 266 assert(id == (N + 1)); | |
| 267 | |
| 268 var root = addrToId[0]; | |
| 269 assert(root == 1); | |
| 270 | |
| 271 _positions = positions; | |
| 272 } | |
| 273 | |
| 274 void _buildPostOrder() { | |
| 275 var N = _N; | |
| 276 var E = 0; | |
| 277 var addrToId = _addrToId; | |
|
koda
2015/05/21 02:05:09
Ditto.
| |
| 278 var positions = _positions; | |
| 279 | |
| 280 var postOrderOrdinals = new Uint32List(N); | |
| 281 var postOrderIndices = new Uint32List(N + 1); | |
| 282 var stackNodes = new Uint32List(N); | |
| 283 var stackCurrentEdgePos = new Uint32List(N); | |
| 284 | |
| 285 var visited = new Uint8List(N + 1); | |
| 286 var postOrderIndex = 0; | |
| 287 var stackTop = 0; | |
| 288 var root = 1; | |
| 289 | |
| 290 stackNodes[0] = root; | |
| 291 | |
| 292 var stream = new _ReadStream(_chunks); | |
| 293 stream.position = positions[root]; | |
| 294 stream.readUnsigned(); // addr | |
| 295 stream.readUnsigned(); // shallowSize | |
| 296 stream.readUnsigned(); // cid | |
| 297 stackCurrentEdgePos[0] = stream.position; | |
| 298 visited[root] = 1; | |
| 299 | |
| 300 while (stackTop >= 0) { | |
| 301 var n = stackNodes[stackTop]; | |
| 302 var edgePos = stackCurrentEdgePos[stackTop]; | |
| 303 | |
| 304 stream.position = edgePos; | |
| 305 var childAddr = stream.readUnsigned(); | |
| 306 if (childAddr != 0) { | |
| 307 stackCurrentEdgePos[stackTop] = stream.position; | |
| 308 var childId = addrToId[childAddr]; | |
| 309 if (childId == null) continue; // Reference to VM isolate's heap. | |
| 310 ++E; | |
| 311 if (visited[childId] == 1) continue; | |
| 312 | |
| 313 ++stackTop; | |
| 314 stackNodes[stackTop] = childId; | |
| 315 | |
| 316 stream.position = positions[childId]; | |
| 317 stream.readUnsigned(); // addr | |
| 318 stream.readUnsigned(); // shallowSize | |
| 319 stream.readUnsigned(); // cid | |
| 320 stackCurrentEdgePos[stackTop] = stream.position; // i.e., first edge | |
| 321 visited[childId] = 1; | |
| 322 } else { | |
| 323 // Done with all children. | |
| 324 postOrderIndices[n] = postOrderIndex; | |
| 325 postOrderOrdinals[postOrderIndex++] = n; | |
| 326 --stackTop; | |
| 327 } | |
| 328 } | |
| 329 | |
| 330 assert(postOrderIndex == N); | |
| 331 assert(postOrderOrdinals[N - 1] == root); | |
| 332 | |
| 333 _postOrderOrdinals = postOrderOrdinals; | |
| 334 _postOrderIndices = postOrderIndices; | |
| 335 _E = E; | |
| 336 } | |
| 337 | |
| 338 void _buildPredecessors() { | |
| 339 var N = _N; | |
| 340 var E = _E; | |
| 341 var addrToId = _addrToId; | |
| 342 var positions = _positions; | |
| 343 | |
| 344 // This is first filled with the predecessor counts, then reused to hold the | |
| 345 // offset to the first predecessor (see alias below). | |
| 346 // + 1 because 0 is a sentinel | |
| 347 // + 1 so the number of predecessors can be found from the difference with | |
| 348 // the next node's offset. | |
| 349 var numPreds = new Uint32List(N + 2); | |
| 350 var preds = new Uint32List(E); | |
| 351 | |
| 352 // Count predecessors of each node. | |
| 353 var stream = new _ReadStream(_chunks); | |
| 354 for (var i = 1; i <= N; i++) { | |
| 355 stream.position = positions[i]; | |
| 356 stream.readUnsigned(); // addr | |
| 357 stream.readUnsigned(); // shallowSize | |
| 358 stream.readUnsigned(); // cid | |
| 359 var succAddr = stream.readUnsigned(); | |
| 360 while (succAddr != 0) { | |
| 361 var succId = addrToId[succAddr]; | |
| 362 if (succId != null) { | |
| 363 numPreds[succId]++; | |
| 364 } else { | |
| 365 // Reference to VM isolate's heap. | |
| 366 } | |
| 367 succAddr = stream.readUnsigned(); | |
| 368 } | |
| 369 } | |
| 370 | |
| 371 // Assign indices into predecessors array. | |
| 372 var firstPreds = numPreds; // Alias. | |
| 373 var nextPreds = new Uint32List(N + 1); | |
| 374 var predIndex = 0; | |
| 375 for (var i = 1; i <= N; i++) { | |
| 376 var thisPredIndex = predIndex; | |
| 377 predIndex += numPreds[i]; | |
| 378 firstPreds[i] = thisPredIndex; | |
| 379 nextPreds[i] = thisPredIndex; | |
| 380 } | |
| 381 assert(predIndex == E); | |
| 382 firstPreds[N + 1] = E; // Extra entry for cheap boundry detection. | |
|
koda
2015/05/21 02:05:09
boundry -> boundary
| |
| 383 | |
| 384 // Fill predecessors array. | |
| 385 for (var i = 1; i <= N; i++) { | |
| 386 stream.position = positions[i]; | |
| 387 stream.readUnsigned(); // addr | |
| 388 stream.readUnsigned(); // shallowSize | |
| 389 stream.readUnsigned(); // cid | |
| 390 var succAddr = stream.readUnsigned(); | |
| 391 while (succAddr != 0) { | |
| 392 var succId = addrToId[succAddr]; | |
| 393 if (succId != null) { | |
| 394 var predIndex = nextPreds[succId]++; | |
| 395 preds[predIndex] = i; | |
| 396 } else { | |
| 397 // Reference to VM isolate's heap. | |
| 398 } | |
| 399 succAddr = stream.readUnsigned(); | |
| 400 } | |
| 401 } | |
| 402 | |
| 403 _firstPreds = firstPreds; | |
| 404 _preds = preds; | |
| 405 } | |
| 406 | |
| 407 // "A Simple, Fast Dominance Algorithm" | |
| 408 // Keith D. Cooper, Timothy J. Harvey, and Ken Kennedy | |
| 409 void _buildDominators() { | |
| 410 var N = _N; | |
| 411 var E = _E; | |
| 412 var addrToId = _addrToId; | |
| 413 var postOrder = _postOrderOrdinals; | |
| 414 var postOrderIndex = _postOrderIndices; | |
| 415 var firstPreds = _firstPreds; | |
| 416 var preds = _preds; | |
| 417 | |
| 418 var root = 1; | |
| 419 var rootPostOrderIndex = postOrderIndex[root]; | |
| 420 var domByPOI = new Uint32List(N + 1); | |
| 421 | |
| 422 domByPOI[rootPostOrderIndex] = rootPostOrderIndex; | |
| 423 | |
| 424 var iteration = 0; | |
| 425 var changed = true; | |
| 426 while (changed) { | |
| 427 changed = false; | |
| 428 Logger.root.info("Find dominators iteration $iteration"); | |
| 429 iteration++; // dart2js heaps typically converge in 10 iterations. | |
| 430 | |
| 431 // Visit the nodes, except the root, in reverse post order (top down). | |
| 432 for (var curPostOrderIndex = rootPostOrderIndex - 1; | |
| 433 curPostOrderIndex > 1; | |
| 434 --curPostOrderIndex) { | |
|
koda
2015/05/21 02:05:10
Be consistent w.r.t. pre/post-increment.
| |
| 435 if (domByPOI[curPostOrderIndex] == rootPostOrderIndex) | |
| 436 continue; | |
| 437 | |
| 438 var nodeOrdinal = postOrder[curPostOrderIndex]; | |
| 439 var newDomIndex = 0; // 0 = undefined | |
| 440 | |
| 441 // Intersect the DOM sets of the node's precedessors. | |
| 442 var beginPredIndex = firstPreds[nodeOrdinal]; | |
| 443 var endPredIndex = firstPreds[nodeOrdinal + 1]; | |
| 444 for (var predIndex = beginPredIndex; | |
| 445 predIndex < endPredIndex; | |
| 446 predIndex++) { | |
| 447 var predOrdinal = preds[predIndex]; | |
| 448 var predPostOrderIndex = postOrderIndex[predOrdinal]; | |
| 449 if (domByPOI[predPostOrderIndex] != 0) { | |
| 450 if (newDomIndex == 0) { | |
| 451 newDomIndex = predPostOrderIndex; | |
| 452 } else { | |
| 453 // Note this two finger algorithm to find the DOM intersection | |
| 454 // relies on comparing nodes by their post order index. | |
| 455 while (predPostOrderIndex != newDomIndex) { | |
| 456 while(predPostOrderIndex < newDomIndex) | |
| 457 predPostOrderIndex = domByPOI[predPostOrderIndex]; | |
| 458 while (newDomIndex < predPostOrderIndex) | |
| 459 newDomIndex = domByPOI[newDomIndex]; | |
| 460 } | |
| 461 } | |
| 462 if (newDomIndex == rootPostOrderIndex) { | |
| 463 break; | |
| 464 } | |
| 465 } | |
| 466 } | |
| 467 if (newDomIndex != 0 && domByPOI[curPostOrderIndex] != newDomIndex) { | |
| 468 domByPOI[curPostOrderIndex] = newDomIndex; | |
| 469 changed = true; | |
| 470 } | |
| 471 } | |
| 472 } | |
| 473 | |
| 474 // Reindex doms by id instead of post order index so we can throw away | |
| 475 // the post order arrays. | |
| 476 var domById = new Uint32List(N + 1); | |
| 477 for (var id = 1; id <= N; id++) { | |
| 478 domById[id] = postOrder[domByPOI[postOrderIndex[id]]]; | |
| 479 } | |
| 480 | |
| 481 domById[root] = 0; | |
| 482 | |
| 483 _doms = domById; | |
| 484 } | |
| 485 | |
| 486 void _calculateRetainedSizes() { | |
| 487 final N = _N; | |
|
koda
2015/05/21 02:05:09
Be consistent in whether you make locals final.
| |
| 488 final E = _E; | |
| 489 | |
| 490 var size = 0; | |
| 491 var positions = _positions; | |
| 492 var postOrderOrdinals = _postOrderOrdinals; | |
| 493 var doms = _doms; | |
| 494 var retainedSizes = new Uint32List(N + 1); | |
| 495 | |
| 496 // Start with retained size as shallow size. | |
| 497 var reader = new _ReadStream(_chunks); | |
| 498 for (var i = 1; i <= N; i++) { | |
| 499 reader.position = positions[i]; | |
| 500 reader.readUnsigned(); // addr | |
| 501 var shallowSize = reader.readUnsigned(); | |
| 502 retainedSizes[i] = shallowSize; | |
| 503 size += shallowSize; | |
| 504 } | |
| 505 | |
| 506 // In post order (bottom up), add retained size to dominator's retained | |
| 507 // size, skipping root. | |
| 508 for (var o = 0; o < (N - 1); o++) { | |
| 509 var i = postOrderOrdinals[o]; | |
| 510 assert(i != 1); | |
| 511 retainedSizes[doms[i]] += retainedSizes[i]; | |
| 512 } | |
| 513 | |
| 514 _retainedSizes = retainedSizes; | |
| 515 _size = size; | |
| 516 } | |
| 517 } | |
| OLD | NEW |