/src/hermes/lib/AST/BlockScopingTransformations.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) Meta Platforms, Inc. and 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 | | #include "SemanticValidator.h" |
9 | | |
10 | | #include "hermes/AST/RecursiveVisitor.h" |
11 | | #include "hermes/Support/InternalIdentifierMaker.h" |
12 | | #include "hermes/Support/StringTable.h" |
13 | | #include "llvh/ADT/SetVector.h" |
14 | | #include "llvh/Support/SaveAndRestore.h" |
15 | | |
16 | | namespace hermes { |
17 | | namespace sem { |
18 | | namespace { |
19 | | |
20 | | /// Implements an AST traversal that performs transformations that simplify |
21 | | /// block scoping downstream. |
22 | | class BlockScopingTransformations { |
23 | | public: |
24 | | explicit BlockScopingTransformations(Context &astContext) |
25 | 0 | : astContext_(astContext), |
26 | 0 | internalIDs_(astContext.getStringTable()), |
27 | 0 | identAssign_(astContext.getIdentifier("=").getUnderlyingPointer()), |
28 | 0 | identExclaim_(astContext.getIdentifier("!").getUnderlyingPointer()), |
29 | 0 | identLet_(astContext.getIdentifier("let").getUnderlyingPointer()), |
30 | | identUndefined_( |
31 | 0 | astContext.getIdentifier("undefined").getUnderlyingPointer()), |
32 | 0 | identVar_(astContext.getIdentifier("var").getUnderlyingPointer()) {} |
33 | | |
34 | 0 | void visit(Node *node) { |
35 | 0 | visitESTreeChildren(*this, node); |
36 | 0 | } |
37 | | |
38 | 0 | ESTree::VisitResult visit(ForInStatementNode *forInStmt) { |
39 | 0 | return visitAndRewriteForInOf(forInStmt, forInStmt); |
40 | 0 | } |
41 | | |
42 | 0 | ESTree::VisitResult visit(ForOfStatementNode *forOfStmt) { |
43 | 0 | return visitAndRewriteForInOf(forOfStmt, forOfStmt); |
44 | 0 | } |
45 | | |
46 | 0 | ESTree::VisitResult visit(ForStatementNode *forStmt) { |
47 | 0 | return visitAndRewriteFor(forStmt, forStmt); |
48 | 0 | } |
49 | | |
50 | | /// Intercepts LabeledStatements |
51 | | /// |
52 | | /// label : [label:]+ for-in |
53 | | /// label : [label:]+ for-of |
54 | | /// label : [label:]+ for(const/let |
55 | | /// |
56 | | /// and applies the relevant transformation. |
57 | 0 | ESTree::VisitResult visit(LabeledStatementNode *labeledStmt) { |
58 | 0 | LabeledStatementNode *firstLabel = labeledStmt; |
59 | 0 | LabeledStatementNode *lastLabel = labeledStmt; |
60 | |
|
61 | 0 | while (auto *bodyLabel = |
62 | 0 | llvh::dyn_cast<LabeledStatementNode>(lastLabel->_body)) { |
63 | 0 | lastLabel = bodyLabel; |
64 | 0 | } |
65 | |
|
66 | 0 | Node *body = lastLabel->_body; |
67 | 0 | switch (body->getKind()) { |
68 | 0 | case NodeKind::ForInStatement: |
69 | 0 | case NodeKind::ForOfStatement: |
70 | 0 | return visitAndRewriteForInOf(body, firstLabel); |
71 | | |
72 | 0 | case NodeKind::ForStatement: |
73 | 0 | return visitAndRewriteFor( |
74 | 0 | llvh::cast<ForStatementNode>(body), firstLabel); |
75 | | |
76 | 0 | default: |
77 | 0 | visitESTreeNode(*this, body, lastLabel); |
78 | 0 | return ESTree::Unmodified; |
79 | 0 | } |
80 | 0 | } |
81 | | |
82 | 0 | bool incRecursionDepth(Node *n) { |
83 | 0 | if (LLVM_UNLIKELY(recursionDepth_ == 0)) { |
84 | 0 | return false; |
85 | 0 | } |
86 | 0 | --recursionDepth_; |
87 | 0 | if (LLVM_UNLIKELY(recursionDepth_ == 0)) { |
88 | 0 | return false; |
89 | 0 | } |
90 | 0 | return true; |
91 | 0 | } |
92 | | |
93 | 0 | void decRecursionDepth() { |
94 | 0 | assert( |
95 | 0 | recursionDepth_ < MAX_RECURSION_DEPTH && |
96 | 0 | "recursionDepth_ cannot go negative"); |
97 | 0 | if (LLVM_LIKELY(recursionDepth_ != 0)) |
98 | 0 | ++recursionDepth_; |
99 | 0 | } |
100 | | |
101 | | private: |
102 | | Context &astContext_; |
103 | | InternalIdentifierMaker internalIDs_; |
104 | | |
105 | | /// Rewrites |
106 | | /// |
107 | | /// |---- left ---| |
108 | | /// [label:]* for (let/const/var x in/of right) body |
109 | | /// |
110 | | /// into |
111 | | /// |
112 | | /// wrapperBlock: { |
113 | | /// let temp; |
114 | | /// [label:]* for (temp in/of right) { |
115 | | /// let/const/var x = temp; |
116 | | /// body; |
117 | | /// } |
118 | | /// break wrapperBlock; |
119 | | /// let x; // for TDZ (e.g., right: { x }) |
120 | | /// } |
121 | 0 | ESTree::VisitResult visitAndRewriteForInOf(Node *forInOfStmt, Node *current) { |
122 | 0 | visitESTreeChildren(*this, forInOfStmt); |
123 | |
|
124 | 0 | Node **left{}; |
125 | 0 | VariableDeclarationNode *decl; |
126 | 0 | Node **body; |
127 | |
|
128 | 0 | if (auto *forIn = llvh::dyn_cast<ForInStatementNode>(forInOfStmt)) { |
129 | 0 | left = &forIn->_left; |
130 | 0 | body = &forIn->_body; |
131 | 0 | } else if (auto *forOf = llvh::dyn_cast<ForOfStatementNode>(forInOfStmt)) { |
132 | 0 | left = &forOf->_left; |
133 | 0 | body = &forOf->_body; |
134 | 0 | } else { |
135 | 0 | assert(false && "If not for-in or for-of, then what?"); |
136 | 0 | } |
137 | | |
138 | 0 | decl = llvh::dyn_cast_or_null<VariableDeclarationNode>(*left); |
139 | |
|
140 | 0 | if (!decl) { |
141 | | // forInOfStmt is not in the form |
142 | | // |
143 | | // for (const/let/var |
144 | | // |
145 | | // so nothing to rewrite. |
146 | 0 | return ESTree::Unmodified; |
147 | 0 | } |
148 | | |
149 | | // The parser ensures there's a single declaration. |
150 | 0 | assert( |
151 | 0 | decl->_declarations.size() == 1 && |
152 | 0 | "for-in/of(let/const/var should have a single declaration"); |
153 | | |
154 | 0 | auto *varDecl = |
155 | 0 | llvh::cast<VariableDeclaratorNode>(&decl->_declarations.front()); |
156 | |
|
157 | 0 | if (varDecl->_init != nullptr) { |
158 | | // This is a semantic error (unless ES2023 B.3.5 is implemented), so don't |
159 | | // transform the AST. |
160 | 0 | return ESTree::Unmodified; |
161 | 0 | } |
162 | | |
163 | 0 | UniqueString *temp = internalIDs_.next("forInOf").getUnderlyingPointer(); |
164 | | |
165 | | // The outer block is: |
166 | | // |
167 | | // wrapperBlock: { |
168 | | // let temp; |
169 | | // [label:]* for (temp in right) ... |
170 | | // break wrapperBlock; |
171 | | // let x; // for TDZ |
172 | | // } |
173 | 0 | UniqueString *wrapperBlockLabel = |
174 | 0 | internalIDs_.next("wrapperBlock").getUnderlyingPointer(); |
175 | 0 | BlockStatementNode *wrapperBlock = makeBlock(); |
176 | |
|
177 | 0 | VariableDeclarationNode *tempDecl = makeVarDeclaration(identLet_); |
178 | 0 | tempDecl->_declarations.push_back(*makeVarDeclarator(temp)); |
179 | 0 | wrapperBlock->_body.push_back(*tempDecl); |
180 | |
|
181 | 0 | wrapperBlock->_body.push_back(*current); |
182 | |
|
183 | 0 | if (decl->_kind != identVar_) { |
184 | | // forInOfStmt is in the for |
185 | | // |
186 | | // for (const/let ... |
187 | | // |
188 | | // Thus emit a fake "let x" declaration after the loop to preserve TDZ |
189 | | // semantics in case right is, e.g., right: { x }. |
190 | 0 | VariableDeclarationNode *xTDZDecl = makeVarDeclaration(identLet_); |
191 | 0 | llvh::SetVector<UniqueString *> ids; |
192 | 0 | collectAllIDs(varDecl->_id, ids); |
193 | 0 | for (UniqueString *id : ids) { |
194 | 0 | xTDZDecl->_declarations.push_back(*makeVarDeclarator(id)); |
195 | 0 | } |
196 | 0 | wrapperBlock->_body.push_back(*makeBreak(wrapperBlockLabel)); |
197 | 0 | wrapperBlock->_body.push_back(*xTDZDecl); |
198 | 0 | } |
199 | | |
200 | | // Patching forInOfStmt in-place so that it no longer has forDeclarations. |
201 | 0 | *left = makeIdentifier(temp); |
202 | | |
203 | | // Now add an initializer (= temp) to decl. |
204 | 0 | varDecl->_init = makeIdentifier(temp); |
205 | | |
206 | | // Build the new for-in/of body: |
207 | | // |
208 | | // { |
209 | | // const/let/var x = temp; |
210 | | // body; |
211 | | // } |
212 | 0 | BlockStatementNode *newBody = makeBlock(); |
213 | 0 | newBody->_body.push_back(*decl); |
214 | 0 | newBody->_body.push_back(**body); |
215 | 0 | *body = newBody; |
216 | |
|
217 | 0 | return makeLabel(wrapperBlockLabel, wrapperBlock); |
218 | 0 | } |
219 | | |
220 | | /// Rewrites |
221 | | /// |
222 | | /// [label:]* for (const/let x = init; test; update) body |
223 | | /// |
224 | | /// into |
225 | | /// |
226 | | /// { |
227 | | /// const/let x = init; |
228 | | /// let temp_x = x; |
229 | | /// let first = true; |
230 | | /// undefined; |
231 | | /// for (;;) { |
232 | | /// const/let x = temp_x; |
233 | | /// if (first) { |
234 | | /// first = false; |
235 | | /// } else { |
236 | | /// update; |
237 | | /// } |
238 | | /// if (!test) break; |
239 | | /// control = true; |
240 | | /// [label:]* for (; control; control = false, temp_x = x) { |
241 | | /// body |
242 | | /// } |
243 | | /// if (control) break; |
244 | | /// } |
245 | | /// } |
246 | | /// |
247 | | /// which preserves the semantics required by ES2023 14.7.4.3: |
248 | | /// |
249 | | /// * each iteration happens in a new environment in which the |
250 | | /// forDeclarations are copied to. |
251 | | /// * update executes in the next iteration's context. |
252 | | /// |
253 | | /// while preserving the original statement's completion value. |
254 | | ESTree::VisitResult visitAndRewriteFor( |
255 | | ForStatementNode *forStmt, |
256 | 0 | Node *current) { |
257 | 0 | visitESTreeChildren(*this, forStmt); |
258 | |
|
259 | 0 | auto *init = |
260 | 0 | llvh::dyn_cast_or_null<VariableDeclarationNode>(forStmt->_init); |
261 | 0 | if (!init || init->_kind == identVar_) { |
262 | | // forStmt is |
263 | | // |
264 | | // for (var ...; ...; ...) |
265 | | // for (expr; ...; ...) |
266 | | // |
267 | | // which doesn't need to be rewritten. |
268 | 0 | return ESTree::Unmodified; |
269 | 0 | } |
270 | | |
271 | 0 | BlockStatementNode *wrapperBlock = makeBlock(); |
272 | |
|
273 | 0 | BlockStatementNode *outerBlock = makeBlock(); // outer for body. |
274 | |
|
275 | 0 | Node *test = forStmt->_test; |
276 | 0 | Node *update = forStmt->_update; |
277 | | |
278 | | // forStmt becomes the inner loop in the transformed AST, and its init, |
279 | | // test, and update expressions are used by the outer loop. Thus, "break" |
280 | | // the link between forStmt and those components early to prevent unintended |
281 | | // usage. |
282 | 0 | forStmt->_init = nullptr; |
283 | 0 | forStmt->_test = nullptr; |
284 | 0 | forStmt->_update = nullptr; |
285 | | |
286 | | // The transformation introduces one temporary variable for each declaration |
287 | | // in init, and then "copies" between then at specific points: |
288 | | // |
289 | | // * tempsDecl declares the temp variables (one per declaration in init), |
290 | | // and each temporary is initialized with the values introduced by init. |
291 | | // |
292 | | // * initFromTemps re-declares all variables in init inside the outer |
293 | | // loop; this is a const/let declaration (based on the original init |
294 | | // kind), and each variable is initialize with its corresponding |
295 | | // temporary. |
296 | | // |
297 | | // * newUpdate copies the values from the declarations introduced by |
298 | | // initFromTemps back into the temporaries; this means that next time |
299 | | // the outer loop executes initFromTemps will use the updated values. |
300 | 0 | VariableDeclarationNode *tempsDecl = makeVarDeclaration(identLet_); |
301 | 0 | VariableDeclarationNode *initFromTemps = makeVarDeclaration(init->_kind); |
302 | 0 | SequenceExpressionNode *newUpdate = makeSequenceExpression(); |
303 | |
|
304 | 0 | llvh::DenseMap<UniqueString *, UniqueString *> tempIds; |
305 | 0 | for (Node &n : init->_declarations) { |
306 | 0 | auto *decl = llvh::cast<VariableDeclaratorNode>(&n); |
307 | 0 | traverseForLexicalDecl( |
308 | 0 | decl->_id, tempIds, tempsDecl, initFromTemps, newUpdate); |
309 | 0 | } |
310 | | |
311 | | // Creating the wrapper block: |
312 | | // |
313 | | // { |
314 | | // const/let x = init; |
315 | | // let temp_x = x, first = true, first; |
316 | | // undefined; |
317 | | // outer : for (;;) outerBlock |
318 | | // } |
319 | 0 | wrapperBlock->_body.push_back(*init); |
320 | 0 | wrapperBlock->_body.push_back(*tempsDecl); |
321 | 0 | wrapperBlock->_body.push_back( |
322 | 0 | *toStatement(makeIdentifier(identUndefined_))); |
323 | 0 | wrapperBlock->_body.push_back( |
324 | 0 | *makeFor(nullptr, nullptr, nullptr, outerBlock)); |
325 | | |
326 | | // Creating outerBlock: |
327 | | // { |
328 | | // const/let x = temp_x; |
329 | | // if (first) // |
330 | | // first = false; // only when |
331 | | // else // update != nullptr |
332 | | // update; // |
333 | | // if (!cond); // only when |
334 | | // break; // cond != nullptr |
335 | | // control = true; |
336 | | // [label:]* for (; control; control = 0, temp_x = x) body; |
337 | | // if (control) break; |
338 | | // } |
339 | 0 | outerBlock->_body.push_back(*initFromTemps); |
340 | |
|
341 | 0 | if (update) { |
342 | | // if (first) |
343 | | // first = false; |
344 | | // else |
345 | | // update; |
346 | 0 | UniqueString *first = internalIDs_.next("first").getUnderlyingPointer(); |
347 | 0 | tempsDecl->_declarations.push_back( |
348 | 0 | *makeVarDeclarator(first, makeBooleanLiteral(true))); |
349 | |
|
350 | 0 | IdentifierNode *firstIsTrue = makeIdentifier(first); |
351 | 0 | Node *firstEqFalse = toStatement( |
352 | 0 | makeAssignment(makeIdentifier(first), makeBooleanLiteral(false))); |
353 | 0 | outerBlock->_body.push_back( |
354 | 0 | *makeIf(firstIsTrue, firstEqFalse, toStatement(update))); |
355 | 0 | } |
356 | |
|
357 | 0 | if (test) { |
358 | | // if (!cond) break; |
359 | 0 | outerBlock->_body.push_back(*makeIf(makeNot(test), makeBreak())); |
360 | 0 | } |
361 | | |
362 | | // control = true (also add the variable to tempsDecl in the wrapperBlock) |
363 | 0 | UniqueString *control = |
364 | 0 | internalIDs_.next("forControl").getUnderlyingPointer(); |
365 | 0 | tempsDecl->_declarations.push_back(*makeVarDeclarator(control)); |
366 | 0 | outerBlock->_body.push_back(*toStatement( |
367 | 0 | makeAssignment(makeIdentifier(control), makeBooleanLiteral(true)))); |
368 | | |
369 | | // Add control = false to new update expression list. |
370 | 0 | newUpdate->_expressions.push_front( |
371 | 0 | *makeAssignment(makeIdentifier(control), makeBooleanLiteral(false))); |
372 | | |
373 | | // Rewrite forStatement into |
374 | | // |
375 | | // for (; control; control = 0, temp_x = x) |
376 | 0 | forStmt->_test = makeIdentifier(control); |
377 | 0 | forStmt->_update = newUpdate; |
378 | 0 | outerBlock->_body.push_back(*current); |
379 | | |
380 | | // if (control) break; |
381 | 0 | outerBlock->_body.push_back(*makeIf(makeIdentifier(control), makeBreak())); |
382 | |
|
383 | 0 | return wrapperBlock; |
384 | 0 | } |
385 | | |
386 | | /// The maximum AST nesting level. Once we reach it, we report an error and |
387 | | /// stop. |
388 | | static constexpr unsigned MAX_RECURSION_DEPTH = |
389 | | #if defined(HERMES_LIMIT_STACK_DEPTH) || defined(_MSC_VER) |
390 | | 512 |
391 | | #else |
392 | | 1024 |
393 | | #endif |
394 | | ; |
395 | | |
396 | | /// MAX_RECURSION_DEPTH minus the current AST nesting level. Once it reaches |
397 | | /// 0 stop transforming it. |
398 | | unsigned recursionDepth_ = MAX_RECURSION_DEPTH; |
399 | | |
400 | | UniqueString *const identAssign_; |
401 | | UniqueString *const identExclaim_; |
402 | | UniqueString *const identLet_; |
403 | | UniqueString *const identUndefined_; |
404 | | UniqueString *const identVar_; |
405 | | |
406 | | /// Collect all IDs appearing in \p node and store them in \p ids. These IDs |
407 | | /// appear in a forInOf declaration. |
408 | 0 | void collectAllIDs(Node *node, llvh::SetVector<UniqueString *> &ids) { |
409 | 0 | switch (node->getKind()) { |
410 | 0 | case NodeKind::Empty: { |
411 | 0 | break; |
412 | 0 | } |
413 | 0 | case NodeKind::AssignmentPattern: { |
414 | 0 | auto *assignment = llvh::cast<AssignmentPatternNode>(node); |
415 | 0 | collectAllIDs(assignment->_left, ids); |
416 | 0 | break; |
417 | 0 | } |
418 | 0 | case NodeKind::RestElement: { |
419 | 0 | auto *rest = llvh::cast<RestElementNode>(node); |
420 | 0 | collectAllIDs(rest->_argument, ids); |
421 | 0 | break; |
422 | 0 | } |
423 | 0 | case NodeKind::Property: { |
424 | 0 | auto *prop = llvh::cast<PropertyNode>(node); |
425 | 0 | collectAllIDs(prop->_value, ids); |
426 | 0 | break; |
427 | 0 | } |
428 | 0 | case NodeKind::ObjectPattern: { |
429 | 0 | auto *objectPattern = llvh::cast<ObjectPatternNode>(node); |
430 | 0 | for (Node &prop : objectPattern->_properties) { |
431 | 0 | collectAllIDs(&prop, ids); |
432 | 0 | } |
433 | 0 | break; |
434 | 0 | } |
435 | 0 | case NodeKind::ArrayPattern: { |
436 | 0 | auto *arrayPattern = llvh::cast<ArrayPatternNode>(node); |
437 | 0 | for (Node &element : arrayPattern->_elements) { |
438 | 0 | collectAllIDs(&element, ids); |
439 | 0 | } |
440 | 0 | break; |
441 | 0 | } |
442 | 0 | case NodeKind::Identifier: { |
443 | 0 | auto *id = llvh::cast<IdentifierNode>(node); |
444 | 0 | ids.insert(id->_name); |
445 | 0 | break; |
446 | 0 | } |
447 | 0 | default: |
448 | 0 | llvm_unreachable("unhandled node in collectAllIDs"); |
449 | 0 | } |
450 | 0 | } |
451 | | |
452 | | /// Traverses \p node, which is a ForLexicalDeclaration in a |
453 | | /// |
454 | | /// for (const/let ... ; ... ; ...) ... |
455 | | /// |
456 | | /// statement, and emits VariableDeclarators and AssignmentExpressions used |
457 | | /// during for rewrite. |
458 | | void traverseForLexicalDecl( |
459 | | Node *node, |
460 | | llvh::DenseMap<UniqueString *, UniqueString *> &tempIds, |
461 | | VariableDeclarationNode *tempsDecl, |
462 | | VariableDeclarationNode *initFromTemps, |
463 | 0 | SequenceExpressionNode *newUpdate) { |
464 | 0 | switch (node->getKind()) { |
465 | 0 | case NodeKind::Empty: { |
466 | 0 | break; |
467 | 0 | } |
468 | 0 | case NodeKind::AssignmentPattern: { |
469 | 0 | auto *assignment = llvh::cast<AssignmentPatternNode>(node); |
470 | 0 | traverseForLexicalDecl( |
471 | 0 | assignment->_left, tempIds, tempsDecl, initFromTemps, newUpdate); |
472 | 0 | break; |
473 | 0 | } |
474 | 0 | case NodeKind::RestElement: { |
475 | 0 | auto *rest = llvh::dyn_cast<RestElementNode>(node); |
476 | 0 | traverseForLexicalDecl( |
477 | 0 | rest->_argument, tempIds, tempsDecl, initFromTemps, newUpdate); |
478 | 0 | break; |
479 | 0 | } |
480 | 0 | case NodeKind::Property: { |
481 | 0 | auto *prop = llvh::cast<PropertyNode>(node); |
482 | 0 | traverseForLexicalDecl( |
483 | 0 | prop->_value, tempIds, tempsDecl, initFromTemps, newUpdate); |
484 | 0 | break; |
485 | 0 | } |
486 | 0 | case NodeKind::ObjectPattern: { |
487 | 0 | auto *objectPattern = llvh::cast<ObjectPatternNode>(node); |
488 | 0 | for (Node &prop : objectPattern->_properties) { |
489 | 0 | traverseForLexicalDecl( |
490 | 0 | &prop, tempIds, tempsDecl, initFromTemps, newUpdate); |
491 | 0 | } |
492 | 0 | break; |
493 | 0 | } |
494 | 0 | case NodeKind::ArrayPattern: { |
495 | 0 | auto *arrayPattern = llvh::cast<ArrayPatternNode>(node); |
496 | 0 | for (Node &elt : arrayPattern->_elements) { |
497 | 0 | traverseForLexicalDecl( |
498 | 0 | &elt, tempIds, tempsDecl, initFromTemps, newUpdate); |
499 | 0 | } |
500 | 0 | break; |
501 | 0 | } |
502 | 0 | case NodeKind::Identifier: { |
503 | 0 | auto *id = llvh::cast<IdentifierNode>(node); |
504 | 0 | auto res = tempIds.insert(std::make_pair(id->_name, nullptr)); |
505 | 0 | if (res.second) { |
506 | 0 | res.first->second = |
507 | 0 | internalIDs_.next("forDecl").getUnderlyingPointer(); |
508 | 0 | } |
509 | |
|
510 | 0 | UniqueString *temp = res.first->second; |
511 | | |
512 | | // let temp_x = x (in wrapper block) |
513 | 0 | tempsDecl->_declarations.push_back( |
514 | 0 | *makeVarDeclarator(temp, makeIdentifier(id->_name))); |
515 | | |
516 | | // const/let x = temp_x (in outer loop) |
517 | 0 | initFromTemps->_declarations.push_back( |
518 | 0 | *makeVarDeclarator(id->_name, makeIdentifier(temp))); |
519 | | |
520 | | // temp_x = x; (inner loop update) |
521 | 0 | newUpdate->_expressions.push_back( |
522 | 0 | *makeAssignment(makeIdentifier(temp), makeIdentifier(id->_name))); |
523 | |
|
524 | 0 | break; |
525 | 0 | } |
526 | 0 | default: |
527 | 0 | llvm_unreachable("unhandled node in duplicateID"); |
528 | 0 | } |
529 | 0 | } |
530 | | |
531 | | //****** Helpers for creating AST nodes ******// |
532 | | |
533 | | /// \return (IdentifierNode <<name>>) |
534 | 0 | IdentifierNode *makeIdentifier(UniqueString *name) { |
535 | 0 | return new (astContext_) IdentifierNode(name, nullptr, false); |
536 | 0 | } |
537 | | |
538 | | /// \return (VariableDeclarationNode <<kind>> <<{}>>) |
539 | 0 | VariableDeclarationNode *makeVarDeclaration(UniqueString *kind) { |
540 | 0 | return new (astContext_) VariableDeclarationNode(kind, {}); |
541 | 0 | } |
542 | | |
543 | | /// \return (VariableDeclaratorNode <<init>> (Identifier <<name>>)) |
544 | | VariableDeclaratorNode *makeVarDeclarator( |
545 | | UniqueString *name, |
546 | 0 | Node *init = nullptr) { |
547 | 0 | return new (astContext_) VariableDeclaratorNode(init, makeIdentifier(name)); |
548 | 0 | } |
549 | | |
550 | | /// \return (AssignmentExpressionNode <<"=">> <<dst>> <<src>>) |
551 | 0 | AssignmentExpressionNode *makeAssignment(Node *dst, Node *src) { |
552 | 0 | return new (astContext_) AssignmentExpressionNode(identAssign_, dst, src); |
553 | 0 | } |
554 | | |
555 | | /// \return <<n>> if n is a StatementNode; |
556 | | /// (ExpressionStatementNode <<n>> <<>>) otherwise |
557 | 0 | StatementNode *toStatement(Node *n) { |
558 | 0 | if (auto *stmt = llvh::dyn_cast<StatementNode>(n)) { |
559 | 0 | return stmt; |
560 | 0 | } |
561 | 0 | return new (astContext_) ExpressionStatementNode(n, nullptr); |
562 | 0 | } |
563 | | |
564 | | /// \return (UnaryExpressionNode "!" <<n>>) |
565 | 0 | UnaryExpressionNode *makeNot(Node *n) { |
566 | 0 | return new (astContext_) UnaryExpressionNode(identExclaim_, n, false); |
567 | 0 | } |
568 | | |
569 | | /// \return (LabeledStatementNode (Identifier <<label>>) <<body>>) |
570 | 0 | LabeledStatementNode *makeLabel(UniqueString *label, Node *body) { |
571 | 0 | return new (astContext_) LabeledStatementNode(makeIdentifier(label), body); |
572 | 0 | } |
573 | | |
574 | | /// \return (BreakStatementNode (Identifier <<label>>)?) |
575 | 0 | BreakStatementNode *makeBreak(UniqueString *label = nullptr) { |
576 | 0 | Node *labelNode{}; |
577 | 0 | if (label) { |
578 | 0 | labelNode = makeIdentifier(label); |
579 | 0 | } |
580 | 0 | return new (astContext_) BreakStatementNode(labelNode); |
581 | 0 | } |
582 | | |
583 | | /// \return (BooleanLiteralNode <<value>>) |
584 | 0 | BooleanLiteralNode *makeBooleanLiteral(bool value) { |
585 | 0 | return new (astContext_) BooleanLiteralNode(value); |
586 | 0 | } |
587 | | |
588 | | /// \return (BlockStatementNode <<{}>>) |
589 | 0 | BlockStatementNode *makeBlock() { |
590 | 0 | return new (astContext_) BlockStatementNode({}); |
591 | 0 | } |
592 | | |
593 | | /// \return (SequenceExpressionNode <<{}>>) |
594 | 0 | SequenceExpressionNode *makeSequenceExpression() { |
595 | 0 | return new (astContext_) SequenceExpressionNode({}); |
596 | 0 | } |
597 | | |
598 | | /// \return (IfStatementNode <<test>> <<consequent>> <<alternate>>?) |
599 | | IfStatementNode * |
600 | 0 | makeIf(Node *test, Node *consequent, Node *alternate = nullptr) { |
601 | 0 | return new (astContext_) IfStatementNode(test, consequent, alternate); |
602 | 0 | } |
603 | | |
604 | | /// \return (ForStatementNode <<init>>? <<test>>? <<update>>? <<body>>) |
605 | 0 | ForStatementNode *makeFor(Node *init, Node *test, Node *update, Node *body) { |
606 | 0 | return new (astContext_) ForStatementNode(init, test, update, body); |
607 | 0 | } |
608 | | }; |
609 | | |
610 | | } // namespace |
611 | | |
612 | 0 | void canonicalizeForBlockScoping(Context &astContext, Node *root) { |
613 | 0 | BlockScopingTransformations BST{astContext}; |
614 | 0 | visitESTreeNode(BST, root); |
615 | 0 | } |
616 | | } // namespace sem |
617 | | } // namespace hermes |