Skip to content

Commit 84fa006

Browse files
committed
perf(napi/parser): lazy deser: faster construction of NodeArray iterators (#11870)
Make `NodeArray` iterators (e.g. `NodeArrayValuesIterator`) constructors take a `Proxy<NodeArray>`. This makes them safe to construct from user code. Therefore can remove the `TOKEN` check which previously blocked user code from constructing these classes. This makes iterating over a `NodeArray` very slightly faster.
1 parent fb02e6c commit 84fa006

File tree

1 file changed

+28
-23
lines changed

1 file changed

+28
-23
lines changed

napi/parser/raw-transfer/node-array.js

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ const { TOKEN, constructorError } = require('./lazy-common.js');
1212
// via `Object.getOwnPropertySymbols` or `Reflect.ownKeys`. Therefore user code cannot unwrap the proxy.
1313
const ARRAY = Symbol();
1414

15-
// Functions to get length of and get element from an array. Initialized in class static block below.
16-
let getLength, getElement;
15+
// Functions to get internal properties of a `NodeArray`. Initialized in class static block below.
16+
let getInternalFromProxy, getLength, getElement;
1717

1818
/**
1919
* An array of AST nodes where elements are deserialized lazily upon access.
@@ -55,19 +55,19 @@ class NodeArray extends Array {
5555
// Override `values` method with a more efficient one that avoids going via proxy for every iteration.
5656
// TODO: Benchmark to check that this is actually faster.
5757
values() {
58-
return new NodeArrayValuesIterator(this[ARRAY].#internal);
58+
return new NodeArrayValuesIterator(this);
5959
}
6060

6161
// Override `keys` method with a more efficient one that avoids going via proxy for every iteration.
6262
// TODO: Benchmark to check that this is actually faster.
6363
keys() {
64-
return new NodeArrayKeysIterator(this[ARRAY].#internal.length);
64+
return new NodeArrayKeysIterator(this);
6565
}
6666

6767
// Override `entries` method with a more efficient one that avoids going via proxy for every iteration.
6868
// TODO: Benchmark to check that this is actually faster.
6969
entries() {
70-
return new NodeArrayEntriesIterator(this[ARRAY].#internal);
70+
return new NodeArrayEntriesIterator(this);
7171
}
7272

7373
// This method is overwritten with reference to `values` method below.
@@ -122,6 +122,13 @@ class NodeArray extends Array {
122122
}
123123

124124
static {
125+
/**
126+
* Get internal properties of `NodeArray`, given a proxy wrapping a `NodeArray`.
127+
* @param {Proxy} proxy - Proxy wrapping `NodeArray` object
128+
* @returns {Object} - Internal properties object
129+
*/
130+
getInternalFromProxy = proxy => proxy[ARRAY].#internal;
131+
125132
/**
126133
* Get length of `NodeArray`.
127134
* @param {NodeArray} arr - `NodeArray` object
@@ -155,15 +162,15 @@ module.exports = NodeArray;
155162
class NodeArrayValuesIterator {
156163
#internal;
157164

158-
constructor(arrInternal) {
159-
const { ast, pos, stride } = arrInternal || {};
160-
if (ast?.token !== TOKEN) constructorError();
165+
constructor(proxy) {
166+
const internal = getInternalFromProxy(proxy),
167+
{ pos, stride } = internal;
161168

162169
this.#internal = {
163170
pos,
164-
endPos: pos + arrInternal.length * stride,
165-
ast,
166-
construct: arrInternal.construct,
171+
endPos: pos + internal.length * stride,
172+
ast: internal.ast,
173+
construct: internal.construct,
167174
stride,
168175
};
169176
}
@@ -187,10 +194,9 @@ class NodeArrayValuesIterator {
187194
class NodeArrayKeysIterator {
188195
#internal;
189196

190-
constructor(length) {
191-
// Don't bother gating constructor with `TOKEN` check.
192-
// This iterator doesn't access the buffer, so is harmless.
193-
this.#internal = { index: 0, length };
197+
constructor(proxy) {
198+
const internal = getInternalFromProxy(proxy);
199+
this.#internal = { index: 0, length: internal.length };
194200
}
195201

196202
next() {
@@ -212,17 +218,16 @@ class NodeArrayKeysIterator {
212218
class NodeArrayEntriesIterator {
213219
#internal;
214220

215-
constructor(arrInternal) {
216-
const { ast } = arrInternal || {};
217-
if (ast?.token !== TOKEN) constructorError();
221+
constructor(proxy) {
222+
const internal = getInternalFromProxy(proxy);
218223

219224
this.#internal = {
220225
index: 0,
221-
length: arrInternal.length,
222-
pos: arrInternal.pos,
223-
ast,
224-
construct: arrInternal.construct,
225-
stride: arrInternal.stride,
226+
length: internal.length,
227+
pos: internal.pos,
228+
ast: internal.ast,
229+
construct: internal.construct,
230+
stride: internal.stride,
226231
};
227232
}
228233

0 commit comments

Comments
 (0)