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