• 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 "assembly-context.h"
17 #include "assembly-emitter.h"
18 #include "file_items.h"
19 #include "file_writer.h"
20 #include "literal_data_accessor.h"
21 #include "mangling.h"
22 #include "os/file.h"
23 #include "runtime/include/profiling_gen.h"
24 #include "libpandafile/type_helper.h"
25 
26 #include <algorithm>
27 #include <cstddef>
28 #include <cstdint>
29 #include <iostream>
30 
31 namespace {
32 
33 using ark::panda_file::AnnotationItem;
34 using ark::panda_file::ArrayValueItem;
35 using ark::panda_file::BaseClassItem;
36 using ark::panda_file::BaseFieldItem;
37 using ark::panda_file::BaseMethodItem;
38 using ark::panda_file::ClassItem;
39 using ark::panda_file::CodeItem;
40 using ark::panda_file::DebugInfoItem;
41 using ark::panda_file::FieldItem;
42 using ark::panda_file::FileWriter;
43 using ark::panda_file::ForeignClassItem;
44 using ark::panda_file::ForeignFieldItem;
45 using ark::panda_file::ForeignMethodItem;
46 using ark::panda_file::ItemContainer;
47 using ark::panda_file::MemoryBufferWriter;
48 using ark::panda_file::MethodHandleItem;
49 using ark::panda_file::MethodItem;
50 using ark::panda_file::MethodParamItem;
51 using ark::panda_file::ParamAnnotationsItem;
52 using ark::panda_file::PrimitiveTypeItem;
53 using ark::panda_file::ScalarValueItem;
54 using ark::panda_file::StringItem;
55 using ark::panda_file::Type;
56 using ark::panda_file::TypeItem;
57 using ark::panda_file::ValueItem;
58 using ark::panda_file::Writer;
59 
CreatePrimitiveTypes(ItemContainer * container)60 std::unordered_map<Type::TypeId, PrimitiveTypeItem *> CreatePrimitiveTypes(ItemContainer *container)
61 {
62     auto res = std::unordered_map<Type::TypeId, PrimitiveTypeItem *> {};
63     res.insert({Type::TypeId::VOID, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID)});
64     res.insert({Type::TypeId::U1, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::U1)});
65     res.insert({Type::TypeId::I8, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::I8)});
66     res.insert({Type::TypeId::U8, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::U8)});
67     res.insert({Type::TypeId::I16, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::I16)});
68     res.insert({Type::TypeId::U16, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::U16)});
69     res.insert({Type::TypeId::I32, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::I32)});
70     res.insert({Type::TypeId::U32, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::U32)});
71     res.insert({Type::TypeId::I64, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::I64)});
72     res.insert({Type::TypeId::U64, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::U64)});
73     res.insert({Type::TypeId::F32, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::F32)});
74     res.insert({Type::TypeId::F64, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::F64)});
75     res.insert({Type::TypeId::TAGGED, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::TAGGED)});
76     return res;
77 }
78 
79 template <class T>
Find(const T & map,typename T::key_type key)80 typename T::mapped_type Find(const T &map, typename T::key_type key)
81 {
82     auto res = map.find(key);
83     ASSERT(res != map.end());
84     return res != map.end() ? res->second : nullptr;
85 }
86 
87 }  // anonymous namespace
88 
89 namespace ark::pandasm {
90 
91 /* static */
92 // NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
93 std::string AsmEmitter::lastError_ {};
94 
GetTypeId(Value::Type type)95 static panda_file::Type::TypeId GetTypeId(Value::Type type)
96 {
97     switch (type) {
98         case Value::Type::U1:
99             return panda_file::Type::TypeId::U1;
100         case Value::Type::I8:
101             return panda_file::Type::TypeId::I8;
102         case Value::Type::U8:
103             return panda_file::Type::TypeId::U8;
104         case Value::Type::I16:
105             return panda_file::Type::TypeId::I16;
106         case Value::Type::U16:
107             return panda_file::Type::TypeId::U16;
108         case Value::Type::I32:
109             return panda_file::Type::TypeId::I32;
110         case Value::Type::U32:
111             return panda_file::Type::TypeId::U32;
112         case Value::Type::I64:
113             return panda_file::Type::TypeId::I64;
114         case Value::Type::U64:
115             return panda_file::Type::TypeId::U64;
116         case Value::Type::F32:
117             return panda_file::Type::TypeId::F32;
118         case Value::Type::F64:
119             return panda_file::Type::TypeId::F64;
120         case Value::Type::VOID:
121             return panda_file::Type::TypeId::VOID;
122         default:
123             return panda_file::Type::TypeId::REFERENCE;
124     }
125 }
126 
127 /* static */
CheckValueType(Value::Type valueType,const Type & type,const Program & program)128 bool AsmEmitter::CheckValueType(Value::Type valueType, const Type &type, const Program &program)
129 {
130     auto valueTypeId = GetTypeId(valueType);
131     if (valueTypeId != type.GetId()) {
132         SetLastError("Inconsistent element (" + AnnotationElement::TypeToString(valueType) +
133                      ") and function's return type (" + type.GetName() + ")");
134         return false;
135     }
136 
137     switch (valueType) {
138         case Value::Type::STRING:
139         case Value::Type::RECORD:
140         case Value::Type::ANNOTATION:
141         case Value::Type::ENUM: {
142             auto it = program.recordTable.find(type.GetName());
143             if (it == program.recordTable.cend()) {
144                 SetLastError("Record " + type.GetName() + " not found");
145                 return false;
146             }
147 
148             auto &record = it->second;
149             if (valueType == Value::Type::ANNOTATION && !record.metadata->IsAnnotation() &&
150                 !record.metadata->IsRuntimeAnnotation() && !record.metadata->IsRuntimeTypeAnnotation() &&
151                 !record.metadata->IsTypeAnnotation()) {
152                 SetLastError("Record " + type.GetName() + " isn't annotation");
153                 return false;
154             }
155 
156             if (valueType == Value::Type::ENUM && (record.metadata->GetAccessFlags() & ACC_ENUM) == 0) {
157                 SetLastError("Record " + type.GetName() + " isn't enum");
158                 return false;
159             }
160 
161             break;
162         }
163         case Value::Type::ARRAY: {
164             if (!type.IsArray()) {
165                 SetLastError("Inconsistent element (" + AnnotationElement::TypeToString(valueType) +
166                              ") and function's return type (" + type.GetName() + ")");
167                 return false;
168             }
169 
170             break;
171         }
172         default: {
173             break;
174         }
175     }
176 
177     return true;
178 }
179 
180 /* static */
GetMethodSignatureFromProgram(const std::string & name,const Program & program)181 std::string AsmEmitter::GetMethodSignatureFromProgram(const std::string &name, const Program &program)
182 {
183     if (IsSignatureOrMangled(name)) {
184         return name;
185     }
186 
187     const auto itSynonym = program.functionSynonyms.find(name);
188     const bool isMethodKnown = (itSynonym != program.functionSynonyms.end());
189     const bool isSingleSynonym = (isMethodKnown && (itSynonym->second.size() == 1));
190     if (isSingleSynonym) {
191         return itSynonym->second[0];
192     }
193     SetLastError("More than one alternative for method " + name);
194     return std::string("");
195 }
196 
CreateLiteralItemFromUint8(const Value * value,std::vector<panda_file::LiteralItem> * out)197 static panda_file::LiteralItem *CreateLiteralItemFromUint8(const Value *value,
198                                                            std::vector<panda_file::LiteralItem> *out)
199 {
200     auto v = value->GetAsScalar()->GetValue<uint8_t>();
201     out->emplace_back(v);
202     return &out->back();
203 }
204 
CreateLiteralItemFromUint16(const Value * value,std::vector<panda_file::LiteralItem> * out)205 static panda_file::LiteralItem *CreateLiteralItemFromUint16(const Value *value,
206                                                             std::vector<panda_file::LiteralItem> *out)
207 {
208     auto v = value->GetAsScalar()->GetValue<uint16_t>();
209     out->emplace_back(v);
210     return &out->back();
211 }
212 
CreateLiteralItemFromUint32(const Value * value,std::vector<panda_file::LiteralItem> * out)213 static panda_file::LiteralItem *CreateLiteralItemFromUint32(const Value *value,
214                                                             std::vector<panda_file::LiteralItem> *out)
215 {
216     auto v = value->GetAsScalar()->GetValue<uint32_t>();
217     out->emplace_back(v);
218     return &out->back();
219 }
220 
CreateLiteralItemFromUint64(const Value * value,std::vector<panda_file::LiteralItem> * out)221 static panda_file::LiteralItem *CreateLiteralItemFromUint64(const Value *value,
222                                                             std::vector<panda_file::LiteralItem> *out)
223 {
224     auto v = value->GetAsScalar()->GetValue<uint64_t>();
225     out->emplace_back(v);
226     return &out->back();
227 }
228 
CreateLiteralItemFromFloat(const Value * value,std::vector<panda_file::LiteralItem> * out)229 static panda_file::LiteralItem *CreateLiteralItemFromFloat(const Value *value,
230                                                            std::vector<panda_file::LiteralItem> *out)
231 {
232     auto v = bit_cast<uint32_t>(value->GetAsScalar()->GetValue<float>());
233     out->emplace_back(v);
234     return &out->back();
235 }
236 
CreateLiteralItemFromDouble(const Value * value,std::vector<panda_file::LiteralItem> * out)237 static panda_file::LiteralItem *CreateLiteralItemFromDouble(const Value *value,
238                                                             std::vector<panda_file::LiteralItem> *out)
239 {
240     auto v = bit_cast<uint64_t>(value->GetAsScalar()->GetValue<double>());
241     out->emplace_back(v);
242     return &out->back();
243 }
244 
CreateLiteralItemFromString(ItemContainer * container,const Value * value,std::vector<panda_file::LiteralItem> * out)245 static panda_file::LiteralItem *CreateLiteralItemFromString(ItemContainer *container, const Value *value,
246                                                             std::vector<panda_file::LiteralItem> *out)
247 {
248     auto *stringItem = container->GetOrCreateStringItem(value->GetAsScalar()->GetValue<std::string>());
249     out->emplace_back(stringItem);
250     return &out->back();
251 }
252 
253 /* static */
CreateLiteralItemFromMethod(const Value * value,std::vector<panda_file::LiteralItem> * out,const AsmEmitter::AsmEntityCollections & entities)254 panda_file::LiteralItem *AsmEmitter::CreateLiteralItemFromMethod(const Value *value,
255                                                                  std::vector<panda_file::LiteralItem> *out,
256                                                                  const AsmEmitter::AsmEntityCollections &entities)
257 {
258     auto name = value->GetAsScalar()->GetValue<std::string>();
259     ASSERT(value->GetType() == Value::Type::METHOD);
260     auto *methodItem = FindAmongAllMethods(name, entities, value);
261     out->emplace_back(methodItem);
262     return &out->back();
263 }
264 
CreateLiteralItemFromLiteralArray(const Value * value,std::vector<panda_file::LiteralItem> * out,const AsmEmitter::AsmEntityCollections & entities)265 static panda_file::LiteralItem *CreateLiteralItemFromLiteralArray(const Value *value,
266                                                                   std::vector<panda_file::LiteralItem> *out,
267                                                                   const AsmEmitter::AsmEntityCollections &entities)
268 {
269     auto key = value->GetAsScalar()->GetValue<std::string>();
270     auto litItem = Find(entities.literalarrayItems, key);
271     out->emplace_back(litItem);
272     return &out->back();
273 }
274 
275 // 重构后的 CreateLiteralItem 方法
CreateLiteralItem(ItemContainer * container,const Value * value,std::vector<panda_file::LiteralItem> * out,const AsmEmitter::AsmEntityCollections & entities)276 panda_file::LiteralItem *AsmEmitter::CreateLiteralItem(ItemContainer *container, const Value *value,
277                                                        std::vector<panda_file::LiteralItem> *out,
278                                                        const AsmEmitter::AsmEntityCollections &entities)
279 {
280     ASSERT(out != nullptr);
281 
282     auto valueType = value->GetType();
283 
284     switch (valueType) {
285         case Value::Type::U1:
286         case Value::Type::I8:
287         case Value::Type::U8:
288             return CreateLiteralItemFromUint8(value, out);
289         case Value::Type::I16:
290         case Value::Type::U16:
291             return CreateLiteralItemFromUint16(value, out);
292         case Value::Type::I32:
293         case Value::Type::U32:
294         case Value::Type::STRING_NULLPTR:
295             return CreateLiteralItemFromUint32(value, out);
296         case Value::Type::I64:
297         case Value::Type::U64:
298             return CreateLiteralItemFromUint64(value, out);
299         case Value::Type::F32:
300             return CreateLiteralItemFromFloat(value, out);
301         case Value::Type::F64:
302             return CreateLiteralItemFromDouble(value, out);
303         case Value::Type::STRING:
304             return CreateLiteralItemFromString(container, value, out);
305         case Value::Type::METHOD:
306             return CreateLiteralItemFromMethod(value, out, entities);
307         case Value::Type::LITERALARRAY:
308             return CreateLiteralItemFromLiteralArray(value, out, entities);
309         default:
310             return nullptr;
311     }
312 }
313 
314 /* static */
CheckValueRecordCase(const Value * value,const Program & program)315 bool AsmEmitter::CheckValueRecordCase(const Value *value, const Program &program)
316 {
317     auto t = value->GetAsScalar()->GetValue<Type>();
318     if (!t.IsObject()) {
319         return true;
320     }
321 
322     auto recordName = t.GetName();
323     bool isFound;
324     if (t.IsArray()) {
325         auto it = program.arrayTypes.find(t);
326         isFound = it != program.arrayTypes.cend();
327     } else {
328         auto it = program.recordTable.find(recordName);
329         isFound = it != program.recordTable.cend();
330     }
331 
332     if (!isFound) {
333         SetLastError("Incorrect value: record " + recordName + " not found");
334         return false;
335     }
336 
337     return true;
338 }
339 
340 /* static */
CheckValueMethodCase(const Value * value,const Program & program)341 bool AsmEmitter::CheckValueMethodCase(const Value *value, const Program &program)
342 {
343     auto functionName = value->GetAsScalar()->GetValue<std::string>();
344     const auto &functionTable =
345         value->GetAsScalar()->IsStatic() ? program.functionStaticTable : program.functionInstanceTable;
346     auto it = functionTable.find(functionName);
347     if (it == functionTable.cend()) {
348         SetLastError("Incorrect value: function " + functionName + " not found");
349         return false;
350     }
351 
352     return true;
353 }
354 
355 /* static */
CheckValueEnumCase(const Value * value,const Type & type,const Program & program)356 bool AsmEmitter::CheckValueEnumCase(const Value *value, const Type &type, const Program &program)
357 {
358     auto enumValue = value->GetAsScalar()->GetValue<std::string>();
359     auto recordName = GetOwnerName(enumValue);
360     auto fieldName = GetItemName(enumValue);
361 
362     if (recordName != type.GetName()) {
363         SetLastError("Incorrect value: Expected " + type.GetName() + " enum record");
364         return false;
365     }
366 
367     const auto &record = program.recordTable.find(recordName)->second;
368     auto it = std::find_if(record.fieldList.cbegin(), record.fieldList.cend(),
369                            [&fieldName](const Field &field) { return field.name == fieldName; });
370     if (it == record.fieldList.cend()) {
371         SetLastError("Incorrect value: Enum field " + enumValue + " not found");
372         return false;
373     }
374 
375     const auto &field = *it;
376     if ((field.metadata->GetAccessFlags() & ACC_ENUM) == 0) {
377         SetLastError("Incorrect value: Field " + enumValue + " isn't enum");
378         return false;
379     }
380 
381     return true;
382 }
383 
384 /* static */
CheckValueArrayCase(const Value * value,const Type & type,const Program & program)385 bool AsmEmitter::CheckValueArrayCase(const Value *value, const Type &type, const Program &program)
386 {
387     auto componentType = type.GetComponentType();
388     auto valueComponentType = value->GetAsArray()->GetComponentType();
389     if (valueComponentType == Value::Type::VOID && value->GetAsArray()->GetValues().empty()) {
390         return true;
391     }
392 
393     if (!CheckValueType(valueComponentType, componentType, program)) {
394         SetLastError("Incorrect array's component type: " + GetLastError());
395         return false;
396     }
397 
398     for (auto &elemValue : value->GetAsArray()->GetValues()) {
399         if (!CheckValue(&elemValue, componentType, program)) {
400             SetLastError("Incorrect array's element: " + GetLastError());
401             return false;
402         }
403     }
404 
405     return true;
406 }
407 
408 /* static */
CheckValue(const Value * value,const Type & type,const Program & program)409 bool AsmEmitter::CheckValue(const Value *value, const Type &type, const Program &program)
410 {
411     auto valueType = value->GetType();
412     if (!CheckValueType(valueType, type, program)) {
413         SetLastError("Incorrect type: " + GetLastError());
414         return false;
415     }
416 
417     switch (valueType) {
418         case Value::Type::RECORD: {
419             if (!CheckValueRecordCase(value, program)) {
420                 return false;
421             }
422 
423             break;
424         }
425         case Value::Type::METHOD: {
426             if (!CheckValueMethodCase(value, program)) {
427                 return false;
428             }
429 
430             break;
431         }
432         case Value::Type::ENUM: {
433             if (!CheckValueEnumCase(value, type, program)) {
434                 return false;
435             }
436 
437             break;
438         }
439         case Value::Type::ARRAY: {
440             if (!CheckValueArrayCase(value, type, program)) {
441                 return false;
442             }
443 
444             break;
445         }
446         default: {
447             break;
448         }
449     }
450 
451     return true;
452 }
453 
454 /* static */
CreateScalarStringValueItem(ItemContainer * container,const Value * value,std::vector<ScalarValueItem> * out)455 ScalarValueItem *AsmEmitter::CreateScalarStringValueItem(ItemContainer *container, const Value *value,
456                                                          std::vector<ScalarValueItem> *out)
457 {
458     auto *stringItem = container->GetOrCreateStringItem(value->GetAsScalar()->GetValue<std::string>());
459     if (out != nullptr) {
460         out->emplace_back(stringItem);
461         return &out->back();
462     }
463 
464     return container->CreateItem<ScalarValueItem>(stringItem);
465 }
466 
467 /* static */
CreateScalarRecordValueItem(ItemContainer * container,const Value * value,std::vector<ScalarValueItem> * out,const std::unordered_map<std::string,BaseClassItem * > & classes)468 ScalarValueItem *AsmEmitter::CreateScalarRecordValueItem(
469     ItemContainer *container, const Value *value, std::vector<ScalarValueItem> *out,
470     const std::unordered_map<std::string, BaseClassItem *> &classes)
471 {
472     auto type = value->GetAsScalar()->GetValue<Type>();
473     BaseClassItem *classItem;
474     if (type.IsObject()) {
475         auto name = type.GetName();
476         auto it = classes.find(name);
477         if (it == classes.cend()) {
478             return nullptr;
479         }
480 
481         classItem = it->second;
482     } else {
483         classItem = container->GetOrCreateForeignClassItem(type.GetDescriptor());
484     }
485 
486     if (out != nullptr) {
487         out->emplace_back(classItem);
488         return &out->back();
489     }
490 
491     return container->CreateItem<ScalarValueItem>(classItem);
492 }
493 
494 /* static */
495 // CC-OFFNXT(G.FUN.01-CPP) solid logic
CreateScalarMethodValueItem(ItemContainer * container,const Value * value,std::vector<ScalarValueItem> * out,const Program & program,const AsmEmitter::AsmEntityCollections & entities,std::pair<bool,bool> searchInfo)496 ScalarValueItem *AsmEmitter::CreateScalarMethodValueItem(ItemContainer *container, const Value *value,
497                                                          std::vector<ScalarValueItem> *out, const Program &program,
498                                                          const AsmEmitter::AsmEntityCollections &entities,
499                                                          std::pair<bool, bool> searchInfo)
500 {
501     auto name = value->GetAsScalar()->GetValue<std::string>();
502 
503     name = GetMethodSignatureFromProgram(name, program);
504 
505     BaseMethodItem *methodItem {nullptr};
506     if (searchInfo.first) {
507         auto &methods = searchInfo.second ? entities.staticMethodItems : entities.methodItems;
508         auto it = methods.find(name);
509         if (it != methods.cend()) {
510             methodItem = it->second;
511         }
512     } else {
513         methodItem = FindAmongAllMethods(name, entities, value);
514     }
515     if (methodItem == nullptr) {
516         return nullptr;
517     }
518 
519     if (out != nullptr) {
520         out->emplace_back(methodItem);
521         return &out->back();
522     }
523 
524     return container->CreateItem<ScalarValueItem>(methodItem);
525 }
526 
527 /* static */
CreateScalarLiteralArrayItem(ItemContainer * container,const Value * value,std::vector<ScalarValueItem> * out,const std::unordered_map<std::string,panda_file::LiteralArrayItem * > & literalarrays)528 ScalarValueItem *AsmEmitter::CreateScalarLiteralArrayItem(
529     ItemContainer *container, const Value *value, std::vector<ScalarValueItem> *out,
530     const std::unordered_map<std::string, panda_file::LiteralArrayItem *> &literalarrays)
531 {
532     auto name = value->GetAsScalar()->GetValue<std::string>();
533     auto it = literalarrays.find(name);
534     ASSERT(it != literalarrays.end());
535     auto *literalarrayItem = it->second;
536     if (out != nullptr) {
537         out->emplace_back(literalarrayItem);
538         return &out->back();
539     }
540 
541     return container->CreateItem<ScalarValueItem>(literalarrayItem);
542 }
543 
544 /* static */
CreateScalarEnumValueItem(ItemContainer * container,const Value * value,std::vector<ScalarValueItem> * out,const std::unordered_map<std::string,BaseFieldItem * > & fields)545 ScalarValueItem *AsmEmitter::CreateScalarEnumValueItem(ItemContainer *container, const Value *value,
546                                                        std::vector<ScalarValueItem> *out,
547                                                        const std::unordered_map<std::string, BaseFieldItem *> &fields)
548 {
549     auto name = value->GetAsScalar()->GetValue<std::string>();
550     auto it = fields.find(name);
551     if (it == fields.cend()) {
552         return nullptr;
553     }
554 
555     auto *fieldItem = it->second;
556     if (out != nullptr) {
557         out->emplace_back(fieldItem);
558         return &out->back();
559     }
560 
561     return container->CreateItem<ScalarValueItem>(fieldItem);
562 }
563 
564 /* static */
565 // CC-OFFNXT(G.FUN.01-CPP) solid logic
CreateScalarAnnotationValueItem(ItemContainer * container,const Value * value,std::vector<ScalarValueItem> * out,const Program & program,const AsmEmitter::AsmEntityCollections & entities)566 ScalarValueItem *AsmEmitter::CreateScalarAnnotationValueItem(ItemContainer *container, const Value *value,
567                                                              std::vector<ScalarValueItem> *out, const Program &program,
568                                                              const AsmEmitter::AsmEntityCollections &entities)
569 {
570     auto annotation = value->GetAsScalar()->GetValue<AnnotationData>();
571     auto *annotationItem = CreateAnnotationItem(container, annotation, program, entities);
572     if (annotationItem == nullptr) {
573         return nullptr;
574     }
575 
576     if (out != nullptr) {
577         out->emplace_back(annotationItem);
578         return &out->back();
579     }
580 
581     return container->CreateItem<ScalarValueItem>(annotationItem);
582 }
583 
584 /* static */
585 // CC-OFFNXT(G.FUN.01-CPP) solid logic
CreateScalarValueItem(ItemContainer * container,const Value * value,std::vector<ScalarValueItem> * out,const Program & program,const AsmEmitter::AsmEntityCollections & entities,std::pair<bool,bool> searchInfo)586 ScalarValueItem *AsmEmitter::CreateScalarValueItem(ItemContainer *container, const Value *value,
587                                                    std::vector<ScalarValueItem> *out, const Program &program,
588                                                    const AsmEmitter::AsmEntityCollections &entities,
589                                                    std::pair<bool, bool> searchInfo)
590 {
591     auto valueType = value->GetType();
592 
593     switch (valueType) {
594         case Value::Type::U1:
595         case Value::Type::I8:
596         case Value::Type::U8:
597         case Value::Type::I16:
598         case Value::Type::U16:
599         case Value::Type::I32:
600         case Value::Type::U32:
601         case Value::Type::STRING_NULLPTR: {
602             return CreateScalarPrimValueItem<uint32_t>(container, value, out);
603         }
604         case Value::Type::I64:
605         case Value::Type::U64: {
606             return CreateScalarPrimValueItem<uint64_t>(container, value, out);
607         }
608         case Value::Type::F32: {
609             return CreateScalarPrimValueItem<float>(container, value, out);
610         }
611         case Value::Type::F64: {
612             return CreateScalarPrimValueItem<double>(container, value, out);
613         }
614         case Value::Type::STRING: {
615             return CreateScalarStringValueItem(container, value, out);
616         }
617         case Value::Type::RECORD: {
618             return CreateScalarRecordValueItem(container, value, out, entities.classItems);
619         }
620         case Value::Type::METHOD: {
621             return CreateScalarMethodValueItem(container, value, out, program, entities, searchInfo);
622         }
623         case Value::Type::ENUM: {
624             return CreateScalarEnumValueItem(container, value, out, entities.fieldItems);
625         }
626         case Value::Type::ANNOTATION: {
627             return CreateScalarAnnotationValueItem(container, value, out, program, entities);
628         }
629         case Value::Type::LITERALARRAY: {
630             return CreateScalarLiteralArrayItem(container, value, out, entities.literalarrayItems);
631         }
632         default: {
633             UNREACHABLE();
634             return nullptr;
635         }
636     }
637 }
638 
639 /* static */
640 // CC-OFFNXT(G.FUN.01-CPP) solid logic
CreateValueItem(ItemContainer * container,const Value * value,const Program & program,const AsmEmitter::AsmEntityCollections & entities,std::pair<bool,bool> searchInfo)641 ValueItem *AsmEmitter::CreateValueItem(ItemContainer *container, const Value *value, const Program &program,
642                                        const AsmEmitter::AsmEntityCollections &entities,
643                                        std::pair<bool, bool> searchInfo)
644 {
645     switch (value->GetType()) {
646         case Value::Type::ARRAY: {
647             std::vector<ScalarValueItem> elements;
648             for (const auto &elemValue : value->GetAsArray()->GetValues()) {
649                 auto *item = CreateScalarValueItem(container, &elemValue, &elements, program, entities, searchInfo);
650                 if (item == nullptr) {
651                     return nullptr;
652                 }
653             }
654 
655             auto componentType = value->GetAsArray()->GetComponentType();
656             return container->CreateItem<ArrayValueItem>(panda_file::Type(GetTypeId(componentType)),
657                                                          std::move(elements));
658         }
659         default: {
660             return CreateScalarValueItem(container, value, nullptr, program, entities, searchInfo);
661         }
662     }
663 }
664 
665 /* static */
666 // CC-OFFNXT(G.FMT.10-CPP) project code style
667 std::optional<std::map<std::basic_string<char>, ark::pandasm::Function>::const_iterator>
GetMethodForAnnotationElement(const std::string & functionName,const Value * value,const Program & program)668 AsmEmitter::GetMethodForAnnotationElement(const std::string &functionName, const Value *value, const Program &program)
669 {
670     if (value->GetType() != Value::Type::METHOD) {
671         return program.FindAmongAllFunctions(functionName);
672     }
673     const auto &functionTable =
674         value->GetAsScalar()->IsStatic() ? program.functionStaticTable : program.functionInstanceTable;
675     auto funcIt = functionTable.find(functionName);
676     if (funcIt == functionTable.cend()) {
677         return std::nullopt;
678     }
679     return funcIt;
680 }
681 
682 /* static */
CreateAnnotationItem(ItemContainer * container,const AnnotationData & annotation,const Program & program,const AsmEmitter::AsmEntityCollections & entities)683 AnnotationItem *AsmEmitter::CreateAnnotationItem(ItemContainer *container, const AnnotationData &annotation,
684                                                  const Program &program,
685                                                  const AsmEmitter::AsmEntityCollections &entities)
686 {
687     auto recordName = annotation.GetName();
688     auto it = program.recordTable.find(recordName);
689     if (it == program.recordTable.cend()) {
690         SetLastError("Record " + recordName + " not found");
691         return nullptr;
692     }
693 
694     auto &record = it->second;
695     if (!record.metadata->IsAnnotation()) {
696         SetLastError("Record " + recordName + " isn't annotation");
697         return nullptr;
698     }
699 
700     std::vector<AnnotationItem::Elem> itemElements;
701     std::vector<AnnotationItem::Tag> tagElements;
702 
703     for (const auto &element : annotation.GetElements()) {
704         auto name = element.GetName();
705         auto *value = element.GetValue();
706 
707         auto valueType = value->GetType();
708 
709         uint8_t tagType;
710 
711         if (valueType == Value::Type::ARRAY && !value->GetAsArray()->GetValues().empty()) {
712             auto arrayElementType = value->GetAsArray()->GetComponentType();
713             tagType = Value::GetArrayTypeAsChar(arrayElementType);
714         } else {
715             tagType = Value::GetTypeAsChar(valueType);
716         }
717 
718         ASSERT(tagType != '0');
719 
720         auto functionName = GetMethodSignatureFromProgram(record.name + "." + name, program);
721 
722         auto foundFunc = GetMethodForAnnotationElement(functionName, value, program);
723         if (record.HasImplementation() && !foundFunc.has_value()) {
724             // Definitions of the system annotations may be absent.
725             // So print message and continue if corresponding function isn't found.
726             LOG(INFO, ASSEMBLER) << "Function " << functionName << " not found";
727         } else if (record.HasImplementation() && !CheckValue(value, foundFunc.value()->second.returnType, program)) {
728             SetLastError("Incorrect annotation element " + functionName + ": " + GetLastError());
729             return nullptr;
730         }
731         ValueItem *item {nullptr};
732         if (foundFunc.has_value()) {
733             item = CreateValueItem(container, value, program, entities, {true, foundFunc.value()->second.IsStatic()});
734         } else {
735             item = CreateValueItem(container, value, program, entities);
736         }
737 
738         if (item == nullptr) {
739             SetLastError("Cannot create value item for annotation element " + functionName + ": " + GetLastError());
740             return nullptr;
741         }
742 
743         itemElements.emplace_back(container->GetOrCreateStringItem(name), item);
744         tagElements.emplace_back(tagType);
745     }
746 
747     auto *cls = entities.classItems.find(recordName)->second;
748     return container->CreateItem<AnnotationItem>(cls, std::move(itemElements), std::move(tagElements));
749 }
750 
CreateMethodHandleItem(ItemContainer * container,const MethodHandle & mh,const std::unordered_map<std::string,BaseFieldItem * > & fields,const std::unordered_map<std::string,BaseMethodItem * > & methods)751 MethodHandleItem *AsmEmitter::CreateMethodHandleItem(ItemContainer *container, const MethodHandle &mh,
752                                                      const std::unordered_map<std::string, BaseFieldItem *> &fields,
753                                                      const std::unordered_map<std::string, BaseMethodItem *> &methods)
754 {
755     MethodHandleItem *item = nullptr;
756     switch (mh.type) {
757         case panda_file::MethodHandleType::PUT_STATIC:
758         case panda_file::MethodHandleType::GET_STATIC:
759         case panda_file::MethodHandleType::PUT_INSTANCE:
760         case panda_file::MethodHandleType::GET_INSTANCE: {
761             item = container->CreateItem<MethodHandleItem>(mh.type, fields.at(mh.itemName));
762             break;
763         }
764         case panda_file::MethodHandleType::INVOKE_STATIC:
765         case panda_file::MethodHandleType::INVOKE_INSTANCE:
766         case panda_file::MethodHandleType::INVOKE_CONSTRUCTOR:
767         case panda_file::MethodHandleType::INVOKE_DIRECT:
768         case panda_file::MethodHandleType::INVOKE_INTERFACE: {
769             item = container->CreateItem<MethodHandleItem>(mh.type, methods.at(mh.itemName));
770             break;
771         }
772         default:
773             UNREACHABLE();
774             break;
775     }
776     return item;
777 }
778 
779 /* static */
780 template <class T>
781 // CC-OFFNXT(G.FUN.01-CPP) solid logic
AddAnnotations(T * item,ItemContainer * container,const AnnotationMetadata & metadata,const Program & program,const AsmEmitter::AsmEntityCollections & entities)782 bool AsmEmitter::AddAnnotations(T *item, ItemContainer *container, const AnnotationMetadata &metadata,
783                                 const Program &program, const AsmEmitter::AsmEntityCollections &entities)
784 {
785     for (const auto &annotation : metadata.GetAnnotations()) {
786         auto *annotationItem = CreateAnnotationItem(container, annotation, program, entities);
787         if (annotationItem == nullptr) {
788             return false;
789         }
790 
791         auto &record = program.recordTable.find(annotation.GetName())->second;
792         if (record.metadata->IsRuntimeAnnotation()) {
793             item->AddRuntimeAnnotation(annotationItem);
794         } else if (record.metadata->IsAnnotation()) {
795             item->AddAnnotation(annotationItem);
796         } else if (record.metadata->IsRuntimeTypeAnnotation()) {
797             item->AddRuntimeTypeAnnotation(annotationItem);
798         } else if (record.metadata->IsTypeAnnotation()) {
799             item->AddTypeAnnotation(annotationItem);
800         }
801     }
802 
803     return true;
804 }
805 
806 template <class T>
AddBytecodeIndexDependencies(MethodItem * method,const Ins & insn,const std::unordered_map<std::string,T * > & items,const AsmEmitter::AsmEntityCollections & entities,bool lookupInStatic=true)807 static void AddBytecodeIndexDependencies(MethodItem *method, const Ins &insn,
808                                          const std::unordered_map<std::string, T *> &items,
809                                          const AsmEmitter::AsmEntityCollections &entities, bool lookupInStatic = true)
810 {
811     ASSERT(!insn.ids.empty());
812 
813     for (const auto &id : insn.ids) {
814         auto it = items.find(id);
815         if (it == items.cend()) {
816             if (lookupInStatic && insn.HasFlag(InstFlags::STATIC_METHOD_ID)) {
817                 AddBytecodeIndexDependencies(method, insn, entities.methodItems, entities, false);
818                 return;
819             }
820 #ifdef PANDA_WITH_ECMASCRIPT
821             AddBytecodeIndexDependencies(method, insn, entities.staticMethodItems, entities);
822             return;
823 #endif
824             if (insn.opcode == pandasm::Opcode::INITOBJ_SHORT || insn.opcode == pandasm::Opcode::INITOBJ_RANGE ||
825                 insn.opcode == pandasm::Opcode::INITOBJ) {
826                 AddBytecodeIndexDependencies(method, insn, entities.staticMethodItems, entities);
827                 return;
828             }
829         }
830         ASSERT_PRINT(it != items.cend(), "Symbol '" << id << "' not found");
831 
832         auto *item = it->second;
833         ASSERT(item->GetIndexType() != panda_file::IndexType::NONE);
834         method->AddIndexDependency(item);
835     }
836 }
837 
AddBytecodeIndexDependencies(MethodItem * method,const Function & func,const AsmEmitter::AsmEntityCollections & entities)838 static void AddBytecodeIndexDependencies(MethodItem *method, const Function &func,
839                                          const AsmEmitter::AsmEntityCollections &entities)
840 {
841     for (const auto &insn : func.ins) {
842         if (insn.opcode == Opcode::INVALID) {
843             continue;
844         }
845 
846         if (insn.HasFlag(InstFlags::METHOD_ID)) {
847             AddBytecodeIndexDependencies(method, insn, entities.methodItems, entities);
848             continue;
849         }
850 
851         if (insn.HasFlag(InstFlags::STATIC_METHOD_ID)) {
852             AddBytecodeIndexDependencies(method, insn, entities.staticMethodItems, entities);
853             continue;
854         }
855 
856         if (insn.HasFlag(InstFlags::FIELD_ID)) {
857             AddBytecodeIndexDependencies(method, insn, entities.fieldItems, entities);
858             continue;
859         }
860 
861         if (insn.HasFlag(InstFlags::STATIC_FIELD_ID)) {
862             AddBytecodeIndexDependencies(method, insn, entities.staticFieldItems, entities);
863             continue;
864         }
865 
866         if (insn.HasFlag(InstFlags::TYPE_ID)) {
867             AddBytecodeIndexDependencies(method, insn, entities.classItems, entities);
868             continue;
869         }
870     }
871 
872     for (const auto &catchBlock : func.catchBlocks) {
873         if (catchBlock.exceptionRecord.empty()) {
874             continue;
875         }
876 
877         auto it = entities.classItems.find(catchBlock.exceptionRecord);
878         ASSERT(it != entities.classItems.cend());
879         auto *item = it->second;
880         ASSERT(item->GetIndexType() != panda_file::IndexType::NONE);
881         method->AddIndexDependency(item);
882     }
883 }
884 
885 /* static */
MakeStringItems(ItemContainer * items,const Program & program,AsmEmitter::AsmEntityCollections & entities)886 void AsmEmitter::MakeStringItems(ItemContainer *items, const Program &program,
887                                  AsmEmitter::AsmEntityCollections &entities)
888 {
889     for (const auto &s : program.strings) {
890         auto *item = items->GetOrCreateStringItem(s);
891         entities.stringItems.insert({s, item});
892     }
893 
894     for (const auto &s : program.exportStrMap) {
895         auto *item = items->GetOrCreateStringItem(s.first);
896         entities.stringItems.insert({s.first, item});
897     }
898 }
899 
900 template <Value::Type TYPE, typename CType = ValueTypeHelperT<TYPE>>
CreateValue(const LiteralArray::Literal & literal)901 static ScalarValue CreateValue(const LiteralArray::Literal &literal)
902 {
903     if constexpr (std::is_same_v<CType, ValueTypeHelperT<TYPE>> && std::is_integral_v<CType>) {
904         return ScalarValue::Create<TYPE>(std::get<std::make_unsigned_t<CType>>(literal.value));
905     } else {
906         return ScalarValue::Create<TYPE>(std::get<CType>(literal.value));
907     }
908 }
909 
910 template <Value::Type TYPE, typename... T>
CheckAndCreateArrayValue(const LiteralArray::Literal & literal,const Program & program)911 static ScalarValue CheckAndCreateArrayValue(const LiteralArray::Literal &literal,
912                                             [[maybe_unused]] const Program &program)
913 {
914     ASSERT(program.arrayTypes.find(Type(AnnotationElement::TypeToString(TYPE), 1)) != program.arrayTypes.end());
915     return CreateValue<TYPE, T...>(literal);
916 }
917 
MakeLiteralItemArrayValue(const LiteralArray::Literal & literal,const Program & program)918 static ScalarValue MakeLiteralItemArrayValue(const LiteralArray::Literal &literal, const Program &program)
919 {
920     switch (literal.tag) {
921         case panda_file::LiteralTag::ARRAY_U1:
922             return CheckAndCreateArrayValue<Value::Type::U1, bool>(literal, program);
923         case panda_file::LiteralTag::ARRAY_U8:
924             return CheckAndCreateArrayValue<Value::Type::U8>(literal, program);
925         case panda_file::LiteralTag::ARRAY_I8:
926             return CheckAndCreateArrayValue<Value::Type::I8>(literal, program);
927         case panda_file::LiteralTag::ARRAY_U16:
928             return CheckAndCreateArrayValue<Value::Type::U16>(literal, program);
929         case panda_file::LiteralTag::ARRAY_I16:
930             return CheckAndCreateArrayValue<Value::Type::I16>(literal, program);
931         case panda_file::LiteralTag::ARRAY_U32:
932             return CheckAndCreateArrayValue<Value::Type::U32>(literal, program);
933         case panda_file::LiteralTag::ARRAY_I32:
934             return CheckAndCreateArrayValue<Value::Type::I32>(literal, program);
935         case panda_file::LiteralTag::ARRAY_U64:
936             return CheckAndCreateArrayValue<Value::Type::U64>(literal, program);
937         case panda_file::LiteralTag::ARRAY_I64:
938             return CheckAndCreateArrayValue<Value::Type::I64>(literal, program);
939         case panda_file::LiteralTag::ARRAY_F32:
940             return CheckAndCreateArrayValue<Value::Type::F32>(literal, program);
941         case panda_file::LiteralTag::ARRAY_F64:
942             return CheckAndCreateArrayValue<Value::Type::F64>(literal, program);
943         case panda_file::LiteralTag::ARRAY_STRING: {
944             [[maybe_unused]] auto stringType =
945                 Type::FromDescriptor(ark::panda_file::GetStringClassDescriptor(program.lang));
946             // `arrayTypes` may contain class name both with / and . depending on source language (workaround
947             // for #5776)
948             ASSERT(program.arrayTypes.find(Type(stringType, 1)) != program.arrayTypes.end() ||
949                    program.arrayTypes.find(Type(stringType.GetPandasmName(), 1)) != program.arrayTypes.end());
950             return CreateValue<Value::Type::STRING, std::string>(literal);
951         }
952         default:
953             UNREACHABLE();
954     }
955 }
956 
MakeLiteralItemValue(const LiteralArray::Literal & literal,const Program & program)957 static ScalarValue MakeLiteralItemValue(const LiteralArray::Literal &literal, const Program &program)
958 {
959     if (literal.IsArray()) {
960         return MakeLiteralItemArrayValue(literal, program);
961     }
962     switch (literal.tag) {
963         case panda_file::LiteralTag::TAGVALUE:
964         case panda_file::LiteralTag::ACCESSOR:
965         case panda_file::LiteralTag::NULLVALUE:
966             return CreateValue<Value::Type::U8>(literal);
967         case panda_file::LiteralTag::BOOL:
968             return CreateValue<Value::Type::U8, bool>(literal);
969         case panda_file::LiteralTag::METHODAFFILIATE:
970             return CreateValue<Value::Type::U16>(literal);
971         case panda_file::LiteralTag::INTEGER:
972             return CreateValue<Value::Type::I32>(literal);
973         case panda_file::LiteralTag::BIGINT: {
974             return CreateValue<Value::Type::I64>(literal);
975         }
976         case panda_file::LiteralTag::FLOAT:
977             return CreateValue<Value::Type::F32>(literal);
978         case panda_file::LiteralTag::DOUBLE:
979             return CreateValue<Value::Type::F64>(literal);
980         case panda_file::LiteralTag::STRING:
981             return CreateValue<Value::Type::STRING, std::string>(literal);
982         case panda_file::LiteralTag::METHOD:
983         case panda_file::LiteralTag::GENERATORMETHOD:
984         case panda_file::LiteralTag::ASYNCGENERATORMETHOD:
985         case panda_file::LiteralTag::ASYNCMETHOD:
986             return CreateValue<Value::Type::METHOD, std::string>(literal);
987         case panda_file::LiteralTag::LITERALARRAY:
988             return CreateValue<Value::Type::LITERALARRAY, std::string>(literal);
989         default:
990             UNREACHABLE();
991     }
992 }
993 
SortByLastWord(const std::map<std::string,ark::pandasm::LiteralArray> & literalarrayTable)994 static std::vector<std::pair<std::string, ark::pandasm::LiteralArray>> SortByLastWord(
995     const std::map<std::string, ark::pandasm::LiteralArray> &literalarrayTable)
996 {
997     std::vector<std::pair<std::string, ark::pandasm::LiteralArray>> items(literalarrayTable.begin(),
998                                                                           literalarrayTable.end());
999 
1000     std::sort(items.begin(), items.end(), [](const auto &a, const auto &b) {
1001         size_t posA = a.first.rfind('$');
1002         size_t posB = b.first.rfind('$');
1003 
1004         std::string lastNumberA = (posA != std::string::npos) ? a.first.substr(posA + 1) : "";
1005         std::string lastNumberB = (posB != std::string::npos) ? b.first.substr(posB + 1) : "";
1006 
1007         auto stringToInt = [](const std::string &str) {
1008             int num = 0;
1009             std::stringstream ss(str);
1010             ss >> num;
1011             return num;
1012         };
1013 
1014         return stringToInt(lastNumberA) < stringToInt(lastNumberB);
1015     });
1016 
1017     return items;
1018 }
1019 
1020 /* static */
MakeLiteralItems(ItemContainer * items,const Program & program,AsmEmitter::AsmEntityCollections & entities)1021 void AsmEmitter::MakeLiteralItems(ItemContainer *items, const Program &program,
1022                                   AsmEmitter::AsmEntityCollections &entities)
1023 {
1024     // The insertion order of the literal arrays has been disrupted by the map, but this order is crucial
1025     // (as the root array stores the offsets of the sub-arrays). Therefore, we utilize a sorted vector
1026     // to restore the insertion order for iteration.
1027     auto sortedTable = SortByLastWord(program.literalarrayTable);
1028     for (const auto &[id, l] : sortedTable) {
1029         auto literalArrayItem = items->GetOrCreateLiteralArrayItem(id);
1030         std::vector<panda_file::LiteralItem> literalArray;
1031 
1032         for (auto &literal : l.literals) {
1033             auto value = MakeLiteralItemValue(literal, program);
1034             // the return pointer of vector element should not be rewrited
1035             CreateLiteralItem(items, &value, &literalArray, entities);
1036         }
1037 
1038         literalArrayItem->AddItems(literalArray);
1039         entities.literalarrayItems.insert({id, literalArrayItem});
1040     }
1041 }
1042 
1043 /* static */
MakeArrayTypeItems(ItemContainer * items,const Program & program,AsmEmitter::AsmEntityCollections & entities)1044 void AsmEmitter::MakeArrayTypeItems(ItemContainer *items, const Program &program,
1045                                     AsmEmitter::AsmEntityCollections &entities)
1046 {
1047     for (const auto &t : program.arrayTypes) {
1048         auto *foreignRecord = items->GetOrCreateForeignClassItem(t.GetDescriptor());
1049         entities.classItems.insert({t.GetName(), foreignRecord});
1050     }
1051 }
1052 
1053 /* static */
1054 // CC-OFFNXT(G.FUN.01-CPP) solid logic
HandleRecordAsForeign(ItemContainer * items,const Program & program,AsmEmitter::AsmEntityCollections & entities,const std::unordered_map<panda_file::Type::TypeId,PrimitiveTypeItem * > & primitiveTypes,const std::string & name,const Record & rec)1055 bool AsmEmitter::HandleRecordAsForeign(
1056     ItemContainer *items, const Program &program, AsmEmitter::AsmEntityCollections &entities,
1057     const std::unordered_map<panda_file::Type::TypeId, PrimitiveTypeItem *> &primitiveTypes, const std::string &name,
1058     const Record &rec)
1059 {
1060     Type recordType = Type::FromName(name);
1061     auto *foreignRecord = items->GetOrCreateForeignClassItem(recordType.GetDescriptor(rec.conflict));
1062     entities.classItems.insert({name, foreignRecord});
1063     for (const auto &f : rec.fieldList) {
1064         ASSERT(f.metadata->IsForeign());
1065         auto *fieldName = items->GetOrCreateStringItem(pandasm::DeMangleName(f.name));
1066         std::string fullFieldName = name + "." + f.name;
1067         if (!f.metadata->IsForeign()) {
1068             SetLastError("External record " + name + " has a non-external field " + f.name);
1069             return false;
1070         }
1071         auto *typeItem = GetTypeItem(items, primitiveTypes, f.type, program);
1072         if (typeItem == nullptr) {
1073             SetLastError("Field " + fullFieldName + " has undefined type");
1074             return false;
1075         }
1076         auto *field =
1077             items->CreateItem<ForeignFieldItem>(foreignRecord, fieldName, typeItem, f.metadata->GetAccessFlags());
1078         UpdateFieldList(entities, fullFieldName, field);
1079     }
1080     return true;
1081 }
1082 
1083 /* static */
HandleBaseRecord(ItemContainer * items,const Program & program,const std::string & name,const Record & baseRec,ClassItem * record)1084 bool AsmEmitter::HandleBaseRecord(ItemContainer *items, const Program &program, const std::string &name,
1085                                   const Record &baseRec, ClassItem *record)
1086 {
1087     auto baseName = baseRec.metadata->GetBase();
1088     if (!baseName.empty()) {
1089         auto it = program.recordTable.find(baseName);
1090         if (it == program.recordTable.cend()) {
1091             SetLastError("Base record " + baseName + " is not defined for record " + name);
1092             return false;
1093         }
1094         auto &rec = it->second;
1095         Type baseType(baseName, 0);
1096         if (rec.metadata->IsForeign()) {
1097             record->SetSuperClass(items->GetOrCreateForeignClassItem(baseType.GetDescriptor(rec.conflict)));
1098         } else {
1099             record->SetSuperClass(items->GetOrCreateClassItem(baseType.GetDescriptor(rec.conflict)));
1100         }
1101     }
1102     return true;
1103 }
1104 
1105 /* static */
HandleInterfaces(ItemContainer * items,const Program & program,const std::string & name,const Record & rec,ClassItem * record)1106 bool AsmEmitter::HandleInterfaces(ItemContainer *items, const Program &program, const std::string &name,
1107                                   const Record &rec, ClassItem *record)
1108 {
1109     auto ifaces = rec.metadata->GetInterfaces();
1110     for (const auto &item : ifaces) {
1111         auto it = program.recordTable.find(item);
1112         if (it == program.recordTable.cend()) {
1113             std::stringstream error;
1114             error << "Interface record " << item << " is not defined for record " << name;
1115             SetLastError(error.str());
1116             return false;
1117         }
1118         auto &iface = it->second;
1119         Type ifaceType(item, 0);
1120         if (iface.metadata->IsForeign()) {
1121             record->AddInterface(items->GetOrCreateForeignClassItem(ifaceType.GetDescriptor(iface.conflict)));
1122         } else {
1123             record->AddInterface(items->GetOrCreateClassItem(ifaceType.GetDescriptor(iface.conflict)));
1124         }
1125     }
1126     return true;
1127 }
1128 
1129 /* static */
UpdateFieldList(AsmEmitter::AsmEntityCollections & entities,const std::string & fullFieldName,panda_file::BaseFieldItem * field)1130 void AsmEmitter::UpdateFieldList(AsmEmitter::AsmEntityCollections &entities, const std::string &fullFieldName,
1131                                  panda_file::BaseFieldItem *field)
1132 {
1133     if (field->IsStatic()) {
1134         entities.staticFieldItems.emplace(fullFieldName, field);
1135     } else {
1136         entities.fieldItems.emplace(fullFieldName, field);
1137     }
1138 }
1139 
1140 /* static */
1141 // CC-OFFNXT(G.FUN.01-CPP) solid logic
HandleFields(ItemContainer * items,const Program & program,AsmEmitter::AsmEntityCollections & entities,const std::unordered_map<panda_file::Type::TypeId,PrimitiveTypeItem * > & primitiveTypes,const std::string & name,const Record & rec,ClassItem * record)1142 bool AsmEmitter::HandleFields(ItemContainer *items, const Program &program, AsmEmitter::AsmEntityCollections &entities,
1143                               const std::unordered_map<panda_file::Type::TypeId, PrimitiveTypeItem *> &primitiveTypes,
1144                               const std::string &name, const Record &rec, ClassItem *record)
1145 {
1146     for (const auto &f : rec.fieldList) {
1147         auto *fieldName = items->GetOrCreateStringItem(pandasm::DeMangleName(f.name));
1148         std::string fullFieldName = name + '.' + f.name;
1149         auto *typeItem = GetTypeItem(items, primitiveTypes, f.type, program);
1150         if (typeItem == nullptr) {
1151             SetLastError("Field " + fullFieldName + " has undefined type");
1152             return false;
1153         }
1154         BaseFieldItem *field;
1155         if (f.metadata->IsForeign()) {
1156             field = items->CreateItem<ForeignFieldItem>(record, fieldName, typeItem, f.metadata->GetAccessFlags());
1157         } else {
1158             field = record->AddField(fieldName, typeItem, f.metadata->GetAccessFlags());
1159         }
1160         UpdateFieldList(entities, fullFieldName, field);
1161     }
1162     return true;
1163 }
1164 
1165 /* static */
1166 // CC-OFFNXT(G.FUN.01-CPP) solid logic
HandleRecord(ItemContainer * items,const Program & program,AsmEmitter::AsmEntityCollections & entities,const std::unordered_map<panda_file::Type::TypeId,PrimitiveTypeItem * > & primitiveTypes,const std::string & name,const Record & rec)1167 bool AsmEmitter::HandleRecord(ItemContainer *items, const Program &program, AsmEmitter::AsmEntityCollections &entities,
1168                               const std::unordered_map<panda_file::Type::TypeId, PrimitiveTypeItem *> &primitiveTypes,
1169                               const std::string &name, const Record &rec)
1170 {
1171     Type recordType = Type::FromName(name);
1172     auto *record = items->GetOrCreateClassItem(recordType.GetDescriptor(rec.conflict));
1173     entities.classItems.insert({name, record});
1174 
1175     record->SetAccessFlags(rec.metadata->GetAccessFlags());
1176     record->SetSourceLang(rec.language);
1177 
1178     if (!rec.sourceFile.empty()) {
1179         auto *sourceFileItem = items->GetOrCreateStringItem(rec.sourceFile);
1180         record->SetSourceFile(sourceFileItem);
1181     }
1182 
1183     if (!HandleBaseRecord(items, program, name, rec, record)) {
1184         return false;
1185     }
1186 
1187     if (!HandleInterfaces(items, program, name, rec, record)) {
1188         return false;
1189     }
1190 
1191     if (!HandleFields(items, program, entities, primitiveTypes, name, rec, record)) {
1192         return false;
1193     }
1194 
1195     return true;
1196 }
1197 
1198 /* static */
MakeRecordItems(ItemContainer * items,const Program & program,AsmEmitter::AsmEntityCollections & entities,const std::unordered_map<panda_file::Type::TypeId,PrimitiveTypeItem * > & primitiveTypes)1199 bool AsmEmitter::MakeRecordItems(
1200     ItemContainer *items, const Program &program, AsmEmitter::AsmEntityCollections &entities,
1201     const std::unordered_map<panda_file::Type::TypeId, PrimitiveTypeItem *> &primitiveTypes)
1202 {
1203     for (const auto &[name, rec] : program.recordTable) {
1204         if (rec.metadata->IsForeign()) {
1205             if (!HandleRecordAsForeign(items, program, entities, primitiveTypes, name, rec)) {
1206                 return false;
1207             }
1208         } else {
1209             if (!HandleRecord(items, program, entities, primitiveTypes, name, rec)) {
1210                 return false;
1211             }
1212         }
1213     }
1214     return true;
1215 }
1216 
1217 /* static */
GetMethodName(ItemContainer * items,const Function & func,const std::string & name)1218 StringItem *AsmEmitter::GetMethodName(ItemContainer *items, const Function &func, const std::string &name)
1219 {
1220     if (func.metadata->IsCtor()) {
1221         return items->GetOrCreateStringItem(ark::panda_file::GetCtorName(func.language));
1222     }
1223 
1224     if (func.metadata->IsCctor()) {
1225         return items->GetOrCreateStringItem(ark::panda_file::GetCctorName(func.language));
1226     }
1227 
1228     return items->GetOrCreateStringItem(GetItemName(name));
1229 }
1230 
1231 /* static */
1232 // CC-OFFNXT(G.FUN.01-CPP) solid logic
HandleAreaForInner(ItemContainer * items,const Program & program,ClassItem ** area,ForeignClassItem ** foreignArea,const std::string & name,const std::string & recordOwnerName)1233 bool AsmEmitter::HandleAreaForInner(ItemContainer *items, const Program &program, ClassItem **area,
1234                                     ForeignClassItem **foreignArea, const std::string &name,
1235                                     const std::string &recordOwnerName)
1236 {
1237     auto iter = program.recordTable.find(recordOwnerName);
1238     if (iter != program.recordTable.end()) {
1239         auto &rec = iter->second;
1240         Type recordOwnerType = Type::FromName(recordOwnerName);
1241         auto descriptor = recordOwnerType.GetDescriptor(rec.conflict);
1242         if (rec.metadata->IsForeign()) {
1243             *foreignArea = items->GetOrCreateForeignClassItem(descriptor);
1244             if (*foreignArea == nullptr) {
1245                 SetLastError("Unable to create external record " + iter->first);
1246                 return false;
1247             }
1248         } else {
1249             *area = items->GetOrCreateClassItem(descriptor);
1250             (*area)->SetAccessFlags(rec.metadata->GetAccessFlags());
1251         }
1252     } else {
1253         SetLastError("Function " + name + " is bound to undefined record " + recordOwnerName);
1254         return false;
1255     }
1256     return true;
1257 }
1258 
1259 /* static */
1260 // CC-OFFNXT(G.FUN.01-CPP) solid logic
HandleRecordOnwer(ItemContainer * items,const Program & program,ClassItem ** area,ForeignClassItem ** foreignArea,const std::string & name,const std::string & recordOwnerName)1261 bool AsmEmitter::HandleRecordOnwer(ItemContainer *items, const Program &program, ClassItem **area,
1262                                    ForeignClassItem **foreignArea, const std::string &name,
1263                                    const std::string &recordOwnerName)
1264 {
1265     if (recordOwnerName.empty()) {
1266         *area = items->GetOrCreateGlobalClassItem();
1267         (*area)->SetAccessFlags(ACC_PUBLIC);
1268         (*area)->SetSourceLang(program.lang);
1269     } else {
1270         if (!HandleAreaForInner(items, program, area, foreignArea, name, recordOwnerName)) {
1271             return false;
1272         }
1273     }
1274     return true;
1275 }
1276 
1277 /* static */
1278 // CC-OFFNXT(G.FUN.01-CPP) solid logic
HandleFunctionParams(ItemContainer * items,const Program & program,size_t idx,const std::string & name,const Function & func,const std::unordered_map<panda_file::Type::TypeId,PrimitiveTypeItem * > & primitiveTypes,std::vector<MethodParamItem> & params)1279 bool AsmEmitter::HandleFunctionParams(
1280     ItemContainer *items, const Program &program, size_t idx, const std::string &name, const Function &func,
1281     const std::unordered_map<panda_file::Type::TypeId, PrimitiveTypeItem *> &primitiveTypes,
1282     std::vector<MethodParamItem> &params)
1283 {
1284     for (size_t i = idx; i < func.params.size(); i++) {
1285         const auto &p = func.params[i].type;
1286         auto *typeItem = GetTypeItem(items, primitiveTypes, p, program);
1287         if (typeItem == nullptr) {
1288             SetLastError("Argument " + std::to_string(i) + " of function " + name + " has undefined type");
1289             return false;
1290         }
1291         params.emplace_back(typeItem);
1292     }
1293     return true;
1294 }
1295 
1296 /* static */
HandleFunctionLocalVariables(ItemContainer * items,const Function & func,const std::string & name)1297 bool AsmEmitter::HandleFunctionLocalVariables(ItemContainer *items, const Function &func, const std::string &name)
1298 {
1299     for (const auto &v : func.localVariableDebug) {
1300         if (v.name.empty()) {
1301             SetLastError("Function '" + name + "' has an empty local variable name");
1302             return false;
1303         }
1304         if (v.signature.empty()) {
1305             SetLastError("Function '" + name + "' has an empty local variable signature");
1306             return false;
1307         }
1308         items->GetOrCreateStringItem(v.name);
1309         // Skip signature and signature type for parameters
1310         ASSERT(v.reg >= 0);
1311         if (func.IsParameter(v.reg)) {
1312             continue;
1313         }
1314         items->GetOrCreateStringItem(v.signature);
1315         if (!v.signatureType.empty()) {
1316             items->GetOrCreateStringItem(v.signatureType);
1317         }
1318     }
1319     return true;
1320 }
1321 
1322 /* static */
1323 // CC-OFFNXT(G.FUN.01-CPP) solid logic
CreateMethodItem(ItemContainer * items,AsmEmitter::AsmEntityCollections & entities,const Function & func,TypeItem * typeItem,ClassItem * area,ForeignClassItem * foreignArea,StringItem * methodName,const std::string & mangledName,const std::string & name,std::vector<MethodParamItem> & params)1324 bool AsmEmitter::CreateMethodItem(ItemContainer *items, AsmEmitter::AsmEntityCollections &entities,
1325                                   const Function &func, TypeItem *typeItem, ClassItem *area,
1326                                   ForeignClassItem *foreignArea, StringItem *methodName, const std::string &mangledName,
1327                                   const std::string &name, std::vector<MethodParamItem> &params)
1328 {
1329     auto accessFlags = func.metadata->GetAccessFlags();
1330     auto *proto = items->GetOrCreateProtoItem(typeItem, params);
1331     BaseMethodItem *method;
1332     if (foreignArea == nullptr) {
1333         if (func.metadata->IsForeign()) {
1334             method = items->CreateItem<ForeignMethodItem>(area, methodName, proto, accessFlags);
1335         } else {
1336             method = area->AddMethod(methodName, proto, accessFlags, params);
1337         }
1338     } else {
1339         if (!func.metadata->IsForeign()) {
1340             SetLastError("Non-external function " + name + " is bound to external record");
1341             return false;
1342         }
1343         method = items->CreateItem<ForeignMethodItem>(foreignArea, methodName, proto, accessFlags);
1344     }
1345     if (func.IsStatic()) {
1346         entities.staticMethodItems.insert({mangledName, method});
1347     } else {
1348         entities.methodItems.insert({mangledName, method});
1349     }
1350     if (!func.metadata->IsForeign() && func.metadata->HasImplementation()) {
1351         if (!func.sourceFile.empty()) {
1352             items->GetOrCreateStringItem(func.sourceFile);
1353         }
1354         if (!func.sourceCode.empty()) {
1355             items->GetOrCreateStringItem(func.sourceCode);
1356         }
1357     }
1358     return true;
1359 }
1360 
1361 /* static */
MakeFunctionItems(ItemContainer * items,const Program & program,AsmEmitter::AsmEntityCollections & entities,const std::unordered_map<panda_file::Type::TypeId,PrimitiveTypeItem * > & primitiveTypes,bool emitDebugInfo,bool isStatic)1362 bool AsmEmitter::MakeFunctionItems(
1363     ItemContainer *items, const Program &program, AsmEmitter::AsmEntityCollections &entities,
1364     const std::unordered_map<panda_file::Type::TypeId, PrimitiveTypeItem *> &primitiveTypes, bool emitDebugInfo,
1365     bool isStatic)
1366 {
1367     auto &functionTable = isStatic ? program.functionStaticTable : program.functionInstanceTable;
1368     for (const auto &f : functionTable) {
1369         const auto &[mangled_name, func] = f;
1370 
1371         auto name = pandasm::DeMangleName(mangled_name);
1372 
1373         StringItem *methodName = GetMethodName(items, func, name);
1374 
1375         ClassItem *area = nullptr;
1376         ForeignClassItem *foreignArea = nullptr;
1377 
1378         std::string recordOwnerName = GetOwnerName(name);
1379         if (!HandleRecordOnwer(items, program, &area, &foreignArea, name, recordOwnerName)) {
1380             return false;
1381         }
1382 
1383         auto params = std::vector<MethodParamItem> {};
1384 
1385         ASSERT(func.IsStatic() == isStatic);
1386         if (func.params.empty() || func.params[0].type.GetName() != GetOwnerName(func.name) ||
1387             func.metadata->IsCctor()) {
1388             ASSERT(func.IsStatic());
1389         }
1390 
1391         size_t idx = isStatic ? 0 : 1;
1392 
1393         if (!HandleFunctionParams(items, program, idx, name, func, primitiveTypes, params)) {
1394             return false;
1395         }
1396 
1397         if (emitDebugInfo && !HandleFunctionLocalVariables(items, func, name)) {
1398             return false;
1399         }
1400 
1401         auto *typeItem = GetTypeItem(items, primitiveTypes, func.returnType, program);
1402         if (typeItem == nullptr) {
1403             SetLastError("Function " + name + " has undefined return type");
1404             return false;
1405         }
1406 
1407         if (!CreateMethodItem(items, entities, func, typeItem, area, foreignArea, methodName, mangled_name, name,
1408                               params)) {
1409             return false;
1410         }
1411     }
1412     return true;
1413 }
1414 
1415 /* static */
MakeFunctionItems(ItemContainer * items,Program & program,AsmEmitter::AsmEntityCollections & entities,const std::unordered_map<panda_file::Type::TypeId,PrimitiveTypeItem * > & primitiveTypes,bool emitDebugInfo)1416 bool AsmEmitter::MakeFunctionItems(
1417     ItemContainer *items, Program &program, AsmEmitter::AsmEntityCollections &entities,
1418     const std::unordered_map<panda_file::Type::TypeId, PrimitiveTypeItem *> &primitiveTypes, bool emitDebugInfo)
1419 {
1420     std::vector<std::string> funcsShouldBeStatic;
1421     for (auto &[name, func] : program.functionInstanceTable) {
1422         if (func.params.empty() || func.params[0].type.GetName() != GetOwnerName(name) || func.metadata->IsCctor()) {
1423             func.metadata->SetAccessFlags(func.metadata->GetAccessFlags() | ACC_STATIC);
1424             funcsShouldBeStatic.push_back(name);
1425         }
1426     }
1427     for (auto &funcName : funcsShouldBeStatic) {
1428         auto node = program.functionInstanceTable.extract(funcName);
1429         program.functionStaticTable.insert(std::move(node));
1430     }
1431 
1432     if (!MakeFunctionItems(items, program, entities, primitiveTypes, emitDebugInfo, true)) {
1433         return false;
1434     }
1435 
1436     if (!MakeFunctionItems(items, program, entities, primitiveTypes, emitDebugInfo, false)) {
1437         return false;
1438     }
1439 
1440     return true;
1441 }
1442 
1443 /* static */
MakeRecordAnnotations(ItemContainer * items,const Program & program,const AsmEmitter::AsmEntityCollections & entities)1444 bool AsmEmitter::MakeRecordAnnotations(ItemContainer *items, const Program &program,
1445                                        const AsmEmitter::AsmEntityCollections &entities)
1446 {
1447     for (const auto &[name, record] : program.recordTable) {
1448         if (record.metadata->IsForeign()) {
1449             continue;
1450         }
1451 
1452         auto *classItem = static_cast<ClassItem *>(Find(entities.classItems, name));
1453         if (!AddAnnotations(classItem, items, *record.metadata, program, entities)) {
1454             SetLastError("Cannot emit annotations for record " + record.name + ": " + GetLastError());
1455             return false;
1456         }
1457 
1458         for (const auto &field : record.fieldList) {
1459             std::string fieldName = record.name + '.' + field.name;
1460             auto *fieldItem = !field.IsStatic() ? static_cast<FieldItem *>(Find(entities.fieldItems, fieldName))
1461                                                 : static_cast<FieldItem *>(Find(entities.staticFieldItems, fieldName));
1462             if (!AddAnnotations(fieldItem, items, *field.metadata, program, entities)) {
1463                 SetLastError("Cannot emit annotations for field " + fieldName + ": " + GetLastError());
1464                 return false;
1465             }
1466 
1467             auto res = field.metadata->GetValue();
1468             if (res) {
1469                 auto value = res.value();
1470                 auto *item = CreateValueItem(items, &value, program, entities, {true, field.IsStatic()});
1471                 fieldItem->SetValue(item);
1472             }
1473         }
1474     }
1475     return true;
1476 }
1477 
1478 /* static */
SetCodeAndDebugInfo(ItemContainer * items,MethodItem * method,const Function & func,bool emitDebugInfo)1479 void AsmEmitter::SetCodeAndDebugInfo(ItemContainer *items, MethodItem *method, const Function &func, bool emitDebugInfo)
1480 {
1481     auto *code = items->CreateItem<CodeItem>();
1482     method->SetCode(code);
1483     code->AddMethod(method);  // we need it for Profile-Guided optimization
1484 
1485     if (!emitDebugInfo && !func.CanThrow()) {
1486         return;
1487     }
1488 
1489     auto *lineNumberProgram = items->CreateLineNumberProgramItem();
1490     auto *debugInfo = items->CreateItem<DebugInfoItem>(lineNumberProgram);
1491     if (emitDebugInfo) {
1492         for (const auto &v : func.localVariableDebug) {
1493             ASSERT(v.reg >= 0);
1494             if (func.IsParameter(v.reg)) {
1495                 debugInfo->AddParameter(items->GetOrCreateStringItem(v.name));
1496             }
1497         }
1498     } else {
1499         auto nparams = method->GetParams().size();
1500         for (size_t i = 0; i < nparams; i++) {
1501             debugInfo->AddParameter(nullptr);
1502         }
1503     }
1504     method->SetDebugInfo(debugInfo);
1505 }
1506 
1507 /* static */
SetMethodSourceLang(const Program & program,MethodItem * method,const Function & func,const std::string & name)1508 void AsmEmitter::SetMethodSourceLang(const Program &program, MethodItem *method, const Function &func,
1509                                      const std::string &name)
1510 {
1511     std::string recordName = GetOwnerName(name);
1512     if (recordName.empty()) {
1513         method->SetSourceLang(func.language);
1514         return;
1515     }
1516 
1517     auto &rec = program.recordTable.find(recordName)->second;
1518     if (rec.language != func.language) {
1519         method->SetSourceLang(func.language);
1520     }
1521 }
1522 
1523 /* static */
AddMethodAndParamsAnnotations(ItemContainer * items,const Program & program,const AsmEmitter::AsmEntityCollections & entities,MethodItem * method,const Function & func)1524 bool AsmEmitter::AddMethodAndParamsAnnotations(ItemContainer *items, const Program &program,
1525                                                const AsmEmitter::AsmEntityCollections &entities, MethodItem *method,
1526                                                const Function &func)
1527 {
1528     if (!AddAnnotations(method, items, *func.metadata, program, entities)) {
1529         SetLastError("Cannot emit annotations for function " + func.name + ": " + GetLastError());
1530         return false;
1531     }
1532 
1533     auto &paramItems = method->GetParams();
1534     for (size_t protoIdx = 0; protoIdx < paramItems.size(); protoIdx++) {
1535         size_t paramIdx = method->IsStatic() ? protoIdx : protoIdx + 1;
1536         auto &param = func.params[paramIdx];
1537         auto &paramItem = paramItems[protoIdx];
1538         if (!AddAnnotations(&paramItem, items, param.GetOrCreateMetadata(), program, entities)) {
1539             SetLastError("Cannot emit annotations for parameter a" + std::to_string(paramIdx) + " of function " +
1540                          func.name + ": " + GetLastError());
1541             return false;
1542         }
1543     }
1544 
1545     if (method->HasRuntimeParamAnnotations()) {
1546         items->CreateItem<ParamAnnotationsItem>(method, true);
1547     }
1548 
1549     if (method->HasParamAnnotations()) {
1550         items->CreateItem<ParamAnnotationsItem>(method, false);
1551     }
1552 
1553     return true;
1554 }
1555 
1556 /* static */
FindMethod(const Function & func,const std::string & name,const AsmEmitter::AsmEntityCollections & entities)1557 ark::panda_file::MethodItem *AsmEmitter::FindMethod(const Function &func, const std::string &name,
1558                                                     const AsmEmitter::AsmEntityCollections &entities)
1559 {
1560     if (func.IsStatic()) {
1561         return static_cast<MethodItem *>(Find(entities.staticMethodItems, name));
1562     }
1563     return static_cast<MethodItem *>(Find(entities.methodItems, name));
1564 }
1565 
1566 /* static */
FindAmongAllMethods(const std::string & name,const AsmEmitter::AsmEntityCollections & entities,const Value * value)1567 ark::panda_file::MethodItem *AsmEmitter::FindAmongAllMethods(const std::string &name,
1568                                                              const AsmEmitter::AsmEntityCollections &entities,
1569                                                              const Value *value)
1570 {
1571     bool isStatic = value->GetAsScalar()->IsStatic();
1572     auto &primaryMap = isStatic ? entities.staticMethodItems : entities.methodItems;
1573     auto &secondaryMap = isStatic ? entities.methodItems : entities.staticMethodItems;
1574 
1575     auto res = primaryMap.find(name);
1576     if (res != primaryMap.cend()) {
1577         return static_cast<MethodItem *>(res->second);
1578     }
1579 
1580     res = secondaryMap.find(name);
1581     return res == secondaryMap.cend() ? nullptr : static_cast<MethodItem *>(res->second);
1582 }
1583 
1584 /* static */
MakeFunctionDebugInfoAndAnnotations(ItemContainer * items,const Program & program,const std::map<std::string,Function> & functionTable,const AsmEmitter::AsmEntityCollections & entities,bool emitDebugInfo)1585 bool AsmEmitter::MakeFunctionDebugInfoAndAnnotations(ItemContainer *items, const Program &program,
1586                                                      const std::map<std::string, Function> &functionTable,
1587                                                      const AsmEmitter::AsmEntityCollections &entities,
1588                                                      bool emitDebugInfo)
1589 {
1590     for (const auto &[name, func] : functionTable) {
1591         if (func.metadata->IsForeign()) {
1592             continue;
1593         }
1594 
1595         auto *method = FindMethod(func, name, entities);
1596         if (method == nullptr) {
1597             return false;
1598         }
1599 
1600         if (func.metadata->HasImplementation()) {
1601             SetCodeAndDebugInfo(items, method, func, emitDebugInfo);
1602             AddBytecodeIndexDependencies(method, func, entities);
1603         }
1604 
1605         SetMethodSourceLang(program, method, func, name);
1606 
1607         if (!AddMethodAndParamsAnnotations(items, program, entities, method, func)) {
1608             return false;
1609         }
1610         if (func.profileSize <= MethodItem::MAX_PROFILE_SIZE) {
1611             method->SetProfileSize(func.profileSize);
1612         }
1613     }
1614     return true;
1615 }
1616 
1617 /* static */
MakeFunctionDebugInfoAndAnnotations(ItemContainer * items,const Program & program,const AsmEmitter::AsmEntityCollections & entities,bool emitDebugInfo)1618 bool AsmEmitter::MakeFunctionDebugInfoAndAnnotations(ItemContainer *items, const Program &program,
1619                                                      const AsmEmitter::AsmEntityCollections &entities,
1620                                                      bool emitDebugInfo)
1621 {
1622     if (!MakeFunctionDebugInfoAndAnnotations(items, program, program.functionStaticTable, entities, emitDebugInfo)) {
1623         return false;
1624     }
1625     if (!MakeFunctionDebugInfoAndAnnotations(items, program, program.functionInstanceTable, entities, emitDebugInfo)) {
1626         return false;
1627     }
1628 
1629     return true;
1630 }
1631 
1632 /* static */
FillMap(PandaFileToPandaAsmMaps * maps,AsmEmitter::AsmEntityCollections & entities)1633 void AsmEmitter::FillMap(PandaFileToPandaAsmMaps *maps, AsmEmitter::AsmEntityCollections &entities)
1634 {
1635     for (const auto &[name, method] : entities.methodItems) {
1636         maps->methods.insert({method->GetFileId().GetOffset(), std::string(name)});
1637     }
1638 
1639     for (const auto &[name, method] : entities.staticMethodItems) {
1640         maps->methods.insert({method->GetFileId().GetOffset(), std::string(name)});
1641     }
1642 
1643     for (const auto &[name, field] : entities.fieldItems) {
1644         maps->fields.insert({field->GetFileId().GetOffset(), std::string(name)});
1645     }
1646 
1647     for (const auto &[name, field] : entities.staticFieldItems) {
1648         maps->fields.insert({field->GetFileId().GetOffset(), std::string(name)});
1649     }
1650 
1651     for (const auto &[name, cls] : entities.classItems) {
1652         maps->classes.insert({cls->GetFileId().GetOffset(), std::string(name)});
1653     }
1654 
1655     for (const auto &[name, str] : entities.stringItems) {
1656         maps->strings.insert({str->GetFileId().GetOffset(), std::string(name)});
1657     }
1658 
1659     for (const auto &[name, arr] : entities.literalarrayItems) {
1660         maps->literalarrays.emplace(arr->GetFileId().GetOffset(), name);
1661     }
1662 }
1663 
FindOffset(AsmEmitter::AsmEntityCollections & entities,const std::string & name)1664 static uint32_t FindOffset(AsmEmitter::AsmEntityCollections &entities, const std::string &name)
1665 {
1666     auto methodIt = entities.methodItems.find(name);
1667     if (methodIt != entities.methodItems.end()) {
1668         return methodIt->second->GetOffset();
1669     }
1670     auto staticMethodIt = entities.staticMethodItems.find(name);
1671     if (staticMethodIt != entities.staticMethodItems.end()) {
1672         return staticMethodIt->second->GetOffset();
1673     }
1674     auto fieldIt = entities.fieldItems.find(name);
1675     if (fieldIt != entities.fieldItems.end()) {
1676         return fieldIt->second->GetOffset();
1677     }
1678     auto staticFieldIt = entities.staticFieldItems.find(name);
1679     if (staticFieldIt != entities.staticFieldItems.end()) {
1680         return staticFieldIt->second->GetOffset();
1681     }
1682     auto classIt = entities.classItems.find(name);
1683     if (classIt != entities.classItems.end()) {
1684         return classIt->second->GetOffset();
1685     }
1686     UNREACHABLE();
1687 }
1688 
1689 /* static */
SetOffsetForExportTable(std::vector<std::pair<std::string,std::string>> & declToAssmb,const std::string & literalArrayName,AsmEmitter::AsmEntityCollections & entities)1690 static void SetOffsetForExportTable(std::vector<std::pair<std::string, std::string>> &declToAssmb,
1691                                     const std::string &literalArrayName, AsmEmitter::AsmEntityCollections &entities)
1692 {
1693     // export entities are align as:
1694     // exportentites{
1695     //    Type: uint32_t
1696     //    declTextOffset: offset to decl string
1697     //    Type: uint32_t
1698     //    entitiesType: type of function,class,const,interface
1699     //    Type: uint32_t
1700     //    abcOffset: offset to effective bytecode
1701     //    ...
1702     // }
1703     constexpr size_t FIELDS_PER_ENTITY = 6;
1704     constexpr size_t ABC_OFFSET_RELATIVE_POS = 4;  // Position of abcOffset relative to declTextOffset
1705 
1706     auto arrayItemIt = entities.literalarrayItems.find(literalArrayName);
1707     if (arrayItemIt == entities.literalarrayItems.end()) {
1708         return;
1709     }
1710     auto &exportEntitiesList = arrayItemIt->second->GetItemsUnsafe();
1711     ASSERT(exportEntitiesList.size() == declToAssmb.size() * FIELDS_PER_ENTITY);
1712 
1713     for (size_t declTextOffsetIndex = 1; declTextOffsetIndex < exportEntitiesList.size();
1714          declTextOffsetIndex += FIELDS_PER_ENTITY) {
1715         auto stringIt = entities.stringItems.find(declToAssmb[declTextOffsetIndex / FIELDS_PER_ENTITY].first);
1716         ASSERT(stringIt != entities.stringItems.end());
1717         uint32_t declTextOffset = stringIt->second->GetOffset();
1718         exportEntitiesList[declTextOffsetIndex].SetValueUnsafe<uint32_t>(declTextOffset);
1719 
1720         Type recordName = Type::FromName(declToAssmb[declTextOffsetIndex / FIELDS_PER_ENTITY].second);
1721         uint32_t abcOffset = FindOffset(entities, recordName.GetName());
1722         exportEntitiesList[declTextOffsetIndex + ABC_OFFSET_RELATIVE_POS].SetValueUnsafe<uint32_t>(abcOffset);
1723     }
1724 }
1725 
1726 /* static */
1727 // CC-OFFNXT(G.FUN.01-CPP) solid logic
EmitDebugInfo(ItemContainer * items,const Program & program,const std::vector<uint8_t> * bytes,const MethodItem * method,const Function & func,const std::string & name,bool emitDebugInfo)1728 void AsmEmitter::EmitDebugInfo(ItemContainer *items, const Program &program, const std::vector<uint8_t> *bytes,
1729                                const MethodItem *method, const Function &func, const std::string &name,
1730                                bool emitDebugInfo)
1731 {
1732     auto *debugInfo = method->GetDebugInfo();
1733     if (debugInfo == nullptr) {
1734         return;
1735     }
1736 
1737     auto *lineNumberProgram = debugInfo->GetLineNumberProgram();
1738     auto *constantPool = debugInfo->GetConstantPool();
1739 
1740     std::string recordName = GetOwnerName(name);
1741     std::string recordSourceFile;
1742     if (!recordName.empty()) {
1743         auto &rec = program.recordTable.find(recordName)->second;
1744         recordSourceFile = rec.sourceFile;
1745     }
1746     if (!func.sourceFile.empty() && func.sourceFile != recordSourceFile) {
1747         auto *sourceFileItem = items->GetOrCreateStringItem(func.sourceFile);
1748         ASSERT(sourceFileItem->GetOffset() != 0);
1749         lineNumberProgram->EmitSetFile(constantPool, sourceFileItem);
1750     }
1751 
1752     if (!func.sourceCode.empty()) {
1753         auto *sourceCodeItem = items->GetOrCreateStringItem(func.sourceCode);
1754         ASSERT(sourceCodeItem->GetOffset() != 0);
1755         lineNumberProgram->EmitSetSourceCode(constantPool, sourceCodeItem);
1756     }
1757 
1758     func.BuildLineNumberProgram(debugInfo, *bytes, items, constantPool, emitDebugInfo);
1759 }
1760 
1761 /* static */
EmitFunctions(ItemContainer * items,const Program & program,const AsmEmitter::AsmEntityCollections & entities,bool emitDebugInfo,bool isStatic)1762 bool AsmEmitter::EmitFunctions(ItemContainer *items, const Program &program,
1763                                const AsmEmitter::AsmEntityCollections &entities, bool emitDebugInfo, bool isStatic)
1764 {
1765     const auto &functionTable = isStatic ? program.functionStaticTable : program.functionInstanceTable;
1766     for (const auto &f : functionTable) {
1767         const auto &[name, func] = f;
1768 
1769         if (func.metadata->IsForeign() || !func.metadata->HasImplementation()) {
1770             continue;
1771         }
1772 
1773         auto emitter = BytecodeEmitter {};
1774         auto *method = isStatic ? static_cast<MethodItem *>(Find(entities.staticMethodItems, name))
1775                                 : static_cast<MethodItem *>(Find(entities.methodItems, name));
1776         if (!func.Emit(emitter, method, entities.methodItems, entities.staticMethodItems, entities.fieldItems,
1777                        entities.staticFieldItems, entities.classItems, entities.stringItems,
1778                        entities.literalarrayItems)) {
1779             SetLastError("Internal error during emitting function: " + func.name);
1780             return false;
1781         }
1782 
1783         auto *code = method->GetCode();
1784         code->SetNumVregs(func.regsNum);
1785         code->SetNumArgs(func.GetParamsNum());
1786 
1787         auto numIns = static_cast<uint32_t>(
1788             std::count_if(func.ins.begin(), func.ins.end(), [](auto it) { return it.opcode != Opcode::INVALID; }));
1789         code->SetNumInstructions(numIns);
1790 
1791         auto *bytes = code->GetInstructions();
1792         auto status = emitter.Build(static_cast<std::vector<unsigned char> *>(bytes));
1793         if (status != BytecodeEmitter::ErrorCode::SUCCESS) {
1794             SetLastError("Internal error during emitting binary code, status=" +
1795                          std::to_string(static_cast<int>(status)));
1796             return false;
1797         }
1798         auto tryBlocks = func.BuildTryBlocks(method, entities.classItems, *bytes);
1799         for (auto &tryBlock : tryBlocks) {
1800             code->AddTryBlock(tryBlock);
1801         }
1802 
1803         EmitDebugInfo(items, program, bytes, method, func, name, emitDebugInfo);
1804     }
1805     return true;
1806 }
1807 
1808 /* static */
EmitFunctions(ItemContainer * items,const Program & program,const AsmEmitter::AsmEntityCollections & entities,bool emitDebugInfo)1809 bool AsmEmitter::EmitFunctions(ItemContainer *items, const Program &program,
1810                                const AsmEmitter::AsmEntityCollections &entities, bool emitDebugInfo)
1811 {
1812     if (!EmitFunctions(items, program, entities, emitDebugInfo, true)) {
1813         return false;
1814     }
1815     if (!EmitFunctions(items, program, entities, emitDebugInfo, false)) {
1816         return false;
1817     }
1818 
1819     return true;
1820 }
1821 
1822 /* static */
Emit(ItemContainer * items,Program & program,PandaFileToPandaAsmMaps * maps,bool emitDebugInfo,ark::panda_file::pgo::ProfileOptimizer * profileOpt)1823 bool AsmEmitter::Emit(ItemContainer *items, Program &program, PandaFileToPandaAsmMaps *maps, bool emitDebugInfo,
1824                       ark::panda_file::pgo::ProfileOptimizer *profileOpt)
1825 {
1826     auto primitiveTypes = CreatePrimitiveTypes(items);
1827 
1828     auto entities = AsmEmitter::AsmEntityCollections {};
1829 
1830     SetLastError("");
1831 
1832     MakeStringItems(items, program, entities);
1833 
1834     MakeArrayTypeItems(items, program, entities);
1835 
1836     if (!MakeRecordItems(items, program, entities, primitiveTypes)) {
1837         return false;
1838     }
1839 
1840     if (!MakeFunctionItems(items, program, entities, primitiveTypes, emitDebugInfo)) {
1841         return false;
1842     }
1843 
1844     MakeLiteralItems(items, program, entities);
1845 
1846     // Add annotations for records and fields
1847     if (!MakeRecordAnnotations(items, program, entities)) {
1848         return false;
1849     }
1850 
1851     // Add Code and DebugInfo items last due to they have variable size that depends on bytecode
1852     if (!MakeFunctionDebugInfoAndAnnotations(items, program, entities, emitDebugInfo)) {
1853         return false;
1854     }
1855 
1856     if (profileOpt != nullptr) {
1857         items->ReorderItems(profileOpt);
1858     }
1859 
1860     items->ComputeLayout();
1861 
1862     SetOffsetForExportTable(program.exportStrMap, "export_entities", entities);
1863 
1864     if (maps != nullptr) {
1865         FillMap(maps, entities);
1866     }
1867 
1868     return EmitFunctions(items, program, entities, emitDebugInfo);
1869 }
1870 
1871 // CC-OFFNXT(G.FUN.01-CPP) solid logic
Emit(Writer * writer,Program & program,std::map<std::string,size_t> * stat,PandaFileToPandaAsmMaps * maps,bool debugInfo,ark::panda_file::pgo::ProfileOptimizer * profileOpt)1872 bool AsmEmitter::Emit(Writer *writer, Program &program, std::map<std::string, size_t> *stat,
1873                       PandaFileToPandaAsmMaps *maps, bool debugInfo, ark::panda_file::pgo::ProfileOptimizer *profileOpt)
1874 {
1875     auto items = ItemContainer {};
1876     if (!Emit(&items, program, maps, debugInfo, profileOpt)) {
1877         return false;
1878     }
1879 
1880     if (stat != nullptr) {
1881         *stat = items.GetStat();
1882     }
1883 
1884     return items.Write(writer);
1885 }
1886 
1887 // CC-OFFNXT(G.FUN.01-CPP) solid logic
Emit(const std::string & filename,Program & program,std::map<std::string,size_t> * stat,PandaFileToPandaAsmMaps * maps,bool debugInfo,ark::panda_file::pgo::ProfileOptimizer * profileOpt)1888 bool AsmEmitter::Emit(const std::string &filename, Program &program, std::map<std::string, size_t> *stat,
1889                       PandaFileToPandaAsmMaps *maps, bool debugInfo, ark::panda_file::pgo::ProfileOptimizer *profileOpt)
1890 {
1891     auto writer = FileWriter(filename);
1892     if (!writer) {
1893         SetLastError("Unable to open " + filename + " for writing");
1894         return false;
1895     }
1896     return Emit(&writer, program, stat, maps, debugInfo, profileOpt);
1897 }
1898 
Emit(Program & program,PandaFileToPandaAsmMaps * maps)1899 std::unique_ptr<const panda_file::File> AsmEmitter::Emit(Program &program, PandaFileToPandaAsmMaps *maps)
1900 {
1901     auto items = ItemContainer {};
1902     if (!Emit(&items, program, maps)) {
1903         return nullptr;
1904     }
1905 
1906     size_t size = items.ComputeLayout();
1907     auto *buffer = new std::byte[size];
1908 
1909     auto writer = MemoryBufferWriter(reinterpret_cast<uint8_t *>(buffer), size);
1910     if (!items.Write(&writer)) {
1911         delete[] buffer;
1912         return nullptr;
1913     }
1914 
1915     os::mem::ConstBytePtr ptr(
1916         buffer, size, [](std::byte *bufferPtr, [[maybe_unused]] size_t paramSize) noexcept { delete[] bufferPtr; });
1917     return panda_file::File::OpenFromMemory(std::move(ptr));
1918 }
1919 
1920 /* static */
AssignProfileInfo(std::unordered_map<size_t,std::vector<Ins * >> & instMap,const std::map<std::string,pandasm::Function> & functionTable)1921 bool AsmEmitter::AssignProfileInfo(std::unordered_map<size_t, std::vector<Ins *>> &instMap,
1922                                    const std::map<std::string, pandasm::Function> &functionTable)
1923 {
1924     constexpr auto SIZES = profiling::GetOrderedProfileSizes();
1925     for (auto &func : functionTable) {
1926         for (auto &inst : func.second.ins) {
1927             auto profSize = INST_PROFILE_SIZES[static_cast<unsigned>(inst.opcode)];
1928             if (profSize != 0) {
1929                 instMap[profSize].push_back(const_cast<Ins *>(&inst));
1930             }
1931         }
1932         size_t index = 0;
1933         for (auto it = SIZES.rbegin(); it != SIZES.rend(); ++it) {
1934             std::vector<Ins *> &vec = instMap[*it];
1935             for (auto inst : vec) {
1936                 inst->profileId = index;
1937                 index += *it;
1938             }
1939             vec.clear();
1940         }
1941 
1942         const_cast<pandasm::Function *>(&(functionTable.at(func.first)))->profileSize = index;
1943     }
1944     return true;
1945 }
1946 
AssignProfileInfo(Program * program)1947 bool AsmEmitter::AssignProfileInfo(Program *program)
1948 {
1949     std::unordered_map<size_t, std::vector<Ins *>> instMap;
1950     /**
1951      * Since elements in the profile vector should be grouped by its size and ordered in
1952      * descending order, we first save instructions in map, where key is a profile size.
1953      * Then we iterate over this map in descending order - from biggest profile element size
1954      * to smallest. And assign profile index to all instructions with same size.
1955      */
1956 
1957     AssignProfileInfo(instMap, program->functionStaticTable);
1958     AssignProfileInfo(instMap, program->functionInstanceTable);
1959 
1960     return true;
1961 }
1962 
GetTypeItem(ItemContainer * items,const std::unordered_map<panda_file::Type::TypeId,PrimitiveTypeItem * > & primitiveTypes,const Type & type,const Program & program)1963 TypeItem *AsmEmitter::GetTypeItem(
1964     ItemContainer *items, const std::unordered_map<panda_file::Type::TypeId, PrimitiveTypeItem *> &primitiveTypes,
1965     const Type &type, const Program &program)
1966 {
1967     if (!type.IsObject()) {
1968         return Find(primitiveTypes, type.GetId());
1969     }
1970 
1971     if (type.IsArray()) {
1972         return items->GetOrCreateForeignClassItem(type.GetDescriptor());
1973     }
1974 
1975     const auto &name = type.GetName();
1976     auto iter = program.recordTable.find(name);
1977     if (iter == program.recordTable.end()) {
1978         return nullptr;
1979     }
1980 
1981     auto &rec = iter->second;
1982 
1983     if (rec.metadata->IsForeign()) {
1984         return items->GetOrCreateForeignClassItem(type.GetDescriptor());
1985     }
1986 
1987     return items->GetOrCreateClassItem(type.GetDescriptor());
1988 }
1989 
1990 // CC-OFFNXT(G.FUN.01-CPP) solid logic
Emit(BytecodeEmitter & emitter,panda_file::MethodItem * method,const std::unordered_map<std::string,panda_file::BaseMethodItem * > & methods,const std::unordered_map<std::string,panda_file::BaseMethodItem * > & staticMethods,const std::unordered_map<std::string,panda_file::BaseFieldItem * > & fields,const std::unordered_map<std::string,panda_file::BaseFieldItem * > & staticFields,const std::unordered_map<std::string,panda_file::BaseClassItem * > & classes,const std::unordered_map<std::string_view,panda_file::StringItem * > & strings,const std::unordered_map<std::string,panda_file::LiteralArrayItem * > & literalarrays) const1991 bool Function::Emit(BytecodeEmitter &emitter, panda_file::MethodItem *method,
1992                     const std::unordered_map<std::string, panda_file::BaseMethodItem *> &methods,
1993                     const std::unordered_map<std::string, panda_file::BaseMethodItem *> &staticMethods,
1994                     const std::unordered_map<std::string, panda_file::BaseFieldItem *> &fields,
1995                     const std::unordered_map<std::string, panda_file::BaseFieldItem *> &staticFields,
1996                     const std::unordered_map<std::string, panda_file::BaseClassItem *> &classes,
1997                     const std::unordered_map<std::string_view, panda_file::StringItem *> &strings,
1998                     const std::unordered_map<std::string, panda_file::LiteralArrayItem *> &literalarrays) const
1999 {
2000     auto labels = std::unordered_map<std::string_view, ark::Label> {};
2001 
2002     for (const auto &insn : ins) {
2003         if (insn.setLabel) {
2004             labels.insert_or_assign(insn.label, emitter.CreateLabel());
2005         }
2006     }
2007 
2008     for (const auto &insn : ins) {
2009         if (insn.setLabel) {
2010             auto search = labels.find(insn.label);
2011             ASSERT(search != labels.end());
2012             emitter.Bind(search->second);
2013         }
2014 
2015         if (insn.opcode != Opcode::INVALID) {
2016             if (!insn.Emit(emitter, method, methods, staticMethods, fields, staticFields, classes, strings,
2017                            literalarrays, labels)) {
2018                 return false;
2019             }
2020         }
2021     }
2022 
2023     return true;
2024 }
2025 
EmitLocalVariable(panda_file::LineNumberProgramItem * program,ItemContainer * container,std::vector<uint8_t> * constantPool,uint32_t & pcInc,size_t instructionNumber) const2026 void Function::EmitLocalVariable(panda_file::LineNumberProgramItem *program, ItemContainer *container,
2027                                  std::vector<uint8_t> *constantPool, uint32_t &pcInc, size_t instructionNumber) const
2028 {
2029     auto tryEmitPc = [program, constantPool, &pcInc]() -> void {
2030         if (pcInc != 0) {
2031             program->EmitAdvancePc(constantPool, pcInc);
2032             pcInc = 0;
2033         }
2034     };
2035     for (auto &v : localVariableDebug) {
2036         if (IsParameter(v.reg)) {
2037             continue;
2038         }
2039         if (instructionNumber == v.start) {
2040             tryEmitPc();
2041             StringItem *variableName = container->GetOrCreateStringItem(v.name);
2042             StringItem *variableType = container->GetOrCreateStringItem(v.signature);
2043             if (v.signatureType.empty()) {
2044                 program->EmitStartLocal(constantPool, v.reg, variableName, variableType);
2045             } else {
2046                 StringItem *typeSignature = container->GetOrCreateStringItem(v.signatureType);
2047                 program->EmitStartLocalExtended(constantPool, v.reg, variableName, variableType, typeSignature);
2048             }
2049         }
2050         if (instructionNumber == (v.start + v.length)) {
2051             tryEmitPc();
2052             program->EmitEndLocal(v.reg);
2053         }
2054     }
2055 }
2056 
GetLineNumber(size_t i) const2057 size_t Function::GetLineNumber(size_t i) const
2058 {
2059     return static_cast<int32_t>(ins[i].insDebug.lineNumber);
2060 }
2061 
GetColumnNumber(size_t i) const2062 uint32_t Function::GetColumnNumber(size_t i) const
2063 {
2064     return static_cast<int32_t>(ins[i].insDebug.columnNumber);
2065 }
2066 
EmitNumber(panda_file::LineNumberProgramItem * program,std::vector<uint8_t> * constantPool,uint32_t pcInc,int32_t lineInc) const2067 void Function::EmitNumber(panda_file::LineNumberProgramItem *program, std::vector<uint8_t> *constantPool,
2068                           uint32_t pcInc, int32_t lineInc) const
2069 {
2070     if (!program->EmitSpecialOpcode(pcInc, lineInc)) {
2071         if (pcInc != 0) {
2072             program->EmitAdvancePc(constantPool, pcInc);
2073             if (!program->EmitSpecialOpcode(0, lineInc)) {
2074                 program->EmitAdvanceLine(constantPool, lineInc);
2075                 program->EmitSpecialOpcode(0, 0);
2076             }
2077         } else {
2078             program->EmitAdvanceLine(constantPool, lineInc);
2079             program->EmitSpecialOpcode(0, 0);
2080         }
2081     }
2082 }
2083 
EmitLineNumber(panda_file::LineNumberProgramItem * program,std::vector<uint8_t> * constantPool,uint32_t & prevLineNumber,uint32_t & pcInc,size_t instructionNumber) const2084 void Function::EmitLineNumber(panda_file::LineNumberProgramItem *program, std::vector<uint8_t> *constantPool,
2085                               uint32_t &prevLineNumber, uint32_t &pcInc, size_t instructionNumber) const
2086 {
2087     auto lineInc = GetLineNumber(instructionNumber) - prevLineNumber;
2088     if (lineInc != 0) {
2089         prevLineNumber = GetLineNumber(instructionNumber);
2090         EmitNumber(program, constantPool, pcInc, lineInc);
2091         pcInc = 0;
2092     }
2093 }
2094 
EmitColumnNumber(panda_file::LineNumberProgramItem * program,std::vector<uint8_t> * constantPool,uint32_t & prevColumnNumber,uint32_t & pcInc,size_t instructionNumber) const2095 void Function::EmitColumnNumber(panda_file::LineNumberProgramItem *program, std::vector<uint8_t> *constantPool,
2096                                 uint32_t &prevColumnNumber, uint32_t &pcInc, size_t instructionNumber) const
2097 {
2098     uint32_t cn = GetColumnNumber(instructionNumber);
2099     if (cn != prevColumnNumber) {
2100         program->EmitColumn(constantPool, pcInc, cn);
2101         pcInc = 0;
2102         prevColumnNumber = cn;
2103     }
2104 }
2105 
BuildLineNumberProgram(panda_file::DebugInfoItem * debugItem,const std::vector<uint8_t> & bytecode,ItemContainer * container,std::vector<uint8_t> * constantPool,bool emitDebugInfo) const2106 void Function::BuildLineNumberProgram(panda_file::DebugInfoItem *debugItem, const std::vector<uint8_t> &bytecode,
2107                                       ItemContainer *container, std::vector<uint8_t> *constantPool,
2108                                       bool emitDebugInfo) const
2109 {
2110     auto *program = debugItem->GetLineNumberProgram();
2111 
2112     if (ins.empty()) {
2113         program->EmitEnd();
2114         return;
2115     }
2116 
2117     uint32_t pcInc = 0;
2118     uint32_t prevLineNumber = GetLineNumber(0);
2119     uint32_t prevColumnNumber = std::numeric_limits<uint32_t>::max();
2120     BytecodeInstruction bi(bytecode.data());
2121     debugItem->SetLineNumber(prevLineNumber);
2122 
2123     for (size_t i = 0; i < ins.size(); i++) {
2124         if (emitDebugInfo) {
2125             EmitLocalVariable(program, container, constantPool, pcInc, i);
2126         }
2127         if (ins[i].opcode == Opcode::INVALID) {
2128             continue;
2129         }
2130 
2131         if (emitDebugInfo || ins[i].CanThrow()) {
2132             EmitLineNumber(program, constantPool, prevLineNumber, pcInc, i);
2133         }
2134 
2135         if (ark::panda_file::IsDynamicLanguage(language) && emitDebugInfo) {
2136             EmitColumnNumber(program, constantPool, prevColumnNumber, pcInc, i);
2137         }
2138 
2139         pcInc += bi.GetSize();
2140         bi = bi.GetNext();
2141     }
2142 
2143     program->EmitEnd();
2144 }
2145 
MakeOrderAndOffsets(const std::vector<uint8_t> & bytecode) const2146 Function::TryCatchInfo Function::MakeOrderAndOffsets(const std::vector<uint8_t> &bytecode) const
2147 {
2148     std::unordered_map<std::string_view, size_t> tryCatchLabels;
2149     std::unordered_map<std::string, std::vector<const CatchBlock *>> tryCatchMap;
2150     std::vector<std::string> tryCatchOrder;
2151 
2152     for (auto &catchBlock : catchBlocks) {
2153         tryCatchLabels.insert_or_assign(catchBlock.tryBeginLabel, 0);
2154         tryCatchLabels.insert_or_assign(catchBlock.tryEndLabel, 0);
2155         tryCatchLabels.insert_or_assign(catchBlock.catchBeginLabel, 0);
2156         tryCatchLabels.insert_or_assign(catchBlock.catchEndLabel, 0);
2157 
2158         std::string tryKey = catchBlock.tryBeginLabel + ":" + catchBlock.tryEndLabel;
2159         auto it = tryCatchMap.find(tryKey);
2160         if (it == tryCatchMap.cend()) {
2161             std::tie(it, std::ignore) = tryCatchMap.try_emplace(tryKey);
2162             tryCatchOrder.push_back(tryKey);
2163         }
2164         it->second.push_back(&catchBlock);
2165     }
2166 
2167     BytecodeInstruction bi(bytecode.data());
2168     size_t pcOffset = 0;
2169 
2170     for (const auto &i : ins) {
2171         if (i.setLabel) {
2172             auto it = tryCatchLabels.find(i.label);
2173             if (it != tryCatchLabels.cend()) {
2174                 tryCatchLabels[i.label] = pcOffset;
2175             }
2176         }
2177         if (i.opcode == Opcode::INVALID) {
2178             continue;
2179         }
2180 
2181         pcOffset += bi.GetSize();
2182         bi = bi.GetNext();
2183     }
2184 
2185     return Function::TryCatchInfo {tryCatchLabels, tryCatchMap, tryCatchOrder};
2186 }
2187 
BuildTryBlocks(MethodItem * method,const std::unordered_map<std::string,BaseClassItem * > & classItems,const std::vector<uint8_t> & bytecode) const2188 std::vector<CodeItem::TryBlock> Function::BuildTryBlocks(
2189     MethodItem *method, const std::unordered_map<std::string, BaseClassItem *> &classItems,
2190     const std::vector<uint8_t> &bytecode) const
2191 {
2192     std::vector<CodeItem::TryBlock> tryBlocks;
2193 
2194     if (ins.empty()) {
2195         return tryBlocks;
2196     }
2197 
2198     Function::TryCatchInfo tcs = MakeOrderAndOffsets(bytecode);
2199 
2200     for (const auto &tKey : tcs.tryCatchOrder) {
2201         auto kv = tcs.tryCatchMap.find(tKey);
2202         ASSERT(kv != tcs.tryCatchMap.cend());
2203         auto &tryCatchBlocks = kv->second;
2204 
2205         ASSERT(!tryCatchBlocks.empty());
2206 
2207         std::vector<CodeItem::CatchBlock> catchBlockItems;
2208 
2209         for (auto *catchBlock : tryCatchBlocks) {
2210             auto className = catchBlock->exceptionRecord;
2211 
2212             BaseClassItem *classItem = nullptr;
2213             if (!className.empty()) {
2214                 auto it = classItems.find(className);
2215                 ASSERT(it != classItems.cend());
2216                 classItem = it->second;
2217             }
2218 
2219             auto handlerPcOffset = tcs.tryCatchLabels[catchBlock->catchBeginLabel];
2220             auto handlerCodeSize = tcs.tryCatchLabels[catchBlock->catchEndLabel] - handlerPcOffset;
2221             catchBlockItems.emplace_back(method, classItem, handlerPcOffset, handlerCodeSize);
2222         }
2223 
2224         auto tryStartPcOffset = tcs.tryCatchLabels[tryCatchBlocks[0]->tryBeginLabel];
2225         auto tryEndPcOffset = tcs.tryCatchLabels[tryCatchBlocks[0]->tryEndLabel];
2226         ASSERT(tryEndPcOffset >= tryStartPcOffset);
2227         tryBlocks.emplace_back(tryStartPcOffset, tryEndPcOffset - tryStartPcOffset, catchBlockItems);
2228     }
2229 
2230     return tryBlocks;
2231 }
2232 
DebugDump() const2233 void Function::DebugDump() const
2234 {
2235     std::cerr << "name: " << name << std::endl;
2236     for (const auto &i : ins) {
2237         std::cerr << i.ToString("\n", true, regsNum);
2238     }
2239 }
2240 
GetOwnerName(std::string name)2241 std::string GetOwnerName(std::string name)
2242 {
2243     name = DeMangleName(name);
2244     auto superPos = name.find_last_of(PARSE_AREA_MARKER);
2245     if (superPos == std::string::npos) {
2246         return "";
2247     }
2248 
2249     return name.substr(0, superPos);
2250 }
2251 
GetItemName(std::string name)2252 std::string GetItemName(std::string name)
2253 {
2254     name = DeMangleName(name);
2255     auto superPos = name.find_last_of(PARSE_AREA_MARKER);
2256     if (superPos == std::string::npos) {
2257         return name;
2258     }
2259 
2260     return name.substr(superPos + 1);
2261 }
2262 
2263 }  // namespace ark::pandasm
2264