• 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 <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