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