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