• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <gtest/gtest.h>
17 
18 #include <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         // NOLINTNEXTLINE(readability-magic-numbers)
49         options.SetHeapSizeLimit(64_MB);
50         Runtime::Create(options);
51         thread_ = panda::MTManagedThread::GetCurrent();
52         thread_->ManagedCodeBegin();
53     }
54 
~ClassLinkerTest()55     ~ClassLinkerTest() override
56     {
57         thread_->ManagedCodeEnd();
58         Runtime::Destroy();
59     }
60 
61     NO_COPY_SEMANTIC(ClassLinkerTest);
62     NO_MOVE_SEMANTIC(ClassLinkerTest);
63 
64 protected:
65     // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
66     panda::MTManagedThread *thread_;
67 };
68 
CreateClassLinker(ManagedThread * thread)69 static std::unique_ptr<ClassLinker> CreateClassLinker(ManagedThread *thread)
70 {
71     std::vector<std::unique_ptr<ClassLinkerExtension>> extensions;
72     extensions.push_back(std::make_unique<CoreClassLinkerExtension>());
73     auto allocator = thread->GetVM()->GetHeapManager()->GetInternalAllocator();
74     auto classLinker = std::make_unique<ClassLinker>(allocator, std::move(extensions));
75     if (!classLinker->Initialize()) {
76         return nullptr;
77     }
78 
79     return classLinker;
80 }
81 
82 static std::set<std::string> setClasses {"panda.Object",
83                                          "panda.String",
84                                          "panda.Class",
85                                          "[Lpanda/String;",
86                                          "u1",
87                                          "i8",
88                                          "u8",
89                                          "i16",
90                                          "u16",
91                                          "i32",
92                                          "u32",
93                                          "i64",
94                                          "u64",
95                                          "f32",
96                                          "f64",
97                                          "any",
98                                          "[Z",
99                                          "[B",
100                                          "[H",
101                                          "[S",
102                                          "[C",
103                                          "[I",
104                                          "[U",
105                                          "[J",
106                                          "[Q",
107                                          "[F",
108                                          "[D",
109                                          "[A",
110                                          "[Lpanda/Class;",
111                                          "_GLOBAL"};
112 
TEST_F(ClassLinkerTest,TestGetClass)113 TEST_F(ClassLinkerTest, TestGetClass)
114 {
115     pandasm::Parser p;
116 
117     auto source = R"(
118         .function void main() {
119             return.void
120         }
121     )";
122 
123     auto res = p.Parse(source);
124     auto pf = pandasm::AsmEmitter::Emit(res.Value());
125 
126     auto classLinker = CreateClassLinker(thread_);
127     ASSERT_NE(classLinker, nullptr);
128 
129     LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
130     auto *ext = classLinker->GetExtension(ctx);
131 
132     auto *pfPtr = pf.get();
133     classLinker->AddPandaFile(std::move(pf));
134 
135     Class *klass;
136 
137     {
138         // Use temporary string to load class. Class loader shouldn't store it.
139         auto descriptor = std::make_unique<PandaString>();
140         klass = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("_GLOBAL"), descriptor.get()));
141     }
142 
143     PandaString descriptor;
144 
145     EXPECT_EQ(klass, ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("_GLOBAL"), &descriptor)));
146     EXPECT_EQ(klass->GetBase(), ext->GetClassRoot(ClassRoot::OBJECT));
147     EXPECT_EQ(klass->GetPandaFile(), pfPtr);
148     EXPECT_EQ(klass->GetMethods().size(), 1U);
149     EXPECT_EQ(klass->GetComponentSize(), 0U);
150 }
151 
TEST_F(ClassLinkerTest,TestEnumerateClasses)152 TEST_F(ClassLinkerTest, TestEnumerateClasses)
153 {
154     pandasm::Parser p;
155 
156     auto source = R"(
157         .function void main() {
158             return.void
159         }
160     )";
161 
162     auto res = p.Parse(source);
163     auto pf = pandasm::AsmEmitter::Emit(res.Value());
164 
165     auto classLinker = CreateClassLinker(thread_);
166     ASSERT_NE(classLinker, nullptr);
167 
168     classLinker->AddPandaFile(std::move(pf));
169 
170     PandaString descriptor;
171 
172     // Load _GLOBAL class
173     auto *ext = classLinker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
174     ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("_GLOBAL"), &descriptor));
175 
176     std::set<std::string> loadedClasses;
177 
178     classLinker->EnumerateClasses([&loadedClasses](Class *k) {
179         loadedClasses.emplace(k->GetName());
180         return true;
181     });
182 
183     EXPECT_EQ(loadedClasses, setClasses);
184 }
185 
TestPrimitiveClassRoot(const ClassLinkerExtension & classLinkerExt,ClassRoot classRoot,panda_file::Type::TypeId typeId)186 static void TestPrimitiveClassRoot(const ClassLinkerExtension &classLinkerExt, ClassRoot classRoot,
187                                    panda_file::Type::TypeId typeId)
188 {
189     std::string msg = "Test with class root ";
190     msg += std::to_string(static_cast<int>(classRoot));
191 
192     Class *klass = classLinkerExt.GetClassRoot(classRoot);
193     ASSERT_NE(klass, nullptr) << msg;
194     EXPECT_EQ(klass->GetBase(), nullptr) << msg;
195     EXPECT_EQ(klass->GetComponentSize(), 0U) << msg;
196     EXPECT_EQ(klass->GetFlags(), 0U) << msg;
197     EXPECT_EQ(klass->GetAccessFlags(), ACC_PUBLIC | ACC_FINAL | ACC_ABSTRACT) << msg;
198     EXPECT_EQ(klass->GetType().GetId(), typeId) << msg;
199     EXPECT_FALSE(klass->IsArrayClass()) << msg;
200     EXPECT_FALSE(klass->IsStringClass()) << msg;
201     EXPECT_TRUE(klass->IsPrimitive()) << msg;
202     EXPECT_TRUE(klass->IsAbstract()) << msg;
203     EXPECT_FALSE(klass->IsInstantiable()) << msg;
204 }
205 
GetComponentSize(ClassRoot componentRoot)206 static size_t GetComponentSize(ClassRoot componentRoot)
207 {
208     switch (componentRoot) {
209         case ClassRoot::U1:
210         case ClassRoot::I8:
211         case ClassRoot::U8:
212             return sizeof(uint8_t);
213         case ClassRoot::I16:
214         case ClassRoot::U16:
215             return sizeof(uint16_t);
216         case ClassRoot::I32:
217         case ClassRoot::U32:
218         case ClassRoot::F32:
219             return sizeof(uint32_t);
220         case ClassRoot::I64:
221         case ClassRoot::U64:
222         case ClassRoot::F64:
223             return sizeof(uint64_t);
224         default:
225             UNREACHABLE();
226     }
227 }
228 
TestArrayClassRoot(const ClassLinkerExtension & classLinkerExt,ClassRoot classRoot,ClassRoot componentRoot)229 static void TestArrayClassRoot(const ClassLinkerExtension &classLinkerExt, ClassRoot classRoot, ClassRoot componentRoot)
230 {
231     std::string msg = "Test with class root ";
232     msg += std::to_string(static_cast<int>(classRoot));
233 
234     Class *klass = classLinkerExt.GetClassRoot(classRoot);
235     Class *componentClass = classLinkerExt.GetClassRoot(componentRoot);
236     ASSERT_NE(klass, nullptr) << msg;
237     EXPECT_EQ(klass->GetBase(), classLinkerExt.GetClassRoot(ClassRoot::OBJECT)) << msg;
238     EXPECT_EQ(klass->GetComponentType(), componentClass) << msg;
239     EXPECT_EQ(klass->GetComponentSize(), GetComponentSize(componentRoot)) << msg;
240     EXPECT_EQ(klass->GetFlags(), 0U) << msg;
241     EXPECT_EQ(klass->GetAccessFlags(), ACC_PUBLIC | ACC_FINAL | ACC_ABSTRACT) << msg;
242     EXPECT_EQ(klass->GetType().GetId(), panda_file::Type::TypeId::REFERENCE) << msg;
243     EXPECT_EQ(klass->IsObjectArrayClass(), !componentClass->IsPrimitive()) << msg;
244     EXPECT_TRUE(klass->IsArrayClass()) << msg;
245     EXPECT_FALSE(klass->IsStringClass()) << msg;
246     EXPECT_FALSE(klass->IsPrimitive()) << msg;
247     EXPECT_TRUE(klass->IsAbstract()) << msg;
248     EXPECT_TRUE(klass->IsInstantiable()) << msg;
249 }
250 
TEST_F(ClassLinkerTest,TestClassRoots)251 TEST_F(ClassLinkerTest, TestClassRoots)
252 {
253     auto classLinker = CreateClassLinker(thread_);
254     ASSERT_NE(classLinker, nullptr);
255 
256     LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
257     auto *ext = classLinker->GetExtension(ctx);
258 
259     Class *objectClass = ext->GetClassRoot(ClassRoot::OBJECT);
260     ASSERT_NE(objectClass, nullptr);
261     EXPECT_EQ(objectClass->GetBase(), nullptr);
262     EXPECT_EQ(objectClass->GetComponentSize(), 0U);
263     EXPECT_EQ(objectClass->GetFlags(), 0U);
264     EXPECT_EQ(objectClass->GetType().GetId(), panda_file::Type::TypeId::REFERENCE);
265     EXPECT_FALSE(objectClass->IsArrayClass());
266     EXPECT_FALSE(objectClass->IsObjectArrayClass());
267     EXPECT_FALSE(objectClass->IsStringClass());
268     EXPECT_FALSE(objectClass->IsPrimitive());
269 
270     Class *stringClass = ext->GetClassRoot(ClassRoot::STRING);
271     ASSERT_NE(stringClass, nullptr);
272     EXPECT_EQ(stringClass->GetBase(), objectClass);
273     EXPECT_EQ(stringClass->GetComponentSize(), 0U);
274     EXPECT_EQ(stringClass->GetFlags(), Class::STRING_CLASS);
275     EXPECT_EQ(stringClass->GetType().GetId(), panda_file::Type::TypeId::REFERENCE);
276     EXPECT_FALSE(stringClass->IsArrayClass());
277     EXPECT_FALSE(stringClass->IsObjectArrayClass());
278     EXPECT_TRUE(stringClass->IsStringClass());
279     EXPECT_FALSE(stringClass->IsPrimitive());
280 
281     TestPrimitiveClassRoot(*ext, ClassRoot::U1, panda_file::Type::TypeId::U1);
282     TestPrimitiveClassRoot(*ext, ClassRoot::I8, panda_file::Type::TypeId::I8);
283     TestPrimitiveClassRoot(*ext, ClassRoot::U8, panda_file::Type::TypeId::U8);
284     TestPrimitiveClassRoot(*ext, ClassRoot::I16, panda_file::Type::TypeId::I16);
285     TestPrimitiveClassRoot(*ext, ClassRoot::U16, panda_file::Type::TypeId::U16);
286     TestPrimitiveClassRoot(*ext, ClassRoot::I32, panda_file::Type::TypeId::I32);
287     TestPrimitiveClassRoot(*ext, ClassRoot::U32, panda_file::Type::TypeId::U32);
288     TestPrimitiveClassRoot(*ext, ClassRoot::I64, panda_file::Type::TypeId::I64);
289     TestPrimitiveClassRoot(*ext, ClassRoot::U64, panda_file::Type::TypeId::U64);
290     TestPrimitiveClassRoot(*ext, ClassRoot::F32, panda_file::Type::TypeId::F32);
291     TestPrimitiveClassRoot(*ext, ClassRoot::F64, panda_file::Type::TypeId::F64);
292 
293     TestArrayClassRoot(*ext, ClassRoot::ARRAY_U1, ClassRoot::U1);
294     TestArrayClassRoot(*ext, ClassRoot::ARRAY_I8, ClassRoot::I8);
295     TestArrayClassRoot(*ext, ClassRoot::ARRAY_U8, ClassRoot::U8);
296     TestArrayClassRoot(*ext, ClassRoot::ARRAY_I16, ClassRoot::I16);
297     TestArrayClassRoot(*ext, ClassRoot::ARRAY_U16, ClassRoot::U16);
298     TestArrayClassRoot(*ext, ClassRoot::ARRAY_I32, ClassRoot::I32);
299     TestArrayClassRoot(*ext, ClassRoot::ARRAY_U32, ClassRoot::U32);
300     TestArrayClassRoot(*ext, ClassRoot::ARRAY_I64, ClassRoot::I64);
301     TestArrayClassRoot(*ext, ClassRoot::ARRAY_U64, ClassRoot::U64);
302     TestArrayClassRoot(*ext, ClassRoot::ARRAY_F32, ClassRoot::F32);
303     TestArrayClassRoot(*ext, ClassRoot::ARRAY_F64, ClassRoot::F64);
304 }
305 
306 struct FieldData {
307     // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
308     std::string name;
309     // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
310     size_t size;
311     // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
312     size_t offset;
313 
operator ==panda::test::FieldData314     bool operator==(const FieldData &other) const
315     {
316         return name == other.name && size == other.size && offset == other.offset;
317     }
318 
operator <<(std::ostream & os,const FieldData & fieldData)319     friend std::ostream &operator<<(std::ostream &os, const FieldData &fieldData)
320     {
321         return os << "{ name: \"" << fieldData.name << "\", size: " << fieldData.size
322                   << ", offset: " << fieldData.offset << " }";
323     }
324 };
325 
326 struct FieldDataHash {
operator ()panda::test::FieldDataHash327     size_t operator()(const FieldData &fieldData) const
328     {
329         return std::hash<std::string>()(fieldData.name);
330     }
331 };
332 
GetSize(const Field & field)333 size_t GetSize(const Field &field)
334 {
335     size_t size = 0;
336 
337     switch (field.GetTypeId()) {
338         case panda_file::Type::TypeId::U1:
339         case panda_file::Type::TypeId::I8:
340         case panda_file::Type::TypeId::U8: {
341             size = 1;
342             break;
343         }
344         case panda_file::Type::TypeId::I16:
345         case panda_file::Type::TypeId::U16: {
346             constexpr int SIZE_2 = 2;
347             size = SIZE_2;
348             break;
349         }
350         case panda_file::Type::TypeId::I32:
351         case panda_file::Type::TypeId::U32:
352         case panda_file::Type::TypeId::F32: {
353             constexpr int SIZE_4 = 4;
354             size = SIZE_4;
355             break;
356         }
357         case panda_file::Type::TypeId::I64:
358         case panda_file::Type::TypeId::U64:
359         case panda_file::Type::TypeId::F64: {
360             constexpr int SIZE_8 = 8;
361             size = SIZE_8;
362             break;
363         }
364         case panda_file::Type::TypeId::REFERENCE: {
365             size = ClassHelper::OBJECT_POINTER_SIZE;
366             break;
367         }
368         case panda_file::Type::TypeId::TAGGED: {
369             size = coretypes::TaggedValue::TaggedTypeSize();
370             break;
371         }
372         default: {
373             UNREACHABLE();
374             break;
375         }
376     }
377 
378     return size;
379 }
380 
UpdateOffsets(std::vector<FieldData> * fields,size_t offset)381 void UpdateOffsets(std::vector<FieldData> *fields, size_t offset)
382 {
383     for (auto &field : *fields) {
384         offset = AlignUp(offset, field.size);
385         field.offset = offset;
386         offset += field.size;
387     }
388 }
389 
390 static const char *sourceFieldLayout = R"(
391         .record R1 {}
392 
393         .record R2 {
394             # static fields
395 
396             u1  sf_u1  <static>
397             i16 sf_i16 <static>
398             i8  sf_i8  <static>
399             i32 sf_i32 <static>
400             u8  sf_u8  <static>
401             f64 sf_f64 <static>
402             u32 sf_u32 <static>
403             u16 sf_u16 <static>
404             i64 sf_i64 <static>
405             f32 sf_f32 <static>
406             u64 sf_u64 <static>
407             R1  sf_ref <static>
408             any sf_any <static>
409 
410             # instance fields
411 
412             i16 if_i16
413             u1  if_u1
414             i8  if_i8
415             f64 if_f64
416             i32 if_i32
417             u8  if_u8
418             u32 if_u32
419             u16 if_u16
420             f32 if_f32
421             i64 if_i64
422             u64 if_u64
423             R2  if_ref
424             any if_any
425         }
426     )";
427 
428 static std::vector<FieldData> sortedSfields {{"sf_ref", ClassHelper::OBJECT_POINTER_SIZE, 0},
429                                              {"sf_any", coretypes::TaggedValue::TaggedTypeSize(), 0},
430                                              {"sf_f64", sizeof(double), 0},
431                                              {"sf_i64", sizeof(int64_t), 0},
432                                              {"sf_u64", sizeof(uint64_t), 0},
433                                              {"sf_i32", sizeof(int32_t), 0},
434                                              {"sf_u32", sizeof(uint32_t), 0},
435                                              {"sf_f32", sizeof(float), 0},
436                                              {"sf_i16", sizeof(int16_t), 0},
437                                              {"sf_u16", sizeof(uint16_t), 0},
438                                              {"sf_u1", sizeof(uint8_t), 0},
439                                              {"sf_i8", sizeof(int8_t), 0},
440                                              {"sf_u8", sizeof(uint8_t), 0}};
441 
442 static std::vector<FieldData> sortedIfields {{"if_ref", ClassHelper::OBJECT_POINTER_SIZE, 0},
443                                              {"if_any", coretypes::TaggedValue::TaggedTypeSize(), 0},
444                                              {"if_f64", sizeof(double), 0},
445                                              {"if_i64", sizeof(int64_t), 0},
446                                              {"if_u64", sizeof(uint64_t), 0},
447                                              {"if_i32", sizeof(int32_t), 0},
448                                              {"if_u32", sizeof(uint32_t), 0},
449                                              {"if_f32", sizeof(float), 0},
450                                              {"if_i16", sizeof(int16_t), 0},
451                                              {"if_u16", sizeof(uint16_t), 0},
452                                              {"if_u1", sizeof(uint8_t), 0},
453                                              {"if_i8", sizeof(int8_t), 0},
454                                              {"if_u8", sizeof(uint8_t), 0}};
455 
TEST_F(ClassLinkerTest,FieldLayout)456 TEST_F(ClassLinkerTest, FieldLayout)
457 {
458     pandasm::Parser p;
459 
460     auto res = p.Parse(sourceFieldLayout);
461     auto pf = pandasm::AsmEmitter::Emit(res.Value());
462 
463     auto classLinker = CreateClassLinker(thread_);
464     ASSERT_NE(classLinker, nullptr);
465 
466     classLinker->AddPandaFile(std::move(pf));
467 
468     PandaString descriptor;
469     auto *ext = classLinker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
470     Class *klass = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("R2"), &descriptor));
471     ASSERT_NE(klass, nullptr);
472 
473     size_t offset = klass->GetStaticFieldsOffset();
474     if (!IsAligned<sizeof(double)>(offset + ClassHelper::OBJECT_POINTER_SIZE)) {
475         FieldData data {"sf_i32", sizeof(int32_t), 0};
476         // NOLINTNEXTLINE(bugprone-inaccurate-erase)
477         sortedSfields.erase(std::remove(sortedSfields.begin(), sortedSfields.end(), data));
478         sortedSfields.insert(sortedSfields.cbegin() + 1, data);
479     }
480 
481     UpdateOffsets(&sortedSfields, offset);
482     offset = ObjectHeader::ObjectHeaderSize();
483     if (!IsAligned<sizeof(double)>(offset + ClassHelper::OBJECT_POINTER_SIZE)) {
484         FieldData data {"if_i32", sizeof(int32_t), 0};
485         // NOLINTNEXTLINE(bugprone-inaccurate-erase)
486         sortedIfields.erase(std::remove(sortedIfields.begin(), sortedIfields.end(), data));
487         sortedIfields.insert(sortedIfields.cbegin() + 1, data);
488     }
489 
490     UpdateOffsets(&sortedIfields, offset);
491 
492     auto fieldCmp = [](const FieldData &f1, const FieldData &f2) { return f1.offset < f2.offset; };
493 
494     std::vector<FieldData> sfields;
495     for (const auto &field : klass->GetStaticFields()) {
496         sfields.push_back({utf::Mutf8AsCString(field.GetName().data), GetSize(field), field.GetOffset()});
497     }
498     std::sort(sfields.begin(), sfields.end(), fieldCmp);
499     EXPECT_EQ(sfields, sortedSfields);
500 
501     std::unordered_set<FieldData, FieldDataHash> ifields;
502 
503     for (const auto &field : klass->GetInstanceFields()) {
504         ifields.insert({utf::Mutf8AsCString(field.GetName().data), GetSize(field), field.GetOffset()});
505     }
506 
507     std::unordered_set<FieldData, FieldDataHash> sortedIfieldsSet(sortedIfields.cbegin(), sortedIfields.cend());
508     EXPECT_EQ(ifields, sortedIfieldsSet);
509 }
510 
TEST_F(ClassLinkerTest,ResolveExternalClass)511 TEST_F(ClassLinkerTest, ResolveExternalClass)
512 {
513     uint32_t offset;
514 
515     auto classLinker = CreateClassLinker(thread_);
516     ASSERT_NE(classLinker, nullptr);
517 
518     {
519         pandasm::Parser p;
520 
521         auto source = R"(
522             .record Ext.R <external>
523 
524             .function void main() {
525                 newarr v0, v0, Ext.R[]
526                 return.void
527             }
528         )";
529 
530         auto res = p.Parse(source);
531         ASSERT_TRUE(res);
532         auto pf = pandasm::AsmEmitter::Emit(res.Value());
533 
534         // 0 - "LExt/R;"
535         // 1 - "L_GLOBAL;"
536         // 2 - "[LExt/R;"
537         offset = pf->GetClasses()[2];
538 
539         classLinker->AddPandaFile(std::move(pf));
540     }
541 
542     PandaString descriptor;
543 
544     auto *ext = classLinker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
545     auto *klass = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("_GLOBAL"), &descriptor));
546     ASSERT_NE(klass, nullptr);
547 
548     auto *method = klass->GetDirectMethod(utf::CStringAsMutf8("main"));
549     ASSERT_NE(method, nullptr);
550 
551     auto *externalClass = classLinker->GetClass(*method, panda_file::File::EntityId(offset));
552     ASSERT_EQ(externalClass, nullptr);
553 
554     {
555         pandasm::Parser p;
556 
557         auto extSource = R"(
558             .record Ext {}
559             .record Ext.R {}
560         )";
561 
562         auto res = p.Parse(extSource);
563         auto extPf = pandasm::AsmEmitter::Emit(res.Value());
564 
565         classLinker->AddPandaFile(std::move(extPf));
566     }
567 
568     externalClass = classLinker->GetClass(*method, panda_file::File::EntityId(offset));
569     ASSERT_NE(externalClass, nullptr);
570 
571     EXPECT_STREQ(utf::Mutf8AsCString(externalClass->GetDescriptor()),
572                  utf::Mutf8AsCString(ClassHelper::GetArrayDescriptor(utf::CStringAsMutf8("Ext.R"), 1, &descriptor)));
573 }
574 
TEST_F(ClassLinkerTest,ArrayClass)575 TEST_F(ClassLinkerTest, ArrayClass)
576 {
577     pandasm::Parser p;
578 
579     auto source = R"(
580         .record R {}
581     )";
582 
583     auto res = p.Parse(source);
584     auto pf = pandasm::AsmEmitter::Emit(res.Value());
585 
586     auto classLinker = CreateClassLinker(thread_);
587     ASSERT_NE(classLinker, nullptr);
588 
589     classLinker->AddPandaFile(std::move(pf));
590 
591     PandaString descriptor;
592 
593     auto *ext = classLinker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
594     auto *klass = ext->GetClass(ClassHelper::GetArrayDescriptor(utf::CStringAsMutf8("UnknownClass"), 1, &descriptor));
595     ASSERT_EQ(klass, nullptr);
596 
597     // NOLINTNEXTLINE(readability-magic-numbers)
598     for (size_t i = 0; i < 256U; i++) {
599         auto *cls = ext->GetClass(ClassHelper::GetArrayDescriptor(utf::CStringAsMutf8("R"), i, &descriptor));
600         ASSERT_NE(cls, nullptr);
601         EXPECT_EQ(utf::Mutf8AsCString(cls->GetDescriptor()), descriptor);
602     }
603 }
604 
GetMethod(ClassLinker * classLinker,const char * className,const char * methodName,const panda::PandaString & signature)605 static Method *GetMethod(ClassLinker *classLinker, const char *className, const char *methodName,
606                          const panda::PandaString &signature)
607 {
608     PandaString descriptor;
609     auto *ext = classLinker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
610     auto *klass = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8(className), &descriptor));
611     auto *method = klass->GetDirectMethod(utf::CStringAsMutf8(methodName));
612     if (signature != method->GetProto().GetSignature()) {
613         return nullptr;
614     }
615     return method;
616 }
617 
GetMethodsSet(Span<Method> methods)618 static std::unordered_set<Method *> GetMethodsSet(Span<Method> methods)
619 {
620     std::unordered_set<Method *> set;
621     for (auto &method : methods) {
622         set.insert(&method);
623     }
624 
625     return set;
626 }
627 
TEST_F(ClassLinkerTest,VTable)628 TEST_F(ClassLinkerTest, VTable)
629 {
630     {
631         pandasm::Parser p;
632 
633         auto source = R"(
634             .record A {}
635 
636             .function void A.f1() {}
637             .function void A.f2(i32 a0) {}
638 
639             .function void A.f3(A a0) {}
640             .function void A.f4(A a0, i32 a1) {}
641         )";
642 
643         auto res = p.Parse(source);
644         auto pf = pandasm::AsmEmitter::Emit(res.Value());
645 
646         auto classLinker = CreateClassLinker(thread_);
647         ASSERT_NE(classLinker, nullptr);
648 
649         classLinker->AddPandaFile(std::move(pf));
650 
651         PandaString descriptor;
652 
653         auto *ext = classLinker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
654         auto *classA = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("A"), &descriptor));
655         ASSERT_NE(classA, nullptr);
656 
657         auto smethods = classA->GetStaticMethods();
658         ASSERT_EQ(smethods.size(), 2U);
659 
660         auto vmethods = classA->GetVirtualMethods();
661         ASSERT_EQ(vmethods.size(), 2U);
662 
663         {
664             auto set = GetMethodsSet(smethods);
665             ASSERT_NE(set.find(GetMethod(classLinker.get(), "A", "f1", "()V")), set.cend());
666             ASSERT_NE(set.find(GetMethod(classLinker.get(), "A", "f2", "(I)V")), set.cend());
667         }
668 
669         {
670             auto set = GetMethodsSet(vmethods);
671             ASSERT_NE(set.find(GetMethod(classLinker.get(), "A", "f3", "()V")), set.cend());
672             ASSERT_NE(set.find(GetMethod(classLinker.get(), "A", "f4", "(I)V")), set.cend());
673         }
674 
675         {
676             auto vtable = classA->GetVTable();
677             ASSERT_EQ(vtable.size(), vmethods.size());
678 
679             for (const auto &vmethod : vmethods) {
680                 ASSERT_EQ(vtable[vmethod.GetVTableIndex()], &vmethod);
681             }
682         }
683     }
684 }
685 
TEST_F(ClassLinkerTest,VTableInheritance)686 TEST_F(ClassLinkerTest, VTableInheritance)
687 {
688     {
689         pandasm::Parser p;
690 
691         auto source = R"(
692             .record A {}
693             .record B <extends=A> {}
694             .record C {}
695 
696             .function A A.f1(A a0, i32 a1) {}
697             .function A A.f2(A a0, C a1) {}
698 
699             .function B B.f1(B a0, i64 a1) {}
700             .function B B.f2(B a0, C a1) {}
701         )";
702 
703         auto res = p.Parse(source);
704         auto pf = pandasm::AsmEmitter::Emit(res.Value());
705 
706         auto classLinker = CreateClassLinker(thread_);
707         ASSERT_NE(classLinker, nullptr);
708 
709         classLinker->AddPandaFile(std::move(pf));
710 
711         PandaString descriptor;
712 
713         auto *ext = classLinker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
714         auto *classB = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("B"), &descriptor));
715         ASSERT_NE(classB, nullptr);
716 
717         auto *classA = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("A"), &descriptor));
718         ASSERT_NE(classA, nullptr);
719 
720         auto vtableB = classB->GetVTable();
721         ASSERT_EQ(vtableB.size(), 4U);
722 
723         auto vtableA = classA->GetVTable();
724         ASSERT_EQ(vtableA.size(), 2U);
725 
726         {
727             auto set = std::unordered_set<Method *> {};
728             for (const auto &m : vtableA) {
729                 set.insert(m);
730             }
731             ASSERT_NE(set.find(GetMethod(classLinker.get(), "A", "f1", "(I)LA;")), set.cend());
732             ASSERT_NE(set.find(GetMethod(classLinker.get(), "A", "f2", "(LC;)LA;")), set.cend());
733         }
734 
735         {
736             auto set = std::unordered_set<Method *> {};
737             for (const auto &m : vtableB) {
738                 set.insert(m);
739             }
740             ASSERT_NE(set.find(GetMethod(classLinker.get(), "A", "f1", "(I)LA;")), set.cend());
741             ASSERT_NE(set.find(GetMethod(classLinker.get(), "B", "f2", "(LC;)LB;")), set.cend());
742             ASSERT_NE(set.find(GetMethod(classLinker.get(), "B", "f1", "(J)LB;")), set.cend());
743         }
744     }
745 }
746 
TEST_F(ClassLinkerTest,PrimitiveClasses)747 TEST_F(ClassLinkerTest, PrimitiveClasses)
748 {
749     auto classLinker = CreateClassLinker(thread_);
750     ASSERT_NE(classLinker, nullptr);
751 
752     LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
753     auto *ext = classLinker->GetExtension(ctx);
754 
755     PandaString descriptor;
756 
757     auto type = panda_file::Type(panda_file::Type::TypeId::I32);
758 
759     auto *primitiveClass = ext->GetClass(ClassHelper::GetPrimitiveDescriptor(type, &descriptor));
760     ASSERT_NE(primitiveClass, nullptr);
761     EXPECT_STREQ(utf::Mutf8AsCString(primitiveClass->GetDescriptor()),
762                  utf::Mutf8AsCString(ClassHelper::GetPrimitiveDescriptor(type, &descriptor)));
763 
764     auto *primitiveArrayClass1 = ext->GetClass(ClassHelper::GetPrimitiveArrayDescriptor(type, 1, &descriptor));
765     ASSERT_NE(primitiveArrayClass1, nullptr);
766     EXPECT_STREQ(utf::Mutf8AsCString(primitiveArrayClass1->GetDescriptor()),
767                  utf::Mutf8AsCString(ClassHelper::GetPrimitiveArrayDescriptor(type, 1, &descriptor)));
768 
769     static constexpr int size2 = 2;
770     auto *primitiveArrayClass2 = ext->GetClass(ClassHelper::GetPrimitiveArrayDescriptor(type, size2, &descriptor));
771     ASSERT_NE(primitiveArrayClass2, nullptr);
772     EXPECT_STREQ(utf::Mutf8AsCString(primitiveArrayClass2->GetDescriptor()),
773                  utf::Mutf8AsCString(ClassHelper::GetPrimitiveArrayDescriptor(type, size2, &descriptor)));
774 }
775 
776 class TestClassLinkerContext : public ClassLinkerContext {
777 public:
TestClassLinkerContext(const uint8_t * descriptor,bool needCopyDescriptor,Class * klass,panda_file::SourceLang lang)778     TestClassLinkerContext(const uint8_t *descriptor, bool needCopyDescriptor, Class *klass,
779                            panda_file::SourceLang lang)
780         : ClassLinkerContext(lang), descriptor_(descriptor), needCopyDescriptor_(needCopyDescriptor), klass_(klass)
781     {
782     }
783 
LoadClass(const uint8_t * descriptor,bool needCopyDescriptor,ClassLinkerErrorHandler * errorHandler)784     Class *LoadClass(const uint8_t *descriptor, bool needCopyDescriptor,
785                      [[maybe_unused]] ClassLinkerErrorHandler *errorHandler) override
786     {
787         isSuccess_ = utf::IsEqual(descriptor, descriptor_) && needCopyDescriptor == needCopyDescriptor_;
788         InsertClass(klass_);
789         return klass_;
790     }
791 
IsSuccess() const792     bool IsSuccess() const
793     {
794         return isSuccess_;
795     }
796 
797 private:
798     const uint8_t *descriptor_;
799     bool needCopyDescriptor_ {};
800     Class *klass_;
801     bool isSuccess_ {false};
802 };
803 
804 static const char *SOURCE_LOAD_CONTEXT = R"(
805         .record A {}
806         .record B {}
807     )";
808 
TEST_F(ClassLinkerTest,LoadContext)809 TEST_F(ClassLinkerTest, LoadContext)
810 {
811     pandasm::Parser p;
812 
813     auto classLinker = CreateClassLinker(thread_);
814     ASSERT_NE(classLinker, nullptr);
815 
816     auto parsValueEmit = pandasm::AsmEmitter::Emit(p.Parse(SOURCE_LOAD_CONTEXT).Value());
817     classLinker->AddPandaFile(std::move(parsValueEmit));
818 
819     PandaString descriptor;
820     auto *ext = classLinker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
821     auto *classA = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("A"), &descriptor));
822 
823     ASSERT_NE(classA, nullptr);
824     ASSERT_EQ(classA->GetLoadContext()->IsBootContext(), true);
825 
826     auto *classB = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("B"), &descriptor));
827 
828     ASSERT_NE(classB, nullptr);
829     ASSERT_EQ(classB->GetLoadContext()->IsBootContext(), true);
830 
831     auto *desc = ClassHelper::GetDescriptor(utf::CStringAsMutf8("B"), &descriptor);
832     TestClassLinkerContext ctx(desc, true, classB, panda_file::SourceLang::PANDA_ASSEMBLY);
833 
834     ASSERT_EQ(ext->GetClass(desc, true, &ctx), classB);
835     ASSERT_TRUE(ctx.IsSuccess());
836 
837     bool isMatched = false;
838     ctx.EnumerateClasses([&isMatched](Class *klass) {
839         isMatched = klass->GetName() == "B";
840         return true;
841     });
842 
843     ASSERT_TRUE(isMatched);
844 
845     auto *classArrayB =
846         classLinker->GetClass(ClassHelper::GetArrayDescriptor(utf::CStringAsMutf8("B"), 1, &descriptor), true, &ctx);
847 
848     ASSERT_NE(classArrayB, nullptr);
849     ASSERT_EQ(classArrayB->GetLoadContext(), ext->GetBootContext());
850 
851     PandaUnorderedSet<Class *> expected {classB};
852     PandaUnorderedSet<Class *> classesCtx;
853     ctx.EnumerateClasses([&](Class *klass) {
854         classesCtx.insert(klass);
855         return true;
856     });
857 
858     ASSERT_EQ(classesCtx, expected);
859 
860     PandaUnorderedSet<Class *> classes;
861     classLinker->EnumerateClasses([&](Class *klass) {
862         classes.insert(klass);
863         return true;
864     });
865 
866     ASSERT_NE(classes.find(classA), classes.cend());
867     ASSERT_EQ(*classes.find(classA), classA);
868 
869     ASSERT_NE(classes.find(classB), classes.cend());
870     ASSERT_EQ(*classes.find(classB), classB);
871 
872     ASSERT_NE(classes.find(classArrayB), classes.cend());
873     ASSERT_EQ(*classes.find(classArrayB), classArrayB);
874 }
875 
876 static const char *SOURCE_ACCESSES = R"(
877         .record A <access.record=private> {}
878 
879         .record C <access.record=protected> {}
880 
881         .record B <access.record=public> {
882             i32 pub <access.field=public>
883             i32 prt <access.field=protected>
884             i32 prv <access.field=private>
885         }
886 
887         .function void f() <access.function=public> {}
888         .function void A.f() <access.function=protected> {}
889         .function void B.f() <access.function=private> {}
890     )";
891 
TEST_F(ClassLinkerTest,Accesses)892 TEST_F(ClassLinkerTest, Accesses)
893 {
894     auto pf = pandasm::AsmEmitter::Emit(pandasm::Parser {}.Parse(SOURCE_ACCESSES).Value());
895 
896     auto classLinker = CreateClassLinker(thread_);
897     ASSERT_NE(classLinker, nullptr);
898 
899     LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
900     auto *ext = classLinker->GetExtension(ctx);
901 
902     classLinker->AddPandaFile(std::move(pf));
903 
904     // Global
905     {
906         PandaString descriptor;
907         auto klass = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("_GLOBAL"), &descriptor));
908         ASSERT_NE(klass, nullptr);
909         auto f = klass->GetClassMethod(utf::CStringAsMutf8("f"));
910         ASSERT_NE(f, nullptr);
911         ASSERT_TRUE(f->IsPublic());
912     }
913 
914     // record A
915     {
916         PandaString descriptor;
917         auto klass = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("A"), &descriptor));
918         ASSERT_NE(klass, nullptr);
919         ASSERT_TRUE(klass->IsPrivate());
920         auto f = klass->GetClassMethod(utf::CStringAsMutf8("f"));
921         ASSERT_NE(f, nullptr);
922         ASSERT_TRUE(f->IsProtected());
923     }
924 
925     // record B
926     {
927         PandaString descriptor;
928         auto klass = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("B"), &descriptor));
929         ASSERT_NE(klass, nullptr);
930         ASSERT_TRUE(klass->IsPublic());
931         auto f = klass->GetClassMethod(utf::CStringAsMutf8("f"));
932         ASSERT_NE(f, nullptr);
933         ASSERT_TRUE(f->IsPrivate());
934 
935         auto i = 0;
936         auto accessPredicates =
937             std::array {&panda::Field::IsPublic, &panda::Field::IsProtected, &panda::Field::IsPrivate};
938         for (const auto &field : klass->GetFields()) {
939             ASSERT_TRUE((field.*accessPredicates[i])());
940             i++;
941         }
942         ASSERT_EQ(i, klass->GetFields().size());
943     }
944 
945     // record C
946     {
947         PandaString descriptor;
948         auto klass = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("C"), &descriptor));
949         ASSERT_NE(klass, nullptr);
950         ASSERT_TRUE(klass->IsProtected());
951     }
952 }
953 
TEST_F(ClassLinkerTest,Inheritance)954 TEST_F(ClassLinkerTest, Inheritance)
955 {
956     auto source = R"(
957         .record A {}
958         .record B <extends=A> {}
959     )";
960 
961     auto res = pandasm::Parser {}.Parse(source);
962     auto pf = pandasm::AsmEmitter::Emit(res.Value());
963 
964     auto classLinker = CreateClassLinker(thread_);
965     ASSERT_NE(classLinker, nullptr);
966 
967     LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
968     auto *ext = classLinker->GetExtension(ctx);
969 
970     classLinker->AddPandaFile(std::move(pf));
971 
972     PandaString descriptor;
973 
974     auto classA = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("A"), &descriptor));
975     ASSERT_NE(classA, nullptr);
976 
977     auto classB = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("B"), &descriptor));
978     ASSERT_NE(classB, nullptr);
979 
980     ASSERT_EQ(classA->GetBase(), ext->GetClassRoot(ClassRoot::OBJECT));
981     ASSERT_EQ(classB->GetBase(), classA);
982 
983     ASSERT_TRUE(classB->IsSubClassOf(classA));
984 }
985 
TEST_F(ClassLinkerTest,IsSubClassOf)986 TEST_F(ClassLinkerTest, IsSubClassOf)
987 {
988     auto source = R"(
989         .record A {}
990         .record B <extends=A> {}
991         .record C <extends=B> {}
992     )";
993 
994     auto res = pandasm::Parser {}.Parse(source);
995     auto pf = pandasm::AsmEmitter::Emit(res.Value());
996 
997     auto classLinker = CreateClassLinker(thread_);
998     ASSERT_NE(classLinker, nullptr);
999 
1000     LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
1001     auto *ext = classLinker->GetExtension(ctx);
1002 
1003     classLinker->AddPandaFile(std::move(pf));
1004 
1005     PandaString descriptor;
1006 
1007     auto classA = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("A"), &descriptor));
1008     ASSERT_NE(classA, nullptr);
1009 
1010     auto classB = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("B"), &descriptor));
1011     ASSERT_NE(classB, nullptr);
1012 
1013     auto classC = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("C"), &descriptor));
1014     ASSERT_NE(classC, nullptr);
1015 
1016     ASSERT_TRUE(classA->IsSubClassOf(classA));
1017     ASSERT_FALSE(classA->IsSubClassOf(classB));
1018     ASSERT_FALSE(classA->IsSubClassOf(classC));
1019 
1020     ASSERT_TRUE(classB->IsSubClassOf(classA));
1021     ASSERT_TRUE(classB->IsSubClassOf(classB));
1022     ASSERT_FALSE(classB->IsSubClassOf(classC));
1023 
1024     ASSERT_TRUE(classC->IsSubClassOf(classA));
1025     ASSERT_TRUE(classC->IsSubClassOf(classB));
1026     ASSERT_TRUE(classC->IsSubClassOf(classC));
1027 }
1028 
TEST_F(ClassLinkerTest,Final)1029 TEST_F(ClassLinkerTest, Final)
1030 {
1031     auto source = R"(
1032         .record A <final> {}
1033 
1034         .record B {
1035             i32 fld <final>
1036         }
1037 
1038         .function void B.f(B a0) <final> {}
1039     )";
1040 
1041     auto res = pandasm::Parser {}.Parse(source);
1042     auto pf = pandasm::AsmEmitter::Emit(res.Value());
1043 
1044     auto classLinker = CreateClassLinker(thread_);
1045     ASSERT_NE(classLinker, nullptr);
1046 
1047     LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
1048     auto *ext = classLinker->GetExtension(ctx);
1049 
1050     classLinker->AddPandaFile(std::move(pf));
1051 
1052     // record A
1053     {
1054         PandaString descriptor;
1055         auto klass = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("A"), &descriptor));
1056         ASSERT_NE(klass, nullptr);
1057         ASSERT_TRUE(klass->IsFinal());
1058     }
1059 
1060     // record B
1061     {
1062         PandaString descriptor;
1063         auto klass = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("B"), &descriptor));
1064         ASSERT_NE(klass, nullptr);
1065 
1066         auto f = klass->GetClassMethod(utf::CStringAsMutf8("f"));
1067         ASSERT_NE(f, nullptr);
1068         ASSERT_TRUE(f->IsFinal());
1069 
1070         ASSERT_EQ(klass->GetFields().size(), 1);
1071         for (const auto &field : klass->GetFields()) {
1072             ASSERT_TRUE(field.IsFinal());
1073         }
1074     }
1075 }
1076 
1077 }  // namespace panda::test
1078