oxc-parser 0.73.2 → 0.74.0
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/lazy-types.js +193 -0
- package/generated/deserialize/lazy-visit.js +5457 -0
- package/generated/deserialize/lazy.js +410 -11
- package/index.js +95 -18
- package/package.json +25 -21
- package/raw-transfer/{index.js → common.js} +101 -98
- package/raw-transfer/eager.js +42 -3
- package/raw-transfer/lazy-common.js +4 -1
- package/raw-transfer/lazy.js +95 -21
- package/raw-transfer/node-array.js +26 -11
- package/raw-transfer/supported.js +56 -0
- package/raw-transfer/visitor.js +127 -0
package/raw-transfer/lazy.js
CHANGED
|
@@ -1,14 +1,74 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { parseSyncRawImpl, parseAsyncRawImpl, returnBufferToCache } = require('./
|
|
3
|
+
const { parseSyncRawImpl, parseAsyncRawImpl, returnBufferToCache } = require('./common.js'),
|
|
4
|
+
{ TOKEN } = require('./lazy-common.js'),
|
|
5
|
+
constructLazyData = require('../generated/deserialize/lazy.js').construct,
|
|
6
|
+
walkProgram = require('../generated/deserialize/lazy-visit.js'),
|
|
7
|
+
{ Visitor, getVisitorsArr } = require('./visitor.js');
|
|
4
8
|
|
|
5
|
-
module.exports = { parseSyncLazy, parseAsyncLazy };
|
|
9
|
+
module.exports = { parseSyncLazy, parseAsyncLazy, Visitor };
|
|
6
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Parse JS/TS source synchronously on current thread.
|
|
13
|
+
*
|
|
14
|
+
* The data in buffer is not deserialized. Is deserialized to JS objects lazily, when accessing the
|
|
15
|
+
* properties of objects.
|
|
16
|
+
*
|
|
17
|
+
* e.g. `program` in returned object is an instance of `Program` class, with getters for `start`, `end`,
|
|
18
|
+
* `body` etc.
|
|
19
|
+
*
|
|
20
|
+
* Returned object contains a `visit` function which can be used to visit the AST with a `Visitor`
|
|
21
|
+
* (`Visitor` class can be obtained by calling `experimentalGetLazyVisitor()`).
|
|
22
|
+
*
|
|
23
|
+
* Returned object contains a `dispose` method. When finished with this AST, it's advisable to call
|
|
24
|
+
* `dispose`, to return the buffer to the cache, so it can be reused.
|
|
25
|
+
* Garbage collector should do this anyway at some point, but on an unpredictable schedule,
|
|
26
|
+
* so it's preferable to call `dispose` manually, to ensure the buffer can be reused immediately.
|
|
27
|
+
*
|
|
28
|
+
* @param {string} filename - Filename
|
|
29
|
+
* @param {string} sourceText - Source text of file
|
|
30
|
+
* @param {Object} options - Parsing options
|
|
31
|
+
* @returns {Object} - Object with property getters for `program`, `module`, `comments`, and `errors`,
|
|
32
|
+
* and `dispose` and `visit` methods
|
|
33
|
+
* @throws {Error} - If raw transfer is not supported on this platform
|
|
34
|
+
*/
|
|
7
35
|
function parseSyncLazy(filename, sourceText, options) {
|
|
36
|
+
let _;
|
|
37
|
+
({ experimentalLazy: _, ...options } = options);
|
|
8
38
|
return parseSyncRawImpl(filename, sourceText, options, construct);
|
|
9
39
|
}
|
|
10
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Parse JS/TS source asynchronously on a separate thread.
|
|
43
|
+
*
|
|
44
|
+
* The data in buffer is not deserialized. Is deserialized to JS objects lazily, when accessing the
|
|
45
|
+
* properties of objects.
|
|
46
|
+
*
|
|
47
|
+
* e.g. `program` in returned object is an instance of `Program` class, with getters for `start`, `end`,
|
|
48
|
+
* `body` etc.
|
|
49
|
+
*
|
|
50
|
+
* Because this function does not deserialize the AST, unlike `parseAsyncRaw`, very little work happens
|
|
51
|
+
* on current thread in this function. Deserialization work only occurs when properties of the objects
|
|
52
|
+
* are accessed.
|
|
53
|
+
*
|
|
54
|
+
* Returned object contains a `visit` function which can be used to visit the AST with a `Visitor`
|
|
55
|
+
* (`Visitor` class can be obtained by calling `experimentalGetLazyVisitor()`).
|
|
56
|
+
*
|
|
57
|
+
* Returned object contains a `dispose` method. When finished with this AST, it's advisable to call
|
|
58
|
+
* `dispose`, to return the buffer to the cache, so it can be reused.
|
|
59
|
+
* Garbage collector should do this anyway at some point, but on an unpredictable schedule,
|
|
60
|
+
* so it's preferable to call `dispose` manually, to ensure the buffer can be reused immediately.
|
|
61
|
+
*
|
|
62
|
+
* @param {string} filename - Filename
|
|
63
|
+
* @param {string} sourceText - Source text of file
|
|
64
|
+
* @param {Object} options - Parsing options
|
|
65
|
+
* @returns {Object} - Object with property getters for `program`, `module`, `comments`, and `errors`,
|
|
66
|
+
* and `dispose` and `visit` methods
|
|
67
|
+
* @throws {Error} - If raw transfer is not supported on this platform
|
|
68
|
+
*/
|
|
11
69
|
function parseAsyncLazy(filename, sourceText, options) {
|
|
70
|
+
let _;
|
|
71
|
+
({ experimentalLazy: _, ...options } = options);
|
|
12
72
|
return parseAsyncRawImpl(filename, sourceText, options, construct);
|
|
13
73
|
}
|
|
14
74
|
|
|
@@ -23,15 +83,18 @@ const bufferRecycleRegistry = typeof FinalizationRegistry === 'undefined'
|
|
|
23
83
|
? null
|
|
24
84
|
: new FinalizationRegistry(returnBufferToCache);
|
|
25
85
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Get an object with getters which lazy deserialize AST and other data from buffer.
|
|
88
|
+
*
|
|
89
|
+
* Object also includes `dispose` and `visit` functions.
|
|
90
|
+
*
|
|
91
|
+
* @param {Uint8Array} buffer - Buffer containing AST in raw form
|
|
92
|
+
* @param {string} sourceText - Source for the file
|
|
93
|
+
* @param {number} sourceByteLen - Length of source text in UTF-8 bytes
|
|
94
|
+
* @returns {Object} - Object with property getters for `program`, `module`, `comments`, and `errors`,
|
|
95
|
+
* and `dispose` and `visit` methods
|
|
96
|
+
*/
|
|
29
97
|
function construct(buffer, sourceText, sourceLen) {
|
|
30
|
-
// Lazy load deserializer, and get `TOKEN` to store in `ast` objects
|
|
31
|
-
if (constructLazyData === null) {
|
|
32
|
-
({ construct: constructLazyData, TOKEN } = require('../generated/deserialize/lazy.js'));
|
|
33
|
-
}
|
|
34
|
-
|
|
35
98
|
// Create AST object
|
|
36
99
|
const sourceIsAscii = sourceText.length === sourceLen;
|
|
37
100
|
const ast = { buffer, sourceText, sourceLen, sourceIsAscii, nodes: new Map(), token: TOKEN };
|
|
@@ -57,21 +120,32 @@ function construct(buffer, sourceText, sourceLen) {
|
|
|
57
120
|
return data.errors;
|
|
58
121
|
},
|
|
59
122
|
dispose: dispose.bind(null, ast),
|
|
123
|
+
visit(visitor) {
|
|
124
|
+
// (2 * 1024 * 1024 * 1024 - 16) >> 2
|
|
125
|
+
const metadataPos32 = 536870908;
|
|
126
|
+
const pos = buffer.uint32[metadataPos32];
|
|
127
|
+
walkProgram(pos, ast, getVisitorsArr(visitor));
|
|
128
|
+
},
|
|
60
129
|
};
|
|
61
130
|
}
|
|
62
131
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
132
|
+
/**
|
|
133
|
+
* Dispose of this AST.
|
|
134
|
+
*
|
|
135
|
+
* After calling this method, trying to read any nodes from this AST may cause an error.
|
|
136
|
+
*
|
|
137
|
+
* Buffer is returned to the cache to be reused.
|
|
138
|
+
*
|
|
139
|
+
* The buffer would be returned to the cache anyway, once all nodes of the AST are garbage collected,
|
|
140
|
+
* but calling `dispose` is preferable, as it will happen immediately.
|
|
141
|
+
* Otherwise, garbage collector may take time to collect the `ast` object, and new buffers may be created
|
|
142
|
+
* in the meantime, when we could have reused this one.
|
|
143
|
+
*
|
|
144
|
+
* @param {Object} ast - AST object containing buffer etc
|
|
145
|
+
* @returns {undefined}
|
|
146
|
+
*/
|
|
73
147
|
function dispose(ast) {
|
|
74
|
-
// Return buffer to cache to be reused
|
|
148
|
+
// Return buffer to cache, to be reused
|
|
75
149
|
returnBufferToCache(ast.buffer);
|
|
76
150
|
|
|
77
151
|
// Remove connection between `ast` and the buffer
|
|
@@ -12,12 +12,14 @@ const nodeArrays = new WeakMap();
|
|
|
12
12
|
// Function to get element from an array. Initialized in class static block below.
|
|
13
13
|
let getElement;
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
/**
|
|
16
|
+
* An array of AST nodes where elements are deserialized lazily upon access.
|
|
17
|
+
*
|
|
18
|
+
* Extends `Array` to make `Array.isArray` return `true` for a `NodeArray`.
|
|
19
|
+
*
|
|
20
|
+
* TODO: Other methods could maybe be more optimal, avoiding going via proxy multiple times
|
|
21
|
+
* e.g. `some`, `indexOf`.
|
|
22
|
+
*/
|
|
21
23
|
class NodeArray extends Array {
|
|
22
24
|
#internal;
|
|
23
25
|
|
|
@@ -80,7 +82,14 @@ class NodeArray extends Array {
|
|
|
80
82
|
// Defining dummy method here to prevent the later assignment altering the shape of class prototype.
|
|
81
83
|
[Symbol.iterator]() {}
|
|
82
84
|
|
|
83
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Override `slice` method to return a `NodeArray`.
|
|
87
|
+
*
|
|
88
|
+
* @this {NodeArray}
|
|
89
|
+
* @param {*} start - Start of slice
|
|
90
|
+
* @param {*} end - End of slice
|
|
91
|
+
* @returns {NodeArray} - `NodeArray` containing slice of this one
|
|
92
|
+
*/
|
|
84
93
|
slice(start, end) {
|
|
85
94
|
// Get actual `NodeArray`. `this` is a proxy.
|
|
86
95
|
const arr = nodeArrays.get(this);
|
|
@@ -142,8 +151,10 @@ NodeArray.prototype[Symbol.iterator] = NodeArray.prototype.values;
|
|
|
142
151
|
|
|
143
152
|
module.exports = NodeArray;
|
|
144
153
|
|
|
145
|
-
|
|
146
|
-
|
|
154
|
+
/**
|
|
155
|
+
* Iterator over values of a `NodeArray`.
|
|
156
|
+
* Returned by `values` method, and also used as iterator for `for (const node of nodeArray) {}`.
|
|
157
|
+
*/
|
|
147
158
|
class NodeArrayValuesIterator {
|
|
148
159
|
#internal;
|
|
149
160
|
|
|
@@ -173,7 +184,9 @@ class NodeArrayValuesIterator {
|
|
|
173
184
|
}
|
|
174
185
|
}
|
|
175
186
|
|
|
176
|
-
|
|
187
|
+
/**
|
|
188
|
+
* Iterator over keys of a `NodeArray`. Returned by `keys` method.
|
|
189
|
+
*/
|
|
177
190
|
class NodeArrayKeysIterator {
|
|
178
191
|
#internal;
|
|
179
192
|
|
|
@@ -196,7 +209,9 @@ class NodeArrayKeysIterator {
|
|
|
196
209
|
}
|
|
197
210
|
}
|
|
198
211
|
|
|
199
|
-
|
|
212
|
+
/**
|
|
213
|
+
* Iterator over values of a `NodeArray`. Returned by `entries` method.
|
|
214
|
+
*/
|
|
200
215
|
class NodeArrayEntriesIterator {
|
|
201
216
|
#internal;
|
|
202
217
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const rawTransferSupportedBinding = require('../bindings.js').rawTransferSupported;
|
|
4
|
+
|
|
5
|
+
module.exports = rawTransferSupported;
|
|
6
|
+
|
|
7
|
+
let rawTransferIsSupported = null;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Returns `true` if `experimentalRawTransfer` is option is supported.
|
|
11
|
+
*
|
|
12
|
+
* Raw transfer is only supported on 64-bit little-endian systems,
|
|
13
|
+
* and NodeJS >= v22.0.0 or Deno >= v2.0.0.
|
|
14
|
+
*
|
|
15
|
+
* Versions of NodeJS prior to v22.0.0 do not support creating an `ArrayBuffer` larger than 4 GiB.
|
|
16
|
+
* Bun (as at v1.2.4) also does not support creating an `ArrayBuffer` larger than 4 GiB.
|
|
17
|
+
* Support on Deno v1 is unknown and it's EOL, so treating Deno before v2.0.0 as unsupported.
|
|
18
|
+
*
|
|
19
|
+
* No easy way to determining pointer width (64 bit or 32 bit) in JS,
|
|
20
|
+
* so call a function on Rust side to find out.
|
|
21
|
+
*
|
|
22
|
+
* @returns {boolean} - `true` if raw transfer is supported on this platform
|
|
23
|
+
*/
|
|
24
|
+
function rawTransferSupported() {
|
|
25
|
+
if (rawTransferIsSupported === null) {
|
|
26
|
+
rawTransferIsSupported = rawTransferRuntimeSupported() && rawTransferSupportedBinding();
|
|
27
|
+
}
|
|
28
|
+
return rawTransferIsSupported;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Checks copied from:
|
|
32
|
+
// https://github.com/unjs/std-env/blob/ab15595debec9e9115a9c1d31bc7597a8e71dbfd/src/runtimes.ts
|
|
33
|
+
// MIT license: https://github.com/unjs/std-env/blob/ab15595debec9e9115a9c1d31bc7597a8e71dbfd/LICENCE
|
|
34
|
+
function rawTransferRuntimeSupported() {
|
|
35
|
+
let global;
|
|
36
|
+
try {
|
|
37
|
+
global = globalThis;
|
|
38
|
+
} catch (e) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const isBun = !!global.Bun || !!global.process?.versions?.bun;
|
|
43
|
+
if (isBun) return false;
|
|
44
|
+
|
|
45
|
+
const isDeno = !!global.Deno;
|
|
46
|
+
if (isDeno) {
|
|
47
|
+
const match = Deno.version?.deno?.match(/^(\d+)\./);
|
|
48
|
+
return !!match && match[1] * 1 >= 2;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const isNode = global.process?.release?.name === 'node';
|
|
52
|
+
if (!isNode) return false;
|
|
53
|
+
|
|
54
|
+
const match = process.version?.match(/^v(\d+)\./);
|
|
55
|
+
return !!match && match[1] * 1 >= 22;
|
|
56
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { NODE_TYPE_IDS_MAP, LEAF_NODES_COUNT } = require('../generated/deserialize/lazy-types.js');
|
|
4
|
+
|
|
5
|
+
const NODE_TYPES_COUNT = NODE_TYPE_IDS_MAP.size;
|
|
6
|
+
|
|
7
|
+
// Getter for private `#visitorsArr` property of `Visitor` class. Initialized in class body below.
|
|
8
|
+
let getVisitorsArr;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Visitor class, used to visit an AST.
|
|
12
|
+
*/
|
|
13
|
+
class Visitor {
|
|
14
|
+
#visitorsArr;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Create `Visitor`.
|
|
18
|
+
*
|
|
19
|
+
* Provide an object where keys are names of AST nodes you want to visit,
|
|
20
|
+
* and values are visitor functions which receive AST node objects of that type.
|
|
21
|
+
*
|
|
22
|
+
* Keys can also be postfixed with `:exit` to visit when exiting the node, rather than entering.
|
|
23
|
+
*
|
|
24
|
+
* ```js
|
|
25
|
+
* const visitor = new Visitor({
|
|
26
|
+
* BinaryExpression(binExpr) {
|
|
27
|
+
* // Do stuff when entering a `BinaryExpression`
|
|
28
|
+
* },
|
|
29
|
+
* 'BinaryExpression:exit'(binExpr) {
|
|
30
|
+
* // Do stuff when exiting a `BinaryExpression`
|
|
31
|
+
* },
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* @constructor
|
|
36
|
+
* @param {Object} visitor - Object defining visit functions for AST nodes
|
|
37
|
+
* @returns {Visitor}
|
|
38
|
+
*/
|
|
39
|
+
constructor(visitor) {
|
|
40
|
+
this.#visitorsArr = createVisitorsArr(visitor);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static {
|
|
44
|
+
getVisitorsArr = visitor => visitor.#visitorsArr;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = { Visitor, getVisitorsArr };
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Create array of visitors, keyed by node type ID.
|
|
52
|
+
*
|
|
53
|
+
* Each element of array is one of:
|
|
54
|
+
*
|
|
55
|
+
* * No visitor for this type = `null`.
|
|
56
|
+
* * Visitor for leaf node = visit function.
|
|
57
|
+
* * Visitor for non-leaf node = object of form `{ enter, exit }`,
|
|
58
|
+
* where each property is either a visitor function or `null`.
|
|
59
|
+
*
|
|
60
|
+
* @param {Object} visitor - Visitors object from user
|
|
61
|
+
* @returns {Array<Object|function|null>} - Array of visitors
|
|
62
|
+
*/
|
|
63
|
+
function createVisitorsArr(visitor) {
|
|
64
|
+
if (visitor === null || typeof visitor !== 'object') {
|
|
65
|
+
throw new Error('`visitors` must be an object');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Create empty visitors array
|
|
69
|
+
const visitorsArr = [];
|
|
70
|
+
for (let i = 0; i < NODE_TYPES_COUNT; i++) {
|
|
71
|
+
visitorsArr.push(null);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Populate visitors array from provided object
|
|
75
|
+
for (let name of Object.keys(visitor)) {
|
|
76
|
+
const visitFn = visitor[name];
|
|
77
|
+
if (typeof visitFn !== 'function') {
|
|
78
|
+
throw new Error(`'${name}' property of \`visitors\` object is not a function`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const isExit = name.endsWith(':exit');
|
|
82
|
+
if (isExit) name = name.slice(0, -5);
|
|
83
|
+
|
|
84
|
+
const typeId = NODE_TYPE_IDS_MAP.get(name);
|
|
85
|
+
if (typeId === void 0) throw new Error(`Unknown node type '${name}' in \`visitors\` object`);
|
|
86
|
+
|
|
87
|
+
if (typeId < LEAF_NODES_COUNT) {
|
|
88
|
+
// Leaf node. Store just 1 function.
|
|
89
|
+
const existingVisitFn = visitorsArr[typeId];
|
|
90
|
+
if (existingVisitFn === null) {
|
|
91
|
+
visitorsArr[typeId] = visitFn;
|
|
92
|
+
} else if (isExit) {
|
|
93
|
+
visitorsArr[typeId] = combineVisitFunctions(existingVisitFn, visitFn);
|
|
94
|
+
} else {
|
|
95
|
+
visitorsArr[typeId] = combineVisitFunctions(visitFn, existingVisitFn);
|
|
96
|
+
}
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
let enterExit = visitorsArr[typeId];
|
|
101
|
+
if (enterExit === null) {
|
|
102
|
+
enterExit = visitorsArr[typeId] = { enter: null, exit: null };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (isExit) {
|
|
106
|
+
enterExit.exit = visitFn;
|
|
107
|
+
} else {
|
|
108
|
+
enterExit.enter = visitFn;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return visitorsArr;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Combine 2 visitor functions into 1.
|
|
117
|
+
*
|
|
118
|
+
* @param {function} visit1 - 1st visitor function
|
|
119
|
+
* @param {function} visit2 - 2nd visitor function
|
|
120
|
+
* @returns {function} - Combined visitor function
|
|
121
|
+
*/
|
|
122
|
+
function combineVisitFunctions(visit1, visit2) {
|
|
123
|
+
return function(node) {
|
|
124
|
+
visit1(node);
|
|
125
|
+
visit2(node);
|
|
126
|
+
};
|
|
127
|
+
}
|