Coverage Report

Created: 2025-08-28 06:48

/src/hermes/lib/IRGen/ESTreeIRGen-except.cpp
Line
Count
Source (jump to first uncovered line)
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 "ESTreeIRGen.h"
9
10
#include "llvh/Support/SaveAndRestore.h"
11
12
#include <variant>
13
14
namespace hermes {
15
namespace irgen {
16
17
0
void ESTreeIRGen::genTryStatement(ESTree::TryStatementNode *tryStmt) {
18
0
  LLVM_DEBUG(llvh::dbgs() << "IRGen 'try' statement\n");
19
20
  // try-catch-finally statements must have been transformed by the validator
21
  // into two nested try statements with only "catch" or "finally" each.
22
0
  assert(
23
0
      (!tryStmt->_handler || !tryStmt->_finalizer) &&
24
0
      "Try statement can't have both catch and finally");
25
26
0
  auto *nextBlock = emitTryCatchScaffolding(
27
0
      nullptr,
28
      // emitBody.
29
0
      [this, tryStmt]() {
30
0
        llvh::Optional<SurroundingTry> thisTry;
31
32
0
        if (tryStmt->_finalizer) {
33
0
          thisTry.emplace(
34
0
              curFunction(),
35
0
              tryStmt,
36
0
              tryStmt->_finalizer->getDebugLoc(),
37
0
              [this](ESTree::Node *node, ControlFlowChange, BasicBlock *) {
38
0
                genStatement(
39
0
                    cast<ESTree::TryStatementNode>(node)->_finalizer,
40
0
                    IsLoopBody::No);
41
0
              });
42
0
        } else {
43
0
          thisTry.emplace(curFunction(), tryStmt);
44
0
        }
45
46
0
        genStatement(tryStmt->_block, IsLoopBody::No);
47
48
0
        Builder.setLocation(SourceErrorManager::convertEndToLocation(
49
0
            tryStmt->_block->getSourceRange()));
50
0
      },
51
      // emitNormalCleanup.
52
0
      [this, tryStmt]() {
53
0
        if (tryStmt->_finalizer) {
54
0
          genStatement(tryStmt->_finalizer, IsLoopBody::No);
55
0
          Builder.setLocation(SourceErrorManager::convertEndToLocation(
56
0
              tryStmt->_finalizer->getSourceRange()));
57
0
        }
58
0
      },
59
      // emitHandler.
60
0
      [this, tryStmt](BasicBlock *nextBlock) {
61
        // If we have a catch block.
62
0
        if (tryStmt->_handler) {
63
0
          auto *catchClauseNode =
64
0
              llvh::dyn_cast<ESTree::CatchClauseNode>(tryStmt->_handler);
65
66
          // Catch takes a exception variable, hence we need to create a new
67
          // scope for it. The --block-scoping flag controls how the catch
68
          // variable will be created. N.B.: newScope isn't used directly after
69
          // its contents are emplaced, but the constructors/destructors of its
70
          // payload have important side-effects (named, pushing/popping the
71
          // scope information from the IR generation).
72
0
          std::variant<std::monostate, NameTableScopeTy, EnterBlockScope>
73
0
              newScope;
74
75
0
          if (!Mod->getContext()
76
0
                   .getCodeGenerationSettings()
77
0
                   .enableBlockScoping) {
78
0
            newScope.emplace<NameTableScopeTy>(nameTable_);
79
0
          } else {
80
0
            newScope.emplace<EnterBlockScope>(curFunction());
81
0
          }
82
83
0
          Builder.setLocation(tryStmt->_handler->getDebugLoc());
84
0
          prepareCatch(catchClauseNode);
85
86
0
          genCatchHandler(catchClauseNode->_body);
87
88
0
          Builder.setLocation(SourceErrorManager::convertEndToLocation(
89
0
              tryStmt->_handler->getSourceRange()));
90
0
          Builder.createBranchInst(nextBlock);
91
0
        } else {
92
          // A finally block catches the exception and rethrows is.
93
0
          Builder.setLocation(tryStmt->_finalizer->getDebugLoc());
94
0
          auto *catchReg = Builder.createCatchInst();
95
96
0
          genStatement(tryStmt->_finalizer, IsLoopBody::No);
97
98
0
          Builder.setLocation(SourceErrorManager::convertEndToLocation(
99
0
              tryStmt->_finalizer->getSourceRange()));
100
0
          Builder.createThrowInst(catchReg);
101
0
        }
102
0
      });
103
104
0
  Builder.setInsertionBlock(nextBlock);
105
0
}
106
107
0
CatchInst *ESTreeIRGen::prepareCatch(ESTree::CatchClauseNode *catchHandler) {
108
0
  auto *catchInst = Builder.createCatchInst();
109
110
0
  if (Mod->getContext().getCodeGenerationSettings().enableBlockScoping) {
111
    // Create the catch scope after the Catch instruction so it is part of the
112
    // Catch handler range in the exception tables.
113
0
    blockDeclarationInstantiation(catchHandler);
114
0
  }
115
116
0
  ESTree::NodePtr catchParam = catchHandler->_param;
117
0
  if (!catchParam) {
118
    // Optional catch binding allows us to emit no extra code for the catch.
119
0
    return catchInst;
120
0
  }
121
122
0
  if (!llvh::isa<ESTree::IdentifierNode>(catchParam)) {
123
0
    Builder.getModule()->getContext().getSourceErrorManager().error(
124
0
        catchParam->getSourceRange(),
125
0
        Twine("Destructuring in catch parameters is currently unsupported"));
126
0
    return nullptr;
127
0
  }
128
129
0
  Variable *errorVar{};
130
131
0
  if (!Mod->getContext().getCodeGenerationSettings().enableBlockScoping) {
132
0
    auto catchVariableName =
133
0
        getNameFieldFromID(cast<ESTree::IdentifierNode>(catchParam));
134
135
    // Generate a unique catch variable name and use this name for IRGen purpose
136
    // only. The variable lookup in the catch clause will continue to be done
137
    // using the declared name.
138
0
    auto uniquedCatchVariableName =
139
0
        genAnonymousLabelName(catchVariableName.str());
140
141
0
    errorVar = Builder.createVariable(
142
0
        curFunction()->function->getFunctionScopeDesc(),
143
0
        Variable::DeclKind::Var,
144
0
        uniquedCatchVariableName);
145
146
    /// Insert the synthesized variable into the function name table, so it can
147
    /// be looked up internally.
148
0
    nameTable_.insertIntoScope(
149
0
        curFunction()->functionScope, errorVar->getName(), errorVar);
150
151
    // Alias the lexical name to the synthesized variable.
152
0
    nameTable_.insert(catchVariableName, errorVar);
153
0
  } else {
154
0
    auto catchVariableName =
155
0
        getNameFieldFromID(cast<ESTree::IdentifierNode>(catchParam));
156
157
0
    errorVar = Builder.createVariable(
158
0
        currentIRScopeDesc_, Variable::DeclKind::Var, catchVariableName);
159
160
    /// Insert the synthesized variable into the function name table, so it can
161
    /// be looked up internally.
162
0
    nameTable_.insertIntoScope(
163
0
        curFunction()->blockScope, errorVar->getName(), errorVar);
164
0
  }
165
166
0
  emitStore(catchInst, errorVar, true);
167
0
  return catchInst;
168
0
}
169
170
void ESTreeIRGen::genFinallyBeforeControlChange(
171
    SurroundingTry *sourceTry,
172
    SurroundingTry *targetTry,
173
    ControlFlowChange cfc,
174
1.76k
    BasicBlock *continueTarget) {
175
1.76k
  assert(
176
1.76k
      (cfc == ControlFlowChange::Break || continueTarget != nullptr) &&
177
1.76k
      "Continue ControlFlowChange must have a target");
178
  // We walk the nested try statements starting from the source, until we reach
179
  // the target, generating the finally statements on the way.
180
1.76k
  for (; sourceTry != targetTry; sourceTry = sourceTry->outer) {
181
0
    assert(sourceTry && "invalid try chain");
182
183
    // Emit an end of the try statement.
184
0
    auto *tryEndBlock = Builder.createBasicBlock(curFunction()->function);
185
0
    Builder.createBranchInst(tryEndBlock);
186
0
    Builder.setInsertionBlock(tryEndBlock);
187
188
    // Make sure we use the correct debug location for tryEndInst.
189
0
    if (sourceTry->tryEndLoc.isValid()) {
190
0
      hermes::IRBuilder::ScopedLocationChange slc(
191
0
          Builder, sourceTry->tryEndLoc);
192
0
      Builder.createTryEndInst();
193
0
    } else {
194
0
      Builder.createTryEndInst();
195
0
    }
196
197
0
    if (sourceTry->genFinalizer) {
198
      // Recreate the state of the try stack on entrance to the finally block.
199
0
      llvh::SaveAndRestore<SurroundingTry *> sr{
200
0
          curFunction()->surroundingTry, sourceTry->outer};
201
0
      sourceTry->genFinalizer(sourceTry->node, cfc, continueTarget);
202
0
    }
203
0
  }
204
1.76k
}
205
206
} // namespace irgen
207
} // namespace hermes