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