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