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