in lib/CompilerDriver/CompilerDriver.cpp [1753:2051]
CompileResult processSourceFiles(
std::shared_ptr<Context> context,
SegmentTable fileBufs) {
assert(!fileBufs.empty() && "Need at least one file to compile");
assert(context && "Need a context to compile using");
assert(!cl::BytecodeMode && "Input files must not be bytecode");
llvh::SHA1 hasher;
for (const auto &entry : fileBufs) {
for (const auto &fileAndMap : entry.second) {
const auto &file = fileAndMap.file;
hasher.update(
llvh::StringRef(file->getBufferStart(), file->getBufferSize()));
}
}
auto rawFinalHash = hasher.final();
SHA1 sourceHash{};
assert(
rawFinalHash.size() == SHA1_NUM_BYTES && "Incorrect length of SHA1 hash");
std::copy(rawFinalHash.begin(), rawFinalHash.end(), sourceHash.begin());
#ifndef NDEBUG
if (cl::LexerOnly) {
unsigned count = 0;
for (auto &entry : fileBufs) {
for (auto &fileAndMap : entry.second) {
parser::JSLexer jsLexer(
std::move(fileAndMap.file),
context->getSourceErrorManager(),
context->getAllocator());
while (jsLexer.advance()->getKind() != parser::TokenKind::eof)
++count;
}
}
llvh::outs() << count << " tokens lexed\n";
return Success;
}
#endif
// A list of parsed global definition files.
DeclarationFileListTy declFileList;
// Load the runtime library.
std::unique_ptr<llvh::MemoryBuffer> libBuffer;
switch (cl::BytecodeFormat) {
case cl::BytecodeFormatKind::HBC:
libBuffer = llvh::MemoryBuffer::getMemBuffer(libhermes);
break;
}
if (!loadGlobalDefinition(*context, std::move(libBuffer), declFileList)) {
return LoadGlobalsFailed;
}
// Load the global property definitions.
for (const auto &fileName : cl::IncludeGlobals) {
auto fileBuf = memoryBufferFromFile(fileName);
if (!fileBuf)
return InputFileError;
LLVM_DEBUG(
llvh::dbgs() << "Parsing global definitions from " << fileName << '\n');
if (!loadGlobalDefinition(*context, std::move(fileBuf), declFileList)) {
return LoadGlobalsFailed;
}
}
// Create the source map if requested.
llvh::Optional<SourceMapGenerator> sourceMapGen{};
if (cl::OutputSourceMap) {
sourceMapGen = SourceMapGenerator{};
}
Module M(context);
sem::SemContext semCtx{};
if (context->getUseCJSModules()) {
// Allow the IR generation function to populate inputSourceMaps to ensure
// proper source map ordering.
if (!generateIRForSourcesAsCJSModules(
M,
semCtx,
declFileList,
std::move(fileBufs),
sourceMapGen ? &*sourceMapGen : nullptr)) {
return ParsingFailed;
}
if (cl::DumpTarget < DumpIR) {
return Success;
}
} else {
if (sourceMapGen) {
for (const auto &filename : cl::InputFilenames) {
sourceMapGen->addSource(filename == "-" ? "<stdin>" : filename);
}
}
auto &mainFileBuf = fileBufs[0][0];
std::unique_ptr<SourceMap> sourceMap{nullptr};
if (mainFileBuf.sourceMap) {
SourceErrorManager sm;
sourceMap = SourceMapParser::parse(*mainFileBuf.sourceMap, sm);
if (!sourceMap) {
// parse() returns nullptr on failure and reports its own errors.
return InputFileError;
}
}
auto sourceMapTranslator =
std::make_shared<SourceMapTranslator>(context->getSourceErrorManager());
context->getSourceErrorManager().setTranslator(sourceMapTranslator);
ESTree::NodePtr ast = parseJS(
context,
semCtx,
std::move(mainFileBuf.file),
std::move(sourceMap),
sourceMapTranslator);
if (!ast) {
return ParsingFailed;
}
if (cl::DumpTarget < DumpIR) {
return Success;
}
generateIRFromESTree(ast, &M, declFileList, {});
}
// Bail out if there were any errors. We can't ensure that the module is in
// a valid state.
if (auto N = context->getSourceErrorManager().getErrorCount()) {
llvh::errs() << "Emitted " << N << " errors. exiting.\n";
return ParsingFailed;
}
// Run custom optimization pipeline.
if (!cl::CustomOptimize.empty()) {
std::vector<std::string> opts(
cl::CustomOptimize.begin(), cl::CustomOptimize.end());
if (!runCustomOptimizationPasses(M, opts)) {
llvh::errs() << "Invalid custom optimizations selected.\n\n"
<< PassManager::getCustomPassText();
return InvalidFlags;
}
} else {
switch (cl::OptimizationLevel) {
case cl::OptLevel::O0:
runNoOptimizationPasses(M);
break;
case cl::OptLevel::Og:
runDebugOptimizationPasses(M);
break;
case cl::OptLevel::OMax:
runFullOptimizationPasses(M);
break;
}
}
// Bail out if there were any errors during optimization.
if (auto N = context->getSourceErrorManager().getErrorCount()) {
llvh::errs() << "Emitted " << N << " errors. exiting.\n";
return OptimizationFailed;
}
// In dbg builds, verify the module before we emit bytecode.
if (cl::VerifyIR) {
bool failedVerification = verifyModule(M, &llvh::errs());
if (failedVerification) {
M.dump();
return VerificationFailed;
}
assert(!failedVerification && "Module verification failed!");
}
if (cl::DumpTarget == DumpIR) {
M.dump();
return Success;
}
#ifndef NDEBUG
if (cl::DumpTarget == ViewCFG) {
M.viewGraph();
return Success;
}
#endif
BytecodeGenerationOptions genOptions{cl::DumpTarget};
genOptions.optimizationEnabled = cl::OptimizationLevel > cl::OptLevel::Og;
genOptions.prettyDisassemble = cl::Pretty;
genOptions.basicBlockProfiling = cl::BasicBlockProfiling;
// The static builtin setting should be set correctly after command line
// options parsing and js parsing. Set the bytecode header flag here.
genOptions.staticBuiltinsEnabled = context->getStaticBuiltinOptimization();
genOptions.padFunctionBodiesPercent = cl::PadFunctionBodiesPercent;
// If the user requests to output a source map, then do not also emit debug
// info into the bytecode.
genOptions.stripDebugInfoSection =
cl::OutputSourceMap || cl::DebugInfoLevel == cl::DebugLevel::g0;
genOptions.stripFunctionNames = cl::StripFunctionNames;
// If the dump target is None, return bytecode in an executable form.
if (cl::DumpTarget == Execute) {
assert(
!sourceMapGen &&
"validateFlags() should enforce no source map output for execution");
return generateBytecodeForExecution(M, genOptions);
}
BaseBytecodeMap baseBytecodeMap;
if (cl::BytecodeFormat == cl::BytecodeFormatKind::HBC &&
!cl::BaseBytecodeFile.empty()) {
if (!readBaseBytecodeMap(
baseBytecodeMap, cl::BaseBytecodeFile, context->getAllocator())) {
return InputFileError;
}
}
CompileResult result{Success};
StringRef base = cl::BytecodeOutputFilename;
if (context->getSegments().size() < 2) {
OutputStream fileOS{llvh::outs()};
if (!base.empty() && !fileOS.open(base, F_None)) {
return OutputFileError;
}
auto result = generateBytecodeForSerialization(
fileOS.os(),
M,
genOptions,
sourceHash,
llvh::None,
sourceMapGen ? sourceMapGen.getPointer() : nullptr,
baseBytecodeMap);
if (result.status != Success) {
return result;
}
if (!fileOS.close())
return OutputFileError;
} else {
OutputStream manifestOS{llvh::nulls()};
if (!base.empty() && !cl::BytecodeManifestFilename.empty()) {
llvh::SmallString<32> manifestPath = llvh::sys::path::parent_path(base);
llvh::sys::path::append(manifestPath, cl::BytecodeManifestFilename);
if (!manifestOS.open(manifestPath, F_Text))
return OutputFileError;
}
JSONEmitter manifest{manifestOS.os(), /* pretty */ true};
manifest.openArray();
for (const auto segment : context->getSegments()) {
std::string filename = base.str();
if (segment != 0) {
filename += "." + std::to_string(segment);
}
std::string flavor = "hbc-seg-" + std::to_string(segment);
OutputStream fileOS{llvh::outs()};
if (!base.empty() && !fileOS.open(filename, F_None)) {
return OutputFileError;
}
auto segResult = generateBytecodeForSerialization(
fileOS.os(),
M,
genOptions,
sourceHash,
segment,
sourceMapGen ? sourceMapGen.getPointer() : nullptr,
baseBytecodeMap);
if (segResult.status != Success) {
return segResult;
}
if (!fileOS.close())
return OutputFileError;
// Add to the manifest.
manifest.openDict();
manifest.emitKeyValue("resource", llvh::sys::path::filename(base));
manifest.emitKeyValue("flavor", flavor);
manifest.emitKeyValue("location", llvh::sys::path::filename(filename));
manifest.closeDict();
}
manifest.closeArray();
if (!manifestOS.close()) {
return OutputFileError;
}
result = Success;
}
// Output the source map if requested.
if (cl::OutputSourceMap) {
OutputStream OS;
if (!OS.open(base.str() + ".map", F_Text))
return OutputFileError;
sourceMapGen->outputAsJSON(OS.os());
if (!OS.close())
return OutputFileError;
}
return result;
}