Skip to content

Commit fcf01c2

Browse files
Improve iterator performance
Each iterator is now constructed using the same class which makes sure v8 does not abandon optimizing the next function
1 parent 314b465 commit fcf01c2

File tree

2 files changed

+101
-136
lines changed

2 files changed

+101
-136
lines changed

lib/SymbolTree.js

Lines changed: 32 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
const SymbolTreeNode = require('./SymbolTreeNode');
99
const TreePosition = require('./TreePosition');
10+
const TreeIterator = require('./TreeIterator');
1011

1112
function returnTrue() {
1213
return true;
@@ -362,34 +363,14 @@ class SymbolTree {
362363
* @return {Object} An iterable iterator (ES6)
363364
*/
364365
childrenIterator(parent, reverse) {
365-
const _node = this._node.bind(this);
366-
367-
let nextObject = this._node(parent)[reverse ? 'last' : 'first'];
368-
369-
const iterator = {};
370-
371-
iterator.next = function() {
372-
if (!nextObject) {
373-
return {
374-
done : true,
375-
value : parent
376-
};
377-
}
378-
379-
const value = nextObject;
380-
nextObject = _node(nextObject)[reverse ? 'prev' : 'next'];
381-
382-
return {
383-
done : false,
384-
value : value
385-
};
386-
};
387-
388-
iterator[Symbol.iterator] = function() {
389-
return iterator;
390-
};
366+
const parentNode = this._node(parent);
391367

392-
return iterator;
368+
return new TreeIterator(
369+
this,
370+
parent,
371+
reverse ? parentNode.last : parentNode.first,
372+
reverse ? TreeIterator.PREV : TreeIterator.NEXT
373+
);
393374
}
394375

395376
/**
@@ -403,33 +384,12 @@ class SymbolTree {
403384
* @return {Object} An iterable iterator (ES6)
404385
*/
405386
prevSiblingsIterator(object) {
406-
const _node = this._node.bind(this);
407-
408-
let nextObject = _node(object).prev;
409-
const iterator = {};
410-
411-
iterator.next = function() {
412-
if (!nextObject) {
413-
return {
414-
done : true,
415-
value : object
416-
};
417-
}
418-
419-
const value = nextObject;
420-
nextObject = _node(nextObject).prev;
421-
422-
return {
423-
done : false,
424-
value : value
425-
};
426-
};
427-
428-
iterator[Symbol.iterator] = function() {
429-
return iterator;
430-
};
431-
432-
return iterator;
387+
return new TreeIterator(
388+
this,
389+
object,
390+
this._node(object).prev,
391+
TreeIterator.PREV
392+
);
433393
}
434394

435395
/**
@@ -443,33 +403,12 @@ class SymbolTree {
443403
* @return {Object} An iterable iterator (ES6)
444404
*/
445405
nextSiblingsIterator(object) {
446-
const _node = this._node.bind(this);
447-
448-
let nextObject = _node(object).next;
449-
const iterator = {};
450-
451-
iterator.next = function() {
452-
if (!nextObject) {
453-
return {
454-
done : true,
455-
value : object
456-
};
457-
}
458-
459-
const value = nextObject;
460-
nextObject = _node(nextObject).next;
461-
462-
return {
463-
done : false,
464-
value : value
465-
};
466-
};
467-
468-
iterator[Symbol.iterator] = function() {
469-
return iterator;
470-
};
471-
472-
return iterator;
406+
return new TreeIterator(
407+
this,
408+
object,
409+
this._node(object).next,
410+
TreeIterator.NEXT
411+
);
473412
}
474413

475414
/**
@@ -483,33 +422,12 @@ class SymbolTree {
483422
* @return {Object} An iterable iterator (ES6)
484423
*/
485424
ancestorsIterator(object) {
486-
const _node = this._node.bind(this);
487-
488-
let nextObject = object;
489-
const iterator = {};
490-
491-
iterator.next = function() {
492-
if (!nextObject) {
493-
return {
494-
done : true,
495-
value : object
496-
};
497-
}
498-
499-
const value = nextObject;
500-
nextObject = _node(nextObject).parent;
501-
502-
return {
503-
done : false,
504-
value : value
505-
};
506-
};
507-
508-
iterator[Symbol.iterator] = function() {
509-
return iterator;
510-
};
511-
512-
return iterator;
425+
return new TreeIterator(
426+
this,
427+
object,
428+
object,
429+
TreeIterator.PARENT
430+
);
513431
}
514432

515433
/**
@@ -525,34 +443,12 @@ class SymbolTree {
525443
* @return {Object} An iterable iterator (ES6)
526444
*/
527445
treeIterator(root, reverse) {
528-
let nextObject = reverse
529-
? this.lastInclusiveDescendant(root)
530-
: root;
531-
const findNext = (reverse ? this.preceding : this.following).bind(this);
532-
const iterator = {};
533-
534-
iterator.next = function() {
535-
if (!nextObject) {
536-
return {
537-
done : true,
538-
value : root
539-
};
540-
}
541-
542-
const value = nextObject;
543-
nextObject = findNext(nextObject, root);
544-
545-
return {
546-
done : false,
547-
value : value
548-
};
549-
};
550-
551-
iterator[Symbol.iterator] = function() {
552-
return iterator;
553-
};
554-
555-
return iterator;
446+
return new TreeIterator(
447+
this,
448+
root,
449+
reverse ? this.lastInclusiveDescendant(root) : root,
450+
reverse ? TreeIterator.PRECEDING : TreeIterator.FOLLOWING
451+
);
556452
}
557453

558454
/**

lib/TreeIterator.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
'use strict';
2+
3+
const TREE = Symbol();
4+
const ROOT = Symbol();
5+
const NEXT = Symbol();
6+
const ITERATE_FUNC = Symbol();
7+
8+
class TreeIterator {
9+
constructor(tree, root, firstResult, iterateFunction) {
10+
this[TREE] = tree;
11+
this[ROOT] = root;
12+
this[NEXT] = firstResult;
13+
this[ITERATE_FUNC] = iterateFunction;
14+
}
15+
16+
next() {
17+
const tree = this[TREE];
18+
const iterateFunc = this[ITERATE_FUNC];
19+
const root = this[ROOT];
20+
21+
if (!this[NEXT]) {
22+
return {
23+
done : true,
24+
value : root
25+
};
26+
}
27+
28+
const value = this[NEXT];
29+
30+
if (iterateFunc === 1) {
31+
this[NEXT] = tree._node(value).prev;
32+
}
33+
else if (iterateFunc === 2) {
34+
this[NEXT] = tree._node(value).next;
35+
}
36+
else if (iterateFunc === 3) {
37+
this[NEXT] = tree._node(value).parent;
38+
}
39+
else if (iterateFunc === 4) {
40+
this[NEXT] = tree.preceding(value, root);
41+
}
42+
else /*if (iterateFunc === 5)*/ {
43+
this[NEXT] = tree.following(value, root);
44+
}
45+
46+
return {
47+
done : false,
48+
value : value
49+
};
50+
}
51+
}
52+
53+
Object.defineProperty(TreeIterator.prototype, Symbol.iterator, {
54+
writable : false,
55+
value : function() {
56+
return this;
57+
}
58+
});
59+
60+
TreeIterator.PREV = 1;
61+
TreeIterator.NEXT = 2;
62+
TreeIterator.PARENT = 3;
63+
TreeIterator.PRECEDING = 4;
64+
TreeIterator.FOLLOWING = 5;
65+
66+
Object.freeze(TreeIterator);
67+
Object.freeze(TreeIterator.prototype);
68+
69+
module.exports = TreeIterator;

0 commit comments

Comments
 (0)