Coverage Report

Created: 2025-12-12 07:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hermes/lib/BCGen/HBC/BytecodeProviderFromSrc.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 "hermes/BCGen/HBC/BytecodeProviderFromSrc.h"
9
10
#include "hermes/AST/SemValidate.h"
11
#include "hermes/BCGen/HBC/HBC.h"
12
#include "hermes/Parser/JSParser.h"
13
#include "hermes/Runtime/Libhermes.h"
14
#include "hermes/SourceMap/SourceMapTranslator.h"
15
#include "hermes/Support/MemoryBuffer.h"
16
#include "hermes/Support/SimpleDiagHandler.h"
17
18
namespace hermes {
19
namespace hbc {
20
21
namespace {
22
147
bool isSingleFunctionExpression(ESTree::NodePtr ast) {
23
147
  auto *prog = llvh::dyn_cast<ESTree::ProgramNode>(ast);
24
147
  if (!prog) {
25
0
    return false;
26
0
  }
27
147
  ESTree::NodeList &body = prog->_body;
28
147
  if (body.size() != 1) {
29
27
    return false;
30
27
  }
31
120
  auto *exprStatement =
32
120
      llvh::dyn_cast<ESTree::ExpressionStatementNode>(&body.front());
33
120
  if (!exprStatement) {
34
10
    return false;
35
10
  }
36
110
  return llvh::isa<ESTree::FunctionExpressionNode>(
37
110
             exprStatement->_expression) ||
38
30
      llvh::isa<ESTree::ArrowFunctionExpressionNode>(
39
30
             exprStatement->_expression);
40
120
}
41
} // namespace
42
43
BCProviderFromSrc::BCProviderFromSrc(
44
    std::unique_ptr<hbc::BytecodeModule> module)
45
147
    : module_(std::move(module)) {
46
147
  options_ = module_->getBytecodeOptions();
47
48
147
  functionCount_ = module_->getNumFunctions();
49
50
147
  globalFunctionIndex_ = module_->getGlobalFunctionIndex();
51
52
147
  stringKinds_ = module_->getStringKinds();
53
147
  identifierHashes_ = module_->getIdentifierHashes();
54
147
  stringCount_ = module_->getStringTable().size();
55
147
  stringStorage_ = module_->getStringStorage();
56
57
147
  bigIntStorage_ = module_->getBigIntStorage();
58
147
  bigIntTable_ = module_->getBigIntTable();
59
60
147
  regExpStorage_ = module_->getRegExpStorage();
61
147
  regExpTable_ = module_->getRegExpTable();
62
63
147
  arrayBuffer_ = module_->getArrayBuffer();
64
147
  objKeyBuffer_ = module_->getObjectBuffer().first;
65
147
  objValueBuffer_ = module_->getObjectBuffer().second;
66
67
147
  segmentID_ = module_->getSegmentID();
68
147
  cjsModuleTable_ = module_->getCJSModuleTable();
69
147
  cjsModuleTableStatic_ = module_->getCJSModuleTableStatic();
70
71
147
  functionSourceTable_ = module_->getFunctionSourceTable();
72
73
147
  debugInfo_ = &module_->getDebugInfo();
74
147
}
75
76
std::pair<std::unique_ptr<BCProviderFromSrc>, std::string>
77
BCProviderFromSrc::createBCProviderFromSrc(
78
    std::unique_ptr<Buffer> buffer,
79
    llvh::StringRef sourceURL,
80
0
    const CompileFlags &compileFlags) {
81
0
  return createBCProviderFromSrc(
82
0
      std::move(buffer), sourceURL, /*sourceMap*/ nullptr, compileFlags);
83
0
}
84
85
std::pair<std::unique_ptr<BCProviderFromSrc>, std::string>
86
BCProviderFromSrc::createBCProviderFromSrc(
87
    std::unique_ptr<Buffer> buffer,
88
    llvh::StringRef sourceURL,
89
    std::unique_ptr<SourceMap> sourceMap,
90
113
    const CompileFlags &compileFlags) {
91
113
  return createBCProviderFromSrc(
92
113
      std::move(buffer), sourceURL, std::move(sourceMap), compileFlags, {}, {});
93
113
}
94
95
std::pair<std::unique_ptr<BCProviderFromSrc>, std::string>
96
BCProviderFromSrc::createBCProviderFromSrc(
97
    std::unique_ptr<Buffer> buffer,
98
    llvh::StringRef sourceURL,
99
    std::unique_ptr<SourceMap> sourceMap,
100
    const CompileFlags &compileFlags,
101
    const ScopeChain &scopeChain,
102
    SourceErrorManager::DiagHandlerTy diagHandler,
103
    void *diagContext,
104
    const std::function<void(Module &)> &runOptimizationPasses,
105
196
    const BytecodeGenerationOptions &defaultBytecodeGenerationOptions) {
106
196
  return createBCProviderFromSrcImpl(
107
196
      std::move(buffer),
108
196
      sourceURL,
109
196
      std::move(sourceMap),
110
196
      compileFlags,
111
196
      scopeChain,
112
196
      diagHandler,
113
196
      diagContext,
114
196
      runOptimizationPasses,
115
196
      defaultBytecodeGenerationOptions);
116
196
}
117
118
std::pair<std::unique_ptr<BCProviderFromSrc>, std::string>
119
BCProviderFromSrc::createBCProviderFromSrcImpl(
120
    std::unique_ptr<Buffer> buffer,
121
    llvh::StringRef sourceURL,
122
    std::unique_ptr<SourceMap> sourceMap,
123
    const CompileFlags &compileFlags,
124
    const ScopeChain &scopeChain,
125
    SourceErrorManager::DiagHandlerTy diagHandler,
126
    void *diagContext,
127
    const std::function<void(Module &)> &runOptimizationPasses,
128
196
    const BytecodeGenerationOptions &defaultBytecodeGenerationOptions) {
129
196
  assert(
130
196
      buffer->data()[buffer->size()] == 0 &&
131
196
      "The input buffer must be null terminated");
132
133
196
  CodeGenerationSettings codeGenOpts{};
134
196
  codeGenOpts.unlimitedRegisters = false;
135
196
  codeGenOpts.instrumentIR = compileFlags.instrumentIR;
136
196
  codeGenOpts.enableBlockScoping = compileFlags.enableBlockScoping;
137
138
196
  OptimizationSettings optSettings;
139
  // If the optional value is not set, the parser will automatically detect
140
  // the 'use static builtin' directive and we will set it correctly.
141
196
  optSettings.staticBuiltins = compileFlags.staticBuiltins.hasValue()
142
196
      ? compileFlags.staticBuiltins.getValue()
143
196
      : false;
144
145
196
  auto context = std::make_shared<Context>(codeGenOpts, optSettings);
146
196
  std::unique_ptr<SimpleDiagHandlerRAII> outputManager;
147
196
  if (diagHandler) {
148
0
    context->getSourceErrorManager().setDiagHandler(diagHandler, diagContext);
149
196
  } else {
150
196
    outputManager.reset(
151
196
        new SimpleDiagHandlerRAII(context->getSourceErrorManager()));
152
196
  }
153
  // If a custom diagHandler was provided, it will receive the details and we
154
  // just return the string "error" on failure.
155
196
  auto getErrorString = [&outputManager]() {
156
49
    return outputManager ? outputManager->getErrorString()
157
49
                         : std::string("error");
158
49
  };
159
160
  // To avoid frequent source buffer rescans, avoid emitting warnings about
161
  // undefined variables.
162
196
  context->getSourceErrorManager().setWarningStatus(
163
196
      Warning::UndefinedVariable, false);
164
165
196
  context->setStrictMode(compileFlags.strict);
166
196
  context->setEnableEval(true);
167
196
  context->setConvertES6Classes(compileFlags.enableES6Classes);
168
196
  context->setPreemptiveFunctionCompilationThreshold(
169
196
      compileFlags.preemptiveFunctionCompilationThreshold);
170
196
  context->setPreemptiveFileCompilationThreshold(
171
196
      compileFlags.preemptiveFileCompilationThreshold);
172
173
196
  if (compileFlags.lazy && !runOptimizationPasses) {
174
196
    context->setLazyCompilation(true);
175
196
  }
176
177
196
  context->setGeneratorEnabled(compileFlags.enableGenerator);
178
196
  context->setDebugInfoSetting(
179
196
      compileFlags.debug ? DebugInfoSetting::ALL : DebugInfoSetting::THROWING);
180
196
  context->setEmitAsyncBreakCheck(compileFlags.emitAsyncBreakCheck);
181
182
  // Populate the declFileList.
183
196
  DeclarationFileListTy declFileList;
184
196
  if (compileFlags.includeLibHermes) {
185
113
    auto libBuffer = llvh::MemoryBuffer::getMemBuffer(libhermes);
186
113
    parser::JSParser libParser(*context, std::move(libBuffer));
187
113
    auto libParsed = libParser.parse();
188
113
    assert(libParsed && "Libhermes failed to parse");
189
113
    libParser.registerMagicURLs();
190
113
    declFileList.push_back(libParsed.getValue());
191
113
  }
192
193
196
  bool isLargeFile =
194
196
      buffer->size() >= context->getPreemptiveFileCompilationThreshold();
195
196
  int fileBufId = context->getSourceErrorManager().addNewSourceBuffer(
196
196
      std::make_unique<HermesLLVMMemoryBuffer>(std::move(buffer), sourceURL));
197
196
  if (sourceMap != nullptr) {
198
0
    auto sourceMapTranslator =
199
0
        std::make_shared<SourceMapTranslator>(context->getSourceErrorManager());
200
0
    context->getSourceErrorManager().setTranslator(sourceMapTranslator);
201
0
    sourceMapTranslator->addSourceMap(fileBufId, std::move(sourceMap));
202
0
  }
203
204
196
  auto parserMode = parser::FullParse;
205
196
  bool useStaticBuiltinDetected = false;
206
196
  if (context->isLazyCompilation() && isLargeFile) {
207
196
    auto preParser = parser::JSParser::preParseBuffer(*context, fileBufId);
208
196
    if (!preParser)
209
42
      return {nullptr, getErrorString()};
210
154
    useStaticBuiltinDetected = preParser->getUseStaticBuiltin();
211
154
    preParser->registerMagicURLs();
212
154
    parserMode = parser::LazyParse;
213
154
  }
214
215
154
  sem::SemContext semCtx{};
216
154
  parser::JSParser parser(*context, fileBufId, parserMode);
217
154
  auto parsed = parser.parse();
218
219
  // If we are using lazy parse mode, we should have already detected the 'use
220
  // static builtin' directive and magic URLs in the pre-parsing stage.
221
154
  if (parsed && parserMode != parser::LazyParse) {
222
0
    useStaticBuiltinDetected = parser.getUseStaticBuiltin();
223
0
    parser.registerMagicURLs();
224
0
  }
225
226
154
  if (!parsed || !hermes::sem::validateAST(*context, semCtx, *parsed)) {
227
7
    return {nullptr, getErrorString()};
228
7
  }
229
230
  // The compiler flag is not set, automatically detect 'use static builtin'
231
  // from the source.
232
147
  if (!compileFlags.staticBuiltins) {
233
147
    context->setStaticBuiltinOptimization(useStaticBuiltinDetected);
234
147
  }
235
236
147
  Module M(context);
237
147
  hermes::generateIRFromESTree(parsed.getValue(), &M, declFileList, scopeChain);
238
147
  if (context->getSourceErrorManager().getErrorCount() > 0) {
239
0
    return {nullptr, getErrorString()};
240
0
  }
241
242
147
  if (runOptimizationPasses)
243
0
    runOptimizationPasses(M);
244
245
147
  auto opts = defaultBytecodeGenerationOptions;
246
147
  opts.format = compileFlags.format;
247
147
  opts.optimizationEnabled = !!runOptimizationPasses;
248
147
  opts.staticBuiltinsEnabled =
249
147
      context->getOptimizationSettings().staticBuiltins;
250
147
  opts.verifyIR = compileFlags.verifyIR;
251
252
147
  auto BM = hbc::generateBytecodeModule(&M, M.getTopLevelFunction(), opts);
253
147
  if (context->getSourceErrorManager().getErrorCount() > 0) {
254
0
    return {nullptr, getErrorString()};
255
0
  }
256
147
  auto bytecode = createBCProviderFromSrc(std::move(BM));
257
147
  bytecode->singleFunction_ = isSingleFunctionExpression(parsed.getValue());
258
147
  return {std::move(bytecode), std::string{}};
259
147
}
260
261
BCProviderLazy::BCProviderLazy(hbc::BytecodeFunction *bytecodeFunction)
262
80
    : bytecodeFunction_(bytecodeFunction) {
263
  // Lazy module should always contain one function to begin with.
264
80
  functionCount_ = 1;
265
80
}
266
267
} // namespace hbc
268
} // namespace hermes