/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 |