• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2023 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 "field_data_accessor.h"
30 #include "file_items.h"
31 #include "lexer.h"
32 #include "method_data_accessor-inl.h"
33 #include "param_annotations_data_accessor.h"
34 #include "proto_data_accessor-inl.h"
35 #include "utils/span.h"
36 #include "utils/leb128.h"
37 #include "utils/utf.h"
38 #include "bytecode_instruction-inl.h"
39 
40 namespace panda::test {
41 
42 // NOLINTNEXTLINE(google-build-using-namespace)
43 using namespace panda::pandasm;
44 
GetTypeDescriptor(const std::string & name,std::string * storage)45 static const uint8_t *GetTypeDescriptor(const std::string &name, std::string *storage)
46 {
47     *storage = "L" + name + ";";
48     std::replace(storage->begin(), storage->end(), '.', '/');
49     return utf::CStringAsMutf8(storage->c_str());
50 }
51 
TEST(emittertests,test)52 TEST(emittertests, test)
53 {
54     Parser p;
55 
56     auto source = R"(            # 1
57         .record R {              # 2
58             i32 sf <static>      # 3
59             i8  if               # 4
60         }                        # 5
61                                  # 6
62         .function void main() {  # 7
63             return.void          # 8
64         }                        # 9
65     )";
66 
67     std::string sourceFilename = "source.pa";
68     auto res = p.Parse(source, sourceFilename);
69     ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
70 
71     auto pf = AsmEmitter::Emit(res.Value());
72     ASSERT_NE(pf, nullptr);
73 
74     // Check _GLOBAL class
75 
76     {
77         std::string descriptor;
78         auto classId = pf->GetClassId(GetTypeDescriptor("_GLOBAL", &descriptor));
79         ASSERT_TRUE(classId.IsValid());
80         ASSERT_FALSE(pf->IsExternal(classId));
81 
82         panda_file::ClassDataAccessor cda(*pf, classId);
83         ASSERT_EQ(cda.GetSuperClassId().GetOffset(), 0U);
84         ASSERT_EQ(cda.GetAccessFlags(), ACC_PUBLIC);
85         ASSERT_EQ(cda.GetFieldsNumber(), 0U);
86         ASSERT_EQ(cda.GetMethodsNumber(), 1U);
87         ASSERT_EQ(cda.GetIfacesNumber(), 0U);
88 
89         ASSERT_FALSE(cda.GetSourceFileId().has_value());
90 
91         cda.EnumerateRuntimeAnnotations([](panda_file::File::EntityId) { ASSERT_TRUE(false); });
92 
93         cda.EnumerateAnnotations([](panda_file::File::EntityId) { ASSERT_TRUE(false); });
94 
95         cda.EnumerateFields([](panda_file::FieldDataAccessor &) { ASSERT_TRUE(false); });
96 
97         cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
98             ASSERT_FALSE(mda.IsExternal());
99             ASSERT_EQ(mda.GetClassId(), classId);
100             ASSERT_EQ(utf::CompareMUtf8ToMUtf8(pf->GetStringData(mda.GetNameId()).data, utf::CStringAsMutf8("main")),
101                       0);
102 
103             panda_file::ProtoDataAccessor pda(*pf, mda.GetProtoId());
104             ASSERT_EQ(pda.GetNumArgs(), 0U);
105             ASSERT_EQ(pda.GetReturnType().GetId(), panda_file::Type::TypeId::VOID);
106 
107             ASSERT_EQ(mda.GetAccessFlags(), ACC_STATIC);
108             ASSERT_TRUE(mda.GetCodeId().has_value());
109 
110             panda_file::CodeDataAccessor cdacc(*pf, mda.GetCodeId().value());
111             ASSERT_EQ(cdacc.GetNumVregs(), 0U);
112             ASSERT_EQ(cdacc.GetNumArgs(), 0U);
113             ASSERT_EQ(cdacc.GetCodeSize(), 1U);
114             ASSERT_EQ(cdacc.GetTriesSize(), 0U);
115 
116             ASSERT_FALSE(mda.GetRuntimeParamAnnotationId().has_value());
117             ASSERT_FALSE(mda.GetParamAnnotationId().has_value());
118             ASSERT_TRUE(mda.GetDebugInfoId().has_value());
119 
120             panda_file::DebugInfoDataAccessor dda(*pf, mda.GetDebugInfoId().value());
121             ASSERT_EQ(dda.GetLineStart(), 8U);
122             ASSERT_EQ(dda.GetNumParams(), 0U);
123 
124             mda.EnumerateRuntimeAnnotations([](panda_file::File::EntityId) { ASSERT_TRUE(false); });
125             mda.EnumerateAnnotations([](panda_file::File::EntityId) { ASSERT_TRUE(false); });
126         });
127     }
128 
129     // Check R class
130 
131     {
132         std::string descriptor;
133         auto classId = pf->GetClassId(GetTypeDescriptor("R", &descriptor));
134         ASSERT_TRUE(classId.IsValid());
135         ASSERT_FALSE(pf->IsExternal(classId));
136 
137         panda_file::ClassDataAccessor cda(*pf, classId);
138         ASSERT_EQ(cda.GetSuperClassId().GetOffset(), 0U);
139         ASSERT_EQ(cda.GetAccessFlags(), 0);
140         ASSERT_EQ(cda.GetFieldsNumber(), 2U);
141         ASSERT_EQ(cda.GetMethodsNumber(), 0U);
142         ASSERT_EQ(cda.GetIfacesNumber(), 0U);
143 
144         // We emit SET_FILE in debuginfo
145         ASSERT_TRUE(cda.GetSourceFileId().has_value());
146 
147         EXPECT_EQ(std::string(reinterpret_cast<const char *>(pf->GetStringData(cda.GetSourceFileId().value()).data)),
148                   sourceFilename);
149 
150         cda.EnumerateRuntimeAnnotations([](panda_file::File::EntityId) { ASSERT_TRUE(false); });
151 
152         cda.EnumerateAnnotations([](panda_file::File::EntityId) { ASSERT_TRUE(false); });
153 
154         struct FieldData {
155             std::string name;
156             panda_file::Type::TypeId typeId;
157             uint32_t accessFlags;
158         };
159 
160         std::vector<FieldData> fields {{"sf", panda_file::Type::TypeId::I32, ACC_STATIC},
161                                        {"if", panda_file::Type::TypeId::I8, 0}};
162 
163         size_t i = 0;
164         cda.EnumerateFields([&](panda_file::FieldDataAccessor &fda) {
165             ASSERT_FALSE(fda.IsExternal());
166             ASSERT_EQ(fda.GetClassId(), classId);
167             ASSERT_EQ(utf::CompareMUtf8ToMUtf8(pf->GetStringData(fda.GetNameId()).data,
168                                                utf::CStringAsMutf8(fields[i].name.c_str())),
169                       0);
170 
171             ASSERT_EQ(fda.GetType(), panda_file::Type(fields[i].typeId).GetFieldEncoding());
172             ASSERT_EQ(fda.GetAccessFlags(), fields[i].accessFlags);
173 
174             fda.EnumerateRuntimeAnnotations([](panda_file::File::EntityId) { ASSERT_TRUE(false); });
175             fda.EnumerateAnnotations([](panda_file::File::EntityId) { ASSERT_TRUE(false); });
176 
177             ++i;
178         });
179 
180         cda.EnumerateMethods([&](panda_file::MethodDataAccessor &) { ASSERT_TRUE(false); });
181     }
182 }
183 
GetSpecialOpcode(uint32_t pcInc,int32_t lineInc)184 uint8_t GetSpecialOpcode(uint32_t pcInc, int32_t lineInc)
185 {
186     return (lineInc - panda_file::LineNumberProgramItem::LINE_BASE) +
187            (pcInc * panda_file::LineNumberProgramItem::LINE_RANGE) + panda_file::LineNumberProgramItem::OPCODE_BASE;
188 }
189 
TEST(emittertests,debuginfo)190 TEST(emittertests, debuginfo)
191 {
192     Parser p;
193 
194     auto source = R"(
195         .function void main() {
196             ldai.64 0   # line 3, pc 0
197                         # line 4
198                         # line 5
199                         # line 6
200                         # line 7
201                         # line 8
202                         # line 9
203                         # line 10
204                         # line 11
205                         # line 12
206                         # line 13
207                         # line 14
208             ldai.64 1   # line 15, pc 9
209             return.void # line 16, pc 18
210         }
211     )";
212 
213     std::string sourceFilename = "source.pa";
214     auto res = p.Parse(source, sourceFilename);
215     ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
216 
217     auto pf = AsmEmitter::Emit(res.Value());
218     ASSERT_NE(pf, nullptr);
219 
220     std::string descriptor;
221     auto classId = pf->GetClassId(GetTypeDescriptor("_GLOBAL", &descriptor));
222     ASSERT_TRUE(classId.IsValid());
223 
224     panda_file::ClassDataAccessor cda(*pf, classId);
225 
226     cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
227         panda_file::CodeDataAccessor cdacc(*pf, mda.GetCodeId().value());
228         ASSERT_TRUE(mda.GetDebugInfoId().has_value());
229 
230         panda_file::DebugInfoDataAccessor dda(*pf, mda.GetDebugInfoId().value());
231         ASSERT_EQ(dda.GetLineStart(), 3U);
232         ASSERT_EQ(dda.GetNumParams(), 0U);
233 
234         const uint8_t *program = dda.GetLineNumberProgram();
235         Span<const uint8_t> constantPool = dda.GetConstantPool();
236 
237         std::vector<uint8_t> opcodes {
238             static_cast<uint8_t>(panda_file::LineNumberProgramItem::Opcode::SET_FILE),
239             static_cast<uint8_t>(panda_file::LineNumberProgramItem::Opcode::ADVANCE_PC),
240             static_cast<uint8_t>(panda_file::LineNumberProgramItem::Opcode::ADVANCE_LINE), GetSpecialOpcode(0, 0),
241             // NOLINTNEXTLINE(readability-magic-numbers)
242             GetSpecialOpcode(9, 1), static_cast<uint8_t>(panda_file::LineNumberProgramItem::Opcode::END_SEQUENCE)};
243 
244         EXPECT_THAT(opcodes, ::testing::ElementsAreArray(program, opcodes.size()));
245 
246         size_t size {};
247         bool isFull {};
248         size_t constantPoolOffset = 0;
249 
250         uint32_t offset {};
251 
252         std::tie(offset, size, isFull) = leb128::DecodeUnsigned<uint32_t>(&constantPool[constantPoolOffset]);
253         constantPoolOffset += size;
254         ASSERT_TRUE(isFull);
255         EXPECT_EQ(
256             std::string(reinterpret_cast<const char *>(pf->GetStringData(panda_file::File::EntityId(offset)).data)),
257             sourceFilename);
258 
259         uint32_t pcInc;
260         std::tie(pcInc, size, isFull) = leb128::DecodeUnsigned<uint32_t>(&constantPool[constantPoolOffset]);
261         constantPoolOffset += size;
262         ASSERT_TRUE(isFull);
263         EXPECT_EQ(pcInc, 9U);
264 
265         int32_t lineInc;
266         std::tie(lineInc, size, isFull) = leb128::DecodeSigned<int32_t>(&constantPool[constantPoolOffset]);
267         constantPoolOffset += size;
268         ASSERT_TRUE(isFull);
269         EXPECT_EQ(lineInc, 12);
270 
271         EXPECT_EQ(constantPoolOffset, constantPool.size());
272     });
273 }
274 
TEST(emittertests,exceptions)275 TEST(emittertests, exceptions)
276 {
277     Parser p;
278 
279     auto source = R"(
280         .record Exception1 {}
281         .record Exception2 {}
282 
283         .function void main() {
284             ldai.64 0
285         try_begin:
286             ldai.64 1
287             ldai.64 2
288         try_end:
289             ldai.64 3
290         catch_begin1:
291             ldai.64 4
292         catch_begin2:
293             ldai.64 5
294         catchall_begin:
295             ldai.64 6
296 
297         .catch Exception1, try_begin, try_end, catch_begin1
298         .catch Exception2, try_begin, try_end, catch_begin2
299         .catchall try_begin, try_end, catchall_begin
300         }
301     )";
302 
303     auto res = p.Parse(source);
304     ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
305 
306     auto pf = AsmEmitter::Emit(res.Value());
307     ASSERT_NE(pf, nullptr);
308 
309     std::string descriptor;
310 
311     auto classId = pf->GetClassId(GetTypeDescriptor("_GLOBAL", &descriptor));
312     ASSERT_TRUE(classId.IsValid());
313 
314     panda_file::ClassDataAccessor cda(*pf, classId);
315 
316     cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
317         panda_file::CodeDataAccessor cdacc(*pf, mda.GetCodeId().value());
318         ASSERT_EQ(cdacc.GetNumVregs(), 0U);
319         ASSERT_EQ(cdacc.GetNumArgs(), 0U);
320         ASSERT_EQ(cdacc.GetTriesSize(), 1);
321 
322         cdacc.EnumerateTryBlocks([&](panda_file::CodeDataAccessor::TryBlock &tryBlock) {
323             EXPECT_EQ(tryBlock.GetStartPc(), 9);
324             EXPECT_EQ(tryBlock.GetLength(), 18);
325             EXPECT_EQ(tryBlock.GetNumCatches(), 3);
326 
327             struct CatchInfo {
328                 panda_file::File::EntityId typeId;
329                 uint32_t handlerPc;
330             };
331 
332             // NOLINTBEGIN(readability-magic-numbers)
333             std::vector<CatchInfo> catchInfos {{pf->GetClassId(GetTypeDescriptor("Exception1", &descriptor)), 4 * 9},
334                                                {pf->GetClassId(GetTypeDescriptor("Exception2", &descriptor)), 5 * 9},
335                                                {panda_file::File::EntityId(), 6 * 9}};
336             // NOLINTEND(readability-magic-numbers)
337 
338             size_t i = 0;
339             tryBlock.EnumerateCatchBlocks([&](panda_file::CodeDataAccessor::CatchBlock &catchBlock) {
340                 auto idx = catchBlock.GetTypeIdx();
341                 auto id = idx != panda_file::INVALID_INDEX ? pf->ResolveClassIndex(mda.GetMethodId(), idx)
342                                                            : panda_file::File::EntityId();
343                 EXPECT_EQ(id, catchInfos[i].typeId);
344                 EXPECT_EQ(catchBlock.GetHandlerPc(), catchInfos[i].handlerPc);
345                 ++i;
346 
347                 return true;
348             });
349 
350             return true;
351         });
352     });
353 }
354 
TEST(emittertests,errors)355 TEST(emittertests, errors)
356 {
357     {
358         Parser p;
359         auto source = R"(
360             .record A {
361                 B b
362             }
363         )";
364 
365         auto res = p.Parse(source);
366         ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
367 
368         auto pf = AsmEmitter::Emit(res.Value());
369         ASSERT_EQ(pf, nullptr);
370         ASSERT_EQ(AsmEmitter::GetLastError(), "Field A.b has undefined type");
371     }
372 
373     {
374         Parser p;
375         auto source = R"(
376             .function void A.b() {}
377         )";
378 
379         auto res = p.Parse(source);
380         ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
381 
382         auto pf = AsmEmitter::Emit(res.Value());
383         ASSERT_EQ(pf, nullptr);
384         ASSERT_EQ(AsmEmitter::GetLastError(), "Function A.b is bound to undefined record A");
385     }
386 
387     {
388         Parser p;
389         auto source = R"(
390             .function A b() {}
391         )";
392 
393         auto res = p.Parse(source);
394         ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
395 
396         auto pf = AsmEmitter::Emit(res.Value());
397         ASSERT_EQ(pf, nullptr);
398         ASSERT_EQ(AsmEmitter::GetLastError(), "Function b has undefined return type");
399     }
400 
401     {
402         Parser p;
403         auto source = R"(
404             .function void a(b a0) {}
405         )";
406 
407         auto res = p.Parse(source);
408         ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
409 
410         auto pf = AsmEmitter::Emit(res.Value());
411         ASSERT_EQ(pf, nullptr);
412         ASSERT_EQ(AsmEmitter::GetLastError(), "Argument 0 of function a has undefined type");
413     }
414 
415     {
416         Parser p;
417         auto source = R"(
418             .record A <external>
419             .function void A.x() {}
420         )";
421 
422         auto res = p.Parse(source);
423         ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
424 
425         auto pf = AsmEmitter::Emit(res.Value());
426         ASSERT_EQ(pf, nullptr);
427         ASSERT_EQ(AsmEmitter::GetLastError(), "Non-external function A.x is bound to external record");
428     }
429 }
430 
TEST(emittertests,language)431 TEST(emittertests, language)
432 {
433     {
434         Parser p;
435         auto source = R"(
436             .function void foo() {}
437         )";
438 
439         auto res = p.Parse(source);
440         ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
441 
442         auto pf = AsmEmitter::Emit(res.Value());
443         ASSERT_NE(pf, nullptr);
444 
445         std::string descriptor;
446 
447         auto classId = pf->GetClassId(GetTypeDescriptor("_GLOBAL", &descriptor));
448         ASSERT_TRUE(classId.IsValid());
449 
450         panda_file::ClassDataAccessor cda(*pf, classId);
451 
452         ASSERT_FALSE(cda.GetSourceLang());
453 
454         cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) { ASSERT_FALSE(mda.GetSourceLang()); });
455     }
456 }
457 
TEST(emittertests,constructors)458 TEST(emittertests, constructors)
459 {
460     {
461         Parser p;
462         auto source = R"(
463             .record R {}
464             .function void R.foo(R a0) <ctor> {}
465         )";
466 
467         auto res = p.Parse(source);
468         ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
469 
470         auto pf = AsmEmitter::Emit(res.Value());
471         ASSERT_NE(pf, nullptr);
472 
473         std::string descriptor;
474 
475         auto classId = pf->GetClassId(GetTypeDescriptor("R", &descriptor));
476         ASSERT_TRUE(classId.IsValid());
477 
478         panda_file::ClassDataAccessor cda(*pf, classId);
479 
480         cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
481             auto *name = utf::Mutf8AsCString(pf->GetStringData(mda.GetNameId()).data);
482             ASSERT_STREQ(name, ".ctor");
483         });
484     }
485 
486     {
487         Parser p;
488         auto source = R"(
489             .record R {}
490             .function void R.foo(R a0) <cctor> {}
491         )";
492 
493         auto res = p.Parse(source);
494         ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
495 
496         auto pf = AsmEmitter::Emit(res.Value());
497         ASSERT_NE(pf, nullptr);
498 
499         std::string descriptor;
500 
501         auto classId = pf->GetClassId(GetTypeDescriptor("R", &descriptor));
502         ASSERT_TRUE(classId.IsValid());
503 
504         panda_file::ClassDataAccessor cda(*pf, classId);
505 
506         cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
507             auto *name = utf::Mutf8AsCString(pf->GetStringData(mda.GetNameId()).data);
508             ASSERT_STREQ(name, ".cctor");
509         });
510     }
511 }
512 
TEST(emittertests,field_value)513 TEST(emittertests, field_value)
514 {
515     Parser p;
516 
517     auto source = R"(
518         .record panda.String <external>
519 
520         .record R {
521             u1 f_u1 <value=1>
522             i8 f_i8 <value=2>
523             u8 f_u8 <value=128>
524             i16 f_i16 <value=256>
525             u16 f_u16 <value=32768>
526             i32 f_i32 <value=65536>
527             u32 f_u32 <value=2147483648>
528             i64 f_i64 <value=4294967296>
529             u64 f_u64 <value=9223372036854775808>
530             f32 f_f32 <value=1.0>
531             f64 f_f64 <value=2.0>
532             panda.String f_str <value="str">
533         }
534     )";
535 
536     struct FieldData {
537         std::string name;
538         panda_file::Type::TypeId typeId;
539         std::variant<int32_t, uint32_t, int64_t, uint64_t, float, double, std::string> value;
540     };
541 
542     // NOLINTBEGIN(readability-magic-numbers)
543     std::vector<FieldData> data {
544         {"f_u1", panda_file::Type::TypeId::U1, static_cast<uint32_t>(1)},
545         {"f_i8", panda_file::Type::TypeId::I8, static_cast<int32_t>(2)},
546         {"f_u8", panda_file::Type::TypeId::U8, static_cast<uint32_t>(128)},
547         {"f_i16", panda_file::Type::TypeId::I16, static_cast<int32_t>(256)},
548         {"f_u16", panda_file::Type::TypeId::U16, static_cast<uint32_t>(32768)},
549         {"f_i32", panda_file::Type::TypeId::I32, static_cast<int32_t>(65536)},
550         {"f_u32", panda_file::Type::TypeId::U32, static_cast<uint32_t>(2147483648)},
551         {"f_i64", panda_file::Type::TypeId::I64, static_cast<int64_t>(4294967296)},
552         {"f_u64", panda_file::Type::TypeId::U64, static_cast<uint64_t>(9223372036854775808ULL)},
553         {"f_f32", panda_file::Type::TypeId::F32, static_cast<float>(1.0)},
554         {"f_f64", panda_file::Type::TypeId::F64, static_cast<double>(2.0)},
555         {"f_str", panda_file::Type::TypeId::REFERENCE, "str"}};
556     // NOLINTEND(readability-magic-numbers)
557 
558     auto res = p.Parse(source);
559     ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
560 
561     auto pf = AsmEmitter::Emit(res.Value());
562     ASSERT_NE(pf, nullptr);
563 
564     std::string descriptor;
565     auto classId = pf->GetClassId(GetTypeDescriptor("R", &descriptor));
566     ASSERT_TRUE(classId.IsValid());
567     ASSERT_FALSE(pf->IsExternal(classId));
568 
569     panda_file::ClassDataAccessor cda(*pf, classId);
570     ASSERT_EQ(cda.GetFieldsNumber(), data.size());
571 
572     auto pandaStringId = pf->GetClassId(GetTypeDescriptor("panda.String", &descriptor));
573 
574     size_t idx = 0;
575     cda.EnumerateFields([&](panda_file::FieldDataAccessor &fda) {
576         const FieldData &fieldData = data[idx];
577 
578         ASSERT_EQ(utf::CompareMUtf8ToMUtf8(pf->GetStringData(fda.GetNameId()).data,
579                                            utf::CStringAsMutf8(fieldData.name.c_str())),
580                   0);
581 
582         panda_file::Type type(fieldData.typeId);
583         uint32_t typeValue;
584         if (type.IsReference()) {
585             typeValue = pandaStringId.GetOffset();
586         } else {
587             typeValue = type.GetFieldEncoding();
588         }
589 
590         ASSERT_EQ(fda.GetType(), typeValue);
591 
592         switch (fieldData.typeId) {
593             case panda_file::Type::TypeId::U1: {
594                 auto result = fda.GetValue<uint8_t>();
595                 ASSERT_TRUE(result);
596                 ASSERT_EQ(result.value(), std::get<uint32_t>(fieldData.value));
597                 break;
598             }
599             case panda_file::Type::TypeId::I8: {
600                 auto result = fda.GetValue<int8_t>();
601                 ASSERT_TRUE(result);
602                 ASSERT_EQ(result.value(), std::get<int32_t>(fieldData.value));
603                 break;
604             }
605             case panda_file::Type::TypeId::U8: {
606                 auto result = fda.GetValue<uint8_t>();
607                 ASSERT_TRUE(result);
608                 ASSERT_EQ(result.value(), std::get<uint32_t>(fieldData.value));
609                 break;
610             }
611             case panda_file::Type::TypeId::I16: {
612                 auto result = fda.GetValue<int16_t>();
613                 ASSERT_TRUE(result);
614                 ASSERT_EQ(result.value(), std::get<int32_t>(fieldData.value));
615                 break;
616             }
617             case panda_file::Type::TypeId::U16: {
618                 auto result = fda.GetValue<uint16_t>();
619                 ASSERT_TRUE(result);
620                 ASSERT_EQ(result.value(), std::get<uint32_t>(fieldData.value));
621                 break;
622             }
623             case panda_file::Type::TypeId::I32: {
624                 auto result = fda.GetValue<int32_t>();
625                 ASSERT_TRUE(result);
626                 ASSERT_EQ(result.value(), std::get<int32_t>(fieldData.value));
627                 break;
628             }
629             case panda_file::Type::TypeId::U32: {
630                 auto result = fda.GetValue<uint32_t>();
631                 ASSERT_TRUE(result);
632                 ASSERT_EQ(result.value(), std::get<uint32_t>(fieldData.value));
633                 break;
634             }
635             case panda_file::Type::TypeId::I64: {
636                 auto result = fda.GetValue<int64_t>();
637                 ASSERT_TRUE(result);
638                 ASSERT_EQ(result.value(), std::get<int64_t>(fieldData.value));
639                 break;
640             }
641             case panda_file::Type::TypeId::U64: {
642                 auto result = fda.GetValue<uint64_t>();
643                 ASSERT_TRUE(result);
644                 ASSERT_EQ(result.value(), std::get<uint64_t>(fieldData.value));
645                 break;
646             }
647             case panda_file::Type::TypeId::F32: {
648                 auto result = fda.GetValue<float>();
649                 ASSERT_TRUE(result);
650                 ASSERT_EQ(result.value(), std::get<float>(fieldData.value));
651                 break;
652             }
653             case panda_file::Type::TypeId::F64: {
654                 auto result = fda.GetValue<double>();
655                 ASSERT_TRUE(result);
656                 ASSERT_EQ(result.value(), std::get<double>(fieldData.value));
657                 break;
658             }
659             case panda_file::Type::TypeId::REFERENCE: {
660                 auto result = fda.GetValue<uint32_t>();
661                 ASSERT_TRUE(result);
662 
663                 panda_file::File::EntityId stringId(result.value());
664                 auto val = std::get<std::string>(fieldData.value);
665 
666                 ASSERT_EQ(utf::CompareMUtf8ToMUtf8(pf->GetStringData(stringId).data, utf::CStringAsMutf8(val.c_str())),
667                           0);
668                 break;
669             }
670             default: {
671                 UNREACHABLE();
672                 break;
673             }
674         }
675 
676         ++idx;
677     });
678 }
679 
TEST(emittertests,tagged_in_func_decl)680 TEST(emittertests, tagged_in_func_decl)
681 {
682     Parser p;
683     auto source = R"(
684         .function any foo(any a0) <noimpl>
685     )";
686 
687     auto res = p.Parse(source);
688     ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
689 
690     auto pf = AsmEmitter::Emit(res.Value());
691     ASSERT_NE(pf, nullptr);
692 
693     std::string descriptor;
694 
695     auto classId = pf->GetClassId(GetTypeDescriptor("_GLOBAL", &descriptor));
696     ASSERT_TRUE(classId.IsValid());
697 
698     panda_file::ClassDataAccessor cda(*pf, classId);
699 
700     size_t numMethods = 0;
701     const auto tagged = panda_file::Type(panda_file::Type::TypeId::TAGGED);
702     cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
703         panda_file::ProtoDataAccessor pda(*pf, mda.GetProtoId());
704         ASSERT_EQ(tagged, pda.GetReturnType());
705         ASSERT_EQ(1, pda.GetNumArgs());
706         ASSERT_EQ(tagged, pda.GetArgType(0));
707 
708         ++numMethods;
709     });
710     ASSERT_EQ(1, numMethods);
711 }
712 
TEST(emittertests,tagged_in_field_decl)713 TEST(emittertests, tagged_in_field_decl)
714 {
715     Parser p;
716     auto source = R"(
717         .record Test {
718             any foo
719         }
720     )";
721 
722     auto res = p.Parse(source);
723     ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
724 
725     auto pf = AsmEmitter::Emit(res.Value());
726     ASSERT_NE(pf, nullptr);
727 
728     std::string descriptor;
729 
730     auto classId = pf->GetClassId(GetTypeDescriptor("Test", &descriptor));
731     ASSERT_TRUE(classId.IsValid());
732 
733     panda_file::ClassDataAccessor cda(*pf, classId);
734 
735     size_t numFields = 0;
736     const auto tagged = panda_file::Type(panda_file::Type::TypeId::TAGGED);
737     cda.EnumerateFields([&](panda_file::FieldDataAccessor &fda) {
738         uint32_t type = fda.GetType();
739         ASSERT_EQ(tagged.GetFieldEncoding(), type);
740 
741         ++numFields;
742     });
743     ASSERT_EQ(1, numFields);
744 }
745 
TEST(emittertests,function_overloading_1)746 TEST(emittertests, function_overloading_1)
747 {
748     Parser p;
749     auto source = R"(
750         .function void foo(i8 a0) {}
751         .function u1 foo(u1 a0) {}
752 
753         .function i8 f(i32 a0) {
754             call foo:(i8), v0
755             call foo:(u1), v1
756         }
757     )";
758 
759     auto res = p.Parse(source);
760     ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
761 
762     auto pf = AsmEmitter::Emit(res.Value());
763     ASSERT_NE(pf, nullptr);
764 
765     std::string descriptor {};
766 
767     auto classId = pf->GetClassId(GetTypeDescriptor("_GLOBAL", &descriptor));
768     ASSERT_TRUE(classId.IsValid());
769 
770     panda_file::ClassDataAccessor cda(*pf, classId);
771 
772     size_t numMethods = 0;
773 
774     std::unordered_map<panda_file::File::EntityId, panda_file::Type> idToArgType {};
775     panda_file::File::EntityId idF {};
776 
777     cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
778         ++numMethods;
779 
780         panda_file::ProtoDataAccessor pda(*pf, mda.GetProtoId());
781 
782         if (pda.GetArgType(0) == panda_file::Type(panda_file::Type::TypeId::I32)) {
783             idF = mda.GetMethodId();
784 
785             return;
786         }
787 
788         idToArgType.emplace(mda.GetMethodId(), pda.GetArgType(0));
789     });
790 
791     panda_file::MethodDataAccessor mdaF(*pf, idF);
792     panda_file::CodeDataAccessor cdaF(*pf, mdaF.GetCodeId().value());
793 
794     const auto insSz = cdaF.GetCodeSize();
795     const auto insArr = cdaF.GetInstructions();
796 
797     auto bcIns = BytecodeInstruction(insArr);
798     const auto bcInsLast = bcIns.JumpTo(insSz);
799 
800     while (bcIns.GetAddress() != bcInsLast.GetAddress()) {
801         const auto argMethodIdx = bcIns.GetId().AsIndex();
802         const auto argMethodId = pf->ResolveMethodIndex(idF, argMethodIdx);
803 
804         panda_file::MethodDataAccessor methodAccessor(*pf, argMethodId);
805         panda_file::ProtoDataAccessor protoAccessor(*pf, methodAccessor.GetProtoId());
806 
807         ASSERT_EQ(protoAccessor.GetArgType(0), idToArgType.at(argMethodId));
808         ASSERT_EQ(std::string(utf::Mutf8AsCString(pf->GetStringData(methodAccessor.GetNameId()).data)), "foo");
809 
810         bcIns = bcIns.GetNext();
811     }
812 
813     ASSERT_EQ(3, numMethods);
814 }
815 
TEST(emittertests,access_modifiers)816 TEST(emittertests, access_modifiers)
817 {
818     Parser p;
819 
820     auto source = R"(
821         .record A <access.record=private> {}
822 
823         .record B <access.record=public> {
824             i32 pub <access.field=public>
825             i32 prt <access.field=protected>
826             i32 prv <access.field=private>
827         }
828 
829         .record C <access.record=protected> {}
830 
831         .function void f() <access.function=public> {}
832         .function void A.f() <access.function=protected> {}
833         .function void B.f() <access.function=private> {}
834         .function void C.f() <access.function=public> {}
835     )";
836 
837     std::string sourceFilename = "source.pa";
838     auto res = p.Parse(source, sourceFilename);
839     ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
840 
841     auto pf = AsmEmitter::Emit(res.Value());
842     ASSERT_NE(pf, nullptr);
843 
844     // Global
845     {
846         std::string descriptor;
847         auto classId = pf->GetClassId(GetTypeDescriptor("_GLOBAL", &descriptor));
848 
849         panda_file::ClassDataAccessor cda(*pf, classId);
850 
851         ASSERT_TRUE(cda.GetMethodsNumber() == 1);
852         cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
853             ASSERT_EQ(utf::CompareMUtf8ToMUtf8(pf->GetStringData(mda.GetNameId()).data, utf::CStringAsMutf8("f")), 0);
854 
855             ASSERT_TRUE(mda.IsPublic());
856         });
857     }
858 
859     // record A
860     {
861         std::string descriptor;
862         auto classId = pf->GetClassId(GetTypeDescriptor("A", &descriptor));
863 
864         panda_file::ClassDataAccessor cda(*pf, classId);
865 
866         ASSERT_TRUE(cda.IsPrivate());
867         ASSERT_TRUE(cda.GetMethodsNumber() == 1);
868         cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
869             ASSERT_EQ(utf::CompareMUtf8ToMUtf8(pf->GetStringData(mda.GetNameId()).data, utf::CStringAsMutf8("f")), 0);
870 
871             ASSERT_TRUE(mda.IsProtected());
872         });
873     }
874 
875     // record B
876     {
877         std::string descriptor;
878         auto classId = pf->GetClassId(GetTypeDescriptor("B", &descriptor));
879 
880         panda_file::ClassDataAccessor cda(*pf, classId);
881 
882         ASSERT_TRUE(cda.IsPublic());
883         ASSERT_TRUE(cda.GetFieldsNumber() == 3);
884         auto i = 0;
885         auto accessPredicates =
886             std::array {&panda_file::FieldDataAccessor::IsPublic, &panda_file::FieldDataAccessor::IsProtected,
887                         &panda_file::FieldDataAccessor::IsPrivate};
888         cda.EnumerateFields([&](panda_file::FieldDataAccessor &fda) {
889             EXPECT_TRUE((fda.*accessPredicates[i])());
890 
891             ++i;
892         });
893 
894         ASSERT_TRUE(cda.GetMethodsNumber() == 1);
895         cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
896             ASSERT_EQ(utf::CompareMUtf8ToMUtf8(pf->GetStringData(mda.GetNameId()).data, utf::CStringAsMutf8("f")), 0);
897 
898             ASSERT_TRUE(mda.IsPrivate());
899         });
900     }
901 
902     // record C
903     {
904         std::string descriptor;
905         auto classId = pf->GetClassId(GetTypeDescriptor("C", &descriptor));
906 
907         panda_file::ClassDataAccessor cda(*pf, classId);
908 
909         ASSERT_TRUE(cda.IsProtected());
910         ASSERT_TRUE(cda.GetMethodsNumber() == 1);
911         cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
912             ASSERT_EQ(utf::CompareMUtf8ToMUtf8(pf->GetStringData(mda.GetNameId()).data, utf::CStringAsMutf8("f")), 0);
913 
914             ASSERT_TRUE(mda.IsPublic());
915         });
916     }
917 }
918 
TEST(emittertests,valid_inheritance)919 TEST(emittertests, valid_inheritance)
920 {
921     Parser p;
922 
923     auto source = R"(
924         .record A {}
925         .record B <extends=A> {}
926     )";
927 
928     std::string sourceFilename = "source.pa";
929     auto res = p.Parse(source, sourceFilename);
930     ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
931 
932     auto pf = AsmEmitter::Emit(res.Value());
933     ASSERT_NE(pf, nullptr);
934 }
935 
TEST(emittertests,inheritance_undefined_record)936 TEST(emittertests, inheritance_undefined_record)
937 {
938     Parser p;
939 
940     auto source = R"(
941         .record A <extends=B> {}
942     )";
943 
944     std::string sourceFilename = "source.pa";
945     auto res = p.Parse(source, sourceFilename);
946     ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
947 
948     auto pf = AsmEmitter::Emit(res.Value());
949     ASSERT_EQ(pf, nullptr);
950     ASSERT_EQ(AsmEmitter::GetLastError(), "Base record B is not defined for record A");
951 }
952 
TEST(emittertests,primitive_inheritance)953 TEST(emittertests, primitive_inheritance)
954 {
955     Parser p;
956 
957     auto source = R"(
958         .record A <extends=u8> {}
959     )";
960 
961     std::string sourceFilename = "source.pa";
962     auto res = p.Parse(source, sourceFilename);
963     ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
964 
965     auto pf = AsmEmitter::Emit(res.Value());
966     ASSERT_EQ(pf, nullptr);
967 }
968 
TEST(emittertests,final_modifier)969 TEST(emittertests, final_modifier)
970 {
971     Parser p;
972 
973     auto source = R"(
974         .record A <final> {}
975 
976         .record B {
977             i32 fld <final>
978         }
979 
980         .record C {}
981 
982         .function void f0() <final> {}
983         .function void C.f1() <final> {}
984         .function void C.f2(C a0) <final> {}
985         .function void C.f3(i32 a0) <final> {}
986     )";
987 
988     std::string sourceFilename = "source.pa";
989     auto res = p.Parse(source, sourceFilename);
990     ASSERT_EQ(p.ShowError().err, Error::ErrorType::ERR_NONE);
991 
992     auto pf = AsmEmitter::Emit(res.Value());
993     ASSERT_NE(pf, nullptr);
994 
995     // Global
996     {
997         std::string descriptor;
998         auto classId = pf->GetClassId(GetTypeDescriptor("_GLOBAL", &descriptor));
999 
1000         panda_file::ClassDataAccessor cda(*pf, classId);
1001 
1002         ASSERT_TRUE(cda.GetMethodsNumber() == 1);
1003         cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
1004             ASSERT_EQ(utf::CompareMUtf8ToMUtf8(pf->GetStringData(mda.GetNameId()).data, utf::CStringAsMutf8("f0")), 0);
1005 
1006             ASSERT_TRUE(mda.IsFinal());
1007         });
1008     }
1009 
1010     // record A
1011     {
1012         std::string descriptor;
1013         auto classId = pf->GetClassId(GetTypeDescriptor("A", &descriptor));
1014 
1015         panda_file::ClassDataAccessor cda(*pf, classId);
1016 
1017         ASSERT_TRUE(cda.IsFinal());
1018     }
1019 
1020     // record B
1021     {
1022         std::string descriptor;
1023         auto classId = pf->GetClassId(GetTypeDescriptor("B", &descriptor));
1024 
1025         panda_file::ClassDataAccessor cda(*pf, classId);
1026 
1027         ASSERT_TRUE(cda.GetFieldsNumber() == 1);
1028         cda.EnumerateFields([&](panda_file::FieldDataAccessor &fda) { ASSERT_TRUE(fda.IsFinal()); });
1029     }
1030 
1031     // record C
1032     {
1033         std::string descriptor;
1034         auto classId = pf->GetClassId(GetTypeDescriptor("C", &descriptor));
1035 
1036         panda_file::ClassDataAccessor cda(*pf, classId);
1037 
1038         ASSERT_TRUE(cda.GetMethodsNumber() == 3);
1039         cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) { ASSERT_TRUE(mda.IsFinal()); });
1040     }
1041 }
1042 
1043 }  // namespace panda::test