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