Coverage Report

Created: 2025-12-11 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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