Skip to content

Commit fe0e3dd

Browse files
Hans Halversonfacebook-github-bot
authored andcommitted
Generate Hermes AST visitor keys from AST definitions
Summary: We will need to traverse the Hermes AST in order to convert it to a proper ESTree or Babel AST. Let's autogenerate the information needed to traverse the Hermes AST directly from the Hermes AST definitions similar to how this is done for the JS builder methods. We can use the C preprocessor to generate a map of the visitor keys in JS, mapping from the node's type to an object containing all of its node and node list children (string, boolean, and numeric fields are ignored). This allows us efficiently traverse the AST by looking up the visitor keys for each node when it is visited, and subsequently visiting the node child or all of the node list children. Added autogeneration of these visitor keys to the `build.sh` script, being sure to format and then sign the generated file. Also included a simple implementation of traversing the AST using these visitor keys in `HermesASTVisitor`. This is not yet used, but will be a foundation for the handful of conversions we need to do to modify the AST to be Babel or ESTree compliant. Reviewed By: avp Differential Revision: D23768640 fbshipit-source-id: 6ec34b7505075d3b3298c13f27f385c97e766d15
1 parent d954c8f commit fe0e3dd

4 files changed

Lines changed: 327 additions & 0 deletions

File tree

tools/hermes-parser/js/scripts/build.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,13 @@ else
2222
WASM_PARSER="$1"
2323
fi
2424

25+
# Use internal FB build or pass path to include path as second command line argument
26+
FB_GET_INCLUDE_PATH="$THIS_DIR/facebook/getIncludePath.sh"
27+
if [[ -f "$FB_GET_INCLUDE_PATH" ]]; then
28+
INCLUDE_PATH=$("$FB_GET_INCLUDE_PATH")
29+
else
30+
INCLUDE_PATH="$2"
31+
fi
32+
2533
node "$THIS_DIR/genWasmParser.js" "$WASM_PARSER"
34+
node "$THIS_DIR/genVisitorKeys.js" "$INCLUDE_PATH"
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @format
8+
*/
9+
10+
'use strict';
11+
12+
const SignedSource = require('signedsource');
13+
14+
const fs = require('fs');
15+
const path = require('path');
16+
17+
const {execSync} = require('child_process');
18+
19+
const OUTPUT_FILE = path.resolve(__dirname, '../build/HermesASTVisitorKeys.js');
20+
const TEMPLATE_FILE = path.resolve(
21+
__dirname,
22+
'templates/HermesASTVisitorKeys.template',
23+
);
24+
25+
// Create visitor keys file
26+
const visitorKeys = execSync(
27+
`cpp -P -I "${process.argv[2]}" "${TEMPLATE_FILE}"`,
28+
);
29+
const visitorKeysFileContents = `/**
30+
* Copyright (c) Facebook, Inc. and its affiliates.
31+
*
32+
* This source code is licensed under the MIT license found in the
33+
* LICENSE file in the root directory of this source tree.
34+
*
35+
* This file was generated by tools/hermes-parser/js/scripts/build.sh
36+
*
37+
* ${SignedSource.getSigningToken()}
38+
*
39+
* @format
40+
*/
41+
42+
'use strict';
43+
44+
const HERMES_AST_VISITOR_KEYS = {};
45+
const NODE_CHILD = 'Node';
46+
const NODE_LIST_CHILD = 'NodeList';
47+
48+
${visitorKeys}
49+
50+
module.exports = {
51+
HERMES_AST_VISITOR_KEYS,
52+
NODE_CHILD,
53+
NODE_LIST_CHILD,
54+
};
55+
`;
56+
57+
// Format then sign file and write to disk
58+
const formattedContents = execSync('prettier', {
59+
input: visitorKeysFileContents,
60+
}).toString();
61+
fs.writeFileSync(OUTPUT_FILE, SignedSource.signFile(formattedContents));
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
// String, boolean, and number children are leaf nodes in AST and do not need to be visited
9+
#define NodeLabel(ARG)
10+
#define NodeBoolean(ARG)
11+
#define NodeNumber(ARG)
12+
13+
// Mark node child as single child node that can be visited
14+
#define NodePtr(ARG) ARG: NODE_CHILD,
15+
16+
// Mark node list children as multiple child nodes that can be visited
17+
#define NodeList(ARG) ARG: NODE_LIST_CHILD,
18+
19+
#define ESTREE_NODE_0_ARGS( \
20+
NAME, \
21+
BASE) \
22+
HERMES_AST_VISITOR_KEYS[#NAME] = {};
23+
24+
#define ESTREE_NODE_1_ARGS( \
25+
NAME, \
26+
BASE, \
27+
ARG0TY, \
28+
ARG0NM, \
29+
ARG0OPT) \
30+
HERMES_AST_VISITOR_KEYS[#NAME] = { \
31+
ARG0TY(ARG0NM) \
32+
};
33+
34+
#define ESTREE_NODE_2_ARGS( \
35+
NAME, \
36+
BASE, \
37+
ARG0TY, \
38+
ARG0NM, \
39+
ARG0OPT, \
40+
ARG1TY, \
41+
ARG1NM, \
42+
ARG1PT) \
43+
HERMES_AST_VISITOR_KEYS[#NAME] = { \
44+
ARG0TY(ARG0NM) \
45+
ARG1TY(ARG1NM) \
46+
};
47+
48+
#define ESTREE_NODE_3_ARGS( \
49+
NAME, \
50+
BASE, \
51+
ARG0TY, \
52+
ARG0NM, \
53+
ARG0OPT, \
54+
ARG1TY, \
55+
ARG1NM, \
56+
ARG1PT, \
57+
ARG2TY, \
58+
ARG2NM, \
59+
ARG2PT) \
60+
HERMES_AST_VISITOR_KEYS[#NAME] = { \
61+
ARG0TY(ARG0NM) \
62+
ARG1TY(ARG1NM) \
63+
ARG2TY(ARG2NM) \
64+
};
65+
66+
#define ESTREE_NODE_4_ARGS( \
67+
NAME, \
68+
BASE, \
69+
ARG0TY, \
70+
ARG0NM, \
71+
ARG0OPT, \
72+
ARG1TY, \
73+
ARG1NM, \
74+
ARG1PT, \
75+
ARG2TY, \
76+
ARG2NM, \
77+
ARG2PT, \
78+
ARG3TY, \
79+
ARG3NM, \
80+
ARG3PT) \
81+
HERMES_AST_VISITOR_KEYS[#NAME] = { \
82+
ARG0TY(ARG0NM) \
83+
ARG1TY(ARG1NM) \
84+
ARG2TY(ARG2NM) \
85+
ARG3TY(ARG3NM) \
86+
};
87+
88+
#define ESTREE_NODE_5_ARGS( \
89+
NAME, \
90+
BASE, \
91+
ARG0TY, \
92+
ARG0NM, \
93+
ARG0OPT, \
94+
ARG1TY, \
95+
ARG1NM, \
96+
ARG1PT, \
97+
ARG2TY, \
98+
ARG2NM, \
99+
ARG2PT, \
100+
ARG3TY, \
101+
ARG3NM, \
102+
ARG3PT, \
103+
ARG4TY, \
104+
ARG4NM, \
105+
ARG4PT) \
106+
HERMES_AST_VISITOR_KEYS[#NAME] = { \
107+
ARG0TY(ARG0NM) \
108+
ARG1TY(ARG1NM) \
109+
ARG2TY(ARG2NM) \
110+
ARG3TY(ARG3NM) \
111+
ARG4TY(ARG4NM) \
112+
};
113+
114+
#define ESTREE_NODE_6_ARGS( \
115+
NAME, \
116+
BASE, \
117+
ARG0TY, \
118+
ARG0NM, \
119+
ARG0OPT, \
120+
ARG1TY, \
121+
ARG1NM, \
122+
ARG1PT, \
123+
ARG2TY, \
124+
ARG2NM, \
125+
ARG2PT, \
126+
ARG3TY, \
127+
ARG3NM, \
128+
ARG3PT, \
129+
ARG4TY, \
130+
ARG4NM, \
131+
ARG4PT, \
132+
ARG5TY, \
133+
ARG5NM, \
134+
ARG5PT) \
135+
HERMES_AST_VISITOR_KEYS[#NAME] = { \
136+
ARG0TY(ARG0NM) \
137+
ARG1TY(ARG1NM) \
138+
ARG2TY(ARG2NM) \
139+
ARG3TY(ARG3NM) \
140+
ARG4TY(ARG4NM) \
141+
ARG5TY(ARG5NM) \
142+
};
143+
144+
#define ESTREE_NODE_7_ARGS( \
145+
NAME, \
146+
BASE, \
147+
ARG0TY, \
148+
ARG0NM, \
149+
ARG0OPT, \
150+
ARG1TY, \
151+
ARG1NM, \
152+
ARG1PT, \
153+
ARG2TY, \
154+
ARG2NM, \
155+
ARG2PT, \
156+
ARG3TY, \
157+
ARG3NM, \
158+
ARG3PT, \
159+
ARG4TY, \
160+
ARG4NM, \
161+
ARG4PT, \
162+
ARG5TY, \
163+
ARG5NM, \
164+
ARG5PT, \
165+
ARG6TY, \
166+
ARG6NM, \
167+
ARG6PT) \
168+
HERMES_AST_VISITOR_KEYS[#NAME] = { \
169+
ARG0TY(ARG0NM) \
170+
ARG1TY(ARG1NM) \
171+
ARG2TY(ARG2NM) \
172+
ARG3TY(ARG3NM) \
173+
ARG4TY(ARG4NM) \
174+
ARG5TY(ARG5NM) \
175+
ARG6TY(ARG6NM) \
176+
};
177+
178+
#define ESTREE_NODE_8_ARGS( \
179+
NAME, \
180+
BASE, \
181+
ARG0TY, \
182+
ARG0NM, \
183+
ARG0OPT, \
184+
ARG1TY, \
185+
ARG1NM, \
186+
ARG1PT, \
187+
ARG2TY, \
188+
ARG2NM, \
189+
ARG2PT, \
190+
ARG3TY, \
191+
ARG3NM, \
192+
ARG3PT, \
193+
ARG4TY, \
194+
ARG4NM, \
195+
ARG4PT, \
196+
ARG5TY, \
197+
ARG5NM, \
198+
ARG5PT, \
199+
ARG6TY, \
200+
ARG6NM, \
201+
ARG6PT, \
202+
ARG7TY, \
203+
ARG7NM, \
204+
ARG7PT) \
205+
HERMES_AST_VISITOR_KEYS[#NAME] = { \
206+
ARG0TY(ARG0NM) \
207+
ARG1TY(ARG1NM) \
208+
ARG2TY(ARG2NM) \
209+
ARG3TY(ARG3NM) \
210+
ARG4TY(ARG4NM) \
211+
ARG5TY(ARG5NM) \
212+
ARG6TY(ARG6NM) \
213+
ARG7TY(ARG7NM) \
214+
};
215+
216+
#include "hermes/AST/ESTree.def"
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @format
8+
*/
9+
10+
'use strict';
11+
12+
const {
13+
HERMES_AST_VISITOR_KEYS,
14+
NODE_CHILD,
15+
NODE_LIST_CHILD,
16+
} = require('./HermesASTVisitorKeys');
17+
18+
function visitNode(node) {
19+
if (node == null) {
20+
return;
21+
}
22+
23+
const visitorKeys = HERMES_AST_VISITOR_KEYS[node.type];
24+
for (const key in visitorKeys) {
25+
const childType = visitorKeys[key];
26+
if (childType === NODE_CHILD) {
27+
const child = node[key];
28+
if (child != null) {
29+
visitNode(node[key]);
30+
}
31+
} else if (childType === NODE_LIST_CHILD) {
32+
for (const child of node[key]) {
33+
if (child != null) {
34+
visitNode(child);
35+
}
36+
}
37+
}
38+
}
39+
}
40+
41+
module.exports = visitNode;

0 commit comments

Comments
 (0)