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