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