mirror of
https://github.com/openharmony/arkcompiler_ets_frontend.git
synced 2026-08-25 12:00:14 -04:00
684ff0c20b
Issue: https://gitcode.com/openharmony/arkcompiler_ets_frontend/issues/9061 Change-Id: I5801999542dd483c604608879afc284d58a721f3 Signed-off-by: ElevenDuan <duanshiyi1@huawei.com> Signed-off-by: zmw <zhongmingwei2@huawei.com>
329 lines
13 KiB
C++
329 lines
13 KiB
C++
/**
|
|
* 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 {
|
|
// Compiler
|
|
|
|
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);
|
|
/* TODO(dbatyai): pass string view */
|
|
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) {
|
|
// Optimization failed, but the program can still be used as unoptimized
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
|
|
} // namespace panda::es2panda
|