1 /**
2 * Copyright (c) 2021-2025 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 <gtest/gtest.h>
17
18 #include <vector>
19
20 #include "assembly-parser.h"
21 #include "mangling.h"
22 #include "libpandafile/value.h"
23 #include "libpandabase/utils/utils.h"
24 #include "runtime/entrypoints/entrypoints.h"
25 #include "runtime/include/compiler_interface.h"
26 #include "runtime/include/method-inl.h"
27 #include "runtime/include/runtime.h"
28
29 namespace ark::test {
30
31 class MethodTest : public testing::Test {
32 public:
MethodTest()33 MethodTest()
34 {
35 RuntimeOptions options;
36 // NOLINTNEXTLINE(readability-magic-numbers)
37 options.SetHeapSizeLimit(128_MB);
38 options.SetShouldLoadBootPandaFiles(false);
39 options.SetShouldInitializeIntrinsics(false);
40 options.SetGcType("epsilon");
41 Runtime::Create(options);
42 thread_ = ark::MTManagedThread::GetCurrent();
43 thread_->ManagedCodeBegin();
44 }
45
~MethodTest()46 ~MethodTest() override
47 {
48 thread_->ManagedCodeEnd();
49 Runtime::Destroy();
50 }
51
52 NO_COPY_SEMANTIC(MethodTest);
53 NO_MOVE_SEMANTIC(MethodTest);
54
VerifyLineNumber(const std::vector<int> & lines)55 void VerifyLineNumber(const std::vector<int> &lines)
56 {
57 pandasm::Parser p;
58 auto source = R"(
59 .function i32 foo() {
60 movi v0, 0x64 # offset 0x0, size 3
61 mov v1, v0 # offset 0x3, size 2
62 mod v0, v1 # offset 0x5, size 2
63 sta v0 # offset 0x7, size 2
64 mov v2, v0 # offset 0x9, size 2
65 mov v0, v1 # offset 0xb, size 2
66 sta v0 # offset 0xd, size 2
67 mov v2, v0 # offset 0xf, size 2
68 mov v0, v1 # offset 0x11, size 2
69 lda v0 # offset 0x13, size 2
70 return # offset 0x15, size 1
71 movi v0, 0x1 # offset 0x16, size 2
72 lda v0 # offset 0x18, size 2
73 return # offset 0x20, size 1
74 }
75 )";
76 // when verifying line numbers, we do not care about the exact instructions.
77 // we only care about the sequence of line numbers.
78 const std::vector<int> offsets {0x0, 0x3, 0x5, 0x7, 0x9, 0xb, 0xd, 0xf, 0x11, 0x13, 0x15, 0x16, 0x18, 0x20};
79 auto res = p.Parse(source);
80 auto &prog = res.Value();
81 const std::string name = pandasm::GetFunctionSignatureFromName("foo", {});
82 ASSERT_NE(prog.functionStaticTable.find(name), prog.functionStaticTable.end());
83 auto &insVec = prog.functionStaticTable.find(name)->second.ins;
84 const int insNum = insVec.size();
85 ASSERT_EQ(lines.size(), insNum);
86
87 for (int i = 0; i < insNum; i++) {
88 insVec[i].insDebug.SetLineNumber(lines[i]);
89 }
90
91 auto pf = pandasm::AsmEmitter::Emit(res.Value());
92 ASSERT_NE(pf, nullptr);
93
94 ClassLinker *classLinker = Runtime::GetCurrent()->GetClassLinker();
95 classLinker->AddPandaFile(std::move(pf));
96 auto *extension = classLinker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
97
98 PandaString descriptor;
99
100 Class *klass = extension->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("_GLOBAL"), &descriptor));
101 ASSERT_NE(klass, nullptr);
102
103 Method *method = klass->GetDirectMethod(utf::CStringAsMutf8("foo"));
104 ASSERT_NE(method, nullptr);
105
106 for (int i = 0; i < insNum; i++) {
107 ASSERT_EQ(method->GetLineNumFromBytecodeOffset(offsets[i]), lines[i]) << "do not match on i = " << i;
108 }
109 }
110
111 private:
112 ark::MTManagedThread *thread_;
113 };
114
115 template <bool IS_DYNAMIC = false>
CreateFrame(size_t nregs,Method * method,Frame * prev)116 static Frame *CreateFrame(size_t nregs, Method *method, Frame *prev)
117 {
118 return ark::CreateFrameWithSize(Frame::GetActualSize<IS_DYNAMIC>(nregs), nregs, method, prev);
119 }
120
TEST_F(MethodTest,SetIntrinsic)121 TEST_F(MethodTest, SetIntrinsic)
122 {
123 Method method(nullptr, nullptr, panda_file::File::EntityId(), panda_file::File::EntityId(), 0, 0, nullptr);
124 ASSERT_FALSE(method.IsIntrinsic());
125
126 auto intrinsic = intrinsics::Intrinsic::MATH_COS_F64;
127 method.SetIntrinsic(intrinsic);
128 ASSERT_TRUE(method.IsIntrinsic());
129
130 ASSERT_EQ(method.GetIntrinsic(), intrinsic);
131 }
132
EntryPoint(Method *)133 static int32_t EntryPoint(Method * /* unused */)
134 {
135 return 0;
136 }
137
138 static auto g_invokeSource = R"(
139 .function i32 g() {
140 ldai 0
141 return
142 }
143
144 .function i32 f() {
145 ldai 0
146 return
147 }
148
149 .function void main() {
150 call f
151 return.void
152 }
153 )";
154
TEST_F(MethodTest,Invoke)155 TEST_F(MethodTest, Invoke)
156 {
157 pandasm::Parser p;
158
159 auto source = g_invokeSource;
160
161 auto res = p.Parse(source);
162 auto pf = pandasm::AsmEmitter::Emit(res.Value());
163 ASSERT_NE(pf, nullptr);
164
165 ClassLinker *classLinker = Runtime::GetCurrent()->GetClassLinker();
166 classLinker->AddPandaFile(std::move(pf));
167 auto *extension = classLinker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
168
169 PandaString descriptor;
170
171 Class *klass = extension->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("_GLOBAL"), &descriptor));
172 ASSERT_NE(klass, nullptr);
173
174 Method *mainMethod = klass->GetDirectMethod(utf::CStringAsMutf8("main"));
175 ASSERT_NE(mainMethod, nullptr);
176
177 Method *fMethod = klass->GetDirectMethod(utf::CStringAsMutf8("f"));
178 ASSERT_NE(fMethod, nullptr);
179
180 Method *gMethod = klass->GetDirectMethod(utf::CStringAsMutf8("g"));
181 ASSERT_NE(gMethod, nullptr);
182
183 gMethod->SetCompiledEntryPoint(reinterpret_cast<const void *>(EntryPoint));
184
185 EXPECT_EQ(fMethod->GetHotnessCounter(),
186 Runtime::GetCurrent()->IsProfilerEnabled() ? 1500U : std::numeric_limits<int16_t>::max());
187
188 auto frameDeleter = [](Frame *frame) { FreeFrame(frame); };
189 std::unique_ptr<Frame, decltype(frameDeleter)> frame(CreateFrame(0, mainMethod, nullptr), frameDeleter);
190
191 ManagedThread *thread = ManagedThread::GetCurrent();
192 thread->SetCurrentFrame(frame.get());
193
194 // Invoke f calls interpreter
195
196 std::vector<Value> args;
197 Value v = fMethod->Invoke(ManagedThread::GetCurrent(), args.data());
198 EXPECT_EQ(v.GetAs<int64_t>(), 0);
199 EXPECT_EQ(fMethod->GetHotnessCounter(),
200 Runtime::GetCurrent()->IsProfilerEnabled() ? 1499U : std::numeric_limits<int16_t>::max() - 1);
201 EXPECT_EQ(ManagedThread::GetCurrent(), thread);
202
203 // Invoke f called compiled code
204
205 fMethod->SetCompiledEntryPoint(reinterpret_cast<const void *>(EntryPoint));
206
207 v = fMethod->Invoke(ManagedThread::GetCurrent(), args.data());
208 EXPECT_EQ(v.GetAs<int64_t>(), 0);
209 EXPECT_EQ(fMethod->GetHotnessCounter(),
210 Runtime::GetCurrent()->IsProfilerEnabled() ? 1498U : std::numeric_limits<int16_t>::max() - 2U);
211 EXPECT_EQ(ManagedThread::GetCurrent(), thread);
212 }
213
TEST_F(MethodTest,VirtualMethod)214 TEST_F(MethodTest, VirtualMethod)
215 {
216 pandasm::Parser p;
217
218 auto source = R"(
219 .record R {}
220
221 .function void R.foo(R a0, i32 a1) {
222 return
223 }
224 )";
225
226 auto res = p.Parse(source);
227 auto pf = pandasm::AsmEmitter::Emit(res.Value());
228 ASSERT_NE(pf, nullptr);
229
230 ClassLinker *classLinker = Runtime::GetCurrent()->GetClassLinker();
231 classLinker->AddPandaFile(std::move(pf));
232 auto *extension = classLinker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
233
234 PandaString descriptor;
235
236 Class *klass = extension->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("R"), &descriptor));
237 ASSERT_NE(klass, nullptr);
238
239 Method *method = klass->GetDirectMethod(utf::CStringAsMutf8("foo"));
240 ASSERT_NE(method, nullptr);
241
242 ASSERT_FALSE(method->IsStatic());
243 ASSERT_EQ(method->GetNumArgs(), 2U);
244 ASSERT_EQ(method->GetArgType(0).GetId(), panda_file::Type::TypeId::REFERENCE);
245 ASSERT_EQ(method->GetArgType(1).GetId(), panda_file::Type::TypeId::I32);
246 }
247
TEST_F(MethodTest,GetLineNumFromBytecodeOffset1)248 TEST_F(MethodTest, GetLineNumFromBytecodeOffset1)
249 {
250 pandasm::Parser p;
251
252 auto source = R"( # line 1
253 .function void foo() { # line 2
254 mov v0, v1 # line 3, offset 0, size 2
255 mov v100, v200 # line 4, offset 2, size 3
256 movi v0, 4 # line 5, offset 5, size 2
257 movi v0, 100 # line 6, offset 7, size 3
258 movi v0, 300 # line 7, offset 10, size 4
259 return.void # line 8, offset 14, size 1
260 }
261 )";
262
263 auto res = p.Parse(source);
264 auto pf = pandasm::AsmEmitter::Emit(res.Value());
265 ASSERT_NE(pf, nullptr);
266
267 ClassLinker *classLinker = Runtime::GetCurrent()->GetClassLinker();
268 classLinker->AddPandaFile(std::move(pf));
269 auto *extension = classLinker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
270
271 PandaString descriptor;
272
273 Class *klass = extension->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("_GLOBAL"), &descriptor));
274 ASSERT_NE(klass, nullptr);
275
276 Method *method = klass->GetDirectMethod(utf::CStringAsMutf8("foo"));
277 ASSERT_NE(method, nullptr);
278
279 ASSERT_EQ(method->GetLineNumFromBytecodeOffset(0), 3_I);
280 ASSERT_EQ(method->GetLineNumFromBytecodeOffset(2U), 4_I);
281 ASSERT_EQ(method->GetLineNumFromBytecodeOffset(5U), 5_I);
282 ASSERT_EQ(method->GetLineNumFromBytecodeOffset(7U), 6_I);
283 ASSERT_EQ(method->GetLineNumFromBytecodeOffset(10U), 7_I);
284 ASSERT_EQ(method->GetLineNumFromBytecodeOffset(14U), 8_I);
285
286 ASSERT_EQ(method->GetLineNumFromBytecodeOffset(20U), 8_I);
287 }
288
289 // NOLINTBEGIN(readability-magic-numbers)
290
TEST_F(MethodTest,GetLineNumFromBytecodeOffset2)291 TEST_F(MethodTest, GetLineNumFromBytecodeOffset2)
292 {
293 VerifyLineNumber({4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 8, 8, 8});
294 }
295
TEST_F(MethodTest,GetLineNumFromBytecodeOffset3)296 TEST_F(MethodTest, GetLineNumFromBytecodeOffset3)
297 {
298 VerifyLineNumber({4, 4, 4, 4, 4, 7, 5, 5, 6, 6, 6, 8, 8, 8});
299 }
300
TEST_F(MethodTest,GetLineNumFromBytecodeOffset4)301 TEST_F(MethodTest, GetLineNumFromBytecodeOffset4)
302 {
303 VerifyLineNumber({3, 3, 4, 4, 6, 6, 10, 5, 8, 9, 9, 4, 4, 12});
304 }
305
TEST_F(MethodTest,GetLineNumFromBytecodeOffset5)306 TEST_F(MethodTest, GetLineNumFromBytecodeOffset5)
307 {
308 VerifyLineNumber({4, 4, 4, 4, 6, 6, 7, 8, 8, 8, 9, 4, 4, 12});
309 }
310
TEST_F(MethodTest,GetLineNumFromBytecodeOffset6)311 TEST_F(MethodTest, GetLineNumFromBytecodeOffset6)
312 {
313 VerifyLineNumber({4, 17, 5, 7, 7, 13, 19, 19, 11, 10, 2, 7, 8, 18});
314 }
315
TEST_F(MethodTest,GetLineNumFromBytecodeOffset7)316 TEST_F(MethodTest, GetLineNumFromBytecodeOffset7)
317 {
318 VerifyLineNumber({4, 5, 7, 9, 10, 11, 13, 14, 15, 16, 6, 1, 3, 2});
319 }
320
TEST_F(MethodTest,GetLineNumFromBytecodeOffset8)321 TEST_F(MethodTest, GetLineNumFromBytecodeOffset8)
322 {
323 VerifyLineNumber({3, 4, 4, 5, 6, 6, 7, 9, 10, 11, 12, 13, 14, 14});
324 }
325
TEST_F(MethodTest,GetLineNumFromBytecodeOffset9)326 TEST_F(MethodTest, GetLineNumFromBytecodeOffset9)
327 {
328 VerifyLineNumber({3, 4, 5, 6, 6, 7, 9, 10, 16, 12, 13, 14, 15, 11});
329 }
330
TEST_F(MethodTest,GetLineNumFromBytecodeOffset10)331 TEST_F(MethodTest, GetLineNumFromBytecodeOffset10)
332 {
333 pandasm::Parser p;
334
335 auto source = R"( # line 1
336 .function void foo() { # line 2
337 mov v0, v1 # line 3, offset 0, size 2
338 mov v100, v200 # line 4, offset 2, size 3
339 movi v0, 4 # line 5, offset 5, size 2
340 movi v0, 100 # line 6, offset 7, size 3
341 movi v0, 300 # line 7, offset 10, size 4
342 return.void # line 8, offset 14, size 1
343 }
344 )";
345
346 auto res = p.Parse(source);
347 auto &prog = res.Value();
348 auto &function = prog.functionStaticTable.at(pandasm::GetFunctionSignatureFromName("foo", {}));
349
350 pandasm::debuginfo::LocalVariable lv;
351 lv.name = "a";
352 lv.signature = "I";
353 lv.reg = 0;
354 lv.start = 0;
355 lv.length = 5U;
356
357 function.localVariableDebug.push_back(lv);
358
359 lv.name = "b";
360 lv.start = 5U;
361 lv.length = 10U;
362
363 function.localVariableDebug.push_back(lv);
364
365 auto pf = pandasm::AsmEmitter::Emit(res.Value());
366 ASSERT_NE(pf, nullptr);
367
368 ClassLinker *classLinker = Runtime::GetCurrent()->GetClassLinker();
369 classLinker->AddPandaFile(std::move(pf));
370 auto *extension = classLinker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
371
372 PandaString descriptor;
373
374 Class *klass = extension->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("_GLOBAL"), &descriptor));
375 ASSERT_NE(klass, nullptr);
376
377 Method *method = klass->GetDirectMethod(utf::CStringAsMutf8("foo"));
378 ASSERT_NE(method, nullptr);
379
380 ASSERT_EQ(method->GetLineNumFromBytecodeOffset(0U), 3_I);
381 ASSERT_EQ(method->GetLineNumFromBytecodeOffset(2U), 4_I);
382 ASSERT_EQ(method->GetLineNumFromBytecodeOffset(5U), 5_I);
383 ASSERT_EQ(method->GetLineNumFromBytecodeOffset(7U), 6_I);
384 ASSERT_EQ(method->GetLineNumFromBytecodeOffset(10U), 7_I);
385 ASSERT_EQ(method->GetLineNumFromBytecodeOffset(14U), 8_I);
386
387 ASSERT_EQ(method->GetLineNumFromBytecodeOffset(20U), 8_I);
388 }
389
TEST_F(MethodTest,GetClassSourceFile)390 TEST_F(MethodTest, GetClassSourceFile)
391 {
392 pandasm::Parser p;
393
394 auto source = R"(
395 .record R {}
396
397 .function void R.foo() {
398 return.void
399 }
400
401 .function void foo() {
402 return.void
403 }
404 )";
405
406 std::string sourceFilename = "source.pa";
407 auto res = p.Parse(source, sourceFilename);
408 auto pf = pandasm::AsmEmitter::Emit(res.Value());
409 ASSERT_NE(pf, nullptr);
410
411 ClassLinker *classLinker = Runtime::GetCurrent()->GetClassLinker();
412 auto *extension = classLinker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
413 classLinker->AddPandaFile(std::move(pf));
414
415 PandaString descriptor;
416
417 {
418 Class *klass = extension->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("_GLOBAL"), &descriptor));
419 ASSERT_NE(klass, nullptr);
420
421 Method *method = klass->GetDirectMethod(utf::CStringAsMutf8("foo"));
422 ASSERT_NE(method, nullptr);
423
424 auto result = method->GetClassSourceFile();
425 ASSERT_EQ(result.data, nullptr);
426 }
427
428 {
429 Class *klass = extension->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("R"), &descriptor));
430 ASSERT_NE(klass, nullptr);
431
432 Method *method = klass->GetDirectMethod(utf::CStringAsMutf8("foo"));
433 ASSERT_NE(method, nullptr);
434
435 auto result = method->GetClassSourceFile();
436 ASSERT_TRUE(utf::IsEqual(result.data, utf::CStringAsMutf8(sourceFilename.data())));
437 }
438 }
439
StackTraceEntryPoint(Method *)440 static int32_t StackTraceEntryPoint(Method * /* unused */)
441 {
442 auto thread = ManagedThread::GetCurrent();
443
444 struct StackTraceData {
445 // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
446 std::string funcName;
447 // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
448 int32_t lineNum;
449
450 bool operator==(const StackTraceData &st) const
451 {
452 return funcName == st.funcName && lineNum == st.lineNum;
453 }
454 };
455
456 std::vector<StackTraceData> expected {{"f3", 31}, {"f2", 26}, {".cctor", 14},
457 {".ctor", 9}, {"f1", 20}, {"main", 41}};
458 std::vector<StackTraceData> trace;
459 for (auto stack = StackWalker::Create(thread); stack.HasFrame(); stack.NextFrame()) {
460 auto pc = stack.GetBytecodePc();
461 auto *methodFromFrame = stack.GetMethod();
462 auto lineNum = static_cast<int32_t>(methodFromFrame->GetLineNumFromBytecodeOffset(pc));
463 std::string funcName(utf::Mutf8AsCString(methodFromFrame->GetName().data));
464 trace.push_back({funcName, lineNum});
465 }
466
467 if (trace == expected) {
468 return 0;
469 }
470
471 return 1;
472 }
473
474 // NOLINTEND(readability-magic-numbers)
475 static auto g_stackTraceSource = R"( # 1
476 .record R1 {} # 2
477 # 3
478 .record R2 { # 4
479 i32 f1 <static> # 5
480 } # 6
481 .function void R1.ctor(R1 a0) <ctor> { # 7
482 ldai 0 # 8
483 ldstatic R2.f1 # 9
484 return.void # 10
485 } # 11
486 # 12
487 .function void R2.cctor() <cctor> { # 13
488 call f2 # 14
489 ststatic R2.f1 # 15
490 return.void # 16
491 } # 17
492 # 18
493 .function i32 f1() { # 19
494 initobj R1.ctor # 20
495 ldstatic R2.f1 # 21
496 return # 22
497 } # 23
498 # 24
499 .function i32 f2() { # 25
500 call f3 # 26
501 return # 27
502 } # 28
503 # 29
504 .function i32 f3() { # 30
505 call f4 # 31
506 return # 32
507 } # 33
508 # 34
509 .function i32 f4() { # 35
510 ldai 0 # 36
511 return # 37
512 } # 38
513 # 39
514 .function i32 main() { # 40
515 call f1 # 41
516 return # 42
517 } # 43
518 )";
519
TEST_F(MethodTest,StackTrace)520 TEST_F(MethodTest, StackTrace)
521 {
522 pandasm::Parser p;
523
524 auto source = g_stackTraceSource;
525
526 auto res = p.Parse(source);
527 ASSERT_TRUE(res) << res.Error().message;
528
529 auto pf = pandasm::AsmEmitter::Emit(res.Value());
530 ASSERT_NE(pf, nullptr) << pandasm::AsmEmitter::GetLastError();
531
532 ClassLinker *classLinker = Runtime::GetCurrent()->GetClassLinker();
533 classLinker->AddPandaFile(std::move(pf));
534 auto *extension = classLinker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
535
536 PandaString descriptor;
537
538 Class *klass = extension->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("_GLOBAL"), &descriptor));
539 ASSERT_NE(klass, nullptr);
540
541 Method *mainMethod = klass->GetDirectMethod(utf::CStringAsMutf8("main"));
542 ASSERT_NE(mainMethod, nullptr);
543
544 Method *f4Method = klass->GetDirectMethod(utf::CStringAsMutf8("f4"));
545 ASSERT_NE(f4Method, nullptr);
546
547 f4Method->SetCompiledEntryPoint(reinterpret_cast<const void *>(StackTraceEntryPoint));
548
549 ManagedThread *thread = ManagedThread::GetCurrent();
550 thread->SetCurrentFrame(nullptr);
551
552 std::vector<Value> args;
553 Value v = mainMethod->Invoke(thread, args.data());
554 EXPECT_EQ(v.GetAs<int32_t>(), 0);
555 }
556
TEST_F(MethodTest,GetFullName)557 TEST_F(MethodTest, GetFullName)
558 {
559 pandasm::Parser p;
560
561 auto source = R"(
562 .record Foo {}
563 .record R {}
564
565 .function void R.void_fun(R a0) {
566 return.void
567 }
568 .function void R.static_void_fun() <static> {
569 return.void
570 }
571 .function Foo R.multiple_args(R a0, i32 a1, Foo a2, i64[] a3, Foo[] a4) {
572 lda.obj a2
573 return.obj
574 }
575 )";
576
577 auto res = p.Parse(source);
578 auto pf = pandasm::AsmEmitter::Emit(res.Value());
579 ASSERT_NE(pf, nullptr);
580
581 ClassLinker *classLinker = Runtime::GetCurrent()->GetClassLinker();
582 classLinker->AddPandaFile(std::move(pf));
583 auto *extension = classLinker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
584 PandaString descriptor;
585
586 Class *klass = extension->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("R"), &descriptor));
587 ASSERT_NE(klass, nullptr);
588
589 Method *method1 = klass->GetDirectMethod(utf::CStringAsMutf8("void_fun"));
590 ASSERT_NE(method1, nullptr);
591 ASSERT_EQ(PandaStringToStd(method1->GetFullName()), "R::void_fun");
592 ASSERT_EQ(PandaStringToStd(method1->GetFullName(true)), "void R::void_fun(R)");
593
594 Method *method2 = klass->GetDirectMethod(utf::CStringAsMutf8("static_void_fun"));
595 ASSERT_NE(method2, nullptr);
596 ASSERT_EQ(PandaStringToStd(method2->GetFullName()), "R::static_void_fun");
597 ASSERT_EQ(PandaStringToStd(method2->GetFullName(true)), "void R::static_void_fun()");
598
599 Method *method3 = klass->GetDirectMethod(utf::CStringAsMutf8("multiple_args"));
600 ASSERT_NE(method3, nullptr);
601 ASSERT_EQ(PandaStringToStd(method3->GetFullName()), "R::multiple_args");
602 ASSERT_EQ(PandaStringToStd(method3->GetFullName(true)), "Foo R::multiple_args(R, i32, Foo, [J, [LFoo;)");
603 }
604
605 static auto g_testSource = R"(
606 .record Foo {}
607 .record R {}
608 .function void R.void_fun(R a0) {
609 return.void
610 }
611 .function u1 R.u1() {
612 ldai 0
613 return
614 }
615 .function i8 R.i8() {
616 ldai 0
617 return
618 }
619 .function u8 R.u8() {
620 ldai 0
621 return
622 }
623 .function i16 R.i16() {
624 ldai 0
625 return
626 }
627 .function u16 R.u16() {
628 ldai 0
629 return
630 }
631 .function i32 R.i32() {
632 ldai 0
633 return
634 }
635 .function u32 R.u32() {
636 ldai 0
637 return
638 }
639 .function f32 R.f32() {
640 ldai 0
641 return
642 }
643 .function i64 R.i64() {
644 ldai 0
645 return
646 }
647 .function u64 R.u64() {
648 ldai 0
649 return
650 }
651 .function f64 R.f64() {
652 ldai 0
653 return
654 }
655 .function R R.tagged() {
656 ldai 0
657 return
658 }
659 )";
660
TestMethodType(Class * klass,const std::string & s,const std::string & type)661 void TestMethodType(Class *klass, const std::string &s, const std::string &type)
662 {
663 Method *method = klass->GetDirectMethod(utf::CStringAsMutf8(s.c_str()));
664 Method::Proto proto = method->GetProto();
665 const char *descriptor = proto.GetReturnTypeDescriptor().data();
666 ASSERT_EQ(descriptor, std::string_view(type));
667 }
668
TEST_F(MethodTest,GetReturnTypeDescriptor)669 TEST_F(MethodTest, GetReturnTypeDescriptor)
670 {
671 pandasm::Parser p;
672 auto res = p.Parse(g_testSource);
673 auto pf = pandasm::AsmEmitter::Emit(res.Value());
674 ASSERT_NE(pf, nullptr);
675
676 ClassLinker *classLinker = Runtime::GetCurrent()->GetClassLinker();
677 classLinker->AddPandaFile(std::move(pf));
678 auto *extension = classLinker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
679 PandaString cdescriptor;
680
681 Class *klass = extension->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("R"), &cdescriptor));
682 ASSERT_NE(klass, nullptr);
683
684 TestMethodType(klass, "void_fun", "V");
685 TestMethodType(klass, "u1", "Z");
686 TestMethodType(klass, "i8", "B");
687 TestMethodType(klass, "u8", "H");
688 TestMethodType(klass, "i16", "S");
689 TestMethodType(klass, "u16", "C");
690 TestMethodType(klass, "i32", "I");
691 TestMethodType(klass, "u32", "U");
692 TestMethodType(klass, "f32", "F");
693 TestMethodType(klass, "i64", "J");
694 TestMethodType(klass, "u64", "Q");
695 TestMethodType(klass, "f64", "D");
696 }
697 } // namespace ark::test
698