* Copyright (c) 2021-2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "es2panda.h"
#include "utils/perfMetrics.h"
#include "utils/timers.h"
#include <compiler/core/compileQueue.h>
#include <compiler/core/compilerContext.h>
#include <compiler/core/compilerImpl.h>
#include <compiler/core/emitter/emitter.h>
#include <os/stackGuard.h>
#include <parser/parserImpl.h>
#include <parser/transformer/transformer.h>
namespace panda::es2panda {
constexpr size_t DEFAULT_THREAD_COUNT = 2;
size_t Compiler::expectedProgsCount_ = 0;
Compiler::Compiler(ScriptExtension ext) : Compiler(ext, DEFAULT_THREAD_COUNT) {}
Compiler::Compiler(ScriptExtension ext, size_t threadCount)
: parser_(new parser::ParserImpl(ext)), compiler_(new compiler::CompilerImpl(threadCount)),
abcToAsmCompiler_(new panda::abc2program::Abc2ProgramCompiler)
{
panda::StackGuard stackGuard;
stackGuard.Initialize();
parser_->SetStackLimit(stackGuard.StackLimit());
if (parser_->Extension() == ScriptExtension::TS) {
transformer_ = std::make_unique<parser::Transformer>(parser_->Allocator());
}
}
Compiler::~Compiler()
{
delete parser_;
delete compiler_;
delete abcToAsmCompiler_;
}
panda::pandasm::Program *CreateJsonContentProgram(std::string src, std::string rname, util::PatchFix *patchFixHelper)
{
panda::es2panda::compiler::CompilerContext context(nullptr, false, false, false, true, false,
src, "", util::StringView(rname), patchFixHelper);
context.GetEmitter()->GenRecordNameInfo();
return context.GetEmitter()->Finalize(false, nullptr);
}
void Compiler::CheckOptionsAndFileForAbcInput(const std::string &fname, const CompilerOptions &options)
{
if (!options.enableAbcInput) {
throw Error(ErrorType::GENERIC, "\"--enable-abc-input\" is not enabled, abc file " + fname +
"could not be used as the input.");
}
if (options.targetApiVersion < util::Helpers::ABC_TO_PROGRAM_MIN_SUPPORTED_API_VERSION) {
throw Error(ErrorType::GENERIC, "Target api version '" + std::to_string(options.targetApiVersion) +
"' should be greater than or equal to '" +
std::to_string(util::Helpers::ABC_TO_PROGRAM_MIN_SUPPORTED_API_VERSION) + "'.");
}
if (!options.mergeAbc && options.sourceFiles.size() != 1) {
throw Error(ErrorType::GENERIC, "If an abc file is used as input, it must be the only input file "
"when the option '--merge-abc' is not enabled.");
}
if (!abcToAsmCompiler_->OpenAbcFile(fname)) {
throw Error(ErrorType::GENERIC, "Open abc file " + fname + " failed.");
}
if (!abcToAsmCompiler_->CheckFileVersionIsSupported(util::Helpers::ABC_TO_PROGRAM_MIN_SUPPORTED_BYTECODE_VERSION,
options.targetApiVersion, options.targetApiSubVersion)) {
throw Error(ErrorType::GENERIC, "The input abc file '" + fname + "' owns a higher api version or a higher " +
"sdkReleaseType compared to current compilation process.");
}
}
panda::pandasm::Program *Compiler::CompileAbcFile(const std::string &fname, const CompilerOptions &options)
{
try {
CheckOptionsAndFileForAbcInput(fname, options);
return abcToAsmCompiler_->CompileAbcFile();
} catch (const class Error &e) {
std::cerr << e.TypeString() << ": " << e.Message();
std::cerr << " [" << fname << "]" << std::endl;
throw;
}
}
void Compiler::CompileAbcFileInParallel(SourceFile *src, const CompilerOptions &options,
std::map<std::string, panda::es2panda::util::ProgramCache*> &progsInfo,
panda::ArenaAllocator *allocator)
{
try {
CheckOptionsAndFileForAbcInput(src->fileName, options);
} catch (const class Error &e) {
std::cerr << e.TypeString() << ": " << e.Message();
std::cerr << " [" << src->fileName << "]" << std::endl;
throw;
}
if (options.compileContextInfo.needModifyRecord) {
abcToAsmCompiler_->SetBundleName(options.compileContextInfo.bundleName);
}
if (!options.modifiedPkgName.empty()) {
abcToAsmCompiler_->SetModifyPkgName(options.modifiedPkgName);
}
if (!options.compileOhmurlVersionConfigPath.empty()) {
abcToAsmCompiler_->SetPackageName(options.compileOhmurlVersionConfig.packageName);
abcToAsmCompiler_->SetOriginVersion(options.compileOhmurlVersionConfig.originVersion);
abcToAsmCompiler_->SetTargetVersion(options.compileOhmurlVersionConfig.targetVersion);
}
auto *compileAbcClassQueue = new compiler::CompileAbcClassQueue(options.abcClassThreadCount,
options,
*abcToAsmCompiler_,
progsInfo,
allocator,
src);
try {
compileAbcClassQueue->Schedule();
compileAbcClassQueue->Consume();
compileAbcClassQueue->Wait();
} catch (const class Error &e) {
throw e;
}
delete compileAbcClassQueue;
compileAbcClassQueue = nullptr;
}
panda::pandasm::Program *Compiler::Compile(const SourceFile &input, const CompilerOptions &options,
util::SymbolTable *symbolTable)
{
ASSERT(input.isSourceMode);
std::string fname(input.fileName);
std::string src(input.source);
std::string rname(input.recordName);
std::string sourcefile(input.sourcefile);
std::string pkgName(input.pkgName);
auto *patchFixHelper = InitPatchFixHelper(input, options, symbolTable);
if (fname.substr(fname.find_last_of(".") + 1) == "json") {
return CreateJsonContentProgram(src, rname, patchFixHelper);
}
try {
ES2ABC_PERF_SCOPE("@EVENT_EMIT_SINGLE_PROGRAM" + fname);
panda::Timer::timerStart(panda::EVENT_PARSE, fname);
auto ast = parser_->Parse(input, options);
ast.Binder()->SetProgram(&ast);
if (options.dumpAst) {
std::cout << ast.Dump() << std::endl;
}
ProcessAstForTS(&ast, options);
if (options.parseOnly) {
return nullptr;
}
panda::Timer::timerEnd(panda::EVENT_PARSE, fname);
{
ES2ABC_PERF_SCOPE("@EVENT_COMPILE_TO_PROGRAM" + fname);
panda::Timer::timerStart(panda::EVENT_COMPILE_TO_PROGRAM, fname);
std::string debugInfoSourceFile =
options.debugInfoSourceFile.empty() ? sourcefile : options.debugInfoSourceFile;
auto *prog = compiler_->Compile(&ast, options, debugInfoSourceFile, pkgName);
panda::Timer::timerEnd(panda::EVENT_COMPILE_TO_PROGRAM, fname);
CleanPatchFixHelper(patchFixHelper);
return prog;
}
} catch (const class Error &e) {
error_ = e;
CleanPatchFixHelper(patchFixHelper);
return nullptr;
}
}
util::PatchFix *Compiler::InitPatchFixHelper(const SourceFile &input, const CompilerOptions &options,
util::SymbolTable *symbolTable)
{
util::PatchFix *patchFixHelper = nullptr;
bool needDumpSymbolFile = !options.patchFixOptions.dumpSymbolTable.empty();
bool needGeneratePatch = options.patchFixOptions.generatePatch && !options.patchFixOptions.symbolTable.empty();
bool isHotReload = options.patchFixOptions.hotReload;
bool isColdReload = options.patchFixOptions.coldReload;
bool isColdFix = options.patchFixOptions.coldFix;
if (symbolTable && (needDumpSymbolFile || needGeneratePatch || isHotReload || isColdReload)) {
util::PatchFixKind patchFixKind = util::PatchFixKind::DUMPSYMBOLTABLE;
if (needGeneratePatch) {
patchFixKind = isColdFix ? util::PatchFixKind::COLDFIX : util::PatchFixKind::HOTFIX;
}
if (isHotReload) {
patchFixKind = util::PatchFixKind::HOTRELOAD;
}
if (isColdReload) {
patchFixKind = util::PatchFixKind::COLDRELOAD;
}
patchFixHelper = new util::PatchFix(needDumpSymbolFile, needGeneratePatch, patchFixKind, input.recordName,
symbolTable);
parser_->AddPatchFixHelper(patchFixHelper);
compiler_->AddPatchFixHelper(patchFixHelper);
}
return patchFixHelper;
}
void Compiler::CleanPatchFixHelper(const util::PatchFix *patchFixHelper)
{
if (patchFixHelper) {
delete patchFixHelper;
patchFixHelper = nullptr;
}
}
void Compiler::DumpAsm(const panda::pandasm::Program *prog)
{
compiler::CompilerImpl::DumpAsm(prog);
}
int Compiler::CompileFiles(CompilerOptions &options,
std::map<std::string, panda::es2panda::util::ProgramCache*> &progsInfo, panda::ArenaAllocator *allocator)
{
std::unique_ptr<util::SymbolTable> symbolTable;
if (!options.patchFixOptions.symbolTable.empty() || !options.patchFixOptions.dumpSymbolTable.empty()) {
symbolTable = std::make_unique<util::SymbolTable>(options.patchFixOptions.symbolTable,
options.patchFixOptions.dumpSymbolTable);
if (!symbolTable->Initialize(options.targetApiVersion, options.targetApiSubVersion)) {
std::cerr << "Failed to initialize for Hotfix." << std::endl;
return 1;
}
}
bool failed = false;
std::unordered_set<std::string> optimizationPendingProgs;
auto queue = new compiler::CompileFileQueue(options.fileThreadCount, &options, progsInfo,
optimizationPendingProgs, symbolTable.get(), allocator);
try {
queue->Schedule();
queue->Consume();
queue->Wait();
} catch (const class Error &e) {
if (!e.Reported()) {
std::cerr << e.TypeString() << ": " << e.Message() << std::endl;
}
failed = true;
}
delete queue;
queue = nullptr;
if (symbolTable) {
if (!options.patchFixOptions.dumpSymbolTable.empty()) {
symbolTable->WriteSymbolTable();
}
}
if (options.requireGlobalOptimization) {
auto postAnalysisOptimizeQueue = new compiler::PostAnalysisOptimizeFileQueue(options.fileThreadCount,
progsInfo,
optimizationPendingProgs);
try {
postAnalysisOptimizeQueue->Schedule();
postAnalysisOptimizeQueue->Consume();
postAnalysisOptimizeQueue->Wait();
} catch (const class Error &e) {
}
delete postAnalysisOptimizeQueue;
}
return failed ? 1 : 0;
}
panda::pandasm::Program *Compiler::CompileFile(const CompilerOptions &options, SourceFile *src,
util::SymbolTable *symbolTable)
{
auto *program = Compile(*src, options, symbolTable);
if (!program) {
auto &err = GetError();
if (err.Message().empty() && options.parseOnly) {
return nullptr;
}
std::stringstream ss;
ss << err.TypeString() << ": " << err.Message() << " [" << util::Helpers::BaseName(src->fileName) << ":" <<
err.Line() << ":" << err.Col() << "]";
std::cerr << ss.str() << std::endl;
err.SetReported(true);
throw err;
}
return program;
}
void Compiler::ProcessAstForTS(parser::Program *ast, const es2panda::CompilerOptions &options)
{
if (ast->Extension() != ScriptExtension::TS) {
return;
}
transformer_->Transform(ast);
ast->Binder()->IdentifierAnalysis(binder::ResolveBindingFlags::TS_AFTER_TRANSFORM);
if (options.dumpTransformedAst) {
std::cout << ast->Dump() << std::endl;
}
if (options.checkTransformedAstStructure) {
transformer_->CheckTransformedAstStructure(ast);
}
}
}