22
33const { TOKEN , constructorError } = require ( './lazy-common.js' ) ;
44
5- // Mapping from a proxy to the `NodeArray` that it wraps.
6- // Used by `slice`, `values`, `key` and `elements` methods.
5+ // Internal symbol to get `NodeArray` from a proxy wrapping a `NodeArray`.
76//
8- // TODO: Is there any way to avoid this?
9- // Seems necessary because `this` in methods is a proxy, so accessing `this.#internal` throws.
10- const nodeArrays = new WeakMap ( ) ;
7+ // Methods of `NodeArray` are called with `this` being the proxy, rather than the `NodeArray` itself.
8+ // They can "unwrap" the proxy by getting `this[ARRAY]`, and the `get` proxy trap will return
9+ // the actual `NodeArray`.
10+ //
11+ // This symbol is not exported, and it is not actually defined on `NodeArray`s, so user cannot obtain it
12+ // via `Object.getOwnPropertySymbols` or `Reflect.ownKeys`. Therefore user code cannot unwrap the proxy.
13+ const ARRAY = Symbol ( ) ;
1114
1215// Function to get element from an array. Initialized in class static block below.
1316let getElement ;
@@ -43,10 +46,7 @@ class NodeArray extends Array {
4346
4447 super ( length ) ;
4548 this . #internal = { pos, ast, stride, construct } ;
46-
47- const proxy = new Proxy ( this , PROXY_HANDLERS ) ;
48- nodeArrays . set ( proxy , this ) ;
49- return proxy ;
49+ return new Proxy ( this , PROXY_HANDLERS ) ;
5050 }
5151
5252 // Allow `arr.filter`, `arr.map` etc.
@@ -56,25 +56,22 @@ class NodeArray extends Array {
5656 // TODO: Benchmark to check that this is actually faster.
5757 values ( ) {
5858 // Get actual `NodeArray`. `this` is a proxy.
59- const arr = nodeArrays . get ( this ) ;
59+ const arr = this [ ARRAY ] ;
6060 return new NodeArrayValuesIterator ( arr . #internal, arr . length ) ;
6161 }
6262
6363 // Override `keys` method with a more efficient one that avoids going via proxy for every iteration.
6464 // TODO: Benchmark to check that this is actually faster.
6565 keys ( ) {
66- // Get actual `NodeArray`. `this` is a proxy.
67- // TODO: `this.length` would work here.
68- // Not sure which is more expensive - property lookup via proxy, or `WeakMap` lookup.
69- const arr = nodeArrays . get ( this ) ;
70- return new NodeArrayKeysIterator ( arr . length ) ;
66+ // `this` is a proxy, but proxy will pass through getting `this.length` to the underlying `NodeArray`
67+ return new NodeArrayKeysIterator ( this . length ) ;
7168 }
7269
7370 // Override `entries` method with a more efficient one that avoids going via proxy for every iteration.
7471 // TODO: Benchmark to check that this is actually faster.
7572 entries ( ) {
7673 // Get actual `NodeArray`. `this` is a proxy.
77- const arr = nodeArrays . get ( this ) ;
74+ const arr = this [ ARRAY ] ;
7875 return new NodeArrayEntriesIterator ( arr . #internal, arr . length ) ;
7976 }
8077
@@ -92,8 +89,7 @@ class NodeArray extends Array {
9289 */
9390 slice ( start , end ) {
9491 // Get actual `NodeArray`. `this` is a proxy.
95- const arr = nodeArrays . get ( this ) ;
96- if ( arr === void 0 ) throw new Error ( '`slice` called on a value which is not a `NodeArray`' ) ;
92+ const arr = this [ ARRAY ] ;
9793
9894 start = toInt ( start ) ;
9995 if ( start < 0 ) {
@@ -262,11 +258,16 @@ const PROXY_HANDLERS = {
262258
263259 // Get entries which are in bounds.
264260 get ( arr , key ) {
261+ // Methods of `NodeArray` are called with `this` being the proxy, rather than the `NodeArray` itself.
262+ // They can "unwrap" the proxy by getting `this[ARRAY]`.
263+ if ( key === ARRAY ) return arr ;
264+
265265 if ( isIndex ( key ) ) {
266266 key *= 1 ;
267267 if ( key >= arr . length ) return void 0 ;
268268 return getElement ( arr , key ) ;
269269 }
270+
270271 return Reflect . get ( arr , key ) ;
271272 } ,
272273
0 commit comments