• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 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 <iomanip>
17 #include <tuple>
18 #include <vector>
19 
20 #include <gmock/gmock.h>
21 #include <gtest/gtest.h>
22 #include "annotation_data_accessor.h"
23 #include "assembly-emitter.h"
24 #include "assembly-parser.h"
25 #include "class_data_accessor-inl.h"
26 #include "code_data_accessor-inl.h"
27 #include "debug_data_accessor-inl.h"
28 #include "field_data_accessor-inl.h"
29 #include "file_items.h"
30 #include "lexer.h"
31 #include "method_data_accessor-inl.h"
32 #include "param_annotations_data_accessor.h"
33 #include "proto_data_accessor-inl.h"
34 #include "utils/span.h"
35 #include "utils/leb128.h"
36 #include "utils/utf.h"
37 #include "bytecode_instruction-inl.h"
38 
39 namespace panda::test {
40 
41 using namespace panda::pandasm;
42 
GetTypeDescriptor(const std::string & name,std::string * storage)43 static const uint8_t *GetTypeDescriptor(const std::string &name, std::string *storage)
44 {
45     *storage = "L" + name + ";";
46     std::replace(storage->begin(), storage->end(), '.', '/');
47     return utf::CStringAsMutf8(storage->c_str());
48 }
49 
TEST(emittertests,test)50 TEST(emittertests, test)
51 {
52     Parser p;
53 
54     auto source = R"(            # 1
55         .record R {              # 2
56             i32 sf <static>      # 3
57             i8  if               # 4
58         }                        # 5
59                                  # 6
60         .function void main() {  # 7
61             return.void          # 8
62         }                        # 9
63     )";
64 
65     std::string source_filename = "source.pa";
66     auto res = p.Parse(source, source_filename);
67     ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
68 
69     auto pf = AsmEmitter::Emit(res.Value());
70     ASSERT_NE(pf, nullptr);
71 
72     // Check _GLOBAL class
73 
74     {
75         std::string descriptor;
76         auto class_id = pf->GetClassId(GetTypeDescriptor("_GLOBAL", &descriptor));
77         ASSERT_TRUE(class_id.IsValid());
78         ASSERT_FALSE(pf->IsExternal(class_id));
79 
80         panda_file::ClassDataAccessor cda(*pf, class_id);
81         ASSERT_EQ(cda.GetSuperClassId().GetOffset(), 0U);
82         ASSERT_EQ(cda.GetAccessFlags(), ACC_PUBLIC);
83         ASSERT_EQ(cda.GetFieldsNumber(), 0U);
84         ASSERT_EQ(cda.GetMethodsNumber(), 1U);
85         ASSERT_EQ(cda.GetIfacesNumber(), 0U);
86 
87         ASSERT_FALSE(cda.GetSourceFileId().has_value());
88 
89         cda.EnumerateRuntimeAnnotations([](panda_file::File::EntityId) { ASSERT_TRUE(false); });
90 
91         cda.EnumerateAnnotations([](panda_file::File::EntityId) { ASSERT_TRUE(false); });
92 
93         cda.EnumerateFields([](panda_file::FieldDataAccessor &) { ASSERT_TRUE(false); });
94 
95         cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
96             ASSERT_FALSE(mda.IsExternal());
97             ASSERT_EQ(mda.GetClassId(), class_id);
98             ASSERT_EQ(utf::CompareMUtf8ToMUtf8(pf->GetStringData(mda.GetNameId()).data, utf::CStringAsMutf8("main")),
99                       0);
100 
101             panda_file::ProtoDataAccessor pda(*pf, mda.GetProtoId());
102             ASSERT_EQ(pda.GetNumArgs(), 0U);
103             ASSERT_EQ(pda.GetReturnType().GetId(), panda_file::Type::TypeId::VOID);
104 
105             ASSERT_EQ(mda.GetAccessFlags(), ACC_STATIC);
106             ASSERT_TRUE(mda.GetCodeId().has_value());
107 
108             panda_file::CodeDataAccessor cdacc(*pf, mda.GetCodeId().value());
109             ASSERT_EQ(cdacc.GetNumVregs(), 0U);
110             ASSERT_EQ(cdacc.GetNumArgs(), 0U);
111             ASSERT_EQ(cdacc.GetCodeSize(), 1U);
112             ASSERT_EQ(cdacc.GetTriesSize(), 0U);
113 
114             ASSERT_FALSE(mda.GetRuntimeParamAnnotationId().has_value());
115             ASSERT_FALSE(mda.GetParamAnnotationId().has_value());
116             ASSERT_TRUE(mda.GetDebugInfoId().has_value());
117 
118             panda_file::DebugInfoDataAccessor dda(*pf, mda.GetDebugInfoId().value());
119             ASSERT_EQ(dda.GetLineStart(), 8U);
120             ASSERT_EQ(dda.GetNumParams(), 0U);
121 
122             mda.EnumerateRuntimeAnnotations([](panda_file::File::EntityId) { ASSERT_TRUE(false); });
123             mda.EnumerateAnnotations([](panda_file::File::EntityId) { ASSERT_TRUE(false); });
124         });
125     }
126 
127     // Check R class
128 
129     {
130         std::string descriptor;
131         auto class_id = pf->GetClassId(GetTypeDescriptor("R", &descriptor));
132         ASSERT_TRUE(class_id.IsValid());
133         ASSERT_FALSE(pf->IsExternal(class_id));
134 
135         panda_file::ClassDataAccessor cda(*pf, class_id);
136         ASSERT_EQ(cda.GetSuperClassId().GetOffset(), 0U);
137         ASSERT_EQ(cda.GetAccessFlags(), 0);
138         ASSERT_EQ(cda.GetFieldsNumber(), 2U);
139         ASSERT_EQ(cda.GetMethodsNumber(), 0U);
140         ASSERT_EQ(cda.GetIfacesNumber(), 0U);
141 
142         // We emit SET_FILE in debuginfo
143         ASSERT_TRUE(cda.GetSourceFileId().has_value());
144 
145         EXPECT_EQ(std::string(reinterpret_cast<const char *>(pf->GetStringData(cda.GetSourceFileId().value()).data)),
146                   source_filename);
147 
148         cda.EnumerateRuntimeAnnotations([](panda_file::File::EntityId) { ASSERT_TRUE(false); });
149 
150         cda.EnumerateAnnotations([](panda_file::File::EntityId) { ASSERT_TRUE(false); });
151 
152         struct FieldData {
153             std::string name;
154             panda_file::Type::TypeId type_id;
155             uint32_t access_flags;
156         };
157 
158         std::vector<FieldData> fields {{"sf", panda_file::Type::TypeId::I32, ACC_STATIC},
159                                        {"if", panda_file::Type::TypeId::I8, 0}};
160 
161         size_t i = 0;
162         cda.EnumerateFields([&](panda_file::FieldDataAccessor &fda) {
163             ASSERT_FALSE(fda.IsExternal());
164             ASSERT_EQ(fda.GetClassId(), class_id);
165             ASSERT_EQ(utf::CompareMUtf8ToMUtf8(pf->GetStringData(fda.GetNameId()).data,
166                                                utf::CStringAsMutf8(fields[i].name.c_str())),
167                       0);
168 
169             ASSERT_EQ(fda.GetType(), panda_file::Type(fields[i].type_id).GetFieldEncoding());
170             ASSERT_EQ(fda.GetAccessFlags(), fields[i].access_flags);
171 
172             fda.EnumerateRuntimeAnnotations([](panda_file::File::EntityId) { ASSERT_TRUE(false); });
173             fda.EnumerateAnnotations([](panda_file::File::EntityId) { ASSERT_TRUE(false); });
174 
175             ++i;
176         });
177 
178         cda.EnumerateMethods([&](panda_file::MethodDataAccessor &) { ASSERT_TRUE(false); });
179     }
180 }
181 
GetSpecialOpcode(uint32_t pc_inc,int32_t line_inc)182 uint8_t GetSpecialOpcode(uint32_t pc_inc, int32_t line_inc)
183 {
184     return (line_inc - panda_file::LineNumberProgramItem::LINE_BASE) +
185            (pc_inc * panda_file::LineNumberProgramItem::LINE_RANGE) + panda_file::LineNumberProgramItem::OPCODE_BASE;
186 }
187 
TEST(emittertests,debuginfo)188 TEST(emittertests, debuginfo)
189 {
190     Parser p;
191 
192     auto source = R"(
193         .function void main() {
194             ldai.64 0   # line 3, pc 0
195                         # line 4
196                         # line 5
197                         # line 6
198                         # line 7
199                         # line 8
200                         # line 9
201                         # line 10
202                         # line 11
203                         # line 12
204                         # line 13
205                         # line 14
206             ldai.64 1   # line 15, pc 9
207             return.void # line 16, pc 18
208         }
209     )";
210 
211     std::string source_filename = "source.pa";
212     auto res = p.Parse(source, source_filename);
213     ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
214 
215     auto pf = AsmEmitter::Emit(res.Value());
216     ASSERT_NE(pf, nullptr);
217 
218     std::string descriptor;
219     auto class_id = pf->GetClassId(GetTypeDescriptor("_GLOBAL", &descriptor));
220     ASSERT_TRUE(class_id.IsValid());
221 
222     panda_file::ClassDataAccessor cda(*pf, class_id);
223 
224     cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
225         panda_file::CodeDataAccessor cdacc(*pf, mda.GetCodeId().value());
226         ASSERT_TRUE(mda.GetDebugInfoId().has_value());
227 
228         panda_file::DebugInfoDataAccessor dda(*pf, mda.GetDebugInfoId().value());
229         ASSERT_EQ(dda.GetLineStart(), 3U);
230         ASSERT_EQ(dda.GetNumParams(), 0U);
231 
232         const uint8_t *program = dda.GetLineNumberProgram();
233         Span<const uint8_t> constant_pool = dda.GetConstantPool();
234 
235         std::vector<uint8_t> opcodes {static_cast<uint8_t>(panda_file::LineNumberProgramItem::Opcode::SET_FILE),
236                                       static_cast<uint8_t>(panda_file::LineNumberProgramItem::Opcode::ADVANCE_PC),
237                                       static_cast<uint8_t>(panda_file::LineNumberProgramItem::Opcode::ADVANCE_LINE),
238                                       GetSpecialOpcode(0, 0),
239                                       GetSpecialOpcode(9, 1),
240                                       static_cast<uint8_t>(panda_file::LineNumberProgramItem::Opcode::END_SEQUENCE)};
241 
242         EXPECT_THAT(opcodes, ::testing::ElementsAreArray(program, opcodes.size()));
243 
244         size_t size {};
245         bool is_full {};
246         size_t constant_pool_offset = 0;
247 
248         uint32_t offset {};
249 
250         std::tie(offset, size, is_full) = leb128::DecodeUnsigned<uint32_t>(&constant_pool[constant_pool_offset]);
251         constant_pool_offset += size;
252         ASSERT_TRUE(is_full);
253         EXPECT_EQ(
254             std::string(reinterpret_cast<const char *>(pf->GetStringData(panda_file::File::EntityId(offset)).data)),
255             source_filename);
256 
257         uint32_t pc_inc;
258         std::tie(pc_inc, size, is_full) = leb128::DecodeUnsigned<uint32_t>(&constant_pool[constant_pool_offset]);
259         constant_pool_offset += size;
260         ASSERT_TRUE(is_full);
261         EXPECT_EQ(pc_inc, 9U);
262 
263         int32_t line_inc;
264         std::tie(line_inc, size, is_full) = leb128::DecodeSigned<int32_t>(&constant_pool[constant_pool_offset]);
265         constant_pool_offset += size;
266         ASSERT_TRUE(is_full);
267         EXPECT_EQ(line_inc, 12);
268 
269         EXPECT_EQ(constant_pool_offset, constant_pool.size());
270     });
271 }
272 
TEST(emittertests,exceptions)273 TEST(emittertests, exceptions)
274 {
275     Parser p;
276 
277     auto source = R"(
278         .record Exception1 {}
279         .record Exception2 {}
280 
281         .function void main() {
282             ldai.64 0
283         try_begin:
284             ldai.64 1
285             ldai.64 2
286         try_end:
287             ldai.64 3
288         catch_begin1:
289             ldai.64 4
290         catch_begin2:
291             ldai.64 5
292         catchall_begin:
293             ldai.64 6
294 
295         .catch Exception1, try_begin, try_end, catch_begin1
296         .catch Exception2, try_begin, try_end, catch_begin2
297         .catchall try_begin, try_end, catchall_begin
298         }
299     )";
300 
301     auto res = p.Parse(source);
302     ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
303 
304     auto pf = AsmEmitter::Emit(res.Value());
305     ASSERT_NE(pf, nullptr);
306 
307     std::string descriptor;
308 
309     auto class_id = pf->GetClassId(GetTypeDescriptor("_GLOBAL", &descriptor));
310     ASSERT_TRUE(class_id.IsValid());
311 
312     panda_file::ClassDataAccessor cda(*pf, class_id);
313 
314     cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
315         panda_file::CodeDataAccessor cdacc(*pf, mda.GetCodeId().value());
316         ASSERT_EQ(cdacc.GetNumVregs(), 0U);
317         ASSERT_EQ(cdacc.GetNumArgs(), 0U);
318         ASSERT_EQ(cdacc.GetTriesSize(), 1);
319 
320         cdacc.EnumerateTryBlocks([&](panda_file::CodeDataAccessor::TryBlock &try_block) {
321             EXPECT_EQ(try_block.GetStartPc(), 9);
322             EXPECT_EQ(try_block.GetLength(), 18);
323             EXPECT_EQ(try_block.GetNumCatches(), 3);
324 
325             struct CatchInfo {
326                 panda_file::File::EntityId type_id;
327                 uint32_t handler_pc;
328             };
329 
330             std::vector<CatchInfo> catch_infos {{pf->GetClassId(GetTypeDescriptor("Exception1", &descriptor)), 4 * 9},
331                                                 {pf->GetClassId(GetTypeDescriptor("Exception2", &descriptor)), 5 * 9},
332                                                 {panda_file::File::EntityId(), 6 * 9}};
333 
334             size_t i = 0;
335             try_block.EnumerateCatchBlocks([&](panda_file::CodeDataAccessor::CatchBlock &catch_block) {
336                 auto idx = catch_block.GetTypeIdx();
337                 auto id = idx != panda_file::INVALID_INDEX ? pf->ResolveClassIndex(mda.GetMethodId(), idx)
338                                                            : panda_file::File::EntityId();
339                 EXPECT_EQ(id, catch_infos[i].type_id);
340                 EXPECT_EQ(catch_block.GetHandlerPc(), catch_infos[i].handler_pc);
341                 ++i;
342 
343                 return true;
344             });
345 
346             return true;
347         });
348     });
349 }
350 
TEST(emittertests,errors)351 TEST(emittertests, errors)
352 {
353     {
354         Parser p;
355         auto source = R"(
356             .record A {
357                 B b
358             }
359         )";
360 
361         auto res = p.Parse(source);
362         ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
363 
364         auto pf = AsmEmitter::Emit(res.Value());
365         ASSERT_EQ(pf, nullptr);
366         ASSERT_EQ(AsmEmitter::GetLastError(), "Field A.b has undefined type");
367     }
368 
369     {
370         Parser p;
371         auto source = R"(
372             .function void A.b() {}
373         )";
374 
375         auto res = p.Parse(source);
376         ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
377 
378         auto pf = AsmEmitter::Emit(res.Value());
379         ASSERT_EQ(pf, nullptr);
380         ASSERT_EQ(AsmEmitter::GetLastError(), "Function A.b is bound to undefined record A");
381     }
382 
383     {
384         Parser p;
385         auto source = R"(
386             .function A b() {}
387         )";
388 
389         auto res = p.Parse(source);
390         ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
391 
392         auto pf = AsmEmitter::Emit(res.Value());
393         ASSERT_EQ(pf, nullptr);
394         ASSERT_EQ(AsmEmitter::GetLastError(), "Function b has undefined return type");
395     }
396 
397     {
398         Parser p;
399         auto source = R"(
400             .function void a(b a0) {}
401         )";
402 
403         auto res = p.Parse(source);
404         ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
405 
406         auto pf = AsmEmitter::Emit(res.Value());
407         ASSERT_EQ(pf, nullptr);
408         ASSERT_EQ(AsmEmitter::GetLastError(), "Argument 0 of function a has undefined type");
409     }
410 
411     {
412         Parser p;
413         auto source = R"(
414             .record A <external>
415             .function void A.x() {}
416         )";
417 
418         auto res = p.Parse(source);
419         ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
420 
421         auto pf = AsmEmitter::Emit(res.Value());
422         ASSERT_EQ(pf, nullptr);
423         ASSERT_EQ(AsmEmitter::GetLastError(), "Non-external function A.x is bound to external record");
424     }
425 }
426 
TEST(emittertests,language)427 TEST(emittertests, language)
428 {
429     {
430         Parser p;
431         auto source = R"(
432             .function void foo() {}
433         )";
434 
435         auto res = p.Parse(source);
436         ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
437 
438         auto pf = AsmEmitter::Emit(res.Value());
439         ASSERT_NE(pf, nullptr);
440 
441         std::string descriptor;
442 
443         auto class_id = pf->GetClassId(GetTypeDescriptor("_GLOBAL", &descriptor));
444         ASSERT_TRUE(class_id.IsValid());
445 
446         panda_file::ClassDataAccessor cda(*pf, class_id);
447 
448         ASSERT_FALSE(cda.GetSourceLang());
449 
450         cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) { ASSERT_FALSE(mda.GetSourceLang()); });
451     }
452 }
453 
TEST(emittertests,constructors)454 TEST(emittertests, constructors)
455 {
456     {
457         Parser p;
458         auto source = R"(
459             .record R {}
460             .function void R.foo(R a0) <ctor> {}
461         )";
462 
463         auto res = p.Parse(source);
464         ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
465 
466         auto pf = AsmEmitter::Emit(res.Value());
467         ASSERT_NE(pf, nullptr);
468 
469         std::string descriptor;
470 
471         auto class_id = pf->GetClassId(GetTypeDescriptor("R", &descriptor));
472         ASSERT_TRUE(class_id.IsValid());
473 
474         panda_file::ClassDataAccessor cda(*pf, class_id);
475 
476         cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
477             auto *name = utf::Mutf8AsCString(pf->GetStringData(mda.GetNameId()).data);
478             ASSERT_STREQ(name, ".ctor");
479         });
480     }
481 
482     {
483         Parser p;
484         auto source = R"(
485             .record R {}
486             .function void R.foo(R a0) <cctor> {}
487         )";
488 
489         auto res = p.Parse(source);
490         ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
491 
492         auto pf = AsmEmitter::Emit(res.Value());
493         ASSERT_NE(pf, nullptr);
494 
495         std::string descriptor;
496 
497         auto class_id = pf->GetClassId(GetTypeDescriptor("R", &descriptor));
498         ASSERT_TRUE(class_id.IsValid());
499 
500         panda_file::ClassDataAccessor cda(*pf, class_id);
501 
502         cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
503             auto *name = utf::Mutf8AsCString(pf->GetStringData(mda.GetNameId()).data);
504             ASSERT_STREQ(name, ".cctor");
505         });
506     }
507 }
508 
TEST(emittertests,field_value)509 TEST(emittertests, field_value)
510 {
511     Parser p;
512 
513     auto source = R"(
514         .record panda.String <external>
515 
516         .record R {
517             u1 f_u1 <value=1>
518             i8 f_i8 <value=2>
519             u8 f_u8 <value=128>
520             i16 f_i16 <value=256>
521             u16 f_u16 <value=32768>
522             i32 f_i32 <value=65536>
523             u32 f_u32 <value=2147483648>
524             i64 f_i64 <value=4294967296>
525             u64 f_u64 <value=9223372036854775808>
526             f32 f_f32 <value=1.0>
527             f64 f_f64 <value=2.0>
528             panda.String f_str <value="str">
529         }
530     )";
531 
532     struct FieldData {
533         std::string name;
534         panda_file::Type::TypeId type_id;
535         std::variant<int32_t, uint32_t, int64_t, uint64_t, float, double, std::string> value;
536     };
537 
538     std::vector<FieldData> data {
539         {"f_u1", panda_file::Type::TypeId::U1, static_cast<uint32_t>(1)},
540         {"f_i8", panda_file::Type::TypeId::I8, static_cast<int32_t>(2)},
541         {"f_u8", panda_file::Type::TypeId::U8, static_cast<uint32_t>(128)},
542         {"f_i16", panda_file::Type::TypeId::I16, static_cast<int32_t>(256)},
543         {"f_u16", panda_file::Type::TypeId::U16, static_cast<uint32_t>(32768)},
544         {"f_i32", panda_file::Type::TypeId::I32, static_cast<int32_t>(65536)},
545         {"f_u32", panda_file::Type::TypeId::U32, static_cast<uint32_t>(2147483648)},
546         {"f_i64", panda_file::Type::TypeId::I64, static_cast<int64_t>(4294967296)},
547         {"f_u64", panda_file::Type::TypeId::U64, static_cast<uint64_t>(9223372036854775808ULL)},
548         {"f_f32", panda_file::Type::TypeId::F32, static_cast<float>(1.0)},
549         {"f_f64", panda_file::Type::TypeId::F64, static_cast<double>(2.0)},
550         {"f_str", panda_file::Type::TypeId::REFERENCE, "str"}};
551 
552     auto res = p.Parse(source);
553     ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
554 
555     auto pf = AsmEmitter::Emit(res.Value());
556     ASSERT_NE(pf, nullptr);
557 
558     std::string descriptor;
559     auto class_id = pf->GetClassId(GetTypeDescriptor("R", &descriptor));
560     ASSERT_TRUE(class_id.IsValid());
561     ASSERT_FALSE(pf->IsExternal(class_id));
562 
563     panda_file::ClassDataAccessor cda(*pf, class_id);
564     ASSERT_EQ(cda.GetFieldsNumber(), data.size());
565 
566     auto panda_string_id = pf->GetClassId(GetTypeDescriptor("panda.String", &descriptor));
567 
568     size_t idx = 0;
569     cda.EnumerateFields([&](panda_file::FieldDataAccessor &fda) {
570         const FieldData &field_data = data[idx];
571 
572         ASSERT_EQ(utf::CompareMUtf8ToMUtf8(pf->GetStringData(fda.GetNameId()).data,
573                                            utf::CStringAsMutf8(field_data.name.c_str())),
574                   0);
575 
576         panda_file::Type type(field_data.type_id);
577         uint32_t type_value;
578         if (type.IsReference()) {
579             type_value = panda_string_id.GetOffset();
580         } else {
581             type_value = type.GetFieldEncoding();
582         }
583 
584         ASSERT_EQ(fda.GetType(), type_value);
585 
586         switch (field_data.type_id) {
587             case panda_file::Type::TypeId::U1: {
588                 auto result = fda.GetValue<uint8_t>();
589                 ASSERT_TRUE(result);
590                 ASSERT_EQ(result.value(), std::get<uint32_t>(field_data.value));
591                 break;
592             }
593             case panda_file::Type::TypeId::I8: {
594                 auto result = fda.GetValue<int8_t>();
595                 ASSERT_TRUE(result);
596                 ASSERT_EQ(result.value(), std::get<int32_t>(field_data.value));
597                 break;
598             }
599             case panda_file::Type::TypeId::U8: {
600                 auto result = fda.GetValue<uint8_t>();
601                 ASSERT_TRUE(result);
602                 ASSERT_EQ(result.value(), std::get<uint32_t>(field_data.value));
603                 break;
604             }
605             case panda_file::Type::TypeId::I16: {
606                 auto result = fda.GetValue<int16_t>();
607                 ASSERT_TRUE(result);
608                 ASSERT_EQ(result.value(), std::get<int32_t>(field_data.value));
609                 break;
610             }
611             case panda_file::Type::TypeId::U16: {
612                 auto result = fda.GetValue<uint16_t>();
613                 ASSERT_TRUE(result);
614                 ASSERT_EQ(result.value(), std::get<uint32_t>(field_data.value));
615                 break;
616             }
617             case panda_file::Type::TypeId::I32: {
618                 auto result = fda.GetValue<int32_t>();
619                 ASSERT_TRUE(result);
620                 ASSERT_EQ(result.value(), std::get<int32_t>(field_data.value));
621                 break;
622             }
623             case panda_file::Type::TypeId::U32: {
624                 auto result = fda.GetValue<uint32_t>();
625                 ASSERT_TRUE(result);
626                 ASSERT_EQ(result.value(), std::get<uint32_t>(field_data.value));
627                 break;
628             }
629             case panda_file::Type::TypeId::I64: {
630                 auto result = fda.GetValue<int64_t>();
631                 ASSERT_TRUE(result);
632                 ASSERT_EQ(result.value(), std::get<int64_t>(field_data.value));
633                 break;
634             }
635             case panda_file::Type::TypeId::U64: {
636                 auto result = fda.GetValue<uint64_t>();
637                 ASSERT_TRUE(result);
638                 ASSERT_EQ(result.value(), std::get<uint64_t>(field_data.value));
639                 break;
640             }
641             case panda_file::Type::TypeId::F32: {
642                 auto result = fda.GetValue<float>();
643                 ASSERT_TRUE(result);
644                 ASSERT_EQ(result.value(), std::get<float>(field_data.value));
645                 break;
646             }
647             case panda_file::Type::TypeId::F64: {
648                 auto result = fda.GetValue<double>();
649                 ASSERT_TRUE(result);
650                 ASSERT_EQ(result.value(), std::get<double>(field_data.value));
651                 break;
652             }
653             case panda_file::Type::TypeId::REFERENCE: {
654                 auto result = fda.GetValue<uint32_t>();
655                 ASSERT_TRUE(result);
656 
657                 panda_file::File::EntityId string_id(result.value());
658                 auto val = std::get<std::string>(field_data.value);
659 
660                 ASSERT_EQ(utf::CompareMUtf8ToMUtf8(pf->GetStringData(string_id).data, utf::CStringAsMutf8(val.c_str())),
661                           0);
662                 break;
663             }
664             default: {
665                 UNREACHABLE();
666                 break;
667             }
668         }
669 
670         ++idx;
671     });
672 }
673 
TEST(emittertests,tagged_in_func_decl)674 TEST(emittertests, tagged_in_func_decl)
675 {
676     Parser p;
677     auto source = R"(
678         .function any foo(any a0) <noimpl>
679     )";
680 
681     auto res = p.Parse(source);
682     ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
683 
684     auto pf = AsmEmitter::Emit(res.Value());
685     ASSERT_NE(pf, nullptr);
686 
687     std::string descriptor;
688 
689     auto class_id = pf->GetClassId(GetTypeDescriptor("_GLOBAL", &descriptor));
690     ASSERT_TRUE(class_id.IsValid());
691 
692     panda_file::ClassDataAccessor cda(*pf, class_id);
693 
694     size_t num_methods = 0;
695     const auto tagged = panda_file::Type(panda_file::Type::TypeId::TAGGED);
696     cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
697         panda_file::ProtoDataAccessor pda(*pf, mda.GetProtoId());
698         ASSERT_EQ(tagged, pda.GetReturnType());
699         ASSERT_EQ(1, pda.GetNumArgs());
700         ASSERT_EQ(tagged, pda.GetArgType(0));
701 
702         ++num_methods;
703     });
704     ASSERT_EQ(1, num_methods);
705 }
706 
TEST(emittertests,tagged_in_field_decl)707 TEST(emittertests, tagged_in_field_decl)
708 {
709     Parser p;
710     auto source = R"(
711         .record Test {
712             any foo
713         }
714     )";
715 
716     auto res = p.Parse(source);
717     ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
718 
719     auto pf = AsmEmitter::Emit(res.Value());
720     ASSERT_NE(pf, nullptr);
721 
722     std::string descriptor;
723 
724     auto class_id = pf->GetClassId(GetTypeDescriptor("Test", &descriptor));
725     ASSERT_TRUE(class_id.IsValid());
726 
727     panda_file::ClassDataAccessor cda(*pf, class_id);
728 
729     size_t num_fields = 0;
730     const auto tagged = panda_file::Type(panda_file::Type::TypeId::TAGGED);
731     cda.EnumerateFields([&](panda_file::FieldDataAccessor &fda) {
732         uint32_t type = fda.GetType();
733         ASSERT_EQ(tagged.GetFieldEncoding(), type);
734 
735         ++num_fields;
736     });
737     ASSERT_EQ(1, num_fields);
738 }
739 
TEST(emittertests,function_overloading_1)740 TEST(emittertests, function_overloading_1)
741 {
742     Parser p;
743     auto source = R"(
744         .function void foo(i8 a0) {}
745         .function u1 foo(u1 a0) {}
746 
747         .function i8 f(i32 a0) {
748             call foo:(i8), v0
749             call foo:(u1), v1
750         }
751     )";
752 
753     auto res = p.Parse(source);
754     ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
755 
756     auto pf = AsmEmitter::Emit(res.Value());
757     ASSERT_NE(pf, nullptr);
758 
759     std::string descriptor {};
760 
761     auto class_id = pf->GetClassId(GetTypeDescriptor("_GLOBAL", &descriptor));
762     ASSERT_TRUE(class_id.IsValid());
763 
764     panda_file::ClassDataAccessor cda(*pf, class_id);
765 
766     size_t num_methods = 0;
767 
768     std::unordered_map<panda_file::File::EntityId, panda_file::Type> id_to_arg_type {};
769     panda_file::File::EntityId id_f {};
770 
771     cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
772         ++num_methods;
773 
774         panda_file::ProtoDataAccessor pda(*pf, mda.GetProtoId());
775 
776         if (pda.GetArgType(0) == panda_file::Type(panda_file::Type::TypeId::I32)) {
777             id_f = mda.GetMethodId();
778 
779             return;
780         }
781 
782         id_to_arg_type.emplace(mda.GetMethodId(), pda.GetArgType(0));
783     });
784 
785     panda_file::MethodDataAccessor mda_f(*pf, id_f);
786     panda_file::CodeDataAccessor cda_f(*pf, mda_f.GetCodeId().value());
787 
788     const auto ins_sz = cda_f.GetCodeSize();
789     const auto ins_arr = cda_f.GetInstructions();
790 
791     auto bc_ins = BytecodeInstruction(ins_arr);
792     const auto bc_ins_last = bc_ins.JumpTo(ins_sz);
793 
794     while (bc_ins.GetAddress() != bc_ins_last.GetAddress()) {
795         const auto arg_method_idx = bc_ins.GetId().AsIndex();
796         const auto arg_method_id = pf->ResolveMethodIndex(id_f, arg_method_idx);
797 
798         panda_file::MethodDataAccessor method_accessor(*pf, arg_method_id);
799         panda_file::ProtoDataAccessor proto_accessor(*pf, method_accessor.GetProtoId());
800 
801         ASSERT_EQ(proto_accessor.GetArgType(0), id_to_arg_type.at(arg_method_id));
802         ASSERT_EQ(std::string(utf::Mutf8AsCString(pf->GetStringData(method_accessor.GetNameId()).data)), "foo");
803 
804         bc_ins = bc_ins.GetNext();
805     }
806 
807     ASSERT_EQ(3, num_methods);
808 }
809 
810 }  // namespace panda::test
811