lib/BCGen/HBC/BytecodeProviderFromSrc.cpp (199 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "hermes/BCGen/HBC/BytecodeProviderFromSrc.h"
#include "hermes/AST/SemValidate.h"
#include "hermes/BCGen/HBC/HBC.h"
#include "hermes/Parser/JSParser.h"
#include "hermes/Runtime/Libhermes.h"
#include "hermes/SourceMap/SourceMapTranslator.h"
#include "hermes/Support/MemoryBuffer.h"
#include "hermes/Support/SimpleDiagHandler.h"
namespace hermes {
namespace hbc {
namespace {
bool isSingleFunctionExpression(ESTree::NodePtr ast) {
auto *prog = llvh::dyn_cast<ESTree::ProgramNode>(ast);
if (!prog) {
return false;
}
ESTree::NodeList &body = prog->_body;
if (body.size() != 1) {
return false;
}
auto *exprStatement =
llvh::dyn_cast<ESTree::ExpressionStatementNode>(&body.front());
if (!exprStatement) {
return false;
}
return llvh::isa<ESTree::FunctionExpressionNode>(
exprStatement->_expression) ||
llvh::isa<ESTree::ArrowFunctionExpressionNode>(
exprStatement->_expression);
}
} // namespace
BCProviderFromSrc::BCProviderFromSrc(
std::unique_ptr<hbc::BytecodeModule> module)
: module_(std::move(module)) {
options_ = module_->getBytecodeOptions();
functionCount_ = module_->getNumFunctions();
globalFunctionIndex_ = module_->getGlobalFunctionIndex();
stringKinds_ = module_->getStringKinds();
identifierHashes_ = module_->getIdentifierHashes();
stringCount_ = module_->getStringTable().size();
stringStorage_ = module_->getStringStorage();
regExpStorage_ = module_->getRegExpStorage();
regExpTable_ = module_->getRegExpTable();
arrayBuffer_ = module_->getArrayBuffer();
objKeyBuffer_ = module_->getObjectBuffer().first;
objValueBuffer_ = module_->getObjectBuffer().second;
segmentID_ = module_->getSegmentID();
cjsModuleTable_ = module_->getCJSModuleTable();
cjsModuleTableStatic_ = module_->getCJSModuleTableStatic();
functionSourceTable_ = module_->getFunctionSourceTable();
debugInfo_ = &module_->getDebugInfo();
}
std::pair<std::unique_ptr<BCProviderFromSrc>, std::string>
BCProviderFromSrc::createBCProviderFromSrc(
std::unique_ptr<Buffer> buffer,
llvh::StringRef sourceURL,
const CompileFlags &compileFlags) {
return createBCProviderFromSrc(
std::move(buffer), sourceURL, /*sourceMap*/ nullptr, compileFlags);
}
std::pair<std::unique_ptr<BCProviderFromSrc>, std::string>
BCProviderFromSrc::createBCProviderFromSrc(
std::unique_ptr<Buffer> buffer,
llvh::StringRef sourceURL,
std::unique_ptr<SourceMap> sourceMap,
const CompileFlags &compileFlags) {
return createBCProviderFromSrc(
std::move(buffer), sourceURL, std::move(sourceMap), compileFlags, {}, {});
}
std::pair<std::unique_ptr<BCProviderFromSrc>, std::string>
BCProviderFromSrc::createBCProviderFromSrc(
std::unique_ptr<Buffer> buffer,
llvh::StringRef sourceURL,
std::unique_ptr<SourceMap> sourceMap,
const CompileFlags &compileFlags,
const ScopeChain &scopeChain,
SourceErrorManager::DiagHandlerTy diagHandler,
void *diagContext,
const std::function<void(Module &)> &runOptimizationPasses) {
return createBCProviderFromSrcImpl(
std::move(buffer),
sourceURL,
std::move(sourceMap),
compileFlags,
scopeChain,
diagHandler,
diagContext,
runOptimizationPasses);
}
std::pair<std::unique_ptr<BCProviderFromSrc>, std::string>
BCProviderFromSrc::createBCProviderFromSrcImpl(
std::unique_ptr<Buffer> buffer,
llvh::StringRef sourceURL,
std::unique_ptr<SourceMap> sourceMap,
const CompileFlags &compileFlags,
const ScopeChain &scopeChain,
SourceErrorManager::DiagHandlerTy diagHandler,
void *diagContext,
const std::function<void(Module &)> &runOptimizationPasses) {
using llvh::Twine;
assert(
buffer->data()[buffer->size()] == 0 &&
"The input buffer must be null terminated");
CodeGenerationSettings codeGenOpts{};
codeGenOpts.unlimitedRegisters = false;
codeGenOpts.instrumentIR = compileFlags.instrumentIR;
OptimizationSettings optSettings;
// If the optional value is not set, the parser will automatically detect
// the 'use static builtin' directive and we will set it correctly.
optSettings.staticBuiltins = compileFlags.staticBuiltins.hasValue()
? compileFlags.staticBuiltins.getValue()
: false;
auto context = std::make_shared<Context>(codeGenOpts, optSettings);
std::unique_ptr<SimpleDiagHandlerRAII> outputManager;
if (diagHandler) {
context->getSourceErrorManager().setDiagHandler(diagHandler, diagContext);
} else {
outputManager.reset(
new SimpleDiagHandlerRAII(context->getSourceErrorManager()));
}
// If a custom diagHandler was provided, it will receive the details and we
// just return the string "error" on failure.
auto getErrorString = [&outputManager]() {
return outputManager ? outputManager->getErrorString()
: std::string("error");
};
// To avoid frequent source buffer rescans, avoid emitting warnings about
// undefined variables.
context->getSourceErrorManager().setWarningStatus(
Warning::UndefinedVariable, false);
context->setStrictMode(compileFlags.strict);
context->setEnableEval(true);
context->setPreemptiveFunctionCompilationThreshold(
compileFlags.preemptiveFunctionCompilationThreshold);
context->setPreemptiveFileCompilationThreshold(
compileFlags.preemptiveFileCompilationThreshold);
if (compileFlags.lazy && !runOptimizationPasses) {
context->setLazyCompilation(true);
}
context->setGeneratorEnabled(compileFlags.enableGenerator);
context->setDebugInfoSetting(
compileFlags.debug ? DebugInfoSetting::ALL : DebugInfoSetting::THROWING);
context->setEmitAsyncBreakCheck(compileFlags.emitAsyncBreakCheck);
// Populate the declFileList.
DeclarationFileListTy declFileList;
if (compileFlags.includeLibHermes) {
auto libBuffer = llvh::MemoryBuffer::getMemBuffer(libhermes);
parser::JSParser libParser(*context, std::move(libBuffer));
auto libParsed = libParser.parse();
assert(libParsed && "Libhermes failed to parse");
declFileList.push_back(libParsed.getValue());
}
bool isLargeFile =
buffer->size() >= context->getPreemptiveFileCompilationThreshold();
int fileBufId = context->getSourceErrorManager().addNewSourceBuffer(
std::make_unique<HermesLLVMMemoryBuffer>(std::move(buffer), sourceURL));
if (sourceMap != nullptr) {
auto sourceMapTranslator =
std::make_shared<SourceMapTranslator>(context->getSourceErrorManager());
context->getSourceErrorManager().setTranslator(sourceMapTranslator);
sourceMapTranslator->addSourceMap(fileBufId, std::move(sourceMap));
}
auto parserMode = parser::FullParse;
bool useStaticBuiltinDetected = false;
if (context->isLazyCompilation() && isLargeFile) {
if (!parser::JSParser::preParseBuffer(
*context, fileBufId, useStaticBuiltinDetected)) {
return {nullptr, getErrorString()};
}
parserMode = parser::LazyParse;
}
sem::SemContext semCtx{};
parser::JSParser parser(*context, fileBufId, parserMode);
auto parsed = parser.parse();
if (!parsed || !hermes::sem::validateAST(*context, semCtx, *parsed)) {
return {nullptr, getErrorString()};
}
// If we are using lazy parse mode, we should have already detected the 'use
// static builtin' directive in the pre-parsing stage.
if (parserMode != parser::LazyParse) {
useStaticBuiltinDetected = parser.getUseStaticBuiltin();
}
// The compiler flag is not set, automatically detect 'use static builtin'
// from the source.
if (!compileFlags.staticBuiltins) {
context->setStaticBuiltinOptimization(useStaticBuiltinDetected);
}
Module M(context);
hermes::generateIRFromESTree(parsed.getValue(), &M, declFileList, scopeChain);
if (context->getSourceErrorManager().getErrorCount() > 0) {
return {nullptr, getErrorString()};
}
if (runOptimizationPasses)
runOptimizationPasses(M);
BytecodeGenerationOptions opts{compileFlags.format};
opts.optimizationEnabled = !!runOptimizationPasses;
opts.staticBuiltinsEnabled =
context->getOptimizationSettings().staticBuiltins;
opts.verifyIR = compileFlags.verifyIR;
auto bytecode = createBCProviderFromSrc(
hbc::generateBytecodeModule(&M, M.getTopLevelFunction(), opts));
bytecode->singleFunction_ = isSingleFunctionExpression(parsed.getValue());
return {std::move(bytecode), std::string{}};
}
BCProviderLazy::BCProviderLazy(hbc::BytecodeFunction *bytecodeFunction)
: bytecodeFunction_(bytecodeFunction) {
// Lazy module should always contain one function to begin with.
functionCount_ = 1;
}
} // namespace hbc
} // namespace hermes