Skip to content

Commit 93069a5

Browse files
committed
feat(napi/parser): add experimental lazy visitor (#11837)
Add lazy visitor to `oxc-parser`. This API is experimental and not ready for use yet. AST is not ESTree-compatible at present (same as lazy deserializer). Usage: ```js import { parseSync, experimentalGetLazyVisitor } from 'oxc-parser'; // Get `Visitor` class. // `Visitor` is not exported directly from `oxc-parser` as all code // related to lazy deserialization is lazy-loaded. const Visitor = experimentalGetLazyVisitor(); // Define visitor. // This visitor counts how many identifiers in AST. let identCount = 0; const visitor = new Visitor({ BindingIdentifier(ident) { identCount++; }, IdentifierReference(ident) { identCount++; }, IdentifierName(ident) { identCount++; }, }); const code = 'let x, y; x = { z: 2 };'; const { visit, dispose } = parseSync('test.js', code, { experimentalLazy: true }); visit(visitor); assert(identCount === 4); // Dispose of AST now we're done with it. // This returns buffer storing AST data to the pool for reuse. dispose(); ```
1 parent 0260308 commit 93069a5

File tree

10 files changed

+6714
-42
lines changed

10 files changed

+6714
-42
lines changed

.github/generated/ast_changes_watch_list.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ src:
6161
- 'crates/oxc_syntax/src/symbol.rs'
6262
- 'crates/oxc_traverse/src/generated/scopes_collector.rs'
6363
- 'napi/parser/generated/deserialize/js.js'
64+
- 'napi/parser/generated/deserialize/lazy-types.js'
65+
- 'napi/parser/generated/deserialize/lazy-visit.js'
6466
- 'napi/parser/generated/deserialize/lazy.js'
6567
- 'napi/parser/generated/deserialize/ts.js'
6668
- 'napi/parser/src/generated/assert_layouts.rs'

napi/parser/bench.bench.mjs

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import { bench, describe } from 'vitest';
44
import bindings from './bindings.js';
55
import deserializeJS from './generated/deserialize/js.js';
66
import deserializeTS from './generated/deserialize/ts.js';
7-
import { parseAsync, parseSync } from './index.js';
8-
import { isJsAst, prepareRaw } from './raw-transfer/common.js';
7+
import { experimentalGetLazyVisitor, parseAsync, parseSync } from './index.js';
8+
import { isJsAst, prepareRaw, returnBufferToCache } from './raw-transfer/common.js';
99

1010
// Same fixtures as used in Rust parser benchmarks
1111
let fixtureUrls = [
@@ -78,6 +78,12 @@ for (const { filename, code } of fixtures) {
7878
const { program, comments, module, errors } = ret;
7979
});
8080

81+
benchRaw('parser_napi_raw_no_deser', () => {
82+
const { buffer, sourceByteLen } = prepareRaw(code);
83+
bindings.parseSyncRaw(filename, buffer, sourceByteLen, {});
84+
returnBufferToCache(buffer);
85+
});
86+
8187
// Prepare buffer but don't deserialize
8288
const { buffer, sourceByteLen } = prepareRaw(code);
8389
bindings.parseSyncRaw(filename, buffer, sourceByteLen, {});
@@ -86,5 +92,72 @@ for (const { filename, code } of fixtures) {
8692
benchRaw('parser_napi_raw_deser_only', () => {
8793
deserialize(buffer, code, sourceByteLen);
8894
});
95+
96+
// Create visitors
97+
const Visitor = experimentalGetLazyVisitor();
98+
99+
let debuggerCount = 0;
100+
const debuggerVisitor = new Visitor({
101+
DebuggerStatement(debuggerStmt) {
102+
debuggerCount++;
103+
},
104+
});
105+
106+
let identCount = 0;
107+
const identVisitor = new Visitor({
108+
BindingIdentifier(ident) {
109+
identCount++;
110+
},
111+
IdentifierReference(ident) {
112+
identCount++;
113+
},
114+
IdentifierName(ident) {
115+
identCount++;
116+
},
117+
});
118+
119+
benchRaw('parser_napi_raw_lazy_visit(debugger)', () => {
120+
const { visit, dispose } = parseSync(filename, code, { experimentalLazy: true });
121+
debuggerCount = 0;
122+
visit(debuggerVisitor);
123+
dispose();
124+
});
125+
126+
benchRaw('parser_napi_raw_lazy_visit(ident)', () => {
127+
const { visit, dispose } = parseSync(filename, code, { experimentalLazy: true });
128+
identCount = 0;
129+
visit(identVisitor);
130+
dispose();
131+
});
132+
133+
benchRaw('parser_napi_raw_lazy_visitor(debugger)', () => {
134+
const { visit, dispose } = parseSync(filename, code, { experimentalLazy: true });
135+
debuggerCount = 0;
136+
const debuggerVisitor = new Visitor({
137+
DebuggerStatement(debuggerStmt) {
138+
debuggerCount++;
139+
},
140+
});
141+
visit(debuggerVisitor);
142+
dispose();
143+
});
144+
145+
benchRaw('parser_napi_raw_lazy_visitor(ident)', () => {
146+
const { visit, dispose } = parseSync(filename, code, { experimentalLazy: true });
147+
identCount = 0;
148+
const identVisitor = new Visitor({
149+
BindingIdentifier(ident) {
150+
identCount++;
151+
},
152+
IdentifierReference(ident) {
153+
identCount++;
154+
},
155+
IdentifierName(ident) {
156+
identCount++;
157+
},
158+
});
159+
visit(identVisitor);
160+
dispose();
161+
});
89162
});
90163
}
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
// Auto-generated code, DO NOT EDIT DIRECTLY!
2+
// To edit this generated file you have to edit `tasks/ast_tools/src/generators/raw_transfer_lazy.rs`.
3+
4+
'use strict';
5+
6+
// Mapping from node type name to node type ID
7+
const NODE_TYPE_IDS_MAP = new Map([
8+
// Leaf nodes
9+
['IdentifierName', 0],
10+
['IdentifierReference', 1],
11+
['BindingIdentifier', 2],
12+
['LabelIdentifier', 3],
13+
['ThisExpression', 4],
14+
['Elision', 5],
15+
['TemplateElement', 6],
16+
['Super', 7],
17+
['Hashbang', 8],
18+
['EmptyStatement', 9],
19+
['DebuggerStatement', 10],
20+
['PrivateIdentifier', 11],
21+
['BooleanLiteral', 12],
22+
['NullLiteral', 13],
23+
['NumericLiteral', 14],
24+
['StringLiteral', 15],
25+
['BigIntLiteral', 16],
26+
['RegExpLiteral', 17],
27+
['JSXOpeningFragment', 18],
28+
['JSXClosingFragment', 19],
29+
['JSXEmptyExpression', 20],
30+
['JSXIdentifier', 21],
31+
['JSXText', 22],
32+
['TSAnyKeyword', 23],
33+
['TSStringKeyword', 24],
34+
['TSBooleanKeyword', 25],
35+
['TSNumberKeyword', 26],
36+
['TSNeverKeyword', 27],
37+
['TSIntrinsicKeyword', 28],
38+
['TSUnknownKeyword', 29],
39+
['TSNullKeyword', 30],
40+
['TSUndefinedKeyword', 31],
41+
['TSVoidKeyword', 32],
42+
['TSSymbolKeyword', 33],
43+
['TSThisType', 34],
44+
['TSObjectKeyword', 35],
45+
['TSBigIntKeyword', 36],
46+
['JSDocUnknownType', 37],
47+
// Non-leaf nodes
48+
['Program', 38],
49+
['ArrayExpression', 39],
50+
['ObjectExpression', 40],
51+
['ObjectProperty', 41],
52+
['TemplateLiteral', 42],
53+
['TaggedTemplateExpression', 43],
54+
['ComputedMemberExpression', 44],
55+
['StaticMemberExpression', 45],
56+
['PrivateFieldExpression', 46],
57+
['CallExpression', 47],
58+
['NewExpression', 48],
59+
['MetaProperty', 49],
60+
['SpreadElement', 50],
61+
['UpdateExpression', 51],
62+
['UnaryExpression', 52],
63+
['BinaryExpression', 53],
64+
['PrivateInExpression', 54],
65+
['LogicalExpression', 55],
66+
['ConditionalExpression', 56],
67+
['AssignmentExpression', 57],
68+
['ArrayAssignmentTarget', 58],
69+
['ObjectAssignmentTarget', 59],
70+
['AssignmentTargetWithDefault', 60],
71+
['AssignmentTargetPropertyIdentifier', 61],
72+
['AssignmentTargetPropertyProperty', 62],
73+
['SequenceExpression', 63],
74+
['AwaitExpression', 64],
75+
['ChainExpression', 65],
76+
['ParenthesizedExpression', 66],
77+
['BlockStatement', 67],
78+
['VariableDeclaration', 68],
79+
['VariableDeclarator', 69],
80+
['ExpressionStatement', 70],
81+
['IfStatement', 71],
82+
['DoWhileStatement', 72],
83+
['WhileStatement', 73],
84+
['ForStatement', 74],
85+
['ForInStatement', 75],
86+
['ForOfStatement', 76],
87+
['ContinueStatement', 77],
88+
['BreakStatement', 78],
89+
['ReturnStatement', 79],
90+
['WithStatement', 80],
91+
['SwitchStatement', 81],
92+
['SwitchCase', 82],
93+
['LabeledStatement', 83],
94+
['ThrowStatement', 84],
95+
['TryStatement', 85],
96+
['CatchClause', 86],
97+
['AssignmentPattern', 87],
98+
['ObjectPattern', 88],
99+
['BindingProperty', 89],
100+
['ArrayPattern', 90],
101+
['Function', 91],
102+
['FormalParameters', 92],
103+
['FunctionBody', 93],
104+
['ArrowFunctionExpression', 94],
105+
['YieldExpression', 95],
106+
['Class', 96],
107+
['ClassBody', 97],
108+
['MethodDefinition', 98],
109+
['PropertyDefinition', 99],
110+
['StaticBlock', 100],
111+
['AccessorProperty', 101],
112+
['ImportExpression', 102],
113+
['ImportDeclaration', 103],
114+
['ImportSpecifier', 104],
115+
['ImportDefaultSpecifier', 105],
116+
['ImportNamespaceSpecifier', 106],
117+
['ImportAttribute', 107],
118+
['ExportNamedDeclaration', 108],
119+
['ExportDefaultDeclaration', 109],
120+
['ExportAllDeclaration', 110],
121+
['ExportSpecifier', 111],
122+
['V8IntrinsicExpression', 112],
123+
['JSXElement', 113],
124+
['JSXOpeningElement', 114],
125+
['JSXClosingElement', 115],
126+
['JSXFragment', 116],
127+
['JSXNamespacedName', 117],
128+
['JSXMemberExpression', 118],
129+
['JSXExpressionContainer', 119],
130+
['JSXAttribute', 120],
131+
['JSXSpreadAttribute', 121],
132+
['JSXSpreadChild', 122],
133+
['TSEnumDeclaration', 123],
134+
['TSEnumBody', 124],
135+
['TSEnumMember', 125],
136+
['TSTypeAnnotation', 126],
137+
['TSLiteralType', 127],
138+
['TSConditionalType', 128],
139+
['TSUnionType', 129],
140+
['TSIntersectionType', 130],
141+
['TSParenthesizedType', 131],
142+
['TSTypeOperator', 132],
143+
['TSArrayType', 133],
144+
['TSIndexedAccessType', 134],
145+
['TSTupleType', 135],
146+
['TSNamedTupleMember', 136],
147+
['TSOptionalType', 137],
148+
['TSRestType', 138],
149+
['TSTypeReference', 139],
150+
['TSQualifiedName', 140],
151+
['TSTypeParameterInstantiation', 141],
152+
['TSTypeParameter', 142],
153+
['TSTypeParameterDeclaration', 143],
154+
['TSTypeAliasDeclaration', 144],
155+
['TSClassImplements', 145],
156+
['TSInterfaceDeclaration', 146],
157+
['TSInterfaceBody', 147],
158+
['TSPropertySignature', 148],
159+
['TSIndexSignature', 149],
160+
['TSCallSignatureDeclaration', 150],
161+
['TSMethodSignature', 151],
162+
['TSConstructSignatureDeclaration', 152],
163+
['TSIndexSignatureName', 153],
164+
['TSInterfaceHeritage', 154],
165+
['TSTypePredicate', 155],
166+
['TSModuleDeclaration', 156],
167+
['TSModuleBlock', 157],
168+
['TSTypeLiteral', 158],
169+
['TSInferType', 159],
170+
['TSTypeQuery', 160],
171+
['TSImportType', 161],
172+
['TSFunctionType', 162],
173+
['TSConstructorType', 163],
174+
['TSMappedType', 164],
175+
['TSTemplateLiteralType', 165],
176+
['TSAsExpression', 166],
177+
['TSSatisfiesExpression', 167],
178+
['TSTypeAssertion', 168],
179+
['TSImportEqualsDeclaration', 169],
180+
['TSExternalModuleReference', 170],
181+
['TSNonNullExpression', 171],
182+
['Decorator', 172],
183+
['TSExportAssignment', 173],
184+
['TSNamespaceExportDeclaration', 174],
185+
['TSInstantiationExpression', 175],
186+
['JSDocNullableType', 176],
187+
['JSDocNonNullableType', 177],
188+
]);
189+
190+
// Number of AST node types which are leaf nodes
191+
const LEAF_NODES_COUNT = 38;
192+
193+
module.exports = { NODE_TYPE_IDS_MAP, LEAF_NODES_COUNT };

0 commit comments

Comments
 (0)