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