CompileResult processSourceFiles()

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;
}