• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2024 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.functionTable.find(name), prog.functionTable.end());
83         auto &insVec = prog.functionTable.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()->IsJitEnabled() ? 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()->IsJitEnabled() ? 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()->IsJitEnabled() ? 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.functionTable.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 }  // namespace ark::test
606