1/*
2 * Copyright (c) 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 "program_dump.h"
17#include "abc2program_log.h"
18#include "common/abc_file_utils.h"
19#include "dump_utils.h"
20#include "os/file.h"
21
22namespace panda::abc2program {
23
24void PandasmProgramDumper::Dump(std::ostream &os, const pandasm::Program &program)
25{
26    program_ = &program;
27    os << std::flush;
28    DumpAbcFilePath(os);
29    DumpProgramLanguage(os);
30    DumpLiteralArrayTable(os);
31    DumpRecordTable(os);
32    DumpFunctionTable(os);
33    DumpStrings(os);
34}
35
36void PandasmProgramDumper::SetAbcFilePath(const std::string &abc_file_path)
37{
38    abc_file_path_ = abc_file_path;
39}
40
41void PandasmProgramDumper::DumpAbcFilePath(std::ostream &os) const
42{
43    if (abc_file_path_.empty()) {
44        return;
45    }
46    std::string file_abs_path = os::file::File::GetAbsolutePath(abc_file_path_).Value();
47    os << DUMP_TITLE_SOURCE_BINARY << file_abs_path << DUMP_CONTENT_DOUBLE_ENDL;
48}
49
50void PandasmProgramDumper::DumpProgramLanguage(std::ostream &os) const
51{
52    os << DUMP_TITLE_LANGUAGE;
53    if (program_->lang == panda::panda_file::SourceLang::ECMASCRIPT) {
54        os << DUMP_CONTENT_ECMASCRIPT;
55    } else {
56        os << DUMP_CONTENT_PANDA_ASSEMBLY;
57    }
58    os << DUMP_CONTENT_DOUBLE_ENDL;
59}
60
61void PandasmProgramDumper::DumpLiteralArrayTable(std::ostream &os) const
62{
63    os << DUMP_TITLE_SEPARATOR;
64    os << DUMP_TITLE_LITERALS;
65    os << DUMP_CONTENT_DOUBLE_ENDL;
66    auto it = program_->literalarray_table.begin();
67    auto end = program_->literalarray_table.end();
68    // In normalized mode, sort dump result of literal arrays before output.
69    if (is_normalized_) {
70        std::vector<std::string> literal_arrays;
71        for (; it != end; ++it) {
72            auto id = PandasmDumperUtils::GetLiteralArrayIdFromName(it->first);
73            auto lit_arr = SerializeLiteralArray(it->second, id);
74            lit_arr += DUMP_CONTENT_DOUBLE_ENDL;
75            literal_arrays.emplace_back(lit_arr);
76        }
77        std::sort(literal_arrays.begin(), literal_arrays.end());
78        for (auto &str : literal_arrays) {
79            os << str;
80        }
81    } else {
82        for (; it != end; ++it) {
83            os << it->first << DUMP_CONTENT_SPACE;
84            auto id = PandasmDumperUtils::GetLiteralArrayIdFromName(it->first);
85            os << SerializeLiteralArray(it->second, id);
86            os << DUMP_CONTENT_DOUBLE_ENDL;
87        }
88    }
89    os << DUMP_CONTENT_DOUBLE_ENDL;
90}
91
92void PandasmProgramDumper::DumpRecordTable(std::ostream &os) const
93{
94    os << DUMP_TITLE_SEPARATOR;
95    os << DUMP_TITLE_RECORDS;
96    os << DUMP_CONTENT_DOUBLE_ENDL;
97    for (const auto &it : program_->record_table) {
98        if (is_normalized_ && is_debug_ && it.first == SLOT_NUMBER_RECORD_NAME) {
99            continue;
100        }
101        DumpRecord(os, it.second);
102    }
103    os << DUMP_CONTENT_SINGLE_ENDL;
104}
105
106void PandasmProgramDumper::DumpRecord(std::ostream &os, const pandasm::Record &record) const
107{
108    if (is_normalized_) {
109        if (AbcFileUtils::IsGlobalTypeName(record.name)) {
110            return;
111        }
112        if (AbcFileUtils::IsESTypeAnnotationName(record.name)) {
113            return;
114        }
115    }
116    os << DUMP_TITLE_RECORD << record.name;
117    if (DumpRecordMetaData(os, record)) {
118        DumpFieldList(os, record);
119    }
120    DumpRecordSourceFile(os, record);
121}
122
123bool PandasmProgramDumper::DumpRecordMetaData(std::ostream &os, const pandasm::Record &record) const
124{
125    if (record.metadata->IsForeign()) {
126        os << DUMP_CONTENT_SPACE << DUMP_CONTENT_ATTR_EXTERNAL;
127        os << DUMP_CONTENT_SINGLE_ENDL;
128        return false;
129    }
130    return true;
131}
132
133void PandasmProgramDumper::DumpFieldList(std::ostream &os, const pandasm::Record &record) const
134{
135    os << DUMP_CONTENT_SPACE << DUMP_CONTENT_LEFT_CURLY_BRACKET << DUMP_CONTENT_SINGLE_ENDL;
136    for (const auto &it : record.field_list) {
137        DumpField(os, it);
138    }
139    os << DUMP_CONTENT_RIGHT_CURLY_BRACKET << DUMP_CONTENT_SINGLE_ENDL;
140}
141
142void PandasmProgramDumper::DumpField(std::ostream &os, const pandasm::Field &field) const
143{
144    os << DUMP_CONTENT_TAB << field.type.GetPandasmName() << DUMP_CONTENT_SPACE << field.name;
145    DumpFieldMetaData(os, field);
146    os << DUMP_CONTENT_SINGLE_ENDL;
147}
148
149void PandasmProgramDumper::DumpFieldMetaData(std::ostream &os, const pandasm::Field &field) const
150{
151    if (field.metadata->GetValue()) {
152        DumpScalarValue(os, *(field.metadata->GetValue()));
153    }
154}
155
156void PandasmProgramDumper::DumpAnnotationData(std::ostream &os, const pandasm::AnnotationData &anno) const
157{
158    os << DUMP_CONTENT_SPACE << anno.GetName() << DUMP_CONTENT_SINGLE_ENDL;
159    for (const auto &element : anno.GetElements()) {
160        os << DUMP_CONTENT_SPACE << element.GetName();
161        if (element.GetValue()->IsArray()) {
162            DumpArrayValue(os, *(element.GetValue()->GetAsArray()));
163        } else {
164            DumpScalarValue(os, *(element.GetValue()->GetAsScalar()));
165        }
166    }
167}
168
169void PandasmProgramDumper::DumpArrayValue(std::ostream &os, const pandasm::ArrayValue &array) const
170{
171    for (const auto &val : array.GetValues()) {
172        DumpScalarValue(os, val);
173    }
174}
175void PandasmProgramDumper::DumpScalarValue(std::ostream &os, const pandasm::ScalarValue &scalar) const
176{
177    switch (scalar.GetType()) {
178        case pandasm::Value::Type::U1:
179        case pandasm::Value::Type::I8:
180        case pandasm::Value::Type::U8:
181        case pandasm::Value::Type::I16:
182        case pandasm::Value::Type::U16:
183        case pandasm::Value::Type::I32:
184        case pandasm::Value::Type::U32:
185        case pandasm::Value::Type::I64:
186        case pandasm::Value::Type::U64:
187        case pandasm::Value::Type::STRING_NULLPTR: {
188            os << DUMP_CONTENT_SPACE << scalar.GetValue<uint64_t>();
189            break;
190        }
191        case pandasm::Value::Type::F32:
192            os << DUMP_CONTENT_SPACE << scalar.GetValue<float>();
193            break;
194        case pandasm::Value::Type::F64: {
195            os << DUMP_CONTENT_SPACE << scalar.GetValue<double>();
196            break;
197        }
198        case pandasm::Value::Type::STRING:
199        case pandasm::Value::Type::METHOD:
200        case pandasm::Value::Type::ENUM:
201        case pandasm::Value::Type::LITERALARRAY: {
202            if (!is_normalized_) {
203                os << DUMP_CONTENT_SPACE << scalar.GetValue<std::string>();
204            } else {
205                auto literal_array_id_name = scalar.GetValue<std::string>();
206                auto it = program_->literalarray_table.find(literal_array_id_name);
207                ASSERT(it != program_->literalarray_table.end());
208                auto id = PandasmDumperUtils::GetLiteralArrayIdFromName(literal_array_id_name);
209                os << DUMP_CONTENT_SPACE << SerializeLiteralArray(it->second, id);
210            }
211            break;
212        }
213        case pandasm::Value::Type::RECORD: {
214            pandasm::Type type = scalar.GetValue<pandasm::Type>();
215            os << DUMP_CONTENT_SPACE << type.GetComponentName() << DUMP_CONTENT_SPACE << type.GetName();
216            break;
217        }
218        case pandasm::Value::Type::ANNOTATION: {
219            DumpAnnotationData(os, scalar.GetValue<pandasm::AnnotationData>());
220            break;
221        }
222        default :
223            break;
224    }
225}
226
227void PandasmProgramDumper::DumpRecordSourceFile(std::ostream &os, const pandasm::Record &record) const
228{
229    os << DUMP_TITLE_RECORD_SOURCE_FILE << record.source_file << DUMP_CONTENT_DOUBLE_ENDL;
230}
231
232void PandasmProgramDumper::DumpFunctionTable(std::ostream &os)
233{
234    os << DUMP_TITLE_SEPARATOR;
235    os << DUMP_TITLE_METHODS;
236    os << DUMP_CONTENT_DOUBLE_ENDL;
237    for (const auto &it : program_->function_table) {
238        DumpFunction(os, it.second);
239    }
240}
241
242void PandasmProgramDumper::DumpFunction(std::ostream &os, const pandasm::Function &function)
243{
244    regs_num_ = function.regs_num;
245    DumpFunctionKind(os, function);
246    if (!is_normalized_ || !is_debug_) {
247        DumpFunctionAnnotations(os, function);
248    }
249    DumpFunctionHead(os, function);
250    DumpFunctionBody(os, function);
251}
252
253void PandasmProgramDumper::DumpFunctionKind(std::ostream &os, const pandasm::Function &function) const
254{
255    os << DUMP_TITLE_FUNCTION_KIND << PandasmDumperUtils::GetFunctionKindString(function.function_kind);
256    os << DUMP_CONTENT_SINGLE_ENDL;
257}
258
259void PandasmProgramDumper::DumpFunctionAnnotations(std::ostream &os, const pandasm::Function &function) const
260{
261    for (auto &annotation : function.metadata->GetAnnotations()) {
262        DumpAnnotationData(os, annotation);
263    }
264    os << DUMP_CONTENT_SINGLE_ENDL;
265}
266
267void PandasmProgramDumper::DumpFunctionHead(std::ostream &os, const pandasm::Function &function) const
268{
269    os << DUMP_TITLE_FUNCTION;
270    DumpFunctionReturnType(os, function);
271    DumpFunctionName(os, function);
272    DumpFunctionParams(os, function);
273    os << DUMP_CONTENT_SPACE << DUMP_CONTENT_LEFT_CURLY_BRACKET << DUMP_CONTENT_SINGLE_ENDL;
274}
275
276void PandasmProgramDumper::DumpFunctionReturnType(std::ostream &os, const pandasm::Function &function) const
277{
278    os << function.return_type.GetPandasmName() << DUMP_CONTENT_SPACE;
279}
280
281void PandasmProgramDumper::DumpFunctionName(std::ostream &os, const pandasm::Function &function) const
282{
283    os << function.name;
284}
285
286void PandasmProgramDumper::DumpFunctionParams(std::ostream &os, const pandasm::Function &function) const
287{
288    os << DUMP_CONTENT_LEFT_PARENTHESIS;
289    if (function.params.size() > 0) {
290        DumpFunctionParamAtIndex(os, function.params[0], 0);
291        for (size_t i = 1; i < function.params.size(); ++i) {
292            os << DUMP_CONTENT_COMMA << DUMP_CONTENT_SPACE;
293            DumpFunctionParamAtIndex(os, function.params[i], i);
294        }
295    }
296    os << DUMP_CONTENT_RIGHT_PARENTHESIS;
297}
298
299void PandasmProgramDumper::DumpFunctionParamAtIndex(std::ostream &os,
300                                                    const pandasm::Function::Parameter &param,
301                                                    size_t idx) const
302{
303    os << param.type.GetPandasmName() << DUMP_CONTENT_SPACE << DUMP_CONTENT_FUNCTION_PARAM_NAME_PREFIX << idx;
304}
305
306void PandasmProgramDumper::DumpFunctionAttributes(std::ostream &os, const pandasm::Function &function) const
307{
308    log::Unimplemented(__PRETTY_FUNCTION__);
309}
310
311void PandasmProgramDumper::DumpFunctionBody(std::ostream &os, const pandasm::Function &function)
312{
313    DumpFunctionIns(os, function);
314    DumpFunctionCatchBlocks(os, function);
315    DumpFunctionDebugInfo(os, function);
316    os << "}" << DUMP_CONTENT_DOUBLE_ENDL;
317}
318
319void PandasmProgramDumper::DumpFunctionIns(std::ostream &os, const pandasm::Function &function)
320{
321    if (is_normalized_) {
322        DumpNormalizedFunctionIns(os, function);
323    } else {
324        DumpOriginalFunctionIns(os, function);
325    }
326}
327
328void PandasmProgramDumper::DumpOriginalFunctionIns(std::ostream &os, const pandasm::Function &function)
329{
330    for (const pandasm::Ins &pa_ins : function.ins) {
331        std::string insStr = pa_ins.ToString("", true, regs_num_);
332        os << DUMP_CONTENT_TAB << std::setw(LINE_WIDTH)
333           << std::left << insStr
334           << DUMP_CONTENT_LINE_NUMBER << pa_ins.ins_debug.line_number;
335        os << std::setw(COLUMN_WIDTH) << std::left << DUMP_CONTENT_SPACE
336           << DUMP_CONTENT_COLUMN_NUMBER << pa_ins.ins_debug.column_number
337           << DUMP_CONTENT_SINGLE_ENDL;
338    }
339}
340
341void PandasmProgramDumper::DumpNormalizedFunctionIns(std::ostream &os, const pandasm::Function &function)
342{
343    GetOriginalDumpIns(function);
344    GetInvalidOpLabelMap();
345    UpdateLabels4DumpIns(original_dump_ins_ptrs_, invalid_op_label_map_);
346    GetFinalDumpIns();
347    GetFinalLabelMap();
348    UpdateLabels4DumpIns(final_dump_ins_ptrs_, final_label_map_);
349    DumpFinalIns(os);
350}
351
352void PandasmProgramDumper::GetOriginalDumpIns(const pandasm::Function &function)
353{
354    original_dump_ins_.clear();
355    original_dump_ins_ptrs_.clear();
356    original_ins_index_map_.clear();
357    for (const pandasm::Ins &pa_ins : function.ins) {
358        original_dump_ins_.emplace_back(PandasmDumperUtils::DeepCopyIns(pa_ins));
359    }
360    uint32_t idx = 0;
361    for (pandasm::Ins &pa_ins : original_dump_ins_) {
362        original_dump_ins_ptrs_.emplace_back(&pa_ins);
363        original_ins_index_map_[&pa_ins] = idx;
364        idx++;
365    }
366}
367
368void PandasmProgramDumper::GetFinalDumpIns()
369{
370    final_dump_ins_ptrs_.clear();
371    final_ins_index_map_.clear();
372    uint32_t idx = 0;
373    for (pandasm::Ins *pa_ins : original_dump_ins_ptrs_) {
374        final_ins_index_map_[pa_ins] = idx;
375        if (pa_ins->opcode != pandasm::Opcode::INVALID) {
376            final_dump_ins_ptrs_.emplace_back(pa_ins);
377            idx++;
378        }
379    }
380}
381
382void PandasmProgramDumper::DumpFinalIns(std::ostream &os)
383{
384    for (pandasm::Ins *pa_ins : final_dump_ins_ptrs_) {
385        if (PandasmDumperUtils::IsMatchLiteralId(*pa_ins)) {
386            ReplaceLiteralId4Ins(*pa_ins);
387        }
388        std::string insStr = pa_ins->ToString("", true, regs_num_);
389        os << DUMP_CONTENT_TAB << std::setw(LINE_WIDTH)
390           << std::left << insStr
391           << DUMP_CONTENT_LINE_NUMBER << pa_ins->ins_debug.line_number;
392        os << std::setw(COLUMN_WIDTH) << std::left << DUMP_CONTENT_SPACE
393           << DUMP_CONTENT_COLUMN_NUMBER << pa_ins->ins_debug.column_number
394           << DUMP_CONTENT_SINGLE_ENDL;
395    }
396}
397
398void PandasmProgramDumper::GetInvalidOpLabelMap()
399{
400    invalid_op_label_map_.clear();
401    size_t dump_ins_size = original_dump_ins_.size();
402    for (size_t i = 0; i < dump_ins_size; ++i) {
403        pandasm::Ins &curr_ins = original_dump_ins_[i];
404        if (curr_ins.opcode == pandasm::Opcode::INVALID) {
405            HandleInvalidopInsLabel(i, curr_ins);
406        }
407    }
408}
409
410void PandasmProgramDumper::HandleInvalidopInsLabel(size_t invalid_op_idx, pandasm::Ins &invalid_op_ins)
411{
412    if (!invalid_op_ins.set_label) {
413        return;
414    }
415    pandasm::Ins *nearest_valid_op_ins = GetNearestValidopIns4InvalidopIns(invalid_op_idx);
416    if (nearest_valid_op_ins == nullptr) {
417        return;
418    }
419    if (!nearest_valid_op_ins->set_label) {
420        // here, the invalid op ins and its nearest valid op ins has the same label
421        // the invalid op will be removed, so the label is still unique for each inst
422        nearest_valid_op_ins->label = invalid_op_ins.label;
423        nearest_valid_op_ins->set_label = true;
424    }
425    invalid_op_label_map_.emplace(invalid_op_ins.label, nearest_valid_op_ins->label);
426}
427
428pandasm::Ins *PandasmProgramDumper::GetNearestValidopIns4InvalidopIns(size_t invalid_op_ins_idx)
429{
430    size_t dump_ins_size = original_dump_ins_.size();
431    // search downwards
432    for (size_t i = invalid_op_ins_idx + 1; i < dump_ins_size; ++i) {
433        pandasm::Ins &curr_ins = original_dump_ins_[i];
434        if (curr_ins.opcode != pandasm::Opcode::INVALID) {
435            return &curr_ins;
436        }
437    }
438    // search upwards
439    for (size_t i = 0; i < invalid_op_ins_idx; ++i) {
440        pandasm::Ins &curr_ins = original_dump_ins_[invalid_op_ins_idx - i - 1];
441        if (curr_ins.opcode != pandasm::Opcode::INVALID) {
442            return &curr_ins;
443        }
444    }
445    return nullptr;
446}
447
448void PandasmProgramDumper::UpdateLabels4DumpIns(std::vector<pandasm::Ins*> &dump_ins, const LabelMap &label_map) const
449{
450    size_t dump_ins_size = dump_ins.size();
451    for (size_t i = 0; i < dump_ins_size; ++i) {
452        UpdateLabels4DumpInsAtIndex(i, dump_ins, label_map);
453    }
454}
455
456void PandasmProgramDumper::UpdateLabels4DumpInsAtIndex(size_t idx, std::vector<pandasm::Ins*> &dump_ins,
457                                                       const LabelMap &label_map) const
458{
459    pandasm::Ins *curr_ins = dump_ins[idx];
460    std::string mapped_label = PandasmDumperUtils::GetMappedLabel(curr_ins->label, label_map);
461    if (mapped_label != "") {
462        curr_ins->label = mapped_label;
463    }
464    if (curr_ins->IsJump()) {
465        mapped_label = PandasmDumperUtils::GetMappedLabel(curr_ins->ids[0], label_map);
466        if (mapped_label != "") {
467            curr_ins->ids.clear();
468            curr_ins->ids.emplace_back(mapped_label);
469        }
470    }
471}
472
473void PandasmProgramDumper::GetFinalLabelMap()
474{
475    final_label_map_.clear();
476    size_t dump_ins_size = final_dump_ins_ptrs_.size();
477    for (size_t i = 0; i < dump_ins_size; ++i) {
478        HandleFinalLabelAtIndex(i);
479    }
480}
481
482void PandasmProgramDumper::HandleFinalLabelAtIndex(size_t idx)
483{
484    pandasm::Ins *curr_ins = final_dump_ins_ptrs_[idx];
485    std::string new_label_name = AbcFileUtils::GetLabelNameByInstIdx(idx);
486    if (curr_ins->set_label) {
487        final_label_map_.emplace(curr_ins->label, new_label_name);
488    } else {
489        curr_ins->label = new_label_name;
490        curr_ins->set_label = true;
491    }
492}
493
494void PandasmProgramDumper::DumpFunctionCatchBlocks(std::ostream &os, const pandasm::Function &function) const
495{
496    if (is_normalized_) {
497        DumpNormalizedFunctionCatchBlocks(os, function);
498    } else {
499        DumpOriginalFunctionCatchBlocks(os, function);
500    }
501}
502
503void PandasmProgramDumper::DumpOriginalFunctionCatchBlocks(std::ostream &os,
504                                                           const pandasm::Function &function) const
505{
506    for (const pandasm::Function::CatchBlock &catch_block : function.catch_blocks) {
507        DumpCatchBlock(os, catch_block);
508    }
509}
510
511void PandasmProgramDumper::DumpNormalizedFunctionCatchBlocks(std::ostream &os,
512                                                             const pandasm::Function &function) const
513{
514    std::vector<pandasm::Function::CatchBlock> catch_blocks;
515    for (const pandasm::Function::CatchBlock &catch_block : function.catch_blocks) {
516        catch_blocks.emplace_back(PandasmDumperUtils::DeepCopyCatchBlock(catch_block));
517    }
518    for (pandasm::Function::CatchBlock &catch_block : catch_blocks) {
519        UpdateCatchBlock(catch_block);
520    }
521    for (const pandasm::Function::CatchBlock &catch_block : catch_blocks) {
522        DumpCatchBlock(os, catch_block);
523    }
524}
525
526void PandasmProgramDumper::UpdateCatchBlock(pandasm::Function::CatchBlock &catch_block) const
527{
528    catch_block.try_begin_label = GetUpdatedCatchBlockLabel(catch_block.try_begin_label);
529    catch_block.try_end_label = GetUpdatedCatchBlockLabel(catch_block.try_end_label);
530    catch_block.catch_begin_label = GetUpdatedCatchBlockLabel(catch_block.catch_begin_label);
531    catch_block.catch_end_label = GetUpdatedCatchBlockLabel(catch_block.catch_end_label);
532}
533
534std::string PandasmProgramDumper::GetUpdatedCatchBlockLabel(const std::string &orignal_label) const
535{
536    std::string mapped_label1 = PandasmDumperUtils::GetMappedLabel(orignal_label, invalid_op_label_map_);
537    if (mapped_label1 == "") {
538        return orignal_label;
539    }
540    std::string mapped_label2 = PandasmDumperUtils::GetMappedLabel(mapped_label1, final_label_map_);
541    if (mapped_label2 == "") {
542        return mapped_label1;
543    }
544    return mapped_label2;
545}
546
547void PandasmProgramDumper::DumpCatchBlock(std::ostream &os, const pandasm::Function::CatchBlock &catch_block) const
548{
549    if (catch_block.exception_record.empty()) {
550        os << DUMP_TITLE_CATCH_ALL << DUMP_CONTENT_SINGLE_ENDL;
551    } else {
552        os << DUMP_TITLE_CATCH << DUMP_CONTENT_SINGLE_ENDL;
553    }
554    os << DUMP_CONTENT_TAB << DUMP_CONTENT_TRY_BEGIN_LABEL
555       << catch_block.try_begin_label << DUMP_CONTENT_SINGLE_ENDL;
556    os << DUMP_CONTENT_TAB << DUMP_CONTENT_TRY_END_LABEL
557       << catch_block.try_end_label << DUMP_CONTENT_SINGLE_ENDL;
558    os << DUMP_CONTENT_TAB << DUMP_CONTENT_CATCH_BEGIN_LABEL
559       << catch_block.catch_begin_label << DUMP_CONTENT_SINGLE_ENDL;
560    if (!is_normalized_) {
561        os << DUMP_CONTENT_TAB << DUMP_CONTENT_CATCH_END_LABEL
562           << catch_block.catch_end_label << DUMP_CONTENT_SINGLE_ENDL;
563    }
564}
565
566void PandasmProgramDumper::DumpFunctionDebugInfo(std::ostream &os, const pandasm::Function &function)
567{
568    if (function.local_variable_debug.empty()) {
569        return;
570    }
571    std::map<int32_t, panda::pandasm::debuginfo::LocalVariable> local_variable_table;
572    if (is_normalized_) {
573        UpdateLocalVarMap(function, local_variable_table);
574    } else {
575        for (const auto &variable_info : function.local_variable_debug) {
576            local_variable_table[variable_info.reg] = variable_info;
577        }
578    }
579
580    os << DUMP_CONTENT_SINGLE_ENDL;
581    if (local_variable_table.empty()) {
582        return;
583    }
584
585    os << DUMP_TITLE_LOCAL_VAR_TABLE;
586    os << DUMP_CONTENT_SINGLE_ENDL;
587    os << DUMP_CONTENT_LOCAL_VAR_TABLE;
588    for (const auto &iter : local_variable_table) {
589        const auto &variable_info = iter.second;
590        os << DUMP_CONTENT_TAB
591           << std::setw(START_WIDTH) << std::right << variable_info.start << DUMP_CONTENT_TRIPLE_SPACES;
592        os << std::setw(END_WIDTH) << std::right << variable_info.length << DUMP_CONTENT_DOUBLE_SPACES;
593        os << std::setw(REG_WIDTH) << std::right << variable_info.reg << DUMP_CONTENT_DOUBLE_SPACES;
594        os << std::setw(NAME_WIDTH)
595           << std::right << variable_info.name << DUMP_CONTENT_NONUPLE_SPACES << variable_info.signature;
596        if (!variable_info.signature_type.empty() && variable_info.signature_type != variable_info.signature) {
597            os << " (" << variable_info.signature_type << ")";
598        }
599        os << DUMP_CONTENT_SINGLE_ENDL;
600    }
601}
602
603void PandasmProgramDumper::UpdateLocalVarMap(const pandasm::Function &function,
604    std::map<int32_t, panda::pandasm::debuginfo::LocalVariable>& local_variable_table)
605{
606    std::unordered_map<uint32_t, uint32_t> original_to_final_index_map_;
607    uint32_t max_original_idx = 0;
608    uint32_t max_final_idx = 0;
609    for (const auto &[key, value] : original_ins_index_map_) {
610        uint32_t final_idx = final_ins_index_map_[key];
611        original_to_final_index_map_[value] = final_idx;
612        if (value > max_original_idx) {
613            max_original_idx = value;
614            max_final_idx = final_idx;
615        }
616    }
617    original_to_final_index_map_[max_original_idx + 1] = max_final_idx;
618
619    for (const auto &variable_info : function.local_variable_debug) {
620        uint32_t original_start = variable_info.start;
621        uint32_t original_end = variable_info.length + variable_info.start;
622        uint32_t new_start = original_to_final_index_map_[original_start];
623        uint32_t new_length = original_to_final_index_map_[original_end] - new_start;
624        panda::pandasm::debuginfo::LocalVariable local_var = {variable_info.name,
625                                                              variable_info.signature,
626                                                              variable_info.signature_type,
627                                                              variable_info.reg,
628                                                              new_start,
629                                                              new_length};
630        local_variable_table[variable_info.reg] = local_var;
631    }
632}
633
634void PandasmProgramDumper::DumpStrings(std::ostream &os) const
635{
636    if (is_normalized_) {
637        return;
638    }
639    os << DUMP_TITLE_SEPARATOR;
640    os << DUMP_TITLE_STRING;
641    for (const std::string &str : program_->strings) {
642        os << str << DUMP_CONTENT_SINGLE_ENDL;
643    }
644}
645
646void PandasmProgramDumper::ReplaceLiteralId4Ins(pandasm::Ins &pa_ins) const
647{
648    size_t idx = PandasmDumperUtils::GetLiteralIdIndex4Ins(pa_ins);
649    std::string id_str = pa_ins.ids[idx];
650    auto it = program_->literalarray_table.find(id_str);
651    ASSERT(it != program_->literalarray_table.end());
652    const pandasm::LiteralArray &literal_array = it->second;
653    auto id = PandasmDumperUtils::GetLiteralArrayIdFromName(it->first);
654    std::string replaced_value = SerializeLiteralArray(literal_array, id);
655    pa_ins.ids[idx] = replaced_value;
656}
657
658std::string PandasmProgramDumper::SerializeLiteralArray(const pandasm::LiteralArray &lit_array, uint32_t id) const
659{
660    if (lit_array.literals_.empty()) {
661        return "";
662    }
663    std::stringstream ss;
664    ss << DUMP_CONTENT_LEFT_CURLY_BRACKET << DUMP_CONTENT_SPACE;
665    ss << lit_array.literals_.size();
666    ss << DUMP_CONTENT_SPACE << DUMP_CONTENT_LEFT_SQUARE_BRACKET << DUMP_CONTENT_SPACE;
667    processing_literal_array_id_set_.emplace(id);
668    SerializeLiterals(lit_array, ss);
669    processing_literal_array_id_set_.erase(id);
670    ss << DUMP_CONTENT_RIGHT_SQUARE_BRACKET << DUMP_CONTENT_RIGHT_CURLY_BRACKET;
671    return ss.str();
672}
673
674void PandasmProgramDumper::SerializeLiterals(const pandasm::LiteralArray &lit_array, std::stringstream &os) const
675{
676    for (size_t i = 0; i < lit_array.literals_.size(); i++) {
677        SerializeLiteralsAtIndex(lit_array, os, i);
678        os << DUMP_CONTENT_COMMA << DUMP_CONTENT_SPACE;
679    }
680}
681
682void PandasmProgramDumper::SerializeLiteralsAtIndex(
683    const pandasm::LiteralArray &lit_array, std::stringstream &os, size_t i) const
684{
685    const panda_file::LiteralTag &tag = lit_array.literals_[i].tag_;
686    os << PandasmDumperUtils::LiteralTagToString(tag) << DUMP_CONTENT_COLON;
687    const auto &val = lit_array.literals_[i].value_;
688    switch (tag) {
689        case panda_file::LiteralTag::BOOL:
690            os << (std::get<bool>(val));
691            break;
692        case panda_file::LiteralTag::LITERALBUFFERINDEX:
693        case panda_file::LiteralTag::INTEGER:
694            os << (bit_cast<int32_t>(std::get<uint32_t>(val)));
695            break;
696        case panda_file::LiteralTag::DOUBLE:
697            os << (std::get<double>(val));
698            break;
699        case panda_file::LiteralTag::STRING:
700            os << "\"" << (std::get<std::string>(val)) << "\"";
701            break;
702        case panda_file::LiteralTag::METHOD:
703        case panda_file::LiteralTag::GETTER:
704        case panda_file::LiteralTag::SETTER:
705        case panda_file::LiteralTag::GENERATORMETHOD:
706        case panda_file::LiteralTag::ASYNCGENERATORMETHOD:
707            os << (std::get<std::string>(val));
708            break;
709        case panda_file::LiteralTag::NULLVALUE:
710        case panda_file::LiteralTag::ACCESSOR:
711            os << (static_cast<int16_t>(bit_cast<int8_t>(std::get<uint8_t>(val))));
712            break;
713        case panda_file::LiteralTag::METHODAFFILIATE:
714            os << (std::get<uint16_t>(val));
715            break;
716        case panda_file::LiteralTag::LITERALARRAY:
717            SerializeNestedLiteralArrayById(os, std::get<std::string>(val));
718            break;
719        case panda_file::LiteralTag::BUILTINTYPEINDEX:
720            os << (static_cast<int16_t>(std::get<uint8_t>(val)));
721            break;
722        case panda_file::LiteralTag::TAGVALUE:
723            os << (static_cast<int16_t>(std::get<uint8_t>(val)));
724            break;
725        default:
726            UNREACHABLE();
727    }
728}
729
730void PandasmProgramDumper::SerializeNestedLiteralArrayById(
731    std::stringstream &os, const std::string &literal_array_id_name) const
732{
733    if (!is_normalized_) {
734        os << literal_array_id_name;
735        return;
736    }
737    auto id = PandasmDumperUtils::GetLiteralArrayIdFromName(literal_array_id_name);
738    if (processing_literal_array_id_set_.find(id) == processing_literal_array_id_set_.end()) {
739        auto it = program_->literalarray_table.find(literal_array_id_name);
740        ASSERT(it != program_->literalarray_table.end());
741        os << SerializeLiteralArray(it->second, id);
742    } else {
743        UNREACHABLE();
744    }
745}
746
747}  // namespace panda::abc2program
748