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