• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "emitter.h"
17 
18 #include "ir/irnode.h"
19 #include "util/helpers.h"
20 #include "varbinder/scope.h"
21 #include "varbinder/variable.h"
22 #include "compiler/base/literals.h"
23 #include "compiler/core/codeGen.h"
24 #include "compiler/core/regSpiller.h"
25 #include "compiler/debugger/debuginfoDumper.h"
26 #include "compiler/base/catchTable.h"
27 #include "es2panda.h"
28 #include "parser/program/program.h"
29 #include "checker/types/type.h"
30 #include "generated/isa.h"
31 #include "util/es2pandaMacros.h"
32 #include "public/public.h"
33 
34 #include <string>
35 #include <string_view>
36 #include <tuple>
37 #include <utility>
38 
39 namespace ark::es2panda::compiler {
40 using LiteralPair = std::pair<pandasm::LiteralArray::Literal, pandasm::LiteralArray::Literal>;
41 
TransformMethodLiterals(const compiler::Literal * literal)42 static LiteralPair TransformMethodLiterals(const compiler::Literal *literal)
43 {
44     pandasm::LiteralArray::Literal valueLit;
45     pandasm::LiteralArray::Literal tagLit;
46 
47     compiler::LiteralTag tag = literal->Tag();
48 
49     switch (tag) {
50         case compiler::LiteralTag::METHOD: {
51             valueLit.tag = panda_file::LiteralTag::METHOD;
52             valueLit.value = literal->GetMethod();
53             break;
54         }
55         case compiler::LiteralTag::ASYNC_METHOD: {
56             valueLit.tag = panda_file::LiteralTag::ASYNCMETHOD;
57             valueLit.value = literal->GetMethod();
58             break;
59         }
60         case compiler::LiteralTag::GENERATOR_METHOD: {
61             valueLit.tag = panda_file::LiteralTag::GENERATORMETHOD;
62             valueLit.value = literal->GetMethod();
63             break;
64         }
65         case compiler::LiteralTag::ASYNC_GENERATOR_METHOD: {
66             valueLit.tag = panda_file::LiteralTag::ASYNCGENERATORMETHOD;
67             valueLit.value = literal->GetMethod();
68             break;
69         }
70         default: {
71             ES2PANDA_UNREACHABLE();
72             break;
73         }
74     }
75 
76     tagLit.tag = panda_file::LiteralTag::TAGVALUE;
77     tagLit.value = static_cast<uint8_t>(valueLit.tag);
78 
79     return {tagLit, valueLit};
80 }
81 
TransformLiteral(const compiler::Literal * literal)82 static LiteralPair TransformLiteral(const compiler::Literal *literal)
83 {
84     pandasm::LiteralArray::Literal valueLit;
85     pandasm::LiteralArray::Literal tagLit;
86 
87     compiler::LiteralTag tag = literal->Tag();
88 
89     switch (tag) {
90         case compiler::LiteralTag::BOOLEAN: {
91             valueLit.tag = panda_file::LiteralTag::BOOL;
92             valueLit.value = literal->GetBoolean();
93             break;
94         }
95         case compiler::LiteralTag::INTEGER: {
96             valueLit.tag = panda_file::LiteralTag::INTEGER;
97             valueLit.value = literal->GetInteger();
98             break;
99         }
100         case compiler::LiteralTag::DOUBLE: {
101             valueLit.tag = panda_file::LiteralTag::DOUBLE;
102             valueLit.value = literal->GetDouble();
103             break;
104         }
105         case compiler::LiteralTag::STRING: {
106             valueLit.tag = panda_file::LiteralTag::STRING;
107             valueLit.value = literal->GetString();
108             break;
109         }
110         case compiler::LiteralTag::ACCESSOR: {
111             valueLit.tag = panda_file::LiteralTag::ACCESSOR;
112             valueLit.value = static_cast<uint8_t>(0);
113             break;
114         }
115         case compiler::LiteralTag::NULL_VALUE: {
116             valueLit.tag = panda_file::LiteralTag::NULLVALUE;
117             valueLit.value = static_cast<uint8_t>(0);
118             break;
119         }
120         default:
121             return TransformMethodLiterals(literal);
122     }
123 
124     tagLit.tag = panda_file::LiteralTag::TAGVALUE;
125     tagLit.value = static_cast<uint8_t>(valueLit.tag);
126 
127     return {tagLit, valueLit};
128 }
129 
Generate()130 void FunctionEmitter::Generate()
131 {
132     auto *func = GenFunctionSignature();
133     GenFunctionInstructions(func);
134     GenVariablesDebugInfo(func);
135     GenSourceFileDebugInfo(func);
136     GenFunctionCatchTables(func);
137     GenFunctionAnnotations(func);
138 }
139 
SourceCode() const140 util::StringView FunctionEmitter::SourceCode() const
141 {
142     return cg_->VarBinder()->Program()->SourceCode();
143 }
144 
MatchFormat(const IRNode * node,const Formats & formats)145 static Format MatchFormat(const IRNode *node, const Formats &formats)
146 {
147     std::array<const VReg *, IRNode::MAX_REG_OPERAND> regs {};
148     auto regCnt = node->Registers(&regs);
149     auto registers = Span<const VReg *>(regs.data(), regs.data() + regCnt);
150 
151     const auto *iter = formats.begin();
152 
153     for (; iter != formats.end(); iter++) {  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
154         auto format = *iter;
155         size_t limit = 0;
156         for (const auto &formatItem : format.GetFormatItem()) {
157             if (formatItem.IsVReg()) {
158                 limit = 1U << formatItem.BitWidth();
159                 break;
160             }
161         }
162 
163         if (std::all_of(registers.begin(), registers.end(), [limit](const VReg *reg) { return reg->IsValid(limit); })) {
164             return format;
165         }
166     }
167 
168     ES2PANDA_UNREACHABLE();
169     return *iter;
170 }
171 
GetIRNodeWholeLength(const IRNode * node)172 static size_t GetIRNodeWholeLength(const IRNode *node)
173 {
174     Formats formats = node->GetFormats();
175     if (formats.empty()) {
176         return 0;
177     }
178 
179     size_t len = 1;
180     const auto format = MatchFormat(node, formats);
181 
182     for (auto fi : format.GetFormatItem()) {
183         len += fi.BitWidth() / 8U;
184     }
185 
186     return len;
187 }
188 
WholeLine(const lexer::SourceRange & range)189 static std::string WholeLine(const lexer::SourceRange &range)
190 {
191     // NOTE(rsipka, #24105): The program shouldn't be nullptr
192     auto program = range.start.Program();
193     if (program == nullptr || program->SourceCode().Empty()) {
194         return {};
195     }
196 
197     auto source = program->SourceCode();
198 
199     ES2PANDA_ASSERT(range.end.index <= source.Length());
200     ES2PANDA_ASSERT(range.end.index >= range.start.index);
201     return source.Substr(range.start.index, range.end.index).EscapeSymbol<util::StringView::Mutf8Encode>();
202 }
203 
GenInstructionDebugInfo(const IRNode * ins,pandasm::Ins * pandaIns)204 void FunctionEmitter::GenInstructionDebugInfo(const IRNode *ins, pandasm::Ins *pandaIns)
205 {
206     const ir::AstNode *astNode = ins->Node();
207 
208     ES2PANDA_ASSERT(astNode != nullptr);
209 
210     if (astNode == FIRST_NODE_OF_FUNCTION) {
211         astNode = cg_->Debuginfo().FirstStatement();
212         if (astNode == nullptr) {
213             return;
214         }
215     }
216 
217     auto nodeRange = astNode->Range();
218     pandaIns->insDebug.lineNumber = nodeRange.start.line + 1;
219 
220     if (cg_->IsDebug()) {
221         size_t insLen = GetIRNodeWholeLength(ins);
222         if (insLen != 0) {
223             pandaIns->insDebug.boundLeft = offset_;
224             pandaIns->insDebug.boundRight = offset_ + insLen;
225         }
226 
227         offset_ += insLen;
228         pandaIns->insDebug.wholeLine = WholeLine(nodeRange);
229     }
230 }
231 
GenFunctionInstructions(pandasm::Function * func)232 void FunctionEmitter::GenFunctionInstructions(pandasm::Function *func)
233 {
234     func->ins.reserve(cg_->Insns().size());
235 
236     uint32_t totalRegs = cg_->TotalRegsNum();
237 
238     for (const auto *ins : cg_->Insns()) {
239         auto &pandaIns = func->ins.emplace_back();
240 
241         ins->Transform(&pandaIns, programElement_, totalRegs);
242         GenInstructionDebugInfo(ins, &pandaIns);
243     }
244 }
245 
GenFunctionAnnotations(pandasm::Function * func)246 void FunctionEmitter::GenFunctionAnnotations(pandasm::Function *func)
247 {
248     pandasm::AnnotationData funcAnnotationData("_ESAnnotation");
249     pandasm::AnnotationElement icSizeAnnotationElement(
250         "icSize",
251         std::make_unique<pandasm::ScalarValue>(pandasm::ScalarValue::Create<pandasm::Value::Type::U32>(cg_->IcSize())));
252     funcAnnotationData.AddElement(std::move(icSizeAnnotationElement));
253 
254     pandasm::AnnotationElement parameterLengthAnnotationElement(
255         "parameterLength", std::make_unique<pandasm::ScalarValue>(
256                                pandasm::ScalarValue::Create<pandasm::Value::Type::U32>(cg_->FormalParametersCount())));
257     funcAnnotationData.AddElement(std::move(parameterLengthAnnotationElement));
258 
259     pandasm::AnnotationElement funcNameAnnotationElement(
260         "funcName", std::make_unique<pandasm::ScalarValue>(
261                         pandasm::ScalarValue::Create<pandasm::Value::Type::STRING>(cg_->FunctionName().Mutf8())));
262     funcAnnotationData.AddElement(std::move(funcNameAnnotationElement));
263 
264     func->metadata->AddAnnotations({funcAnnotationData});
265 }
266 
GenFunctionCatchTables(pandasm::Function * func)267 void FunctionEmitter::GenFunctionCatchTables(pandasm::Function *func)
268 {
269     func->catchBlocks.reserve(cg_->CatchList().size());
270 
271     for (const auto *catchBlock : cg_->CatchList()) {
272         const auto &labelSet = catchBlock->LabelSet();
273 
274         auto &pandaCatchBlock = func->catchBlocks.emplace_back();
275         pandaCatchBlock.exceptionRecord = catchBlock->Exception();
276         pandaCatchBlock.tryBeginLabel = labelSet.TryBegin()->Id();
277         pandaCatchBlock.tryEndLabel = labelSet.TryEnd()->Id();
278         pandaCatchBlock.catchBeginLabel = labelSet.CatchBegin()->Id();
279         pandaCatchBlock.catchEndLabel = labelSet.CatchBegin()->Id();
280     }
281 }
282 
GenLocalVariableInfo(pandasm::debuginfo::LocalVariable & variableDebug,varbinder::Variable * var,std::tuple<uint32_t,uint32_t,uint32_t> info,ScriptExtension extension)283 static void GenLocalVariableInfo(pandasm::debuginfo::LocalVariable &variableDebug, varbinder::Variable *var,
284                                  std::tuple<uint32_t, uint32_t, uint32_t> info, ScriptExtension extension)
285 {
286     const auto [start, varsLength, totalRegsNum] = info;
287 
288     variableDebug.name = var->Name().Mutf8();
289 
290     if (extension == ScriptExtension::JS) {
291         variableDebug.signature = "any";
292         variableDebug.signatureType = "any";
293     } else {
294         ES2PANDA_ASSERT(var->AsLocalVariable()->TsType() != nullptr);
295         std::stringstream ss;
296         var->AsLocalVariable()->TsType()->ToDebugInfoType(ss);
297         variableDebug.signature = ss.str();
298         variableDebug.signatureType = ss.str();  // NOTE: Handle typeParams, either class or interface
299     }
300 
301     variableDebug.reg =
302         static_cast<int32_t>(IRNode::MapRegister(var->AsLocalVariable()->Vreg().GetIndex(), totalRegsNum));
303     variableDebug.start = start;
304     variableDebug.length = static_cast<uint32_t>(varsLength);
305 }
306 
GenScopeVariableInfoEnd(pandasm::Function * func,const varbinder::Scope * scope,uint32_t count,uint32_t scopeStart,const VariablesStartsMap & starts) const307 void FunctionEmitter::GenScopeVariableInfoEnd(pandasm::Function *func, const varbinder::Scope *scope, uint32_t count,
308                                               uint32_t scopeStart, const VariablesStartsMap &starts) const
309 {
310     const auto extension = cg_->VarBinder()->Program()->Extension();
311     auto varsLength = static_cast<uint32_t>(count - scopeStart + 1);
312 
313     if (scope->IsFunctionScope()) {
314         for (auto *param : scope->AsFunctionScope()->ParamScope()->Params()) {
315             auto &variableDebug = func->localVariableDebug.emplace_back();
316             GenLocalVariableInfo(variableDebug, param, std::make_tuple(scopeStart, varsLength, cg_->TotalRegsNum()),
317                                  extension);
318         }
319     }
320     const auto &unsortedBindings = scope->Bindings();
321     std::map<util::StringView, es2panda::varbinder::Variable *> bindings(unsortedBindings.begin(),
322                                                                          unsortedBindings.end());
323     for (const auto &[_, variable] : bindings) {
324         (void)_;
325         const auto *decl = variable->Declaration();
326         // NOTE(dslynko, #19203): do not generate debug-info for labels and local classes and interfaces
327         if (!variable->IsLocalVariable() || variable->LexicalBound() || decl->IsTypeAliasDecl() ||
328             decl->IsLabelDecl() || decl->IsClassDecl() || decl->IsInterfaceDecl()) {
329             continue;
330         }
331         if (decl->IsParameterDecl() && !scope->IsCatchParamScope()) {
332             continue;
333         }
334 
335         uint32_t localStart = scopeStart;
336         uint32_t localLength = varsLength;
337         auto iter = starts.find(variable);
338         if (iter != starts.end()) {
339             localStart = iter->second;
340             localLength = static_cast<uint32_t>(count - localStart + 1);
341         }
342 
343         auto &variableDebug = func->localVariableDebug.emplace_back();
344         GenLocalVariableInfo(variableDebug, variable, std::make_tuple(localStart, localLength, cg_->TotalRegsNum()),
345                              extension);
346     }
347 }
348 
GenScopeVariableInfo(pandasm::Function * func,const varbinder::Scope * scope) const349 void FunctionEmitter::GenScopeVariableInfo(pandasm::Function *func, const varbinder::Scope *scope) const
350 {
351     const auto &instructions = cg_->Insns();
352     auto lastIter = instructions.end();
353     auto iter = std::find(instructions.begin(), lastIter, scope->ScopeStart());
354     if (iter == lastIter || *iter == scope->ScopeEnd()) {
355         return;
356     }
357     size_t count = iter - instructions.begin();
358     size_t start = count;
359 
360     auto checkNodeIsValid = [](const ir::AstNode *node) { return node != nullptr && node != FIRST_NODE_OF_FUNCTION; };
361     // NOTE(dslynko, #19090): need to track start location for each local variable
362     std::unordered_map<const varbinder::Variable *, uint32_t> varsStarts;
363     for (const auto *scopeEnd = scope->ScopeEnd(); iter != lastIter && *iter != scopeEnd; ++iter, ++count) {
364         const auto *node = (*iter)->Node();
365         if (!checkNodeIsValid(node)) {
366             continue;
367         }
368         auto *parent = node->Parent();
369         if (!checkNodeIsValid(parent)) {
370             continue;
371         }
372 
373         const varbinder::Variable *var = nullptr;
374         if (parent->IsVariableDeclarator()) {
375             var = parent->AsVariableDeclarator()->Id()->Variable();
376         } else if (parent->IsCatchClause()) {
377             var = node->Variable();
378         }
379         if (var != nullptr && varsStarts.count(var) == 0) {
380             varsStarts.emplace(var, count);
381         }
382     }
383     ES2PANDA_ASSERT(iter != lastIter);
384 
385     GenScopeVariableInfoEnd(func, scope, count, start, varsStarts);
386 }
387 
GenVariablesDebugInfo(pandasm::Function * func)388 void FunctionEmitter::GenVariablesDebugInfo(pandasm::Function *func)
389 {
390     if (!cg_->IsDebug()) {
391         return;
392     }
393 
394     for (const auto *scope : cg_->Debuginfo().VariableDebugInfo()) {
395         GenScopeVariableInfo(func, scope);
396     }
397 }
398 
399 // Emitter
400 
Emitter(const public_lib::Context * context)401 Emitter::Emitter(const public_lib::Context *context) : context_(context)
402 {
403     prog_ = new pandasm::Program();
404 }
405 
~Emitter()406 Emitter::~Emitter()
407 {
408     delete prog_;
409 }
410 
UpdateLiteralBufferId(ark::pandasm::Ins * ins,uint32_t offset)411 static void UpdateLiteralBufferId([[maybe_unused]] ark::pandasm::Ins *ins, [[maybe_unused]] uint32_t offset)
412 {
413 #ifdef PANDA_WITH_ECMASCRIPT
414     switch (ins->opcode) {
415         case pandasm::Opcode::ECMA_DEFINECLASSWITHBUFFER: {
416             ins->imms.back() = std::get<int64_t>(ins->imms.back()) + offset;
417             break;
418         }
419         case pandasm::Opcode::ECMA_CREATEARRAYWITHBUFFER:
420         case pandasm::Opcode::ECMA_CREATEOBJECTWITHBUFFER:
421         case pandasm::Opcode::ECMA_CREATEOBJECTHAVINGMETHOD:
422         case pandasm::Opcode::ECMA_DEFINECLASSPRIVATEFIELDS: {
423             constexpr int BASE10 = 10;
424             uint32_t storedOffset = strtoul(ins->ids.back().data(), nullptr, BASE10);
425             storedOffset += offset;
426             ins->ids.back() = std::to_string(storedOffset);
427             break;
428         }
429         default: {
430             ES2PANDA_UNREACHABLE();
431             break;
432         }
433     }
434 #else
435     ES2PANDA_UNREACHABLE();
436 #endif
437 }
438 
AddProgramElement(ProgramElement * programElement)439 void Emitter::AddProgramElement(ProgramElement *programElement)
440 {
441     if (programElement->Function() == nullptr) {
442         return;
443     }
444     prog_->strings.insert(programElement->Strings().begin(), programElement->Strings().end());
445 
446     uint32_t newLiteralBufferIndex = literalBufferIndex_;
447     for (const auto &buff : programElement->BuffStorage()) {
448         AddLiteralBuffer(buff, newLiteralBufferIndex++);
449     }
450 
451     for (auto *ins : programElement->LiteralBufferIns()) {
452         UpdateLiteralBufferId(ins, literalBufferIndex_);
453     }
454 
455     literalBufferIndex_ = newLiteralBufferIndex;
456 
457     auto *function = programElement->Function();
458     prog_->AddToFunctionTable(std::move(*function));
459 }
460 
CanonicalizeName(std::string name)461 static std::string CanonicalizeName(std::string name)
462 {
463     std::replace_if(
464         name.begin(), name.end(), [](char c) { return (c == '<' || c == '>' || c == '.' || c == ':' || c == ';'); },
465         '-');
466     name.append(std::to_string(0));
467     return name;
468 }
469 
DumpAsmFunction(std::string name,const pandasm::Function & func)470 static std::string DumpAsmFunction(std::string name, const pandasm::Function &func)
471 {
472     std::stringstream ss;
473 
474     ss << ".function any " << CanonicalizeName(std::move(name)) << '(';
475 
476     for (uint32_t i = 0; i < func.GetParamsNum(); i++) {
477         ss << "any a" << std::to_string(i);
478 
479         if (i != func.GetParamsNum() - 1) {
480             ss << ", ";
481         }
482     }
483 
484     ss << ") {" << std::endl;
485 
486     for (const auto &ins : func.ins) {
487         ss << (ins.setLabel ? "" : "\t") << ins.ToString("", true, func.GetTotalRegs()) << std::endl;
488     }
489 
490     ss << "}" << std::endl << std::endl;
491 
492     for (const auto &ct : func.catchBlocks) {
493         if (ct.exceptionRecord.empty()) {
494             ss << ".catchall ";
495         } else {
496             ss << ".catch " << ct.exceptionRecord << ", ";
497         }
498         ss << ct.tryBeginLabel << ", " << ct.tryEndLabel << ", " << ct.catchBeginLabel << std::endl << std::endl;
499     }
500     return ss.str();
501 }
502 
DumpAsm(const pandasm::Program * prog)503 void Emitter::DumpAsm(const pandasm::Program *prog)
504 {
505     auto &ss = std::cout;
506 
507     ss << ".language ECMAScript" << std::endl << std::endl;
508 
509     for (const auto &[name, func] : prog->functionStaticTable) {
510         ss << DumpAsmFunction(name, func);
511     }
512 
513     for (const auto &[name, func] : prog->functionInstanceTable) {
514         ss << DumpAsmFunction(name, func);
515     }
516 
517     ss << std::endl;
518 }
519 
AddLiteralBuffer(const LiteralBuffer & literals,uint32_t index)520 void Emitter::AddLiteralBuffer(const LiteralBuffer &literals, uint32_t index)
521 {
522     std::vector<pandasm::LiteralArray::Literal> literalArray;
523 
524     for (const auto &literal : literals) {
525         auto [tagLit, valueLit] = TransformLiteral(&literal);
526         literalArray.emplace_back(tagLit);
527         literalArray.emplace_back(valueLit);
528     }
529 
530     auto literalArrayInstance = pandasm::LiteralArray(std::move(literalArray));
531     prog_->literalarrayTable.emplace(std::to_string(index), std::move(literalArrayInstance));
532 }
533 
Finalize(bool dumpDebugInfo,std::string_view globalClass)534 pandasm::Program *Emitter::Finalize(bool dumpDebugInfo, std::string_view globalClass)
535 {
536     if (dumpDebugInfo) {
537         debuginfo::DebugInfoDumper dumper(prog_);
538         dumper.Dump();
539     }
540 
541     if (context_->parserProgram->VarBinder()->IsGenStdLib() ||
542         context_->parserProgram->VarBinder()->Program()->IsGenAbcForExternal()) {
543         auto it = prog_->recordTable.find(std::string(globalClass));
544         if (it != prog_->recordTable.end()) {
545             prog_->recordTable.erase(it);
546         }
547     }
548     auto *prog = prog_;
549     prog_ = nullptr;
550     return prog;
551 }
552 }  // namespace ark::es2panda::compiler
553