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