oxc-parser 0.72.3 → 0.73.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/generated/deserialize/js.js +118 -260
- package/generated/deserialize/lazy.js +13908 -0
- package/generated/deserialize/ts.js +120 -275
- package/index.js +11 -288
- package/package.json +30 -21
- package/raw-transfer/eager.js +56 -0
- package/raw-transfer/index.js +283 -0
- package/raw-transfer/lazy-common.js +12 -0
- package/raw-transfer/lazy.js +87 -0
- package/raw-transfer/node-array.js +327 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { TOKEN, constructorError } = require('./lazy-common.js');
|
|
4
|
+
|
|
5
|
+
// Mapping from a proxy to the `NodeArray` that it wraps.
|
|
6
|
+
// Used by `slice`, `values`, `key` and `elements` methods.
|
|
7
|
+
//
|
|
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();
|
|
11
|
+
|
|
12
|
+
// Function to get element from an array. Initialized in class static block below.
|
|
13
|
+
let getElement;
|
|
14
|
+
|
|
15
|
+
// An array of AST nodes where elements are deserialized lazily upon access.
|
|
16
|
+
//
|
|
17
|
+
// Extends `Array` to make `Array.isArray` return `true` for a `NodeArray`.
|
|
18
|
+
//
|
|
19
|
+
// TODO: Other methods could maybe be more optimal, avoiding going via proxy multiple times
|
|
20
|
+
// e.g. `some`, `indexOf`.
|
|
21
|
+
class NodeArray extends Array {
|
|
22
|
+
#internal;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Create a `NodeArray`.
|
|
26
|
+
*
|
|
27
|
+
* Constructor does not actually return a `NodeArray`, but one wrapped in a `Proxy`.
|
|
28
|
+
* The proxy intercepts accesses to elements and lazily deserializes them,
|
|
29
|
+
* and blocks mutation of elements or `length` property.
|
|
30
|
+
*
|
|
31
|
+
* @constructor
|
|
32
|
+
* @param {number} pos - Buffer position of first element
|
|
33
|
+
* @param {number} length - Number of elements
|
|
34
|
+
* @param {number} stride - Element size in bytes
|
|
35
|
+
* @param {Function} construct - Function to deserialize element
|
|
36
|
+
* @param {Object} ast - AST object
|
|
37
|
+
* @returns {Proxy<NodeArray>} - `NodeArray` wrapped in a `Proxy`
|
|
38
|
+
*/
|
|
39
|
+
constructor(pos, length, stride, construct, ast) {
|
|
40
|
+
if (ast?.token !== TOKEN) constructorError();
|
|
41
|
+
|
|
42
|
+
super(length);
|
|
43
|
+
this.#internal = { pos, ast, stride, construct };
|
|
44
|
+
|
|
45
|
+
const proxy = new Proxy(this, PROXY_HANDLERS);
|
|
46
|
+
nodeArrays.set(proxy, this);
|
|
47
|
+
return proxy;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Allow `arr.filter`, `arr.map` etc.
|
|
51
|
+
static [Symbol.species] = Array;
|
|
52
|
+
|
|
53
|
+
// Override `values` method with a more efficient one that avoids going via proxy for every iteration.
|
|
54
|
+
// TODO: Benchmark to check that this is actually faster.
|
|
55
|
+
values() {
|
|
56
|
+
// Get actual `NodeArray`. `this` is a proxy.
|
|
57
|
+
const arr = nodeArrays.get(this);
|
|
58
|
+
return new NodeArrayValuesIterator(arr.#internal, arr.length);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Override `keys` method with a more efficient one that avoids going via proxy for every iteration.
|
|
62
|
+
// TODO: Benchmark to check that this is actually faster.
|
|
63
|
+
keys() {
|
|
64
|
+
// Get actual `NodeArray`. `this` is a proxy.
|
|
65
|
+
// TODO: `this.length` would work here.
|
|
66
|
+
// Not sure which is more expensive - property lookup via proxy, or `WeakMap` lookup.
|
|
67
|
+
const arr = nodeArrays.get(this);
|
|
68
|
+
return new NodeArrayKeysIterator(arr.length);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Override `entries` method with a more efficient one that avoids going via proxy for every iteration.
|
|
72
|
+
// TODO: Benchmark to check that this is actually faster.
|
|
73
|
+
entries() {
|
|
74
|
+
// Get actual `NodeArray`. `this` is a proxy.
|
|
75
|
+
const arr = nodeArrays.get(this);
|
|
76
|
+
return new NodeArrayEntriesIterator(arr.#internal, arr.length);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// This method is overwritten with reference to `values` method below.
|
|
80
|
+
// Defining dummy method here to prevent the later assignment altering the shape of class prototype.
|
|
81
|
+
[Symbol.iterator]() {}
|
|
82
|
+
|
|
83
|
+
// Override `slice` method to return a `NodeArray`.
|
|
84
|
+
slice(start, end) {
|
|
85
|
+
// Get actual `NodeArray`. `this` is a proxy.
|
|
86
|
+
const arr = nodeArrays.get(this);
|
|
87
|
+
if (arr === void 0) throw new Error('`slice` called on a value which is not a `NodeArray`');
|
|
88
|
+
|
|
89
|
+
start = toInt(start);
|
|
90
|
+
if (start < 0) {
|
|
91
|
+
start = arr.length + start;
|
|
92
|
+
if (start < 0) start = 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (end === void 0) {
|
|
96
|
+
end = arr.length;
|
|
97
|
+
} else {
|
|
98
|
+
end = toInt(end);
|
|
99
|
+
if (end < 0) {
|
|
100
|
+
end = arr.length + end;
|
|
101
|
+
if (end < 0) end = 0;
|
|
102
|
+
} else if (end > arr.length) {
|
|
103
|
+
end = arr.length;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let length = end - start;
|
|
108
|
+
if (length <= 0 || start >= arr.length) {
|
|
109
|
+
start = 0;
|
|
110
|
+
length = 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const internal = arr.#internal,
|
|
114
|
+
{ stride } = internal;
|
|
115
|
+
return new NodeArray(internal.pos + start * stride, length, stride, internal.construct, internal.ast);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Make `console.log` deserialize all elements.
|
|
119
|
+
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
120
|
+
const values = [...this.values()];
|
|
121
|
+
Object.setPrototypeOf(values, DebugNodeArray.prototype);
|
|
122
|
+
return values;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
static {
|
|
126
|
+
/**
|
|
127
|
+
* Get element of `NodeArray` at index `index`.
|
|
128
|
+
* `index` must be in bounds (i.e. `< arr.length`).
|
|
129
|
+
*
|
|
130
|
+
* @param {NodeArray} arr - `NodeArray` object
|
|
131
|
+
* @param {number} index - Index of element to get
|
|
132
|
+
* @returns {*} - Element at index `index`
|
|
133
|
+
*/
|
|
134
|
+
getElement = (arr, index) => {
|
|
135
|
+
const internal = arr.#internal;
|
|
136
|
+
return (0, internal.construct)(internal.pos + index * internal.stride, internal.ast);
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
NodeArray.prototype[Symbol.iterator] = NodeArray.prototype.values;
|
|
142
|
+
|
|
143
|
+
module.exports = NodeArray;
|
|
144
|
+
|
|
145
|
+
// Iterator over values of a `NodeArray`.
|
|
146
|
+
// Returned by `values` method, and also used as iterator for `for (const node of nodeArray) {}`.
|
|
147
|
+
class NodeArrayValuesIterator {
|
|
148
|
+
#internal;
|
|
149
|
+
|
|
150
|
+
constructor(arrInternal, length) {
|
|
151
|
+
const { ast, pos, stride } = arrInternal || {};
|
|
152
|
+
if (ast?.token !== TOKEN) constructorError();
|
|
153
|
+
|
|
154
|
+
this.#internal = {
|
|
155
|
+
pos,
|
|
156
|
+
endPos: pos + length * stride,
|
|
157
|
+
ast,
|
|
158
|
+
construct: arrInternal.construct,
|
|
159
|
+
stride,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
next() {
|
|
164
|
+
const internal = this.#internal,
|
|
165
|
+
{ pos } = internal;
|
|
166
|
+
if (pos === internal.endPos) return { done: true, value: null };
|
|
167
|
+
internal.pos = pos + internal.stride;
|
|
168
|
+
return { done: false, value: (0, internal.construct)(pos, internal.ast) };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
[Symbol.iterator]() {
|
|
172
|
+
return this;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Iterator over keys of a `NodeArray`. Returned by `keys` method.
|
|
177
|
+
class NodeArrayKeysIterator {
|
|
178
|
+
#internal;
|
|
179
|
+
|
|
180
|
+
constructor(length) {
|
|
181
|
+
// Don't bother gating constructor with `TOKEN` check.
|
|
182
|
+
// This iterator doesn't access the buffer, so is harmless.
|
|
183
|
+
this.#internal = { index: 0, length };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
next() {
|
|
187
|
+
const internal = this.#internal,
|
|
188
|
+
{ index } = internal;
|
|
189
|
+
if (index === internal.length) return { done: true, value: null };
|
|
190
|
+
internal.index = index + 1;
|
|
191
|
+
return { done: false, value: index };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
[Symbol.iterator]() {
|
|
195
|
+
return this;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Iterator over values of a `NodeArray`. Returned by `entries` method.
|
|
200
|
+
class NodeArrayEntriesIterator {
|
|
201
|
+
#internal;
|
|
202
|
+
|
|
203
|
+
constructor(arrInternal, length) {
|
|
204
|
+
const { ast } = arrInternal || {};
|
|
205
|
+
if (ast?.token !== TOKEN) constructorError();
|
|
206
|
+
|
|
207
|
+
this.#internal = {
|
|
208
|
+
index: 0,
|
|
209
|
+
length,
|
|
210
|
+
pos: arrInternal.pos,
|
|
211
|
+
ast,
|
|
212
|
+
construct: arrInternal.construct,
|
|
213
|
+
stride: arrInternal.stride,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
next() {
|
|
218
|
+
const internal = this.#internal,
|
|
219
|
+
{ index } = internal;
|
|
220
|
+
if (index === internal.length) return { done: true, value: null };
|
|
221
|
+
internal.index = index + 1;
|
|
222
|
+
return {
|
|
223
|
+
done: false,
|
|
224
|
+
value: [index, (0, internal.construct)(internal.pos + index * internal.stride, internal.ast)],
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
[Symbol.iterator]() {
|
|
229
|
+
return this;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Class used for `[Symbol.for('nodejs.util.inspect.custom')]` method (for `console.log`).
|
|
234
|
+
const DebugNodeArray = class NodeArray extends Array {};
|
|
235
|
+
|
|
236
|
+
// Proxy handlers.
|
|
237
|
+
//
|
|
238
|
+
// Every `NodeArray` returned to user is wrapped in a `Proxy`, using these handlers.
|
|
239
|
+
// They lazily deserialize array elements upon access, and block mutation of array elements / `length`.
|
|
240
|
+
const PROXY_HANDLERS = {
|
|
241
|
+
// Return `true` for indexes which are in bounds.
|
|
242
|
+
// e.g. `'0' in arr`.
|
|
243
|
+
has(arr, key) {
|
|
244
|
+
if (isIndex(key)) return key * 1 < arr.length;
|
|
245
|
+
return Reflect.has(arr, key);
|
|
246
|
+
},
|
|
247
|
+
|
|
248
|
+
// Get entries which are in bounds.
|
|
249
|
+
get(arr, key) {
|
|
250
|
+
if (isIndex(key)) {
|
|
251
|
+
key *= 1;
|
|
252
|
+
if (key >= arr.length) return void 0;
|
|
253
|
+
return getElement(arr, key);
|
|
254
|
+
}
|
|
255
|
+
return Reflect.get(arr, key);
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
// Get descriptors which are in bounds.
|
|
259
|
+
getOwnPropertyDescriptor(arr, key) {
|
|
260
|
+
if (isIndex(key)) {
|
|
261
|
+
key *= 1;
|
|
262
|
+
if (key >= arr.length) return void 0;
|
|
263
|
+
// Cannot return `configurable: false` unfortunately
|
|
264
|
+
return { value: getElement(arr, key), writable: false, enumerable: true, configurable: true };
|
|
265
|
+
}
|
|
266
|
+
// Cannot return `writable: false` for `length` property unfortunately
|
|
267
|
+
return Reflect.getOwnPropertyDescriptor(arr, key);
|
|
268
|
+
},
|
|
269
|
+
|
|
270
|
+
// Prevent setting `length` or entries.
|
|
271
|
+
// Catches:
|
|
272
|
+
// * `Object.defineProperty(arr, 0, {value: null})`.
|
|
273
|
+
// * `arr[1] = null`.
|
|
274
|
+
// * `arr.length = 0`.
|
|
275
|
+
// * `Object.defineProperty(arr, 'length', {value: 0})`.
|
|
276
|
+
// * Other operations which mutate entries e.g. `arr.push(123)`.
|
|
277
|
+
defineProperty(arr, key, descriptor) {
|
|
278
|
+
if (key === 'length' || isIndex(key)) return false;
|
|
279
|
+
return Reflect.defineProperty(arr, key, descriptor);
|
|
280
|
+
},
|
|
281
|
+
|
|
282
|
+
// Prevent deleting entries.
|
|
283
|
+
deleteProperty(arr, key) {
|
|
284
|
+
// Note: `Reflect.deleteProperty(arr, 'length')` already returns `false`
|
|
285
|
+
if (isIndex(key)) return false;
|
|
286
|
+
return Reflect.deleteProperty(arr, key);
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
// Get keys, including element indexes.
|
|
290
|
+
ownKeys(arr) {
|
|
291
|
+
const keys = [];
|
|
292
|
+
for (let i = 0; i < arr.length; i++) {
|
|
293
|
+
keys.push(i + '');
|
|
294
|
+
}
|
|
295
|
+
keys.push(...Reflect.ownKeys(arr));
|
|
296
|
+
return keys;
|
|
297
|
+
},
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Check if a key is a valid array index.
|
|
302
|
+
* Only strings comprising a plain integer are valid indexes.
|
|
303
|
+
* e.g. `"-1"`, `"01"`, `"0xFF"`, `"1e1"`, `"1 "` are not valid indexes.
|
|
304
|
+
*
|
|
305
|
+
* @param {*} - Key used for property lookup.
|
|
306
|
+
* @returns {boolean} - `true` if `key` is a valid array index.
|
|
307
|
+
*/
|
|
308
|
+
function isIndex(key) {
|
|
309
|
+
// TODO: Any way to do this without a regex?
|
|
310
|
+
return typeof key === 'string' && (key === '0' || INDEX_REGEX.test(key));
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const INDEX_REGEX = /^[1-9]\d*$/;
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Convert value to integer.
|
|
317
|
+
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number#integer_conversion
|
|
318
|
+
*
|
|
319
|
+
* @param {*} value - Value to convert to integer.
|
|
320
|
+
* @returns {number} - Integer
|
|
321
|
+
*/
|
|
322
|
+
function toInt(value) {
|
|
323
|
+
value = Math.trunc(+value);
|
|
324
|
+
// `value === 0` check is to convert -0 to 0
|
|
325
|
+
if (value === 0 || Number.isNaN(value)) return 0;
|
|
326
|
+
return value;
|
|
327
|
+
}
|