1/**
2 * Copyright (c) 2021 - 2024 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 "macros.h"
32#include "public/public.h"
33
34#include <string>
35#include <string_view>
36#include <tuple>
37#include <utility>
38
39namespace ark::es2panda::compiler {
40using LiteralPair = std::pair<pandasm::LiteralArray::Literal, pandasm::LiteralArray::Literal>;
41
42static 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            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
82static 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
130void FunctionEmitter::Generate()
131{
132    auto *func = GenFunctionSignature();
133    GenFunctionInstructions(func);
134    GenVariablesDebugInfo(func);
135    GenSourceFileDebugInfo(func);
136    GenFunctionCatchTables(func);
137    GenFunctionAnnotations(func);
138}
139
140util::StringView FunctionEmitter::SourceCode() const
141{
142    return cg_->VarBinder()->Program()->SourceCode();
143}
144
145static 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    UNREACHABLE();
169    return *iter;
170}
171
172static 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
189static std::string WholeLine(const util::StringView &source, lexer::SourceRange range)
190{
191    if (source.Empty()) {
192        return {};
193    }
194    ASSERT(range.end.index <= source.Length());
195    ASSERT(range.end.index >= range.start.index);
196    return source.Substr(range.start.index, range.end.index).EscapeSymbol<util::StringView::Mutf8Encode>();
197}
198
199void FunctionEmitter::GenInstructionDebugInfo(const IRNode *ins, pandasm::Ins *pandaIns)
200{
201    const ir::AstNode *astNode = ins->Node();
202
203    ASSERT(astNode != nullptr);
204
205    if (astNode == FIRST_NODE_OF_FUNCTION) {
206        astNode = cg_->Debuginfo().FirstStatement();
207        if (astNode == nullptr) {
208            return;
209        }
210    }
211
212    auto nodeRange = astNode->Range();
213    pandaIns->insDebug.lineNumber = nodeRange.start.line + 1;
214
215    if (cg_->IsDebug()) {
216        size_t insLen = GetIRNodeWholeLength(ins);
217        if (insLen != 0) {
218            pandaIns->insDebug.boundLeft = offset_;
219            pandaIns->insDebug.boundRight = offset_ + insLen;
220        }
221
222        offset_ += insLen;
223        pandaIns->insDebug.wholeLine = WholeLine(SourceCode(), nodeRange);
224    }
225}
226
227void FunctionEmitter::GenFunctionInstructions(pandasm::Function *func)
228{
229    func->ins.reserve(cg_->Insns().size());
230
231    uint32_t totalRegs = cg_->TotalRegsNum();
232
233    for (const auto *ins : cg_->Insns()) {
234        auto &pandaIns = func->ins.emplace_back();
235
236        ins->Transform(&pandaIns, programElement_, totalRegs);
237        GenInstructionDebugInfo(ins, &pandaIns);
238    }
239}
240
241void FunctionEmitter::GenFunctionAnnotations(pandasm::Function *func)
242{
243    pandasm::AnnotationData funcAnnotationData("_ESAnnotation");
244    pandasm::AnnotationElement icSizeAnnotationElement(
245        "icSize",
246        std::make_unique<pandasm::ScalarValue>(pandasm::ScalarValue::Create<pandasm::Value::Type::U32>(cg_->IcSize())));
247    funcAnnotationData.AddElement(std::move(icSizeAnnotationElement));
248
249    pandasm::AnnotationElement parameterLengthAnnotationElement(
250        "parameterLength", std::make_unique<pandasm::ScalarValue>(
251                               pandasm::ScalarValue::Create<pandasm::Value::Type::U32>(cg_->FormalParametersCount())));
252    funcAnnotationData.AddElement(std::move(parameterLengthAnnotationElement));
253
254    pandasm::AnnotationElement funcNameAnnotationElement(
255        "funcName", std::make_unique<pandasm::ScalarValue>(
256                        pandasm::ScalarValue::Create<pandasm::Value::Type::STRING>(cg_->FunctionName().Mutf8())));
257    funcAnnotationData.AddElement(std::move(funcNameAnnotationElement));
258
259    func->metadata->AddAnnotations({funcAnnotationData});
260}
261
262void FunctionEmitter::GenFunctionCatchTables(pandasm::Function *func)
263{
264    func->catchBlocks.reserve(cg_->CatchList().size());
265
266    for (const auto *catchBlock : cg_->CatchList()) {
267        const auto &labelSet = catchBlock->LabelSet();
268
269        auto &pandaCatchBlock = func->catchBlocks.emplace_back();
270        pandaCatchBlock.exceptionRecord = catchBlock->Exception();
271        pandaCatchBlock.tryBeginLabel = labelSet.TryBegin()->Id();
272        pandaCatchBlock.tryEndLabel = labelSet.TryEnd()->Id();
273        pandaCatchBlock.catchBeginLabel = labelSet.CatchBegin()->Id();
274        pandaCatchBlock.catchEndLabel = labelSet.CatchBegin()->Id();
275    }
276}
277
278void FunctionEmitter::GenSourceFileDebugInfo(pandasm::Function *func)
279{
280    func->sourceFile = std::string {cg_->VarBinder()->Program()->SourceFile().GetAbsolutePath()};
281
282    if (!cg_->IsDebug()) {
283        return;
284    }
285
286    if (cg_->RootNode()->IsProgram()) {
287        func->sourceCode = SourceCode().EscapeSymbol<util::StringView::Mutf8Encode>();
288    }
289}
290
291static void GenLocalVariableInfo(pandasm::debuginfo::LocalVariable &variableDebug, varbinder::Variable *var,
292                                 std::tuple<uint32_t, uint32_t, uint32_t> info, const ScriptExtension extension)
293{
294    const auto [start, varsLength, totalRegsNum] = info;
295
296    variableDebug.name = var->Name().Mutf8();
297
298    if (extension == ScriptExtension::JS) {
299        variableDebug.signature = "any";
300        variableDebug.signatureType = "any";
301    } else {
302        ASSERT(var->AsLocalVariable()->TsType() != nullptr);
303        std::stringstream ss;
304        var->AsLocalVariable()->TsType()->ToDebugInfoType(ss);
305        variableDebug.signature = ss.str();
306        variableDebug.signatureType = ss.str();  // NOTE: Handle typeParams, either class or interface
307    }
308
309    variableDebug.reg =
310        static_cast<int32_t>(IRNode::MapRegister(var->AsLocalVariable()->Vreg().GetIndex(), totalRegsNum));
311    variableDebug.start = start;
312    variableDebug.length = static_cast<uint32_t>(varsLength);
313}
314
315void FunctionEmitter::GenScopeVariableInfoEnd(pandasm::Function *func, const varbinder::Scope *scope, uint32_t count,
316                                              uint32_t scopeStart, const VariablesStartsMap &starts) const
317{
318    const auto extension = cg_->VarBinder()->Program()->Extension();
319    auto varsLength = static_cast<uint32_t>(count - scopeStart + 1);
320
321    if (scope->IsFunctionScope()) {
322        for (auto *param : scope->AsFunctionScope()->ParamScope()->Params()) {
323            auto &variableDebug = func->localVariableDebug.emplace_back();
324            GenLocalVariableInfo(variableDebug, param, std::make_tuple(scopeStart, varsLength, cg_->TotalRegsNum()),
325                                 extension);
326        }
327    }
328    const auto &unsortedBindings = scope->Bindings();
329    std::map<util::StringView, es2panda::varbinder::Variable *> bindings(unsortedBindings.begin(),
330                                                                         unsortedBindings.end());
331    for (const auto &[_, variable] : bindings) {
332        (void)_;
333        const auto *decl = variable->Declaration();
334        // NOTE(dslynko, #19203): do not generate debug-info for labels and local classes and interfaces
335        if (!variable->IsLocalVariable() || variable->LexicalBound() || decl->IsTypeAliasDecl() ||
336            decl->IsLabelDecl() || decl->IsClassDecl() || decl->IsInterfaceDecl()) {
337            continue;
338        }
339        if (decl->IsParameterDecl() && !scope->IsCatchParamScope()) {
340            continue;
341        }
342
343        uint32_t localStart = scopeStart;
344        uint32_t localLength = varsLength;
345        auto iter = starts.find(variable);
346        if (iter != starts.end()) {
347            localStart = iter->second;
348            localLength = static_cast<uint32_t>(count - localStart + 1);
349        }
350
351        auto &variableDebug = func->localVariableDebug.emplace_back();
352        GenLocalVariableInfo(variableDebug, variable, std::make_tuple(localStart, localLength, cg_->TotalRegsNum()),
353                             extension);
354    }
355}
356
357void FunctionEmitter::GenScopeVariableInfo(pandasm::Function *func, const varbinder::Scope *scope) const
358{
359    const auto &instructions = cg_->Insns();
360    auto lastIter = instructions.end();
361    auto iter = std::find(instructions.begin(), lastIter, scope->ScopeStart());
362    if (iter == lastIter || *iter == scope->ScopeEnd()) {
363        return;
364    }
365    uint32_t count = iter - instructions.begin();
366    uint32_t start = count;
367
368    auto checkNodeIsValid = [](const ir::AstNode *node) { return node != nullptr && node != FIRST_NODE_OF_FUNCTION; };
369    // NOTE(dslynko, #19090): need to track start location for each local variable
370    std::unordered_map<const varbinder::Variable *, uint32_t> varsStarts;
371    for (const auto *scopeEnd = scope->ScopeEnd(); iter != lastIter && *iter != scopeEnd; ++iter, ++count) {
372        const auto *node = (*iter)->Node();
373        if (!checkNodeIsValid(node)) {
374            continue;
375        }
376        auto *parent = node->Parent();
377        if (!checkNodeIsValid(parent)) {
378            continue;
379        }
380
381        const varbinder::Variable *var = nullptr;
382        if (parent->IsVariableDeclarator()) {
383            var = parent->AsVariableDeclarator()->Id()->Variable();
384        } else if (parent->IsCatchClause()) {
385            var = node->Variable();
386        }
387        if (var != nullptr && varsStarts.count(var) == 0) {
388            varsStarts.emplace(var, count);
389        }
390    }
391    ASSERT(iter != lastIter);
392
393    GenScopeVariableInfoEnd(func, scope, count, start, varsStarts);
394}
395
396void FunctionEmitter::GenVariablesDebugInfo(pandasm::Function *func)
397{
398    if (!cg_->IsDebug()) {
399        return;
400    }
401
402    for (const auto *scope : cg_->Debuginfo().VariableDebugInfo()) {
403        GenScopeVariableInfo(func, scope);
404    }
405}
406
407// Emitter
408
409Emitter::Emitter(const public_lib::Context *context) : context_(context)
410{
411    prog_ = new pandasm::Program();
412}
413
414Emitter::~Emitter()
415{
416    delete prog_;
417}
418
419static void UpdateLiteralBufferId([[maybe_unused]] ark::pandasm::Ins *ins, [[maybe_unused]] uint32_t offset)
420{
421#ifdef PANDA_WITH_ECMASCRIPT
422    switch (ins->opcode) {
423        case pandasm::Opcode::ECMA_DEFINECLASSWITHBUFFER: {
424            ins->imms.back() = std::get<int64_t>(ins->imms.back()) + offset;
425            break;
426        }
427        case pandasm::Opcode::ECMA_CREATEARRAYWITHBUFFER:
428        case pandasm::Opcode::ECMA_CREATEOBJECTWITHBUFFER:
429        case pandasm::Opcode::ECMA_CREATEOBJECTHAVINGMETHOD:
430        case pandasm::Opcode::ECMA_DEFINECLASSPRIVATEFIELDS: {
431            uint32_t storedOffset = std::stoi(ins->ids.back());
432            storedOffset += offset;
433            ins->ids.back() = std::to_string(storedOffset);
434            break;
435        }
436        default: {
437            UNREACHABLE();
438            break;
439        }
440    }
441#else
442    UNREACHABLE();
443#endif
444}
445
446void Emitter::AddProgramElement(ProgramElement *programElement)
447{
448    prog_->strings.insert(programElement->Strings().begin(), programElement->Strings().end());
449
450    uint32_t newLiteralBufferIndex = literalBufferIndex_;
451    for (const auto &buff : programElement->BuffStorage()) {
452        AddLiteralBuffer(buff, newLiteralBufferIndex++);
453    }
454
455    for (auto *ins : programElement->LiteralBufferIns()) {
456        UpdateLiteralBufferId(ins, literalBufferIndex_);
457    }
458
459    literalBufferIndex_ = newLiteralBufferIndex;
460
461    auto *function = programElement->Function();
462    prog_->functionTable.emplace(function->name, std::move(*function));
463}
464
465static std::string CanonicalizeName(std::string name)
466{
467    std::replace_if(
468        name.begin(), name.end(), [](char c) { return (c == '<' || c == '>' || c == '.' || c == ':' || c == ';'); },
469        '-');
470    name.append(std::to_string(0));
471    return name;
472}
473
474void Emitter::DumpAsm(const pandasm::Program *prog)
475{
476    auto &ss = std::cout;
477
478    ss << ".language ECMAScript" << std::endl << std::endl;
479
480    for (auto &[name, func] : prog->functionTable) {
481        ss << ".function any " << CanonicalizeName(name) << '(';
482
483        for (uint32_t i = 0; i < func.GetParamsNum(); i++) {
484            ss << "any a" << std::to_string(i);
485
486            if (i != func.GetParamsNum() - 1) {
487                ss << ", ";
488            }
489        }
490
491        ss << ") {" << std::endl;
492
493        for (const auto &ins : func.ins) {
494            ss << (ins.setLabel ? "" : "\t") << ins.ToString("", true, func.GetTotalRegs()) << std::endl;
495        }
496
497        ss << "}" << std::endl << std::endl;
498
499        for (const auto &ct : func.catchBlocks) {
500            if (ct.exceptionRecord.empty()) {
501                ss << ".catchall ";
502            } else {
503                ss << ".catch " << ct.exceptionRecord << ", ";
504            }
505            ss << ct.tryBeginLabel << ", " << ct.tryEndLabel << ", " << ct.catchBeginLabel << std::endl << std::endl;
506        }
507    }
508
509    ss << std::endl;
510}
511
512void Emitter::AddLiteralBuffer(const LiteralBuffer &literals, uint32_t index)
513{
514    std::vector<pandasm::LiteralArray::Literal> literalArray;
515
516    for (const auto &literal : literals) {
517        auto [tagLit, valueLit] = TransformLiteral(&literal);
518        literalArray.emplace_back(tagLit);
519        literalArray.emplace_back(valueLit);
520    }
521
522    auto literalArrayInstance = pandasm::LiteralArray(std::move(literalArray));
523    prog_->literalarrayTable.emplace(std::to_string(index), std::move(literalArrayInstance));
524}
525
526pandasm::Program *Emitter::Finalize(bool dumpDebugInfo, std::string_view globalClass)
527{
528    if (dumpDebugInfo) {
529        debuginfo::DebugInfoDumper dumper(prog_);
530        dumper.Dump();
531    }
532
533    if (context_->parserProgram->VarBinder()->IsGenStdLib()) {
534        auto it = prog_->recordTable.find(std::string(globalClass));
535        if (it != prog_->recordTable.end()) {
536            prog_->recordTable.erase(it);
537        }
538    }
539    auto *prog = prog_;
540    prog_ = nullptr;
541    return prog;
542}
543}  // namespace ark::es2panda::compiler
544