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