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> ¶ms)
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> ¶ms)
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 ¶mItems = method->GetParams();
1319 for (size_t protoIdx = 0; protoIdx < paramItems.size(); protoIdx++) {
1320 size_t paramIdx = method->IsStatic() ? protoIdx : protoIdx + 1;
1321 auto ¶m = func.params[paramIdx];
1322 auto ¶mItem = paramItems[protoIdx];
1323 if (!AddAnnotations(¶mItem, 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