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 <algorithm>
19 #include <memory>
20 #include <ostream>
21 #include <unordered_set>
22 #include <vector>
23
24 #include "assembly-emitter.h"
25 #include "assembly-parser.h"
26 #include "libpandabase/utils/utf.h"
27 #include "libpandafile/modifiers.h"
28 #include "runtime/include/class-inl.h"
29 #include "runtime/include/class_linker-inl.h"
30 #include "runtime/include/class_linker.h"
31 #include "runtime/include/coretypes/tagged_value.h"
32 #include "runtime/include/object_header.h"
33 #include "runtime/include/runtime.h"
34 #include "runtime/core/core_class_linker_extension.h"
35 #include "runtime/tests/class_linker_test_extension.h"
36
37 namespace panda::test {
38
39 class ClassLinkerTest : public testing::Test {
40 public:
ClassLinkerTest()41 ClassLinkerTest()
42 {
43 // Just for internal allocator
44 RuntimeOptions options;
45 options.SetShouldLoadBootPandaFiles(false);
46 options.SetShouldInitializeIntrinsics(false);
47 options.SetGcType("epsilon");
48 options.SetHeapSizeLimit(64_MB);
49 Runtime::Create(options);
50 thread_ = panda::MTManagedThread::GetCurrent();
51 thread_->ManagedCodeBegin();
52 }
53
~ClassLinkerTest()54 ~ClassLinkerTest()
55 {
56 thread_->ManagedCodeEnd();
57 Runtime::Destroy();
58 }
59
60 protected:
61 panda::MTManagedThread *thread_;
62 };
63
CreateClassLinker(ManagedThread * thread)64 static std::unique_ptr<ClassLinker> CreateClassLinker(ManagedThread *thread)
65 {
66 std::vector<std::unique_ptr<ClassLinkerExtension>> extensions;
67 extensions.push_back(std::make_unique<CoreClassLinkerExtension>());
68 auto allocator = thread->GetVM()->GetHeapManager()->GetInternalAllocator();
69 auto class_linker = std::make_unique<ClassLinker>(allocator, std::move(extensions));
70 if (!class_linker->Initialize()) {
71 return nullptr;
72 }
73
74 return class_linker;
75 }
76
TEST_F(ClassLinkerTest,TestGetClass)77 TEST_F(ClassLinkerTest, TestGetClass)
78 {
79 pandasm::Parser p;
80
81 auto source = R"(
82 .function void main() {
83 return.void
84 }
85 )";
86
87 auto res = p.Parse(source);
88 auto pf = pandasm::AsmEmitter::Emit(res.Value());
89
90 auto class_linker = CreateClassLinker(thread_);
91 ASSERT_NE(class_linker, nullptr);
92
93 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
94 auto *ext = class_linker->GetExtension(ctx);
95
96 auto *pf_ptr = pf.get();
97 class_linker->AddPandaFile(std::move(pf));
98
99 Class *klass;
100
101 {
102 // Use temporary string to load class. Class loader shouldn't store it.
103 auto descriptor = std::make_unique<PandaString>();
104 klass = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("_GLOBAL"), descriptor.get()));
105 }
106
107 PandaString descriptor;
108
109 EXPECT_EQ(klass, ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("_GLOBAL"), &descriptor)));
110 EXPECT_EQ(klass->GetBase(), ext->GetClassRoot(ClassRoot::OBJECT));
111 EXPECT_EQ(klass->GetPandaFile(), pf_ptr);
112 EXPECT_EQ(klass->GetMethods().size(), 1U);
113 EXPECT_EQ(klass->GetComponentSize(), 0U);
114 }
115
TEST_F(ClassLinkerTest,TestEnumerateClasses)116 TEST_F(ClassLinkerTest, TestEnumerateClasses)
117 {
118 pandasm::Parser p;
119
120 auto source = R"(
121 .function void main() {
122 return.void
123 }
124 )";
125
126 auto res = p.Parse(source);
127 auto pf = pandasm::AsmEmitter::Emit(res.Value());
128
129 auto class_linker = CreateClassLinker(thread_);
130 ASSERT_NE(class_linker, nullptr);
131
132 class_linker->AddPandaFile(std::move(pf));
133
134 PandaString descriptor;
135
136 // Load _GLOBAL class
137 auto *ext = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
138 ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("_GLOBAL"), &descriptor));
139
140 std::set<std::string> classes {"panda.Object",
141 "panda.String",
142 "panda.Class",
143 "[Lpanda/String;",
144 "u1",
145 "i8",
146 "u8",
147 "i16",
148 "u16",
149 "i32",
150 "u32",
151 "i64",
152 "u64",
153 "f32",
154 "f64",
155 "any",
156 "[Z",
157 "[B",
158 "[H",
159 "[S",
160 "[C",
161 "[I",
162 "[U",
163 "[J",
164 "[Q",
165 "[F",
166 "[D",
167 "[A",
168 "[Lpanda/Class;",
169 "_GLOBAL"};
170
171 std::set<std::string> loaded_classes;
172
173 class_linker->EnumerateClasses([&](Class *k) {
174 loaded_classes.emplace(k->GetName());
175 return true;
176 });
177
178 EXPECT_EQ(loaded_classes, classes);
179 }
180
TestPrimitiveClassRoot(const ClassLinkerExtension & class_linker_ext,ClassRoot class_root,panda_file::Type::TypeId type_id)181 static void TestPrimitiveClassRoot(const ClassLinkerExtension &class_linker_ext, ClassRoot class_root,
182 panda_file::Type::TypeId type_id)
183 {
184 std::string msg = "Test with class root ";
185 msg += static_cast<int>(class_root);
186
187 Class *klass = class_linker_ext.GetClassRoot(class_root);
188 ASSERT_NE(klass, nullptr) << msg;
189 EXPECT_EQ(klass->GetBase(), nullptr) << msg;
190 EXPECT_EQ(klass->GetComponentSize(), 0U) << msg;
191 EXPECT_EQ(klass->GetFlags(), 0U) << msg;
192 EXPECT_EQ(klass->GetAccessFlags(), ACC_PUBLIC | ACC_FINAL | ACC_ABSTRACT) << msg;
193 EXPECT_EQ(klass->GetType().GetId(), type_id) << msg;
194 EXPECT_FALSE(klass->IsArrayClass()) << msg;
195 EXPECT_FALSE(klass->IsStringClass()) << msg;
196 EXPECT_TRUE(klass->IsPrimitive()) << msg;
197 EXPECT_TRUE(klass->IsAbstract()) << msg;
198 EXPECT_FALSE(klass->IsInstantiable()) << msg;
199 }
200
GetComponentSize(ClassRoot component_root)201 static size_t GetComponentSize(ClassRoot component_root)
202 {
203 switch (component_root) {
204 case ClassRoot::U1:
205 case ClassRoot::I8:
206 case ClassRoot::U8:
207 return sizeof(uint8_t);
208 case ClassRoot::I16:
209 case ClassRoot::U16:
210 return sizeof(uint16_t);
211 case ClassRoot::I32:
212 case ClassRoot::U32:
213 case ClassRoot::F32:
214 return sizeof(uint32_t);
215 case ClassRoot::I64:
216 case ClassRoot::U64:
217 case ClassRoot::F64:
218 return sizeof(uint64_t);
219 default:
220 UNREACHABLE();
221 }
222 }
223
TestArrayClassRoot(const ClassLinkerExtension & class_linker_ext,ClassRoot class_root,ClassRoot component_root)224 static void TestArrayClassRoot(const ClassLinkerExtension &class_linker_ext, ClassRoot class_root,
225 ClassRoot component_root)
226 {
227 std::string msg = "Test with class root ";
228 msg += static_cast<int>(class_root);
229
230 Class *klass = class_linker_ext.GetClassRoot(class_root);
231 Class *component_class = class_linker_ext.GetClassRoot(component_root);
232 ASSERT_NE(klass, nullptr) << msg;
233 EXPECT_EQ(klass->GetBase(), class_linker_ext.GetClassRoot(ClassRoot::OBJECT)) << msg;
234 EXPECT_EQ(klass->GetComponentType(), component_class) << msg;
235 EXPECT_EQ(klass->GetComponentSize(), GetComponentSize(component_root)) << msg;
236 EXPECT_EQ(klass->GetFlags(), 0U) << msg;
237 EXPECT_EQ(klass->GetAccessFlags(), ACC_PUBLIC | ACC_FINAL | ACC_ABSTRACT) << msg;
238 EXPECT_EQ(klass->GetType().GetId(), panda_file::Type::TypeId::REFERENCE) << msg;
239 EXPECT_EQ(klass->IsObjectArrayClass(), !component_class->IsPrimitive()) << msg;
240 EXPECT_TRUE(klass->IsArrayClass()) << msg;
241 EXPECT_FALSE(klass->IsStringClass()) << msg;
242 EXPECT_FALSE(klass->IsPrimitive()) << msg;
243 EXPECT_TRUE(klass->IsAbstract()) << msg;
244 EXPECT_TRUE(klass->IsInstantiable()) << msg;
245 }
246
TEST_F(ClassLinkerTest,TestClassRoots)247 TEST_F(ClassLinkerTest, TestClassRoots)
248 {
249 auto class_linker = CreateClassLinker(thread_);
250 ASSERT_NE(class_linker, nullptr);
251
252 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
253 auto *ext = class_linker->GetExtension(ctx);
254
255 Class *object_class = ext->GetClassRoot(ClassRoot::OBJECT);
256 ASSERT_NE(object_class, nullptr);
257 EXPECT_EQ(object_class->GetBase(), nullptr);
258 EXPECT_EQ(object_class->GetComponentSize(), 0U);
259 EXPECT_EQ(object_class->GetFlags(), 0U);
260 EXPECT_EQ(object_class->GetType().GetId(), panda_file::Type::TypeId::REFERENCE);
261 EXPECT_FALSE(object_class->IsArrayClass());
262 EXPECT_FALSE(object_class->IsObjectArrayClass());
263 EXPECT_FALSE(object_class->IsStringClass());
264 EXPECT_FALSE(object_class->IsPrimitive());
265
266 Class *string_class = ext->GetClassRoot(ClassRoot::STRING);
267 ASSERT_NE(string_class, nullptr);
268 EXPECT_EQ(string_class->GetBase(), object_class);
269 EXPECT_EQ(string_class->GetComponentSize(), 0U);
270 EXPECT_EQ(string_class->GetFlags(), Class::STRING_CLASS);
271 EXPECT_EQ(string_class->GetType().GetId(), panda_file::Type::TypeId::REFERENCE);
272 EXPECT_FALSE(string_class->IsArrayClass());
273 EXPECT_FALSE(string_class->IsObjectArrayClass());
274 EXPECT_TRUE(string_class->IsStringClass());
275 EXPECT_FALSE(string_class->IsPrimitive());
276
277 TestPrimitiveClassRoot(*ext, ClassRoot::U1, panda_file::Type::TypeId::U1);
278 TestPrimitiveClassRoot(*ext, ClassRoot::I8, panda_file::Type::TypeId::I8);
279 TestPrimitiveClassRoot(*ext, ClassRoot::U8, panda_file::Type::TypeId::U8);
280 TestPrimitiveClassRoot(*ext, ClassRoot::I16, panda_file::Type::TypeId::I16);
281 TestPrimitiveClassRoot(*ext, ClassRoot::U16, panda_file::Type::TypeId::U16);
282 TestPrimitiveClassRoot(*ext, ClassRoot::I32, panda_file::Type::TypeId::I32);
283 TestPrimitiveClassRoot(*ext, ClassRoot::U32, panda_file::Type::TypeId::U32);
284 TestPrimitiveClassRoot(*ext, ClassRoot::I64, panda_file::Type::TypeId::I64);
285 TestPrimitiveClassRoot(*ext, ClassRoot::U64, panda_file::Type::TypeId::U64);
286 TestPrimitiveClassRoot(*ext, ClassRoot::F32, panda_file::Type::TypeId::F32);
287 TestPrimitiveClassRoot(*ext, ClassRoot::F64, panda_file::Type::TypeId::F64);
288
289 TestArrayClassRoot(*ext, ClassRoot::ARRAY_U1, ClassRoot::U1);
290 TestArrayClassRoot(*ext, ClassRoot::ARRAY_I8, ClassRoot::I8);
291 TestArrayClassRoot(*ext, ClassRoot::ARRAY_U8, ClassRoot::U8);
292 TestArrayClassRoot(*ext, ClassRoot::ARRAY_I16, ClassRoot::I16);
293 TestArrayClassRoot(*ext, ClassRoot::ARRAY_U16, ClassRoot::U16);
294 TestArrayClassRoot(*ext, ClassRoot::ARRAY_I32, ClassRoot::I32);
295 TestArrayClassRoot(*ext, ClassRoot::ARRAY_U32, ClassRoot::U32);
296 TestArrayClassRoot(*ext, ClassRoot::ARRAY_I64, ClassRoot::I64);
297 TestArrayClassRoot(*ext, ClassRoot::ARRAY_U64, ClassRoot::U64);
298 TestArrayClassRoot(*ext, ClassRoot::ARRAY_F32, ClassRoot::F32);
299 TestArrayClassRoot(*ext, ClassRoot::ARRAY_F64, ClassRoot::F64);
300 }
301
302 struct FieldData {
303 std::string name;
304 size_t size;
305 size_t offset;
306
operator ==panda::test::FieldData307 bool operator==(const FieldData &other) const
308 {
309 return name == other.name && size == other.size && offset == other.offset;
310 }
311
operator <<(std::ostream & os,const FieldData & field_data)312 friend std::ostream &operator<<(std::ostream &os, const FieldData &field_data)
313 {
314 return os << "{ name: \"" << field_data.name << "\", size: " << field_data.size
315 << ", offset: " << field_data.offset << " }";
316 }
317 };
318
319 struct FieldDataHash {
operator ()panda::test::FieldDataHash320 size_t operator()(const FieldData &field_data) const
321 {
322 return std::hash<std::string>()(field_data.name);
323 }
324 };
325
GetSize(const Field & field)326 size_t GetSize(const Field &field)
327 {
328 size_t size = 0;
329
330 switch (field.GetTypeId()) {
331 case panda_file::Type::TypeId::U1:
332 case panda_file::Type::TypeId::I8:
333 case panda_file::Type::TypeId::U8: {
334 size = 1;
335 break;
336 }
337 case panda_file::Type::TypeId::I16:
338 case panda_file::Type::TypeId::U16: {
339 size = 2;
340 break;
341 }
342 case panda_file::Type::TypeId::I32:
343 case panda_file::Type::TypeId::U32:
344 case panda_file::Type::TypeId::F32: {
345 size = 4;
346 break;
347 }
348 case panda_file::Type::TypeId::I64:
349 case panda_file::Type::TypeId::U64:
350 case panda_file::Type::TypeId::F64: {
351 size = 8;
352 break;
353 }
354 case panda_file::Type::TypeId::REFERENCE: {
355 size = ClassHelper::OBJECT_POINTER_SIZE;
356 break;
357 }
358 case panda_file::Type::TypeId::TAGGED: {
359 size = coretypes::TaggedValue::TaggedTypeSize();
360 break;
361 }
362 default: {
363 UNREACHABLE();
364 break;
365 }
366 }
367
368 return size;
369 }
370
UpdateOffsets(std::vector<FieldData> * fields,size_t offset)371 void UpdateOffsets(std::vector<FieldData> *fields, size_t offset)
372 {
373 for (auto &field : *fields) {
374 offset = AlignUp(offset, field.size);
375 field.offset = offset;
376 offset += field.size;
377 }
378 }
379
TEST_F(ClassLinkerTest,FieldLayout)380 TEST_F(ClassLinkerTest, FieldLayout)
381 {
382 pandasm::Parser p;
383
384 auto source = R"(
385 .record R1 {}
386
387 .record R2 {
388 # static fields
389
390 u1 sf_u1 <static>
391 i16 sf_i16 <static>
392 i8 sf_i8 <static>
393 i32 sf_i32 <static>
394 u8 sf_u8 <static>
395 f64 sf_f64 <static>
396 u32 sf_u32 <static>
397 u16 sf_u16 <static>
398 i64 sf_i64 <static>
399 f32 sf_f32 <static>
400 u64 sf_u64 <static>
401 R1 sf_ref <static>
402 any sf_any <static>
403
404 # instance fields
405
406 i16 if_i16
407 u1 if_u1
408 i8 if_i8
409 f64 if_f64
410 i32 if_i32
411 u8 if_u8
412 u32 if_u32
413 u16 if_u16
414 f32 if_f32
415 i64 if_i64
416 u64 if_u64
417 R2 if_ref
418 any if_any
419 }
420 )";
421
422 auto res = p.Parse(source);
423 auto pf = pandasm::AsmEmitter::Emit(res.Value());
424
425 auto class_linker = CreateClassLinker(thread_);
426 ASSERT_NE(class_linker, nullptr);
427
428 class_linker->AddPandaFile(std::move(pf));
429
430 PandaString descriptor;
431 auto *ext = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
432 Class *klass = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("R2"), &descriptor));
433 ASSERT_NE(klass, nullptr);
434
435 std::vector<FieldData> sorted_sfields {{"sf_ref", ClassHelper::OBJECT_POINTER_SIZE, 0},
436 {"sf_any", coretypes::TaggedValue::TaggedTypeSize(), 0},
437 {"sf_f64", sizeof(double), 0},
438 {"sf_i64", sizeof(int64_t), 0},
439 {"sf_u64", sizeof(uint64_t), 0},
440 {"sf_i32", sizeof(int32_t), 0},
441 {"sf_u32", sizeof(uint32_t), 0},
442 {"sf_f32", sizeof(float), 0},
443 {"sf_i16", sizeof(int16_t), 0},
444 {"sf_u16", sizeof(uint16_t), 0},
445 {"sf_u1", sizeof(uint8_t), 0},
446 {"sf_i8", sizeof(int8_t), 0},
447 {"sf_u8", sizeof(uint8_t), 0}};
448
449 std::vector<FieldData> sorted_ifields {{"if_ref", ClassHelper::OBJECT_POINTER_SIZE, 0},
450 {"if_any", coretypes::TaggedValue::TaggedTypeSize(), 0},
451 {"if_f64", sizeof(double), 0},
452 {"if_i64", sizeof(int64_t), 0},
453 {"if_u64", sizeof(uint64_t), 0},
454 {"if_i32", sizeof(int32_t), 0},
455 {"if_u32", sizeof(uint32_t), 0},
456 {"if_f32", sizeof(float), 0},
457 {"if_i16", sizeof(int16_t), 0},
458 {"if_u16", sizeof(uint16_t), 0},
459 {"if_u1", sizeof(uint8_t), 0},
460 {"if_i8", sizeof(int8_t), 0},
461 {"if_u8", sizeof(uint8_t), 0}};
462
463 size_t offset = klass->GetStaticFieldsOffset();
464 if (!IsAligned<sizeof(double)>(offset + ClassHelper::OBJECT_POINTER_SIZE)) {
465 FieldData data {"sf_i32", sizeof(int32_t), 0};
466 sorted_sfields.erase(std::remove(sorted_sfields.begin(), sorted_sfields.end(), data));
467 sorted_sfields.insert(sorted_sfields.cbegin() + 1, data);
468 }
469
470 UpdateOffsets(&sorted_sfields, offset);
471
472 offset = ObjectHeader::ObjectHeaderSize();
473 if (!IsAligned<sizeof(double)>(offset + ClassHelper::OBJECT_POINTER_SIZE)) {
474 FieldData data {"if_i32", sizeof(int32_t), 0};
475 sorted_ifields.erase(std::remove(sorted_ifields.begin(), sorted_ifields.end(), data));
476 sorted_ifields.insert(sorted_ifields.cbegin() + 1, data);
477 }
478
479 UpdateOffsets(&sorted_ifields, offset);
480
481 auto field_cmp = [](const FieldData &f1, const FieldData &f2) { return f1.offset < f2.offset; };
482
483 std::vector<FieldData> sfields;
484 for (const auto &field : klass->GetStaticFields()) {
485 sfields.push_back({utf::Mutf8AsCString(field.GetName().data), GetSize(field), field.GetOffset()});
486 }
487 std::sort(sfields.begin(), sfields.end(), field_cmp);
488 EXPECT_EQ(sfields, sorted_sfields);
489
490 std::unordered_set<FieldData, FieldDataHash> ifields;
491
492 for (const auto &field : klass->GetInstanceFields()) {
493 ifields.insert({utf::Mutf8AsCString(field.GetName().data), GetSize(field), field.GetOffset()});
494 }
495
496 std::unordered_set<FieldData, FieldDataHash> sorted_ifields_set(sorted_ifields.cbegin(), sorted_ifields.cend());
497 EXPECT_EQ(ifields, sorted_ifields_set);
498 }
499
TEST_F(ClassLinkerTest,ResolveExternalClass)500 TEST_F(ClassLinkerTest, ResolveExternalClass)
501 {
502 uint32_t offset;
503
504 auto class_linker = CreateClassLinker(thread_);
505 ASSERT_NE(class_linker, nullptr);
506
507 {
508 pandasm::Parser p;
509
510 auto source = R"(
511 .record Ext.R <external>
512
513 .function void main() {
514 newarr v0, v0, Ext.R[]
515 return.void
516 }
517 )";
518
519 auto res = p.Parse(source);
520 ASSERT_TRUE(res);
521 auto pf = pandasm::AsmEmitter::Emit(res.Value());
522
523 // 0 - "LExt/R;"
524 // 1 - "L_GLOBAL;"
525 // 2 - "[LExt/R;"
526 offset = pf->GetClasses()[2];
527
528 class_linker->AddPandaFile(std::move(pf));
529 }
530
531 PandaString descriptor;
532
533 auto *ext = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
534 auto *klass = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("_GLOBAL"), &descriptor));
535 ASSERT_NE(klass, nullptr);
536
537 auto *method = klass->GetDirectMethod(utf::CStringAsMutf8("main"));
538 ASSERT_NE(method, nullptr);
539
540 auto *external_class = class_linker->GetClass(*method, panda_file::File::EntityId(offset));
541 ASSERT_EQ(external_class, nullptr);
542
543 {
544 pandasm::Parser p;
545
546 auto ext_source = R"(
547 .record Ext {}
548 .record Ext.R {}
549 )";
550
551 auto res = p.Parse(ext_source);
552 auto ext_pf = pandasm::AsmEmitter::Emit(res.Value());
553
554 class_linker->AddPandaFile(std::move(ext_pf));
555 }
556
557 external_class = class_linker->GetClass(*method, panda_file::File::EntityId(offset));
558 ASSERT_NE(external_class, nullptr);
559
560 EXPECT_STREQ(utf::Mutf8AsCString(external_class->GetDescriptor()),
561 utf::Mutf8AsCString(ClassHelper::GetArrayDescriptor(utf::CStringAsMutf8("Ext.R"), 1, &descriptor)));
562 }
563
TEST_F(ClassLinkerTest,ArrayClass)564 TEST_F(ClassLinkerTest, ArrayClass)
565 {
566 pandasm::Parser p;
567
568 auto source = R"(
569 .record R {}
570 )";
571
572 auto res = p.Parse(source);
573 auto pf = pandasm::AsmEmitter::Emit(res.Value());
574
575 auto class_linker = CreateClassLinker(thread_);
576 ASSERT_NE(class_linker, nullptr);
577
578 class_linker->AddPandaFile(std::move(pf));
579
580 PandaString descriptor;
581
582 auto *ext = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
583 auto *klass = ext->GetClass(ClassHelper::GetArrayDescriptor(utf::CStringAsMutf8("UnknownClass"), 1, &descriptor));
584 ASSERT_EQ(klass, nullptr);
585
586 for (size_t i = 0; i < 256; i++) {
587 auto *cls = ext->GetClass(ClassHelper::GetArrayDescriptor(utf::CStringAsMutf8("R"), i, &descriptor));
588 ASSERT_NE(cls, nullptr);
589 EXPECT_EQ(utf::Mutf8AsCString(cls->GetDescriptor()), descriptor);
590 }
591 }
592
GetMethod(ClassLinker * class_linker,const char * class_name,const char * method_name)593 static Method *GetMethod(ClassLinker *class_linker, const char *class_name, const char *method_name)
594 {
595 PandaString descriptor;
596 auto *ext = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
597 auto *klass = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8(class_name), &descriptor));
598 return klass->GetDirectMethod(utf::CStringAsMutf8(method_name));
599 }
600
GetMethodsSet(Span<Method> methods)601 static std::unordered_set<Method *> GetMethodsSet(Span<Method> methods)
602 {
603 std::unordered_set<Method *> set;
604 for (auto &method : methods) {
605 set.insert(&method);
606 }
607
608 return set;
609 }
610
TEST_F(ClassLinkerTest,VTable)611 TEST_F(ClassLinkerTest, VTable)
612 {
613 {
614 pandasm::Parser p;
615
616 auto source = R"(
617 .record A {}
618
619 .function void A.f1() {}
620 .function void A.f2(i32 a0) {}
621
622 .function void A.f3(A a0) {}
623 .function void A.f4(A a0, i32 a1) {}
624 )";
625
626 auto res = p.Parse(source);
627 auto pf = pandasm::AsmEmitter::Emit(res.Value());
628
629 auto class_linker = CreateClassLinker(thread_);
630 ASSERT_NE(class_linker, nullptr);
631
632 class_linker->AddPandaFile(std::move(pf));
633
634 PandaString descriptor;
635
636 auto *ext = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
637 auto *class_a = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("A"), &descriptor));
638 ASSERT_NE(class_a, nullptr);
639
640 auto smethods = class_a->GetStaticMethods();
641 ASSERT_EQ(smethods.size(), 2);
642
643 auto vmethods = class_a->GetVirtualMethods();
644 ASSERT_EQ(vmethods.size(), 2);
645
646 {
647 auto set = GetMethodsSet(smethods);
648 ASSERT_NE(set.find(GetMethod(class_linker.get(), "A", "f1")), set.cend());
649 ASSERT_NE(set.find(GetMethod(class_linker.get(), "A", "f2")), set.cend());
650 }
651
652 {
653 auto set = GetMethodsSet(vmethods);
654 ASSERT_NE(set.find(GetMethod(class_linker.get(), "A", "f3")), set.cend());
655 ASSERT_NE(set.find(GetMethod(class_linker.get(), "A", "f4")), set.cend());
656 }
657
658 {
659 auto vtable = class_a->GetVTable();
660 ASSERT_EQ(vtable.size(), vmethods.size());
661
662 for (size_t i = 0; i < vmethods.size(); i++) {
663 ASSERT_EQ(vtable[vmethods[i].GetVTableIndex()], &vmethods[i]);
664 }
665 }
666 }
667 }
668
TEST_F(ClassLinkerTest,PrimitiveClasses)669 TEST_F(ClassLinkerTest, PrimitiveClasses)
670 {
671 auto class_linker = CreateClassLinker(thread_);
672 ASSERT_NE(class_linker, nullptr);
673
674 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
675 auto *ext = class_linker->GetExtension(ctx);
676
677 PandaString descriptor;
678
679 auto type = panda_file::Type(panda_file::Type::TypeId::I32);
680
681 auto *primitive_class = ext->GetClass(ClassHelper::GetPrimitiveDescriptor(type, &descriptor));
682 ASSERT_NE(primitive_class, nullptr);
683 EXPECT_STREQ(utf::Mutf8AsCString(primitive_class->GetDescriptor()),
684 utf::Mutf8AsCString(ClassHelper::GetPrimitiveDescriptor(type, &descriptor)));
685
686 auto *primitive_array_class1 = ext->GetClass(ClassHelper::GetPrimitiveArrayDescriptor(type, 1, &descriptor));
687 ASSERT_NE(primitive_array_class1, nullptr);
688 EXPECT_STREQ(utf::Mutf8AsCString(primitive_array_class1->GetDescriptor()),
689 utf::Mutf8AsCString(ClassHelper::GetPrimitiveArrayDescriptor(type, 1, &descriptor)));
690
691 auto *primitive_array_class2 = ext->GetClass(ClassHelper::GetPrimitiveArrayDescriptor(type, 2, &descriptor));
692 ASSERT_NE(primitive_array_class2, nullptr);
693 EXPECT_STREQ(utf::Mutf8AsCString(primitive_array_class2->GetDescriptor()),
694 utf::Mutf8AsCString(ClassHelper::GetPrimitiveArrayDescriptor(type, 2, &descriptor)));
695 }
696
697 class TestClassLinkerContext : public ClassLinkerContext {
698 public:
TestClassLinkerContext(const uint8_t * descriptor,bool need_copy_descriptor,Class * klass,panda_file::SourceLang lang)699 TestClassLinkerContext(const uint8_t *descriptor, bool need_copy_descriptor, Class *klass,
700 panda_file::SourceLang lang)
701 : ClassLinkerContext(lang), descriptor_(descriptor), need_copy_descriptor_(need_copy_descriptor), klass_(klass)
702 {
703 }
704
LoadClass(const uint8_t * descriptor,bool need_copy_descriptor,ClassLinkerErrorHandler * error_handler=nullptr)705 Class *LoadClass(const uint8_t *descriptor, bool need_copy_descriptor,
706 [[maybe_unused]] ClassLinkerErrorHandler *error_handler = nullptr) override
707 {
708 is_success_ = utf::IsEqual(descriptor, descriptor_) && need_copy_descriptor == need_copy_descriptor_;
709 InsertClass(klass_);
710 return klass_;
711 }
712
IsSuccess() const713 bool IsSuccess() const
714 {
715 return is_success_;
716 }
717
718 private:
719 const uint8_t *descriptor_;
720 bool need_copy_descriptor_ {};
721 Class *klass_;
722 bool is_success_ {false};
723 };
724
TEST_F(ClassLinkerTest,LoadContext)725 TEST_F(ClassLinkerTest, LoadContext)
726 {
727 pandasm::Parser p;
728
729 auto source = R"(
730 .record A {}
731 .record B {}
732 )";
733
734 auto res = p.Parse(source);
735 auto pf = pandasm::AsmEmitter::Emit(res.Value());
736
737 auto class_linker = CreateClassLinker(thread_);
738 ASSERT_NE(class_linker, nullptr);
739
740 class_linker->AddPandaFile(std::move(pf));
741
742 PandaString descriptor;
743 auto *ext = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
744 auto *class_a = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("A"), &descriptor));
745
746 ASSERT_NE(class_a, nullptr);
747 ASSERT_EQ(class_a->GetLoadContext()->IsBootContext(), true);
748
749 auto *class_b = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("B"), &descriptor));
750
751 ASSERT_NE(class_b, nullptr);
752 ASSERT_EQ(class_b->GetLoadContext()->IsBootContext(), true);
753
754 auto *desc = ClassHelper::GetDescriptor(utf::CStringAsMutf8("B"), &descriptor);
755 TestClassLinkerContext ctx(desc, true, class_b, panda_file::SourceLang::PANDA_ASSEMBLY);
756 auto *class_b_ctx = ext->GetClass(desc, true, &ctx);
757
758 ASSERT_TRUE(ctx.IsSuccess());
759 ASSERT_EQ(class_b_ctx, class_b);
760
761 bool is_matched = false;
762 ctx.EnumerateClasses([&is_matched](Class *klass) {
763 is_matched = klass->GetName() == "B";
764 return true;
765 });
766
767 ASSERT_TRUE(is_matched);
768
769 auto *class_array_b =
770 class_linker->GetClass(ClassHelper::GetArrayDescriptor(utf::CStringAsMutf8("B"), 1, &descriptor), true, &ctx);
771
772 ASSERT_NE(class_array_b, nullptr);
773 ASSERT_EQ(class_array_b->GetLoadContext(), ext->GetBootContext());
774
775 {
776 PandaUnorderedSet<Class *> expected {class_b};
777 PandaUnorderedSet<Class *> classes;
778 ctx.EnumerateClasses([&](Class *klass) {
779 classes.insert(klass);
780 return true;
781 });
782
783 ASSERT_EQ(classes, expected);
784 }
785
786 {
787 PandaUnorderedSet<Class *> classes;
788 class_linker->EnumerateClasses([&](Class *klass) {
789 classes.insert(klass);
790 return true;
791 });
792
793 ASSERT_NE(classes.find(class_a), classes.cend());
794 ASSERT_EQ(*classes.find(class_a), class_a);
795
796 ASSERT_NE(classes.find(class_b), classes.cend());
797 ASSERT_EQ(*classes.find(class_b), class_b);
798
799 ASSERT_NE(classes.find(class_array_b), classes.cend());
800 ASSERT_EQ(*classes.find(class_array_b), class_array_b);
801 }
802 }
803
804 } // namespace panda::test
805