/** * Copyright (c) 2021 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 "emitter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace panda::es2panda::compiler { constexpr const auto LANG_EXT = panda::pandasm::extensions::Language::ECMASCRIPT; FunctionEmitter::FunctionEmitter(ArenaAllocator *allocator, const PandaGen *pg) : pg_(pg), literalBuffers_(allocator->Adapter()) { func_ = allocator->New(pg->InternalName().Mutf8(), LANG_EXT); CHECK_NOT_NULL(func_); size_t paramCount = pg->InternalParamCount(); func_->params.reserve(paramCount); for (uint32_t i = 0; i < paramCount; ++i) { func_->params.emplace_back(panda::pandasm::Type("any", 0), LANG_EXT); } func_->regs_num = pg->TotalRegsNum(); func_->return_type = panda::pandasm::Type("any", 0); } void FunctionEmitter::Generate(util::PatchFix *patchFixHelper) { GenFunctionKind(); GenIcSize(); GenFunctionInstructions(); GenVariablesDebugInfo(); GenSourceFileDebugInfo(); GenFunctionCatchTables(); GenLiteralBuffers(); GenConcurrentFunctionModuleRequests(); if (patchFixHelper != nullptr) { patchFixHelper->ProcessFunction(pg_, func_, literalBuffers_); } } const ArenaSet &FunctionEmitter::Strings() const { return pg_->Strings(); } void FunctionEmitter::GenFunctionKind() { func_->SetFunctionKind(static_cast(pg_->GetFunctionKind())); } void FunctionEmitter::GenIcSize() { func_->SetSlotsNum(pg_->GetCurrentSlot()); } void FunctionEmitter::GenBufferLiterals(const LiteralBuffer *buff) { Emitter::GenBufferLiterals(literalBuffers_, buff); } util::StringView FunctionEmitter::SourceCode() const { if (pg_->RootNode()->IsProgram()) { return pg_->Binder()->Program()->SourceCode(); } return static_cast(pg_->RootNode())->SourceCode(pg_->Binder()); } lexer::LineIndex &FunctionEmitter::GetLineIndex() const { return const_cast(pg_->Binder()->Program()->GetLineIndex()); } static Format MatchFormat(const IRNode *node, const Formats &formats) { std::array regs {}; auto regCnt = node->Registers(®s); auto registers = Span(regs.data(), regs.data() + regCnt); const auto *iter = formats.begin(); for (; iter != formats.end(); iter++) { auto format = *iter; size_t limit = 0; for (const auto &formatItem : format.GetFormatItem()) { if (formatItem.IsVReg()) { limit = 1 << formatItem.Bitwidth(); break; } } if (std::all_of(registers.begin(), registers.end(), [limit](const VReg *reg) { return *reg < limit; })) { return format; } } UNREACHABLE(); return *iter; } static size_t GetIRNodeWholeLength(const IRNode *node) { Formats formats = node->GetFormats(); if (formats.empty()) { return 0; } size_t len = 1; constexpr size_t BIT_WIDTH = 8; const auto format = MatchFormat(node, formats); for (auto fi : format.GetFormatItem()) { len += fi.Bitwidth() / BIT_WIDTH; } return len; } [[maybe_unused]] static std::string WholeLine(const util::StringView &source, lexer::SourceRange range) { return source.Substr(range.start.index, range.end.index).EscapeSymbol(); } // This is for supporting setting breakpoint on the right parenthesis of a scriptFunciton. uint32_t FunctionEmitter::UpdateForReturnIns(const ir::AstNode *astNode, panda::pandasm::Ins *pandaIns) { constexpr size_t INVALID_LINE = -1; constexpr uint32_t INVALID_COL = -1; // The GetLocation method calculated position starts with 1, and // the column number in pandaIns->ins_debug starts with 0 constexpr uint32_t OFFSET_COL = 1; uint32_t columnNum = INVALID_COL; if (pandaIns->opcode == pandasm::Opcode::RETURNUNDEFINED || pandaIns->opcode == pandasm::Opcode::RETURN) { while (astNode != nullptr && !astNode->IsScriptFunction()) { if (astNode->IsBlockStatement() && astNode->AsBlockStatement()->Scope() && astNode->AsBlockStatement()->Scope()->Node() && astNode->AsBlockStatement()->Scope()->Node()->IsScriptFunction()) { astNode = astNode->AsBlockStatement()->Scope()->Node(); break; } astNode = astNode->Parent(); } pandaIns->ins_debug.line_number = astNode ? astNode->Range().end.line : INVALID_LINE; columnNum = astNode ? (GetLineIndex().GetLocation(astNode->Range().end).col - OFFSET_COL) : INVALID_COL; } else { pandaIns->ins_debug.line_number = astNode ? astNode->Range().start.line : INVALID_LINE; columnNum = astNode ? (GetLineIndex().GetLocation(astNode->Range().start).col - OFFSET_COL) : INVALID_COL; } return columnNum; } void FunctionEmitter::GenInstructionDebugInfo(const IRNode *ins, panda::pandasm::Ins *pandaIns) { const ir::AstNode *astNode = ins->Node(); if (astNode == FIRST_NODE_OF_FUNCTION) { astNode = pg_->Debuginfo().firstStmt; if (!astNode) { return; } } uint32_t columnNum = UpdateForReturnIns(astNode, pandaIns); if (pg_->IsDebug()) { size_t insLen = GetIRNodeWholeLength(ins); if (insLen != 0) { pandaIns->ins_debug.bound_left = offset_; pandaIns->ins_debug.bound_right = offset_ + insLen; } offset_ += insLen; pandaIns->ins_debug.column_number = columnNum; } } void FunctionEmitter::GenFunctionInstructions() { func_->ins.reserve(pg_->Insns().size()); for (const auto *ins : pg_->Insns()) { auto &pandaIns = func_->ins.emplace_back(); ins->Transform(&pandaIns); GenInstructionDebugInfo(ins, &pandaIns); } } void FunctionEmitter::GenFunctionCatchTables() { func_->catch_blocks.reserve(pg_->CatchList().size()); for (const auto *catchBlock : pg_->CatchList()) { const auto &labelSet = catchBlock->LabelSet(); auto &pandaCatchBlock = func_->catch_blocks.emplace_back(); pandaCatchBlock.try_begin_label = labelSet.TryBegin()->Id(); pandaCatchBlock.try_end_label = labelSet.TryEnd()->Id(); pandaCatchBlock.catch_begin_label = labelSet.CatchBegin()->Id(); pandaCatchBlock.catch_end_label = labelSet.CatchEnd()->Id(); } } void FunctionEmitter::GenLiteralBuffers() { for (const auto *buff : pg_->BuffStorage()) { GenBufferLiterals(buff); } } void FunctionEmitter::GenSourceFileDebugInfo() { if (pg_->SourceFile().empty()) { func_->source_file = std::string {pg_->Binder()->Program()->SourceFile()}; } else { func_->source_file = pg_->SourceFile(); } if (!pg_->IsDebug()) { return; } if (pg_->RootNode()->IsProgram()) { if (pg_->Context()->IsRecordDebugSource()) { func_->source_code = SourceCode().Mutf8(); } else { func_->source_code = "not supported"; } } } void FunctionEmitter::GenScopeVariableInfo(const binder::Scope *scope) { const auto *startIns = scope->ScopeStart(); const auto *endIns = scope->ScopeEnd(); uint32_t start = 0; uint32_t count = 0; for (const auto *it : pg_->Insns()) { if (startIns == it) { start = count; } else if (endIns == it) { auto varsLength = static_cast(count - start + 1); for (const auto &[name, variable] : scope->Bindings()) { if (!variable->IsLocalVariable() || variable->LexicalBound()) { continue; } auto &variableDebug = func_->local_variable_debug.emplace_back(); variableDebug.name = name.Mutf8(); variableDebug.signature = "any"; variableDebug.signature_type = "any"; // Register spill causes an offset being applied to all registers in all instructions (refer to // RegAllocator::AdjustInsRegWhenHasSpill for more details). Therefore, we also add this offset // to the variable-to-register mapping before dumping it to the debug information of the abc file // (the following code). // We do not correct the original variable mapping in the scope object since it is not used after // the register spill phase, while we do not know the number of spilled registers before that phase. // If any future modifications require access to the variable mapping after the register spill, // please correct the mapping first and remove the offset increment operation in the following code. variableDebug.reg = static_cast(variable->AsLocalVariable()->Vreg()) + pg_->GetSpillRegsCount(); variableDebug.start = start; variableDebug.length = static_cast(varsLength); } break; } count++; } } void FunctionEmitter::GenVariablesDebugInfo() { if (!pg_->IsDebug()) { return; } for (const auto *scope : pg_->Debuginfo().variableDebugInfo) { GenScopeVariableInfo(scope); } } void FunctionEmitter::GenConcurrentFunctionModuleRequests() { if (static_cast(pg_->GetFunctionKind()) != panda::panda_file::FunctionKind::CONCURRENT_FUNCTION) { return; } std::vector moduleRequests = static_cast(pg_->RootNode())->GetConcurrentModuleRequests(); func_->concurrent_module_requests.reserve(moduleRequests.size()); for (auto it : moduleRequests) { func_->concurrent_module_requests.emplace_back(it); } } // Emitter Emitter::Emitter(CompilerContext *context) { prog_ = new panda::pandasm::Program(); prog_->lang = LANG_EXT; if (context->IsJsonInputFile()) { GenJsonContentRecord(context); return; } if (context->IsMergeAbc()) { auto recordName = context->Binder()->Program()->FormatedRecordName().Mutf8(); rec_ = new panda::pandasm::Record(recordName.substr(0, recordName.find_last_of('.')), LANG_EXT); SetPkgNameField(context->PkgName()); SetCommonjsField(context->Binder()->Program()->Kind() == parser::ScriptKind::COMMONJS); } else { rec_ = nullptr; if (context->Binder()->Program()->Kind() == parser::ScriptKind::COMMONJS) { GenCommonjsRecord(); } } if (context->Binder()->Program()->Kind() == parser::ScriptKind::MODULE) { AddHasTopLevelAwaitRecord(context->Binder()->Program()->HasTLA(), context); } AddSharedModuleRecord(context); AddScopeNamesRecord(context); } Emitter::~Emitter() { delete prog_; } void Emitter::SetPkgNameField(const std::string &pkgName) { auto pkgNameField = panda::pandasm::Field(LANG_EXT); pkgNameField.name = "pkgName@" + pkgName; pkgNameField.type = panda::pandasm::Type("u8", 0); pkgNameField.metadata->SetValue( panda::pandasm::ScalarValue::Create(static_cast(0))); rec_->field_list.emplace_back(std::move(pkgNameField)); } void Emitter::GenRecordNameInfo() const { if (rec_) { prog_->record_table.emplace(rec_->name, std::move(*rec_)); } } void Emitter::GenJsonContentRecord(const CompilerContext *context) { rec_ = new panda::pandasm::Record(std::string(context->RecordName()), panda::panda_file::SourceLang::ECMASCRIPT); auto jsonContentField = panda::pandasm::Field(panda::panda_file::SourceLang::ECMASCRIPT); jsonContentField.name = "jsonFileContent"; jsonContentField.type = panda::pandasm::Type("u32", 0); jsonContentField.metadata->SetValue(panda::pandasm::ScalarValue::Create( static_cast(context->SourceFile()))); rec_->field_list.emplace_back(std::move(jsonContentField)); if (context->PatchFixHelper()) { context->PatchFixHelper()->ProcessJsonContentRecord(rec_->name, context->SourceFile()); } } void Emitter::AddFunction(FunctionEmitter *func, CompilerContext *context) { std::lock_guard lock(m_); for (const auto &str : func->Strings()) { prog_->strings.insert(str.Mutf8()); } for (auto &[idx, buf] : func->LiteralBuffers()) { auto literalArrayInstance = panda::pandasm::LiteralArray(std::move(buf)); auto litId = std::string(context->Binder()->Program()->RecordName()) + "_" + std::to_string(idx); prog_->literalarray_table.emplace(litId, std::move(literalArrayInstance)); } auto *function = func->Function(); prog_->function_table.emplace(function->name, std::move(*function)); } void Emitter::AddScopeNamesRecord(CompilerContext *context) { std::lock_guard lock(m_); // make literalarray for scope names if (util::Helpers::IsDefaultApiVersion(context->Binder()->Program()->TargetApiVersion(), context->Binder()->Program()->GetTargetApiSubVersion())) { return; } const auto &scopeNamesMap = context->Binder()->GetScopeNames(); panda::pandasm::LiteralArray array; const int32_t DOUBLE_SIZE = 2; array.literals_.resize(scopeNamesMap.size() * DOUBLE_SIZE); auto strTag = panda_file::LiteralTag::STRING; for (const auto &[scopeName, index] : scopeNamesMap) { panda::pandasm::LiteralArray::Literal tag; tag.tag_ = panda_file::LiteralTag::TAGVALUE; tag.value_ = static_cast(strTag); array.literals_.at(index * DOUBLE_SIZE) = std::move(tag); panda::pandasm::LiteralArray::Literal val; val.tag_ = strTag; val.value_ = std::string(scopeName); array.literals_.at(index * DOUBLE_SIZE + 1) = std::move(val); } auto literalKey = std::string(context->Binder()->Program()->RecordName()) + "_" + std::to_string(context->NewLiteralIndex()); prog_->literalarray_table.emplace(literalKey, std::move(array)); // ScopeNames is a literalarray in each record if it is in mergeAbc, it is a string array which put scope names. // _ESScopeNamesRecord is a literalarray in the record when it is not in mergeAbc. if (context->IsMergeAbc()) { auto scopeNamesField = panda::pandasm::Field(LANG_EXT); scopeNamesField.name = "scopeNames"; scopeNamesField.type = panda::pandasm::Type("u32", 0); scopeNamesField.metadata->SetValue( panda::pandasm::ScalarValue::Create( static_cast(literalKey))); rec_->field_list.emplace_back(std::move(scopeNamesField)); } else { auto scopeNamesRecord = panda::pandasm::Record("_ESScopeNamesRecord", LANG_EXT); scopeNamesRecord.metadata->SetAccessFlags(panda::ACC_PUBLIC); auto scopeNamesField = panda::pandasm::Field(LANG_EXT); // If the input arg "source-file" is not specified, context->SourceFile() will be empty, // in this case, use it's absolute path. if (context->SourceFile().empty()) { scopeNamesField.name = context->Binder()->Program()->SourceFile().Mutf8(); } else { scopeNamesField.name = context->SourceFile(); } scopeNamesField.type = panda::pandasm::Type("u32", 0); scopeNamesField.metadata->SetValue( panda::pandasm::ScalarValue::Create( static_cast(literalKey))); scopeNamesRecord.field_list.emplace_back(std::move(scopeNamesField)); prog_->record_table.emplace(scopeNamesRecord.name, std::move(scopeNamesRecord)); } } void Emitter::AddSourceTextModuleRecord(ModuleRecordEmitter *module, CompilerContext *context) { std::lock_guard lock(m_); if (module->NeedEmitPhaseRecord()) { AddModuleRequestPhaseRecord(module, context); } auto moduleLiteral = std::string(context->Binder()->Program()->RecordName()) + "_" + std::to_string(module->Index()); if (context->IsMergeAbc()) { auto moduleIdxField = panda::pandasm::Field(LANG_EXT); moduleIdxField.name = "moduleRecordIdx"; moduleIdxField.type = panda::pandasm::Type("u32", 0); moduleIdxField.metadata->SetValue( panda::pandasm::ScalarValue::Create( static_cast(moduleLiteral))); rec_->field_list.emplace_back(std::move(moduleIdxField)); if (context->PatchFixHelper()) { context->PatchFixHelper()->ProcessModule(rec_->name, module->Buffer()); } } else { auto ecmaModuleRecord = panda::pandasm::Record("_ESModuleRecord", LANG_EXT); ecmaModuleRecord.metadata->SetAccessFlags(panda::ACC_PUBLIC); auto moduleIdxField = panda::pandasm::Field(LANG_EXT); moduleIdxField.name = context->Binder()->Program()->ModuleRecordFieldName().empty() ? std::string {context->Binder()->Program()->SourceFile()} : context->Binder()->Program()->ModuleRecordFieldName(); moduleIdxField.type = panda::pandasm::Type("u32", 0); moduleIdxField.metadata->SetValue( panda::pandasm::ScalarValue::Create( static_cast(moduleLiteral))); ecmaModuleRecord.field_list.emplace_back(std::move(moduleIdxField)); if (context->PatchFixHelper()) { context->PatchFixHelper()->ProcessModule(ecmaModuleRecord.name, module->Buffer()); } prog_->record_table.emplace(ecmaModuleRecord.name, std::move(ecmaModuleRecord)); } auto &moduleLiteralsBuffer = module->Buffer(); auto literalArrayInstance = panda::pandasm::LiteralArray(std::move(moduleLiteralsBuffer)); prog_->literalarray_table.emplace(static_cast(moduleLiteral), std::move(literalArrayInstance)); constant_local_export_slots_ = module->GetConstantLocalExportSlots(); } void Emitter::AddModuleRequestPhaseRecord(ModuleRecordEmitter *module, CompilerContext *context) { auto phaseLiteral = std::string(context->Binder()->Program()->RecordName()) + "_" + std::to_string(module->PhaseIndex()); if (context->IsMergeAbc()) { auto phaseIdxField = panda::pandasm::Field(LANG_EXT); phaseIdxField.name = "moduleRequestPhaseIdx"; phaseIdxField.type = panda::pandasm::Type("u32", 0); phaseIdxField.metadata->SetValue( panda::pandasm::ScalarValue::Create( static_cast(phaseLiteral))); rec_->field_list.emplace_back(std::move(phaseIdxField)); } else { auto moduleRequestPhaseRecord = panda::pandasm::Record("_ModuleRequestPhaseRecord", LANG_EXT); moduleRequestPhaseRecord.metadata->SetAccessFlags(panda::ACC_PUBLIC); auto phaseIdxField = panda::pandasm::Field(LANG_EXT); phaseIdxField.name = "moduleRequestPhaseIdx"; phaseIdxField.type = panda::pandasm::Type("u32", 0); phaseIdxField.metadata->SetValue( panda::pandasm::ScalarValue::Create( static_cast(phaseLiteral))); moduleRequestPhaseRecord.field_list.emplace_back(std::move(phaseIdxField)); prog_->record_table.emplace(moduleRequestPhaseRecord.name, std::move(moduleRequestPhaseRecord)); } auto &moduleRequestPhaseLiteralsBuffer = module->PhaseBuffer(); auto literalArrayInstance = panda::pandasm::LiteralArray(std::move(moduleRequestPhaseLiteralsBuffer)); prog_->literalarray_table.emplace(static_cast(phaseLiteral), std::move(literalArrayInstance)); } void Emitter::AddHasTopLevelAwaitRecord(bool hasTLA, const CompilerContext *context) { if (context->IsMergeAbc()) { auto hasTLAField = panda::pandasm::Field(LANG_EXT); hasTLAField.name = "hasTopLevelAwait"; hasTLAField.type = panda::pandasm::Type("u8", 0); hasTLAField.metadata->SetValue( panda::pandasm::ScalarValue::Create(static_cast(hasTLA)) ); rec_->field_list.emplace_back(std::move(hasTLAField)); } else if (hasTLA) { auto hasTLARecord = panda::pandasm::Record("_HasTopLevelAwait", LANG_EXT); hasTLARecord.metadata->SetAccessFlags(panda::ACC_PUBLIC); auto hasTLAField = panda::pandasm::Field(LANG_EXT); hasTLAField.name = "hasTopLevelAwait"; hasTLAField.type = panda::pandasm::Type("u8", 0); hasTLAField.metadata->SetValue( panda::pandasm::ScalarValue::Create(static_cast(true)) ); hasTLARecord.field_list.emplace_back(std::move(hasTLAField)); prog_->record_table.emplace(hasTLARecord.name, std::move(hasTLARecord)); } } void Emitter::AddSharedModuleRecord(const CompilerContext *context) { bool isShared = context->Binder()->Program()->IsShared(); auto sharedModuleField = panda::pandasm::Field(LANG_EXT); sharedModuleField.name = "isSharedModule"; sharedModuleField.type = panda::pandasm::Type("u8", 0); sharedModuleField.metadata->SetValue( panda::pandasm::ScalarValue::Create(static_cast(isShared)) ); if (context->IsMergeAbc()) { rec_->field_list.emplace_back(std::move(sharedModuleField)); } else if (isShared) { auto sharedModuleRecord = panda::pandasm::Record("_SharedModuleRecord", LANG_EXT); sharedModuleRecord.metadata->SetAccessFlags(panda::ACC_PUBLIC); sharedModuleRecord.field_list.emplace_back(std::move(sharedModuleField)); prog_->record_table.emplace(sharedModuleRecord.name, std::move(sharedModuleRecord)); } } // static void Emitter::GenBufferLiterals(ArenaVector>> &literalBuffers, const LiteralBuffer *buff) { auto &[idx, array] = literalBuffers.emplace_back(); idx = buff->Index(); constexpr size_t ARRAY_EXPANSION = 2; array.reserve(buff->Literals().size() * ARRAY_EXPANSION); for (const auto *literal : buff->Literals()) { panda::pandasm::LiteralArray::Literal valueLit; panda::pandasm::LiteralArray::Literal tagLit; ir::LiteralTag tag = literal->Tag(); switch (tag) { case ir::LiteralTag::BOOLEAN: { valueLit.tag_ = panda::panda_file::LiteralTag::BOOL; valueLit.value_ = literal->GetBoolean(); break; } case ir::LiteralTag::INTEGER: { valueLit.tag_ = panda::panda_file::LiteralTag::INTEGER; valueLit.value_ = literal->GetInt(); break; } case ir::LiteralTag::DOUBLE: { valueLit.tag_ = panda::panda_file::LiteralTag::DOUBLE; valueLit.value_ = literal->GetDouble(); break; } case ir::LiteralTag::STRING: { valueLit.tag_ = panda::panda_file::LiteralTag::STRING; valueLit.value_ = literal->GetString().Mutf8(); break; } case ir::LiteralTag::ACCESSOR: { valueLit.tag_ = panda::panda_file::LiteralTag::ACCESSOR; valueLit.value_ = static_cast(0); break; } case ir::LiteralTag::METHOD: { valueLit.tag_ = panda::panda_file::LiteralTag::METHOD; valueLit.value_ = literal->GetMethod().Mutf8(); break; } case ir::LiteralTag::METHODAFFILIATE: { valueLit.tag_ = panda::panda_file::LiteralTag::METHODAFFILIATE; valueLit.value_ = literal->GetMethodAffiliate(); break; } case ir::LiteralTag::GENERATOR_METHOD: { valueLit.tag_ = panda::panda_file::LiteralTag::GENERATORMETHOD; valueLit.value_ = literal->GetMethod().Mutf8(); break; } case ir::LiteralTag::LITERALBUFFERINDEX: { valueLit.tag_ = panda::panda_file::LiteralTag::LITERALBUFFERINDEX; valueLit.value_ = literal->GetInt(); break; } case ir::LiteralTag::LITERALARRAY: { valueLit.tag_ = panda::panda_file::LiteralTag::LITERALARRAY; valueLit.value_ = literal->GetString().Mutf8(); break; } case ir::LiteralTag::BUILTINTYPEINDEX: { valueLit.tag_ = panda::panda_file::LiteralTag::BUILTINTYPEINDEX; valueLit.value_ = static_cast(literal->GetInt()); break; } case ir::LiteralTag::GETTER: { valueLit.tag_ = panda::panda_file::LiteralTag::GETTER; valueLit.value_ = literal->GetMethod().Mutf8(); break; } case ir::LiteralTag::SETTER: { valueLit.tag_ = panda::panda_file::LiteralTag::SETTER; valueLit.value_ = literal->GetMethod().Mutf8(); break; } case ir::LiteralTag::ASYNC_GENERATOR_METHOD: { valueLit.tag_ = panda::panda_file::LiteralTag::ASYNCGENERATORMETHOD; valueLit.value_ = literal->GetMethod().Mutf8(); break; } case ir::LiteralTag::NULL_VALUE: { valueLit.tag_ = panda::panda_file::LiteralTag::NULLVALUE; valueLit.value_ = static_cast(0); break; } default: break; } tagLit.tag_ = panda::panda_file::LiteralTag::TAGVALUE; tagLit.value_ = static_cast(valueLit.tag_); array.emplace_back(tagLit); array.emplace_back(valueLit); } } void Emitter::DumpAsm(const panda::pandasm::Program *prog) { auto &ss = std::cout; ss << ".language ECMAScript" << std::endl << std::endl; for (auto &[name, func] : prog->function_table) { ss << "slotNum = 0x" << std::hex << func.GetSlotsNum() << std::dec << std::endl; ss << ".function any " << name << '('; for (uint32_t i = 0; i < func.GetParamsNum(); i++) { ss << "any a" << std::to_string(i); if (i != func.GetParamsNum() - 1) { ss << ", "; } } ss << ") {" << std::endl; for (const auto &ins : func.ins) { ss << (ins.set_label ? "" : "\t") << ins.ToString("", true, func.GetTotalRegs()) << std::endl; } ss << "}" << std::endl << std::endl; for (const auto &ct : func.catch_blocks) { ss << ".catchall " << ct.try_begin_label << ", " << ct.try_end_label << ", " << ct.catch_begin_label << std::endl << std::endl; } } ss << std::endl; } panda::pandasm::Program *Emitter::Finalize(bool dumpDebugInfo, util::PatchFix *patchFixHelper) { if (dumpDebugInfo) { debuginfo::DebugInfoDumper dumper(prog_); dumper.Dump(); } if (rec_) { delete rec_; rec_ = nullptr; } if (patchFixHelper) { patchFixHelper->Finalize(&prog_); } auto *prog = prog_; prog_ = nullptr; return prog; } panda::pandasm::Program *Emitter::GetProgram() const { return prog_; } } // namespace panda::es2panda::compiler