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