1 /**
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "runtime/include/class_linker.h"
17 #include "runtime/bridge/bridge.h"
18 #include "runtime/cha.h"
19 #include "runtime/class_initializer.h"
20 #include "runtime/include/coretypes/array.h"
21 #include "runtime/include/coretypes/string.h"
22 #include "runtime/include/field.h"
23 #include "runtime/include/itable_builder.h"
24 #include "runtime/include/method.h"
25 #include "runtime/include/panda_vm.h"
26 #include "runtime/include/runtime.h"
27 #include "runtime/include/runtime_notification.h"
28 #include "libpandabase/macros.h"
29 #include "libpandabase/mem/mem.h"
30 #include "libpandabase/utils/bit_utils.h"
31 #include "libpandabase/utils/span.h"
32 #include "libpandabase/utils/utf.h"
33 #include "libpandafile/class_data_accessor-inl.h"
34 #include "libpandafile/code_data_accessor-inl.h"
35 #include "libpandafile/field_data_accessor-inl.h"
36 #include "libpandafile/method_data_accessor-inl.h"
37 #include "libpandafile/modifiers.h"
38 #include "libpandafile/panda_cache.h"
39 #include "libpandafile/proto_data_accessor-inl.h"
40 #include "runtime/include/tooling/debug_inf.h"
41 #include "trace/trace.h"
42
43 namespace panda {
44
45 using Type = panda_file::Type;
46 using SourceLang = panda_file::SourceLang;
47
AddPandaFile(std::unique_ptr<const panda_file::File> && pf,ClassLinkerContext * context)48 void ClassLinker::AddPandaFile(std::unique_ptr<const panda_file::File> &&pf, ClassLinkerContext *context)
49 {
50 ASSERT(pf != nullptr);
51
52 const panda_file::File *file = pf.get();
53
54 SCOPED_TRACE_STREAM << __FUNCTION__ << " " << file->GetFilename();
55
56 {
57 os::memory::LockHolder lock {pandaFilesLock_};
58 pandaFiles_.push_back({context, std::forward<std::unique_ptr<const panda_file::File>>(pf)});
59 }
60
61 if (context == nullptr || context->IsBootContext()) {
62 os::memory::LockHolder lock {bootPandaFilesLock_};
63 bootPandaFiles_.push_back(file);
64 }
65
66 if (Runtime::GetCurrent()->IsInitialized()) {
67 // LoadModule for initial boot files is called in runtime
68 Runtime::GetCurrent()->GetNotificationManager()->LoadModuleEvent(file->GetFilename());
69 }
70
71 tooling::DebugInf::AddCodeMetaInfo(file);
72 }
73
FreeClassData(Class * classPtr)74 void ClassLinker::FreeClassData(Class *classPtr)
75 {
76 Span<Field> fields = classPtr->GetFields();
77 if (fields.Size() > 0) {
78 allocator_->Free(fields.begin());
79 classPtr->SetFields(Span<Field>(), 0);
80 }
81 Span<Method> methods = classPtr->GetMethods();
82 size_t n = methods.Size() + classPtr->GetNumCopiedMethods();
83 if (n > 0) {
84 mem::InternalAllocatorPtr allocator = Runtime::GetCurrent()->GetInternalAllocator();
85 for (auto &method : methods) {
86 // We create Profiling data in method class via InternalAllocator.
87 // Therefore, we should delete it via InternalAllocator too.
88 allocator->Free(method.GetProfilingData());
89 }
90 allocator_->Free(methods.begin());
91 classPtr->SetMethods(Span<Method>(), 0, 0);
92 }
93 bool hasOwnItable = !classPtr->IsArrayClass();
94 auto itable = classPtr->GetITable().Get();
95 if (hasOwnItable && !itable.Empty()) {
96 for (size_t i = 0; i < itable.Size(); i++) {
97 Span<Method *> imethods = itable[i].GetMethods();
98 if (!imethods.Empty()) {
99 allocator_->Free(imethods.begin());
100 }
101 }
102 allocator_->Free(itable.begin());
103 classPtr->SetITable(ITable());
104 }
105 Span<Class *> interfaces = classPtr->GetInterfaces();
106 if (!interfaces.Empty()) {
107 allocator_->Free(interfaces.begin());
108 classPtr->SetInterfaces(Span<Class *>());
109 }
110 }
111
FreeClass(Class * classPtr)112 void ClassLinker::FreeClass(Class *classPtr)
113 {
114 FreeClassData(classPtr);
115 GetExtension(classPtr->GetSourceLang())->FreeClass(classPtr);
116 }
117
~ClassLinker()118 ClassLinker::~ClassLinker()
119 {
120 for (auto &copiedName : copiedNames_) {
121 allocator_->Free(reinterpret_cast<void *>(const_cast<uint8_t *>(copiedName)));
122 }
123 }
124
ClassLinker(mem::InternalAllocatorPtr allocator,std::vector<std::unique_ptr<ClassLinkerExtension>> && extensions)125 ClassLinker::ClassLinker(mem::InternalAllocatorPtr allocator,
126 std::vector<std::unique_ptr<ClassLinkerExtension>> &&extensions)
127 : allocator_(allocator), aotManager_(MakePandaUnique<AotManager>()), copiedNames_(allocator->Adapter())
128 {
129 for (auto &ext : extensions) {
130 extensions_[panda::panda_file::GetLangArrIndex(ext->GetLanguage())] = std::move(ext);
131 }
132 }
133
ResetExtension(panda_file::SourceLang lang)134 void ClassLinker::ResetExtension(panda_file::SourceLang lang)
135 {
136 extensions_[panda::panda_file::GetLangArrIndex(lang)] =
137 Runtime::GetCurrent()->GetLanguageContext(lang).CreateClassLinkerExtension();
138 }
139
140 template <class T, class... Args>
InitializeMemory(T * mem,Args...args)141 static T *InitializeMemory(T *mem, Args... args)
142 {
143 return new (mem) T(std::forward<Args>(args)...);
144 }
145
Initialize(bool compressedStringEnabled)146 bool ClassLinker::Initialize(bool compressedStringEnabled)
147 {
148 if (isInitialized_) {
149 return true;
150 }
151
152 for (auto &ext : extensions_) {
153 if (ext == nullptr) {
154 continue;
155 }
156
157 if (!ext->Initialize(this, compressedStringEnabled)) {
158 return false;
159 }
160 }
161
162 isInitialized_ = true;
163
164 return true;
165 }
166
InitializeRoots(ManagedThread * thread)167 bool ClassLinker::InitializeRoots(ManagedThread *thread)
168 {
169 for (auto &ext : extensions_) {
170 if (ext == nullptr) {
171 continue;
172 }
173
174 if (!ext->InitializeRoots(thread)) {
175 return false;
176 }
177 }
178
179 return true;
180 }
181
182 using ClassEntry = std::pair<panda_file::File::EntityId, const panda_file::File *>;
183 using PandaFiles = PandaVector<const panda_file::File *>;
184
FindClassInPandaFiles(const uint8_t * descriptor,const PandaFiles & pandaFiles)185 static ClassEntry FindClassInPandaFiles(const uint8_t *descriptor, const PandaFiles &pandaFiles)
186 {
187 for (auto *pf : pandaFiles) {
188 auto classId = pf->GetClassId(descriptor);
189 if (classId.IsValid() && !pf->IsExternal(classId)) {
190 return {classId, pf};
191 }
192 }
193
194 return {};
195 }
196
FindLoadedClass(const uint8_t * descriptor,ClassLinkerContext * context)197 Class *ClassLinker::FindLoadedClass(const uint8_t *descriptor, ClassLinkerContext *context)
198 {
199 ASSERT(context != nullptr);
200 return context->FindClass(descriptor);
201 }
202
203 template <class ClassDataAccessorT>
GetClassSize(ClassDataAccessorT dataAccessor,size_t vtableSize,size_t imtSize,size_t * outNumSfields)204 static size_t GetClassSize(ClassDataAccessorT dataAccessor, size_t vtableSize, size_t imtSize, size_t *outNumSfields)
205 {
206 size_t num8bitSfields = 0;
207 size_t num16bitSfields = 0;
208 size_t num32bitSfields = 0;
209 size_t num64bitSfields = 0;
210 size_t numRefSfields = 0;
211 size_t numTaggedSfields = 0;
212
213 size_t numSfields = 0;
214
215 dataAccessor.template EnumerateStaticFieldTypes([&num8bitSfields, &num16bitSfields, &num32bitSfields,
216 &num64bitSfields, &numRefSfields, &numTaggedSfields,
217 &numSfields](Type fieldType) {
218 ++numSfields;
219
220 switch (fieldType.GetId()) {
221 case Type::TypeId::U1:
222 case Type::TypeId::I8:
223 case Type::TypeId::U8:
224 ++num8bitSfields;
225 break;
226 case Type::TypeId::I16:
227 case Type::TypeId::U16:
228 ++num16bitSfields;
229 break;
230 case Type::TypeId::I32:
231 case Type::TypeId::U32:
232 case Type::TypeId::F32:
233 ++num32bitSfields;
234 break;
235 case Type::TypeId::I64:
236 case Type::TypeId::U64:
237 case Type::TypeId::F64:
238 ++num64bitSfields;
239 break;
240 case Type::TypeId::REFERENCE:
241 ++numRefSfields;
242 break;
243 case Type::TypeId::TAGGED:
244 ++numTaggedSfields;
245 break;
246 default:
247 UNREACHABLE();
248 break;
249 }
250 });
251
252 *outNumSfields = numSfields;
253
254 return Class::ComputeClassSize(vtableSize, imtSize, num8bitSfields, num16bitSfields, num32bitSfields,
255 num64bitSfields, numRefSfields, numTaggedSfields);
256 }
257
258 class ClassDataAccessorWrapper {
259 public:
ClassDataAccessorWrapper(panda_file::ClassDataAccessor * dataAccessor=nullptr)260 explicit ClassDataAccessorWrapper(panda_file::ClassDataAccessor *dataAccessor = nullptr)
261 : dataAccessor_(dataAccessor)
262 {
263 }
264
265 template <class Callback>
EnumerateStaticFieldTypes(const Callback & cb) const266 void EnumerateStaticFieldTypes(const Callback &cb) const
267 {
268 dataAccessor_->EnumerateFields([cb](panda_file::FieldDataAccessor &fda) {
269 if (!fda.IsStatic()) {
270 return;
271 }
272
273 cb(Type::GetTypeFromFieldEncoding(fda.GetType()));
274 });
275 }
276
277 ~ClassDataAccessorWrapper() = default;
278
279 DEFAULT_COPY_SEMANTIC(ClassDataAccessorWrapper);
280 DEFAULT_MOVE_SEMANTIC(ClassDataAccessorWrapper);
281
282 private:
283 panda_file::ClassDataAccessor *dataAccessor_;
284 };
285
GetClassInfo(panda_file::ClassDataAccessor * dataAccessor,Class * base,Span<Class * > interfaces,ClassLinkerContext * context)286 ClassLinker::ClassInfo ClassLinker::GetClassInfo(panda_file::ClassDataAccessor *dataAccessor, Class *base,
287 Span<Class *> interfaces, ClassLinkerContext *context)
288 {
289 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(dataAccessor);
290
291 auto vtableBuilder = ctx.CreateVTableBuilder();
292 auto itableBuilder = ctx.CreateITableBuilder();
293 auto imtableBuilder = ctx.CreateIMTableBuilder();
294
295 itableBuilder->Build(this, base, interfaces, dataAccessor->IsInterface());
296 vtableBuilder->Build(dataAccessor, base, itableBuilder->GetITable(), context);
297 imtableBuilder->Build(dataAccessor, itableBuilder->GetITable());
298
299 ClassDataAccessorWrapper dataAccessorWrapper(dataAccessor);
300 size_t numSfields = 0;
301 size_t size =
302 GetClassSize(dataAccessorWrapper, vtableBuilder->GetVTableSize(), imtableBuilder->GetIMTSize(), &numSfields);
303
304 return {size, numSfields, std::move(vtableBuilder), std::move(itableBuilder), std::move(imtableBuilder)};
305 }
306
307 class ClassDataAccessor {
308 public:
ClassDataAccessor(Span<Field> fields)309 explicit ClassDataAccessor(Span<Field> fields) : fields_(fields) {}
310
311 template <class Callback>
EnumerateStaticFieldTypes(const Callback & cb) const312 void EnumerateStaticFieldTypes(const Callback &cb) const
313 {
314 for (const auto &field : fields_) {
315 if (!field.IsStatic()) {
316 continue;
317 }
318
319 cb(field.GetType());
320 }
321 }
322
323 ~ClassDataAccessor() = default;
324
325 DEFAULT_COPY_SEMANTIC(ClassDataAccessor);
326 DEFAULT_MOVE_SEMANTIC(ClassDataAccessor);
327
328 private:
329 Span<Field> fields_;
330 };
331
GetClassInfo(Span<Method> methods,Span<Field> fields,Class * base,Span<Class * > interfaces,bool isInterface)332 ClassLinker::ClassInfo ClassLinker::GetClassInfo(Span<Method> methods, Span<Field> fields, Class *base,
333 Span<Class *> interfaces, bool isInterface)
334 {
335 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(*base);
336
337 auto vtableBuilder = ctx.CreateVTableBuilder();
338 auto itableBuilder = ctx.CreateITableBuilder();
339 auto imtableBuilder = ctx.CreateIMTableBuilder();
340
341 itableBuilder->Build(this, base, interfaces, isInterface);
342 vtableBuilder->Build(methods, base, itableBuilder->GetITable(), isInterface);
343 imtableBuilder->Build(itableBuilder->GetITable(), isInterface);
344
345 ClassDataAccessor dataAccessor(fields);
346 size_t numSfields = 0;
347 size_t size = GetClassSize(dataAccessor, vtableBuilder->GetVTableSize(), imtableBuilder->GetIMTSize(), &numSfields);
348
349 return {size, numSfields, std::move(vtableBuilder), std::move(itableBuilder), std::move(imtableBuilder)};
350 }
351
LoadMethod(Method * method,panda_file::MethodDataAccessor * methodDataAccessor,Class * klass,const LanguageContext & ctx,const ClassLinkerExtension * ext)352 static void LoadMethod(Method *method, panda_file::MethodDataAccessor *methodDataAccessor, Class *klass,
353 const LanguageContext &ctx, const ClassLinkerExtension *ext)
354 {
355 const auto &pf = methodDataAccessor->GetPandaFile();
356 panda_file::ProtoDataAccessor pda(pf, methodDataAccessor->GetProtoId());
357
358 uint32_t accessFlags = methodDataAccessor->GetAccessFlags();
359
360 auto *methodName = pf.GetStringData(methodDataAccessor->GetNameId()).data;
361 if (utf::IsEqual(methodName, ctx.GetCtorName()) || utf::IsEqual(methodName, ctx.GetCctorName())) {
362 accessFlags |= ACC_CONSTRUCTOR;
363 }
364
365 auto codeId = methodDataAccessor->GetCodeId();
366 size_t numArgs = (methodDataAccessor->IsStatic()) ? pda.GetNumArgs() : (pda.GetNumArgs() + 1);
367
368 if (!codeId.has_value()) {
369 InitializeMemory(method, klass, &pf, methodDataAccessor->GetMethodId(), panda_file::File::EntityId(0),
370 accessFlags, numArgs, reinterpret_cast<const uint16_t *>(pda.GetShorty().Data()));
371
372 if (methodDataAccessor->IsNative()) {
373 method->SetCompiledEntryPoint(ext->GetNativeEntryPointFor(method));
374 } else {
375 method->SetInterpreterEntryPoint();
376 }
377 } else {
378 InitializeMemory(method, klass, &pf, methodDataAccessor->GetMethodId(), codeId.value(), accessFlags, numArgs,
379 reinterpret_cast<const uint16_t *>(pda.GetShorty().Data()));
380 method->SetCompiledEntryPoint(GetCompiledCodeToInterpreterBridge(method));
381 }
382 }
383
MaybeLinkMethodToAotCode(Method * method,const compiler::AotClass & aotClass,size_t methodIndex)384 void MaybeLinkMethodToAotCode(Method *method, const compiler::AotClass &aotClass, size_t methodIndex)
385 {
386 ASSERT(aotClass.IsValid());
387 if (method->IsIntrinsic()) {
388 return;
389 }
390 auto entry = aotClass.FindMethodCodeEntry(methodIndex);
391 if (entry != nullptr) {
392 method->SetCompiledEntryPoint(entry);
393 LOG(INFO, AOT) << "Found AOT entrypoint ["
394 << reinterpret_cast<const void *>(aotClass.FindMethodCodeSpan(methodIndex).data()) << ":"
395 << reinterpret_cast<const void *>(aotClass.FindMethodCodeSpan(methodIndex).end())
396 << "] for method: " << method->GetFullName();
397
398 EVENT_AOT_ENTRYPOINT_FOUND(method->GetFullName());
399 ASSERT(aotClass.FindMethodHeader(methodIndex)->methodId == method->GetFileId().GetOffset());
400 }
401 }
402
LoadMethods(Class * klass,ClassInfo * classInfo,panda_file::ClassDataAccessor * dataAccessor,ClassLinkerErrorHandler * errorHandler)403 bool ClassLinker::LoadMethods(Class *klass, ClassInfo *classInfo, panda_file::ClassDataAccessor *dataAccessor,
404 [[maybe_unused]] ClassLinkerErrorHandler *errorHandler)
405 {
406 uint32_t numMethods = dataAccessor->GetMethodsNumber();
407
408 uint32_t numVmethods = klass->GetNumVirtualMethods();
409 uint32_t numSmethods = numMethods - numVmethods;
410
411 auto &copiedMethods = classInfo->vtableBuilder->GetCopiedMethods();
412 uint32_t n = numMethods + copiedMethods.size();
413 if (n == 0) {
414 return true;
415 }
416
417 Span<Method> methods {allocator_->AllocArray<Method>(n), n};
418
419 size_t smethodIdx = numVmethods;
420 size_t vmethodIdx = 0;
421
422 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(*klass);
423 auto *ext = GetExtension(ctx);
424 ASSERT(ext != nullptr);
425
426 auto aotPfile = aotManager_->FindPandaFile(klass->GetPandaFile()->GetFullFileName());
427 if (aotPfile != nullptr) {
428 EVENT_AOT_LOADED_FOR_CLASS(PandaString(aotPfile->GetFileName()), PandaString(klass->GetName()));
429 }
430
431 compiler::AotClass aotClass =
432 (aotPfile != nullptr) ? aotPfile->GetClass(klass->GetFileId().GetOffset()) : compiler::AotClass::Invalid();
433
434 size_t methodIndex = 0;
435 dataAccessor->EnumerateMethods([klass, &smethodIdx, &vmethodIdx, &methods, aotClass, ctx, ext,
436 &methodIndex](panda_file::MethodDataAccessor &methodDataAccessor) {
437 Method *method = methodDataAccessor.IsStatic() ? &methods[smethodIdx++] : &methods[vmethodIdx++];
438 LoadMethod(method, &methodDataAccessor, klass, ctx, ext);
439 if (aotClass.IsValid()) {
440 MaybeLinkMethodToAotCode(method, aotClass, methodIndex);
441 }
442 // Instead of checking if the method is abstract before every virtual call
443 // the special stub throwing AbstractMethodError is registered as compiled entry point.
444 if (method->IsAbstract()) {
445 method->SetCompiledEntryPoint(GetAbstractMethodStub());
446 }
447 methodIndex++;
448 });
449
450 for (size_t i = 0; i < copiedMethods.size(); i++) {
451 size_t idx = numMethods + i;
452 InitializeMemory(&methods[idx], copiedMethods[i].method);
453 methods[idx].SetIsDefaultInterfaceMethod();
454 if (copiedMethods[i].defaultAbstract) {
455 methods[idx].SetCompiledEntryPoint(GetAbstractMethodStub());
456 }
457 if (copiedMethods[i].defaultConflict) {
458 methods[idx].SetCompiledEntryPoint(GetDefaultConflictMethodStub());
459 }
460 }
461
462 klass->SetMethods(methods, numVmethods, numSmethods);
463
464 return true;
465 }
466
LoadFields(Class * klass,panda_file::ClassDataAccessor * dataAccessor,ClassLinkerErrorHandler * errorHandler)467 bool ClassLinker::LoadFields(Class *klass, panda_file::ClassDataAccessor *dataAccessor,
468 [[maybe_unused]] ClassLinkerErrorHandler *errorHandler)
469 {
470 uint32_t numFields = dataAccessor->GetFieldsNumber();
471 if (numFields == 0) {
472 return true;
473 }
474
475 uint32_t numSfields = klass->GetNumStaticFields();
476
477 Span<Field> fields {allocator_->AllocArray<Field>(numFields), numFields};
478
479 size_t sfieldsIdx = 0;
480 size_t ifieldsIdx = numSfields;
481 dataAccessor->EnumerateFields(
482 [klass, &sfieldsIdx, &ifieldsIdx, &fields](panda_file::FieldDataAccessor &fieldDataAccessor) {
483 Field *field = fieldDataAccessor.IsStatic() ? &fields[sfieldsIdx++] : &fields[ifieldsIdx++];
484 InitializeMemory(field, klass, fieldDataAccessor.GetFieldId(), fieldDataAccessor.GetAccessFlags(),
485 panda_file::Type::GetTypeFromFieldEncoding(fieldDataAccessor.GetType()));
486 });
487
488 klass->SetFields(fields, numSfields);
489
490 return true;
491 }
492
493 template <bool REVERSE_LAYOUT = false>
LayoutFieldsWithoutAlignment(size_t size,size_t * offset,size_t * space,PandaVector<Field * > * fields)494 static void LayoutFieldsWithoutAlignment(size_t size, size_t *offset, size_t *space, PandaVector<Field *> *fields)
495 {
496 auto lastProceededElement = fields->end();
497 // Iterating from beginning to end and erasing elements from the beginning of a vector
498 // is required for correct field layout between language class representation in C++ code
499 // and generated by methods in class linker.
500 // (e.g. class String should have length field before hash field, not vice versa)
501 for (auto i = fields->begin(); i != fields->end(); i++) {
502 if (!(space == nullptr || *space >= size)) {
503 lastProceededElement = i;
504 break;
505 }
506 Field *field = *i;
507 // NOLINTNEXTLINE(readability-braces-around-statements)
508 if constexpr (REVERSE_LAYOUT) {
509 *offset -= size;
510 field->SetOffset(*offset);
511 // NOLINTNEXTLINE(readability-misleading-indentation)
512 } else {
513 field->SetOffset(*offset);
514 *offset += size;
515 }
516 if (space != nullptr) {
517 *space -= size;
518 }
519 }
520 fields->erase(fields->begin(), lastProceededElement);
521 }
522
LayoutReferenceFields(size_t size,size_t * offset,const PandaVector<Field * > & fields)523 static uint32_t LayoutReferenceFields(size_t size, size_t *offset, const PandaVector<Field *> &fields)
524 {
525 uint32_t volatileFieldsNum = 0;
526 // layout volatile fields firstly
527 for (auto *field : fields) {
528 if (field->IsVolatile()) {
529 volatileFieldsNum++;
530 field->SetOffset(*offset);
531 *offset += size;
532 }
533 }
534 for (auto *field : fields) {
535 if (!field->IsVolatile()) {
536 field->SetOffset(*offset);
537 *offset += size;
538 }
539 }
540 return volatileFieldsNum;
541 }
542
LayoutFieldsInBaseClassPadding(Class * klass,PandaVector<Field * > * taggedFields,PandaVector<Field * > * fields64,PandaVector<Field * > * fields32,PandaVector<Field * > * fields16,PandaVector<Field * > * fields8,PandaVector<Field * > * refFields,bool isStatic)543 static size_t LayoutFieldsInBaseClassPadding(Class *klass, PandaVector<Field *> *taggedFields,
544 PandaVector<Field *> *fields64, PandaVector<Field *> *fields32,
545 PandaVector<Field *> *fields16, PandaVector<Field *> *fields8,
546 PandaVector<Field *> *refFields, bool isStatic)
547 {
548 constexpr size_t SIZE_64 = sizeof(uint64_t);
549 constexpr size_t SIZE_32 = sizeof(uint32_t);
550 constexpr size_t SIZE_16 = sizeof(uint16_t);
551 constexpr size_t SIZE_8 = sizeof(uint8_t);
552
553 size_t offset;
554
555 if (isStatic) {
556 offset = klass->GetStaticFieldsOffset();
557 } else {
558 offset = (klass->GetBase() != nullptr) ? klass->GetBase()->GetObjectSize() : ObjectHeader::ObjectHeaderSize();
559 }
560
561 size_t alignOffset = offset;
562 if (!refFields->empty()) {
563 alignOffset = AlignUp(offset, ClassHelper::OBJECT_POINTER_SIZE);
564 } else if (!(fields64->empty()) || !(taggedFields->empty())) {
565 alignOffset = AlignUp(offset, SIZE_64);
566 } else if (!fields32->empty()) {
567 alignOffset = AlignUp(offset, SIZE_32);
568 } else if (!fields16->empty()) {
569 alignOffset = AlignUp(offset, SIZE_16);
570 }
571 if (alignOffset != offset) {
572 size_t endOffset = alignOffset;
573 size_t padding = endOffset - offset;
574 // try to put short fields of child class at end of free space of base class
575 LayoutFieldsWithoutAlignment<true>(SIZE_32, &endOffset, &padding, fields32);
576 LayoutFieldsWithoutAlignment<true>(SIZE_16, &endOffset, &padding, fields16);
577 LayoutFieldsWithoutAlignment<true>(SIZE_8, &endOffset, &padding, fields8);
578 }
579 return alignOffset;
580 }
581
LayoutFields(Class * klass,PandaVector<Field * > * taggedFields,PandaVector<Field * > * fields64,PandaVector<Field * > * fields32,PandaVector<Field * > * fields16,PandaVector<Field * > * fields8,PandaVector<Field * > * refFields,bool isStatic)582 static size_t LayoutFields(Class *klass, PandaVector<Field *> *taggedFields, PandaVector<Field *> *fields64,
583 PandaVector<Field *> *fields32, PandaVector<Field *> *fields16,
584 PandaVector<Field *> *fields8, PandaVector<Field *> *refFields, bool isStatic)
585 {
586 constexpr size_t SIZE_64 = sizeof(uint64_t);
587 constexpr size_t SIZE_32 = sizeof(uint32_t);
588 constexpr size_t SIZE_16 = sizeof(uint16_t);
589 constexpr size_t SIZE_8 = sizeof(uint8_t);
590
591 size_t offset =
592 LayoutFieldsInBaseClassPadding(klass, taggedFields, fields64, fields32, fields16, fields8, refFields, isStatic);
593 if (!refFields->empty()) {
594 offset = AlignUp(offset, ClassHelper::OBJECT_POINTER_SIZE);
595 klass->SetRefFieldsNum(refFields->size(), isStatic);
596 klass->SetRefFieldsOffset(offset, isStatic);
597 auto volatileNum = LayoutReferenceFields(ClassHelper::OBJECT_POINTER_SIZE, &offset, *refFields);
598 klass->SetVolatileRefFieldsNum(volatileNum, isStatic);
599 }
600
601 static_assert(coretypes::TaggedValue::TaggedTypeSize() == SIZE_64,
602 "Please fix alignment of the fields of type \"TaggedValue\"");
603 if (!IsAligned<SIZE_64>(offset) && (!fields64->empty() || !taggedFields->empty())) {
604 size_t padding = AlignUp(offset, SIZE_64) - offset;
605
606 LayoutFieldsWithoutAlignment(SIZE_32, &offset, &padding, fields32);
607 LayoutFieldsWithoutAlignment(SIZE_16, &offset, &padding, fields16);
608 LayoutFieldsWithoutAlignment(SIZE_8, &offset, &padding, fields8);
609
610 offset += padding;
611 }
612
613 LayoutFieldsWithoutAlignment(coretypes::TaggedValue::TaggedTypeSize(), &offset, nullptr, taggedFields);
614 LayoutFieldsWithoutAlignment(SIZE_64, &offset, nullptr, fields64);
615
616 if (!IsAligned<SIZE_32>(offset) && !fields32->empty()) {
617 size_t padding = AlignUp(offset, SIZE_32) - offset;
618
619 LayoutFieldsWithoutAlignment(SIZE_16, &offset, &padding, fields16);
620 LayoutFieldsWithoutAlignment(SIZE_8, &offset, &padding, fields8);
621
622 offset += padding;
623 }
624
625 LayoutFieldsWithoutAlignment(SIZE_32, &offset, nullptr, fields32);
626
627 if (!IsAligned<SIZE_16>(offset) && !fields16->empty()) {
628 size_t padding = AlignUp(offset, SIZE_16) - offset;
629
630 LayoutFieldsWithoutAlignment(SIZE_8, &offset, &padding, fields8);
631
632 offset += padding;
633 }
634
635 LayoutFieldsWithoutAlignment(SIZE_16, &offset, nullptr, fields16);
636
637 LayoutFieldsWithoutAlignment(SIZE_8, &offset, nullptr, fields8);
638
639 return offset;
640 }
641
642 /* static */
LayoutFields(Class * klass,Span<Field> fields,bool isStatic,ClassLinkerErrorHandler * errorHandler)643 bool ClassLinker::LayoutFields(Class *klass, Span<Field> fields, bool isStatic,
644 [[maybe_unused]] ClassLinkerErrorHandler *errorHandler)
645 {
646 // These containers must be optimized
647 PandaVector<Field *> taggedFields;
648 PandaVector<Field *> fields64;
649 PandaVector<Field *> fields32;
650 PandaVector<Field *> fields16;
651 PandaVector<Field *> fields8;
652 PandaVector<Field *> refFields;
653 taggedFields.reserve(fields.size());
654 fields64.reserve(fields.size());
655 fields32.reserve(fields.size());
656 fields16.reserve(fields.size());
657 fields8.reserve(fields.size());
658 refFields.reserve(fields.size());
659
660 for (auto &field : fields) {
661 auto type = field.GetType();
662 if (!type.IsPrimitive()) {
663 refFields.push_back(&field);
664 continue;
665 }
666
667 switch (type.GetId()) {
668 case Type::TypeId::U1:
669 case Type::TypeId::I8:
670 case Type::TypeId::U8:
671 fields8.push_back(&field);
672 break;
673 case Type::TypeId::I16:
674 case Type::TypeId::U16:
675 fields16.push_back(&field);
676 break;
677 case Type::TypeId::I32:
678 case Type::TypeId::U32:
679 case Type::TypeId::F32:
680 fields32.push_back(&field);
681 break;
682 case Type::TypeId::I64:
683 case Type::TypeId::U64:
684 case Type::TypeId::F64:
685 fields64.push_back(&field);
686 break;
687 case Type::TypeId::TAGGED:
688 taggedFields.push_back(&field);
689 break;
690 default:
691 UNREACHABLE();
692 break;
693 }
694 }
695
696 size_t size =
697 panda::LayoutFields(klass, &taggedFields, &fields64, &fields32, &fields16, &fields8, &refFields, isStatic);
698
699 if (!isStatic && !klass->IsVariableSize()) {
700 klass->SetObjectSize(size);
701 }
702
703 return true;
704 }
705
LinkMethods(Class * klass,ClassInfo * classInfo,ClassLinkerErrorHandler * errorHandler)706 bool ClassLinker::LinkMethods(Class *klass, ClassInfo *classInfo,
707 [[maybe_unused]] ClassLinkerErrorHandler *errorHandler)
708 {
709 classInfo->vtableBuilder->UpdateClass(klass);
710 classInfo->itableBuilder->Resolve(klass);
711 classInfo->itableBuilder->UpdateClass(klass);
712 classInfo->imtableBuilder->UpdateClass(klass);
713
714 return true;
715 }
716
LinkFields(Class * klass,ClassLinkerErrorHandler * errorHandler)717 bool ClassLinker::LinkFields(Class *klass, ClassLinkerErrorHandler *errorHandler)
718 {
719 if (!LayoutFields(klass, klass->GetStaticFields(), true, errorHandler)) {
720 LOG(ERROR, CLASS_LINKER) << "Cannot layout static fields of class '" << klass->GetName() << "'";
721 return false;
722 }
723
724 if (!LayoutFields(klass, klass->GetInstanceFields(), false, errorHandler)) {
725 LOG(ERROR, CLASS_LINKER) << "Cannot layout instance fields of class '" << klass->GetName() << "'";
726 return false;
727 }
728
729 return true;
730 }
731
LoadBaseClass(panda_file::ClassDataAccessor * cda,const LanguageContext & ctx,ClassLinkerContext * context,ClassLinkerErrorHandler * errorHandler)732 Class *ClassLinker::LoadBaseClass(panda_file::ClassDataAccessor *cda, const LanguageContext &ctx,
733 ClassLinkerContext *context, ClassLinkerErrorHandler *errorHandler)
734 {
735 auto baseClassId = cda->GetSuperClassId();
736 auto *ext = GetExtension(ctx);
737 ASSERT(ext != nullptr);
738 if (baseClassId.GetOffset() == 0) {
739 return ext->GetClassRoot(ClassRoot::OBJECT);
740 }
741
742 auto &pf = cda->GetPandaFile();
743 auto *baseClass = ext->GetClass(pf, baseClassId, context, errorHandler);
744 if (baseClass == nullptr) {
745 LOG(INFO, CLASS_LINKER) << "Cannot find base class '" << utf::Mutf8AsCString(pf.GetStringData(baseClassId).data)
746 << "' of class '" << utf::Mutf8AsCString(pf.GetStringData(cda->GetClassId()).data)
747 << "' in ctx " << context;
748 return nullptr;
749 }
750
751 return baseClass;
752 }
753
LoadInterfaces(panda_file::ClassDataAccessor * cda,ClassLinkerContext * context,ClassLinkerErrorHandler * errorHandler)754 std::optional<Span<Class *>> ClassLinker::LoadInterfaces(panda_file::ClassDataAccessor *cda,
755 ClassLinkerContext *context,
756 ClassLinkerErrorHandler *errorHandler)
757 {
758 ASSERT(context != nullptr);
759 size_t ifacesNum = cda->GetIfacesNumber();
760 if (ifacesNum == 0) {
761 return Span<Class *>(nullptr, ifacesNum);
762 }
763
764 Span<Class *> ifaces {allocator_->AllocArray<Class *>(ifacesNum), ifacesNum};
765
766 for (size_t i = 0; i < ifacesNum; i++) {
767 auto id = cda->GetInterfaceId(i);
768 auto &pf = cda->GetPandaFile();
769 auto *iface = GetClass(pf, id, context, errorHandler);
770 if (iface == nullptr) {
771 LOG(INFO, CLASS_LINKER) << "Cannot find interface '" << utf::Mutf8AsCString(pf.GetStringData(id).data)
772 << "' of class '" << utf::Mutf8AsCString(pf.GetStringData(cda->GetClassId()).data)
773 << "' in ctx " << context;
774 ASSERT(!ifaces.Empty());
775 allocator_->Free(ifaces.begin());
776 return {};
777 }
778
779 ifaces[i] = iface;
780 }
781
782 return ifaces;
783 }
784
785 using ClassLoadingSet = std::unordered_set<uint64_t>;
786
787 // This class is required to clear static unordered_set on return
788 class ClassScopeStaticSetAutoCleaner {
789 public:
790 ClassScopeStaticSetAutoCleaner() = default;
ClassScopeStaticSetAutoCleaner(ClassLoadingSet * setPtr,ClassLoadingSet ** tlSetPtr)791 explicit ClassScopeStaticSetAutoCleaner(ClassLoadingSet *setPtr, ClassLoadingSet **tlSetPtr)
792 : setPtr_(setPtr), tlSetPtr_(tlSetPtr)
793 {
794 }
~ClassScopeStaticSetAutoCleaner()795 ~ClassScopeStaticSetAutoCleaner()
796 {
797 setPtr_->clear();
798 if (tlSetPtr_ != nullptr) {
799 *tlSetPtr_ = nullptr;
800 }
801 }
802
803 NO_COPY_SEMANTIC(ClassScopeStaticSetAutoCleaner);
804 NO_MOVE_SEMANTIC(ClassScopeStaticSetAutoCleaner);
805
806 private:
807 ClassLoadingSet *setPtr_;
808 ClassLoadingSet **tlSetPtr_;
809 };
810
GetClassUniqueHash(uint32_t pandaFileHash,uint32_t classId)811 static uint64_t GetClassUniqueHash(uint32_t pandaFileHash, uint32_t classId)
812 {
813 const uint8_t bitsToShuffle = 32;
814 return (static_cast<uint64_t>(pandaFileHash) << bitsToShuffle) | static_cast<uint64_t>(classId);
815 }
816
LoadClass(panda_file::ClassDataAccessor * classDataAccessor,const uint8_t * descriptor,Class * baseClass,Span<Class * > interfaces,ClassLinkerContext * context,ClassLinkerExtension * ext,ClassLinkerErrorHandler * errorHandler)817 Class *ClassLinker::LoadClass(panda_file::ClassDataAccessor *classDataAccessor, const uint8_t *descriptor,
818 Class *baseClass, Span<Class *> interfaces, ClassLinkerContext *context,
819 ClassLinkerExtension *ext, ClassLinkerErrorHandler *errorHandler)
820 {
821 ASSERT(context != nullptr);
822 ClassInfo classInfo = GetClassInfo(classDataAccessor, baseClass, interfaces, context);
823
824 auto *klass = ext->CreateClass(descriptor, classInfo.vtableBuilder->GetVTableSize(),
825 classInfo.imtableBuilder->GetIMTSize(), classInfo.size);
826
827 if (UNLIKELY(klass == nullptr)) {
828 return nullptr;
829 }
830
831 klass->SetLoadContext(context);
832 klass->SetBase(baseClass);
833 klass->SetInterfaces(interfaces);
834 klass->SetFileId(classDataAccessor->GetClassId());
835 klass->SetPandaFile(&classDataAccessor->GetPandaFile());
836 klass->SetAccessFlags(classDataAccessor->GetAccessFlags());
837
838 auto &pf = classDataAccessor->GetPandaFile();
839 auto classId = classDataAccessor->GetClassId();
840 klass->SetClassIndex(pf.GetClassIndex(classId));
841 klass->SetMethodIndex(pf.GetMethodIndex(classId));
842 klass->SetFieldIndex(pf.GetFieldIndex(classId));
843
844 klass->SetNumVirtualMethods(classInfo.vtableBuilder->GetNumVirtualMethods());
845 klass->SetNumCopiedMethods(classInfo.vtableBuilder->GetCopiedMethods().size());
846 klass->SetNumStaticFields(classInfo.numSfields);
847
848 if (!LoadMethods(klass, &classInfo, classDataAccessor, errorHandler)) {
849 FreeClass(klass);
850 LOG(ERROR, CLASS_LINKER) << "Cannot load methods of class '" << descriptor << "'";
851 return nullptr;
852 }
853
854 if (!LoadFields(klass, classDataAccessor, errorHandler)) {
855 FreeClass(klass);
856 LOG(ERROR, CLASS_LINKER) << "Cannot load fields of class '" << descriptor << "'";
857 return nullptr;
858 }
859
860 if (!LinkMethods(klass, &classInfo, errorHandler)) {
861 FreeClass(klass);
862 LOG(ERROR, CLASS_LINKER) << "Cannot link methods of class '" << descriptor << "'";
863 return nullptr;
864 }
865
866 if (!LinkFields(klass, errorHandler)) {
867 FreeClass(klass);
868 LOG(ERROR, CLASS_LINKER) << "Cannot link fields of class '" << descriptor << "'";
869 return nullptr;
870 }
871
872 return klass;
873 }
874
LoadClass(const panda_file::File * pf,const uint8_t * descriptor,panda_file::SourceLang lang)875 Class *ClassLinker::LoadClass(const panda_file::File *pf, const uint8_t *descriptor, panda_file::SourceLang lang)
876 {
877 panda_file::File::EntityId classId = pf->GetClassId(descriptor);
878 if (!classId.IsValid() || pf->IsExternal(classId)) {
879 return nullptr;
880 }
881 ClassLinkerContext *context = GetExtension(lang)->GetBootContext();
882 return LoadClass(pf, classId, descriptor, context, nullptr);
883 }
884
LoadClass(const panda_file::File * pf,panda_file::File::EntityId classId,const uint8_t * descriptor,ClassLinkerContext * context,ClassLinkerErrorHandler * errorHandler,bool addToRuntime)885 Class *ClassLinker::LoadClass(const panda_file::File *pf, panda_file::File::EntityId classId, const uint8_t *descriptor,
886 ClassLinkerContext *context, ClassLinkerErrorHandler *errorHandler,
887 bool addToRuntime /* = true */)
888 {
889 ASSERT(!pf->IsExternal(classId));
890 ASSERT(context != nullptr);
891 panda_file::ClassDataAccessor classDataAccessor(*pf, classId);
892 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(&classDataAccessor);
893 if (ctx.GetLanguage() != context->GetSourceLang()) {
894 LanguageContext currentCtx = Runtime::GetCurrent()->GetLanguageContext(context->GetSourceLang());
895 PandaStringStream ss;
896 ss << "Cannot load " << ctx << " class " << descriptor << " into " << currentCtx << " context";
897 LOG(ERROR, CLASS_LINKER) << ss.str();
898 OnError(errorHandler, Error::CLASS_NOT_FOUND, ss.str());
899 return nullptr;
900 }
901
902 if (!HasExtension(ctx)) {
903 PandaStringStream ss;
904 ss << "Cannot load class '" << descriptor << "' as class linker hasn't " << ctx << " language extension";
905 LOG(ERROR, CLASS_LINKER) << ss.str();
906 OnError(errorHandler, Error::CLASS_NOT_FOUND, ss.str());
907 return nullptr;
908 }
909
910 // This set is used to find out if the class is its own superclass
911 ClassLoadingSet loadingSet;
912 static thread_local ClassLoadingSet *threadLocalSet = nullptr;
913 ClassLoadingSet **threadLocalSetPtr = nullptr;
914 if (threadLocalSet == nullptr) {
915 threadLocalSet = &loadingSet;
916 threadLocalSetPtr = &threadLocalSet;
917 }
918 ClassScopeStaticSetAutoCleaner classSetAutoCleanerOnReturn(threadLocalSet, threadLocalSetPtr);
919
920 auto *ext = GetExtension(ctx);
921 Class *baseClass = nullptr;
922 bool needLoadBase = IsInitialized() || !utf::IsEqual(ctx.GetObjectClassDescriptor(), descriptor);
923 if (needLoadBase) {
924 uint32_t classIdInt = classId.GetOffset();
925 uint32_t pandaFileHash = pf->GetFilenameHash();
926 if (!threadLocalSet->insert(GetClassUniqueHash(pandaFileHash, classIdInt)).second) {
927 const PandaString &className = utf::Mutf8AsCString(pf->GetStringData(classDataAccessor.GetClassId()).data);
928 PandaString msg = "Class or interface \"" + className + "\" is its own superclass or superinterface";
929 OnError(errorHandler, Error::CLASS_CIRCULARITY, msg);
930 return nullptr;
931 }
932
933 baseClass = LoadBaseClass(&classDataAccessor, ctx, context, errorHandler);
934 if (baseClass == nullptr) {
935 LOG(INFO, CLASS_LINKER) << "Cannot load base class of class '" << descriptor << "'";
936 return nullptr;
937 }
938 }
939
940 auto res = LoadInterfaces(&classDataAccessor, context, errorHandler);
941 if (!res) {
942 LOG(INFO, CLASS_LINKER) << "Cannot load interfaces of class '" << descriptor << "'";
943 return nullptr;
944 }
945
946 auto *klass = LoadClass(&classDataAccessor, descriptor, baseClass, res.value(), context, ext, errorHandler);
947 if (klass == nullptr) {
948 return nullptr;
949 }
950
951 Runtime::GetCurrent()->GetCha()->Update(klass);
952
953 if (LIKELY(ext->CanInitializeClasses())) {
954 if (!ext->InitializeClass(klass)) {
955 LOG(ERROR, CLASS_LINKER) << "Language specific initialization for class '" << descriptor << "' failed";
956 FreeClass(klass);
957 return nullptr;
958 }
959 klass->SetState(Class::State::LOADED);
960 }
961
962 if (LIKELY(addToRuntime)) {
963 Runtime::GetCurrent()->GetNotificationManager()->ClassLoadEvent(klass);
964
965 auto *otherKlass = context->InsertClass(klass);
966 if (otherKlass != nullptr) {
967 // Someone has created the class in the other thread (increase the critical section?)
968 FreeClass(klass);
969 return otherKlass;
970 }
971
972 RemoveCreatedClassInExtension(klass);
973 Runtime::GetCurrent()->GetNotificationManager()->ClassPrepareEvent(klass);
974 }
975 return klass;
976 }
977
CopyMutf8String(mem::InternalAllocatorPtr allocator,const uint8_t * descriptor)978 static const uint8_t *CopyMutf8String(mem::InternalAllocatorPtr allocator, const uint8_t *descriptor)
979 {
980 size_t size = utf::Mutf8Size(descriptor) + 1; // + 1 - null terminate
981 auto *ptr = allocator->AllocArray<uint8_t>(size);
982 memcpy_s(ptr, size, descriptor, size);
983 return ptr;
984 }
985
BuildClass(const uint8_t * descriptor,bool needCopyDescriptor,uint32_t accessFlags,Span<Method> methods,Span<Field> fields,Class * baseClass,Span<Class * > interfaces,ClassLinkerContext * context,bool isInterface)986 Class *ClassLinker::BuildClass(const uint8_t *descriptor, bool needCopyDescriptor, uint32_t accessFlags,
987 Span<Method> methods, Span<Field> fields, Class *baseClass, Span<Class *> interfaces,
988 ClassLinkerContext *context, bool isInterface)
989 {
990 ASSERT(context != nullptr);
991 if (needCopyDescriptor) {
992 descriptor = CopyMutf8String(allocator_, descriptor);
993 os::memory::LockHolder lock(copiedNamesLock_);
994 copiedNames_.push_front(descriptor);
995 }
996
997 auto *ext = GetExtension(baseClass->GetSourceLang());
998 ASSERT(ext != nullptr);
999
1000 ClassInfo classInfo = GetClassInfo(methods, fields, baseClass, interfaces, isInterface);
1001
1002 // Need to protect ArenaAllocator and loaded_classes_
1003 auto *klass = ext->CreateClass(descriptor, classInfo.vtableBuilder->GetVTableSize(),
1004 classInfo.imtableBuilder->GetIMTSize(), classInfo.size);
1005
1006 if (UNLIKELY(klass == nullptr)) {
1007 return nullptr;
1008 }
1009
1010 klass->SetLoadContext(context);
1011 klass->SetBase(baseClass);
1012 klass->SetInterfaces(interfaces);
1013 klass->SetAccessFlags(accessFlags);
1014
1015 klass->SetNumVirtualMethods(classInfo.vtableBuilder->GetNumVirtualMethods());
1016 klass->SetNumCopiedMethods(classInfo.vtableBuilder->GetCopiedMethods().size());
1017 klass->SetNumStaticFields(classInfo.numSfields);
1018
1019 ASSERT(klass->GetNumCopiedMethods() == 0);
1020
1021 size_t numSmethods = methods.size() - klass->GetNumVirtualMethods();
1022 klass->SetMethods(methods, klass->GetNumVirtualMethods(), numSmethods);
1023 klass->SetFields(fields, klass->GetNumStaticFields());
1024
1025 for (auto &method : methods) {
1026 method.SetClass(klass);
1027 }
1028
1029 for (auto &field : fields) {
1030 field.SetClass(klass);
1031 }
1032
1033 if (!LinkMethods(klass, &classInfo, ext->GetErrorHandler())) {
1034 LOG(ERROR, CLASS_LINKER) << "Cannot link class methods '" << descriptor << "'";
1035 return nullptr;
1036 }
1037
1038 if (!LinkFields(klass, ext->GetErrorHandler())) {
1039 LOG(ERROR, CLASS_LINKER) << "Cannot link class fields '" << descriptor << "'";
1040 return nullptr;
1041 }
1042
1043 if (!ext->InitializeClass(klass)) {
1044 LOG(ERROR, CLASS_LINKER) << "Language specific initialization for class '" << descriptor << "' failed";
1045 FreeClass(klass);
1046 return nullptr;
1047 }
1048
1049 klass->SetState(Class::State::LOADED);
1050
1051 Runtime::GetCurrent()->GetNotificationManager()->ClassLoadEvent(klass);
1052
1053 auto *otherKlass = context->InsertClass(klass);
1054 if (otherKlass != nullptr) {
1055 // Someone has created the class in the other thread (increase the critical section?)
1056 FreeClass(klass);
1057 return otherKlass;
1058 }
1059
1060 RemoveCreatedClassInExtension(klass);
1061 Runtime::GetCurrent()->GetNotificationManager()->ClassPrepareEvent(klass);
1062
1063 return klass;
1064 }
1065
CreateArrayClass(ClassLinkerExtension * ext,const uint8_t * descriptor,bool needCopyDescriptor,Class * componentClass)1066 Class *ClassLinker::CreateArrayClass(ClassLinkerExtension *ext, const uint8_t *descriptor, bool needCopyDescriptor,
1067 Class *componentClass)
1068 {
1069 if (needCopyDescriptor) {
1070 descriptor = CopyMutf8String(allocator_, descriptor);
1071 os::memory::LockHolder lock(copiedNamesLock_);
1072 copiedNames_.push_front(descriptor);
1073 }
1074
1075 auto *arrayClass = ext->CreateClass(descriptor, ext->GetArrayClassVTableSize(), ext->GetArrayClassIMTSize(),
1076 ext->GetArrayClassSize());
1077
1078 if (UNLIKELY(arrayClass == nullptr)) {
1079 return nullptr;
1080 }
1081
1082 arrayClass->SetLoadContext(componentClass->GetLoadContext());
1083
1084 if (UNLIKELY(!ext->InitializeArrayClass(arrayClass, componentClass))) {
1085 return nullptr;
1086 }
1087
1088 return arrayClass;
1089 }
1090
LoadArrayClass(const uint8_t * descriptor,bool needCopyDescriptor,ClassLinkerContext * context,ClassLinkerErrorHandler * errorHandler)1091 Class *ClassLinker::LoadArrayClass(const uint8_t *descriptor, bool needCopyDescriptor, ClassLinkerContext *context,
1092 ClassLinkerErrorHandler *errorHandler)
1093 {
1094 Span<const uint8_t> sp(descriptor, 1);
1095
1096 Class *componentClass = GetClass(sp.cend(), needCopyDescriptor, context, errorHandler);
1097
1098 if (componentClass == nullptr) {
1099 return nullptr;
1100 }
1101
1102 if (UNLIKELY(componentClass->GetType().GetId() == panda_file::Type::TypeId::VOID)) {
1103 OnError(errorHandler, Error::NO_CLASS_DEF, "Try to create array with void component type");
1104 return nullptr;
1105 }
1106
1107 auto *ext = GetExtension(componentClass->GetSourceLang());
1108 ASSERT(ext != nullptr);
1109
1110 auto *componentClassContext = componentClass->GetLoadContext();
1111 ASSERT(componentClassContext != nullptr);
1112 if (componentClassContext != context) {
1113 auto *loadedClass = FindLoadedClass(descriptor, componentClassContext);
1114 if (loadedClass != nullptr) {
1115 return loadedClass;
1116 }
1117 }
1118
1119 auto *arrayClass = CreateArrayClass(ext, descriptor, needCopyDescriptor, componentClass);
1120
1121 if (UNLIKELY(arrayClass == nullptr)) {
1122 return nullptr;
1123 }
1124
1125 Runtime::GetCurrent()->GetNotificationManager()->ClassLoadEvent(arrayClass);
1126
1127 auto *otherKlass = componentClassContext->InsertClass(arrayClass);
1128 if (otherKlass != nullptr) {
1129 FreeClass(arrayClass);
1130 return otherKlass;
1131 }
1132
1133 RemoveCreatedClassInExtension(arrayClass);
1134 Runtime::GetCurrent()->GetNotificationManager()->ClassPrepareEvent(arrayClass);
1135
1136 return arrayClass;
1137 }
1138
PandaFilesToString(const PandaVector<const panda_file::File * > & pandaFiles)1139 static PandaString PandaFilesToString(const PandaVector<const panda_file::File *> &pandaFiles)
1140 {
1141 PandaStringStream ss;
1142 ss << "[";
1143
1144 size_t n = pandaFiles.size();
1145 for (size_t i = 0; i < n; i++) {
1146 ss << pandaFiles[i]->GetFilename();
1147
1148 if (i < n - 1) {
1149 ss << ", ";
1150 }
1151 }
1152
1153 ss << "]";
1154 return ss.str();
1155 }
1156
GetClass(const uint8_t * descriptor,bool needCopyDescriptor,ClassLinkerContext * context,ClassLinkerErrorHandler * errorHandler)1157 Class *ClassLinker::GetClass(const uint8_t *descriptor, bool needCopyDescriptor, ClassLinkerContext *context,
1158 ClassLinkerErrorHandler *errorHandler /* = nullptr */)
1159 {
1160 ASSERT(context != nullptr);
1161 ASSERT(!MTManagedThread::ThreadIsMTManagedThread(Thread::GetCurrent()) ||
1162 !PandaVM::GetCurrent()->GetGC()->IsGCRunning() || PandaVM::GetCurrent()->GetMutatorLock()->HasLock());
1163
1164 Class *cls = FindLoadedClass(descriptor, context);
1165 if (cls != nullptr) {
1166 return cls;
1167 }
1168
1169 if (ClassHelper::IsArrayDescriptor(descriptor)) {
1170 return LoadArrayClass(descriptor, needCopyDescriptor, context, errorHandler);
1171 }
1172
1173 if (context->IsBootContext()) {
1174 panda_file::File::EntityId classId;
1175 const panda_file::File *pandaFile {nullptr};
1176 {
1177 {
1178 os::memory::LockHolder lock {bootPandaFilesLock_};
1179 std::tie(classId, pandaFile) = FindClassInPandaFiles(descriptor, bootPandaFiles_);
1180 }
1181
1182 if (!classId.IsValid()) {
1183 PandaStringStream ss;
1184 {
1185 // can't make a wider scope for lock here - will get recursion
1186 os::memory::LockHolder lock {bootPandaFilesLock_};
1187 ss << "Cannot find class " << descriptor
1188 << " in boot panda files: " << PandaFilesToString(bootPandaFiles_);
1189 }
1190 OnError(errorHandler, Error::CLASS_NOT_FOUND, ss.str());
1191 return nullptr;
1192 }
1193 }
1194
1195 return LoadClass(pandaFile, classId, pandaFile->GetStringData(classId).data, context, errorHandler);
1196 }
1197
1198 return context->LoadClass(descriptor, needCopyDescriptor, errorHandler);
1199 }
1200
GetClass(const panda_file::File & pf,panda_file::File::EntityId id,ClassLinkerContext * context,ClassLinkerErrorHandler * errorHandler)1201 Class *ClassLinker::GetClass(const panda_file::File &pf, panda_file::File::EntityId id, ClassLinkerContext *context,
1202 ClassLinkerErrorHandler *errorHandler /* = nullptr */)
1203 {
1204 ASSERT(context != nullptr);
1205 ASSERT(!MTManagedThread::ThreadIsMTManagedThread(Thread::GetCurrent()) ||
1206 !PandaVM::GetCurrent()->GetGC()->IsGCRunning() || PandaVM::GetCurrent()->GetMutatorLock()->HasLock());
1207
1208 Class *cls = pf.GetPandaCache()->GetClassFromCache(id);
1209 if (cls != nullptr) {
1210 return cls;
1211 }
1212 const uint8_t *descriptor = pf.GetStringData(id).data;
1213
1214 cls = FindLoadedClass(descriptor, context);
1215 if (cls != nullptr) {
1216 pf.GetPandaCache()->SetClassCache(id, cls);
1217 return cls;
1218 }
1219
1220 if (ClassHelper::IsArrayDescriptor(descriptor)) {
1221 cls = LoadArrayClass(descriptor, false, context, errorHandler);
1222 if (LIKELY(cls != nullptr)) {
1223 pf.GetPandaCache()->SetClassCache(id, cls);
1224 }
1225 return cls;
1226 }
1227
1228 if (context->IsBootContext()) {
1229 const panda_file::File *pfPtr = nullptr;
1230 panda_file::File::EntityId extId;
1231 {
1232 os::memory::LockHolder lock {bootPandaFilesLock_};
1233 std::tie(extId, pfPtr) = FindClassInPandaFiles(descriptor, bootPandaFiles_);
1234 }
1235
1236 if (!extId.IsValid()) {
1237 PandaStringStream ss;
1238 {
1239 // can't make a wider scope for lock here - will get recursion
1240 os::memory::LockHolder lock {bootPandaFilesLock_};
1241 ss << "Cannot find class " << descriptor
1242 << " in boot panda files: " << PandaFilesToString(bootPandaFiles_);
1243 }
1244 OnError(errorHandler, Error::CLASS_NOT_FOUND, ss.str());
1245 return nullptr;
1246 }
1247
1248 cls = LoadClass(pfPtr, extId, descriptor, context, errorHandler);
1249 if (LIKELY(cls != nullptr)) {
1250 pf.GetPandaCache()->SetClassCache(id, cls);
1251 }
1252 return cls;
1253 }
1254
1255 return context->LoadClass(descriptor, false, errorHandler);
1256 }
1257
GetMethod(const panda_file::File & pf,panda_file::File::EntityId id,ClassLinkerContext * context,ClassLinkerErrorHandler * errorHandler)1258 Method *ClassLinker::GetMethod(const panda_file::File &pf, panda_file::File::EntityId id,
1259 ClassLinkerContext *context /* = nullptr */,
1260 ClassLinkerErrorHandler *errorHandler /* = nullptr */)
1261 {
1262 Method *method = pf.GetPandaCache()->GetMethodFromCache(id);
1263 if (method != nullptr) {
1264 return method;
1265 }
1266 panda_file::MethodDataAccessor methodDataAccessor(pf, id);
1267
1268 auto classId = methodDataAccessor.GetClassId();
1269 if (context == nullptr) {
1270 panda_file::ClassDataAccessor classDataAccessor(pf, classId);
1271 auto lang = classDataAccessor.GetSourceLang();
1272 if (!lang) {
1273 LOG(INFO, CLASS_LINKER) << "Cannot resolve language context for class_id " << classId << " in file "
1274 << pf.GetFilename();
1275 return nullptr;
1276 }
1277 auto *extension = GetExtension(lang.value());
1278 context = extension->GetBootContext();
1279 }
1280
1281 Class *klass = GetClass(pf, classId, context, errorHandler);
1282
1283 if (klass == nullptr) {
1284 auto className = pf.GetStringData(classId).data;
1285 LOG(INFO, CLASS_LINKER) << "Cannot find class '" << className << "' in ctx " << context;
1286 return nullptr;
1287 }
1288 method = GetMethod(klass, methodDataAccessor, errorHandler);
1289 if (LIKELY(method != nullptr)) {
1290 pf.GetPandaCache()->SetMethodCache(id, method);
1291 }
1292 return method;
1293 }
1294
GetMethod(const Method & caller,panda_file::File::EntityId id,ClassLinkerErrorHandler * errorHandler)1295 Method *ClassLinker::GetMethod(const Method &caller, panda_file::File::EntityId id,
1296 ClassLinkerErrorHandler *errorHandler /* = nullptr */)
1297 {
1298 auto *pf = caller.GetPandaFile();
1299 Method *method = pf->GetPandaCache()->GetMethodFromCache(id);
1300 if (method != nullptr) {
1301 return method;
1302 }
1303
1304 panda_file::MethodDataAccessor methodDataAccessor(*pf, id);
1305 auto classId = methodDataAccessor.GetClassId();
1306
1307 auto *context = caller.GetClass()->GetLoadContext();
1308 auto *ext = GetExtension(caller.GetClass()->GetSourceLang());
1309 Class *klass = ext->GetClass(*pf, classId, context, errorHandler);
1310
1311 if (klass == nullptr) {
1312 auto className = pf->GetStringData(classId).data;
1313 LOG(INFO, CLASS_LINKER) << "Cannot find class '" << className << "' in ctx " << context;
1314 return nullptr;
1315 }
1316
1317 method = GetMethod(klass, methodDataAccessor, (errorHandler == nullptr) ? ext->GetErrorHandler() : errorHandler);
1318 if (LIKELY(method != nullptr)) {
1319 pf->GetPandaCache()->SetMethodCache(id, method);
1320 }
1321 return method;
1322 }
1323
GetMethod(const Class * klass,const panda_file::MethodDataAccessor & methodDataAccessor,ClassLinkerErrorHandler * errorHandler)1324 Method *ClassLinker::GetMethod(const Class *klass, const panda_file::MethodDataAccessor &methodDataAccessor,
1325 ClassLinkerErrorHandler *errorHandler)
1326 {
1327 Method *method;
1328 auto id = methodDataAccessor.GetMethodId();
1329 const auto &pf = methodDataAccessor.GetPandaFile();
1330
1331 bool isStatic = methodDataAccessor.IsStatic();
1332 if (!methodDataAccessor.IsExternal() && klass->GetPandaFile() == &pf) {
1333 if (klass->IsInterface()) {
1334 method = isStatic ? klass->GetStaticInterfaceMethod(id) : klass->GetVirtualInterfaceMethod(id);
1335 } else {
1336 method = isStatic ? klass->GetStaticClassMethod(id) : klass->GetVirtualClassMethod(id);
1337 }
1338
1339 if (method == nullptr) {
1340 Method::Proto proto(pf, methodDataAccessor.GetProtoId());
1341 PandaStringStream ss;
1342 ss << "Cannot find method '" << methodDataAccessor.GetName().data << " " << proto.GetSignature(true)
1343 << "' in class '" << klass->GetName() << "'";
1344 OnError(errorHandler, Error::METHOD_NOT_FOUND, ss.str());
1345 return nullptr;
1346 }
1347
1348 return method;
1349 }
1350
1351 auto name = methodDataAccessor.GetName();
1352 Method::Proto proto(pf, methodDataAccessor.GetProtoId());
1353 if (klass->IsInterface()) {
1354 method = isStatic ? klass->GetStaticInterfaceMethodByName(name, proto)
1355 : klass->GetVirtualInterfaceMethodByName(name, proto);
1356 } else {
1357 method =
1358 isStatic ? klass->GetStaticClassMethodByName(name, proto) : klass->GetVirtualClassMethodByName(name, proto);
1359 if (method == nullptr && klass->IsAbstract()) {
1360 method = klass->GetInterfaceMethod(name, proto);
1361 }
1362 }
1363
1364 if (method == nullptr) {
1365 PandaStringStream ss;
1366 ss << "Cannot find method '" << methodDataAccessor.GetName().data << " " << proto.GetSignature(true)
1367 << "' in class '" << klass->GetName() << "'";
1368 OnError(errorHandler, Error::METHOD_NOT_FOUND, ss.str());
1369 return nullptr;
1370 }
1371
1372 LOG_IF(method->IsStatic() != methodDataAccessor.IsStatic(), FATAL, CLASS_LINKER)
1373 << "Expected ACC_STATIC for method " << name.data << " in class " << klass->GetName()
1374 << " does not match loaded value";
1375
1376 return method;
1377 }
1378
GetFieldById(Class * klass,const panda_file::FieldDataAccessor & fieldDataAccessor,ClassLinkerErrorHandler * errorHandler)1379 Field *ClassLinker::GetFieldById(Class *klass, const panda_file::FieldDataAccessor &fieldDataAccessor,
1380 ClassLinkerErrorHandler *errorHandler)
1381 {
1382 bool isStatic = fieldDataAccessor.IsStatic();
1383 auto &pf = fieldDataAccessor.GetPandaFile();
1384 auto id = fieldDataAccessor.GetFieldId();
1385
1386 Field *field = isStatic ? klass->FindStaticFieldById(id) : klass->FindInstanceFieldById(id);
1387
1388 if (field == nullptr) {
1389 PandaStringStream ss;
1390 ss << "Cannot find field '" << pf.GetStringData(fieldDataAccessor.GetNameId()).data << "' in class '"
1391 << klass->GetName() << "'";
1392 OnError(errorHandler, Error::FIELD_NOT_FOUND, ss.str());
1393 return nullptr;
1394 }
1395
1396 pf.GetPandaCache()->SetFieldCache(id, field);
1397 return field;
1398 }
1399
GetFieldBySignature(Class * klass,const panda_file::FieldDataAccessor & fieldDataAccessor,ClassLinkerErrorHandler * errorHandler)1400 Field *ClassLinker::GetFieldBySignature(Class *klass, const panda_file::FieldDataAccessor &fieldDataAccessor,
1401 ClassLinkerErrorHandler *errorHandler)
1402 {
1403 auto &pf = fieldDataAccessor.GetPandaFile();
1404 auto id = fieldDataAccessor.GetFieldId();
1405 auto fieldName = pf.GetStringData(fieldDataAccessor.GetNameId());
1406 auto fieldType = panda_file::Type::GetTypeFromFieldEncoding(fieldDataAccessor.GetType());
1407 Field *field = klass->FindField([&fieldDataAccessor, &fieldType, &fieldName, &id, &pf](const Field &fld) {
1408 if (fieldType == fld.GetType() && fieldName == fld.GetName()) {
1409 if (!fieldType.IsReference()) {
1410 return true;
1411 }
1412
1413 // compare field class type
1414 if (&pf == fld.GetPandaFile() && id == fld.GetFileId()) {
1415 return true;
1416 }
1417 auto typeId = panda_file::FieldDataAccessor::GetTypeId(*fld.GetPandaFile(), fld.GetFileId());
1418 if (pf.GetStringData(panda_file::File::EntityId(fieldDataAccessor.GetType())) ==
1419 fld.GetPandaFile()->GetStringData(typeId)) {
1420 return true;
1421 }
1422 }
1423 return false;
1424 });
1425
1426 if (field == nullptr) {
1427 PandaStringStream ss;
1428 ss << "Cannot find field '" << fieldName.data << "' in class '" << klass->GetName() << "'";
1429 OnError(errorHandler, Error::FIELD_NOT_FOUND, ss.str());
1430 return nullptr;
1431 }
1432
1433 pf.GetPandaCache()->SetFieldCache(id, field);
1434 return field;
1435 }
1436
GetField(const panda_file::File & pf,panda_file::File::EntityId id,ClassLinkerContext * context,ClassLinkerErrorHandler * errorHandler)1437 Field *ClassLinker::GetField(const panda_file::File &pf, panda_file::File::EntityId id,
1438 ClassLinkerContext *context /* = nullptr */,
1439 ClassLinkerErrorHandler *errorHandler /* = nullptr */)
1440 {
1441 Field *field = pf.GetPandaCache()->GetFieldFromCache(id);
1442 if (field != nullptr) {
1443 return field;
1444 }
1445 panda_file::FieldDataAccessor fieldDataAccessor(pf, id);
1446
1447 Class *klass = GetClass(pf, fieldDataAccessor.GetClassId(), context, errorHandler);
1448
1449 if (klass == nullptr) {
1450 auto className = pf.GetStringData(fieldDataAccessor.GetClassId()).data;
1451 LOG(INFO, CLASS_LINKER) << "Cannot find class '" << className << "' in ctx " << context;
1452 return nullptr;
1453 }
1454
1455 if (!fieldDataAccessor.IsExternal() && klass->GetPandaFile() == &pf) {
1456 field = GetFieldById(klass, fieldDataAccessor, errorHandler);
1457 } else {
1458 field = GetFieldBySignature(klass, fieldDataAccessor, errorHandler);
1459 }
1460 return field;
1461 }
1462
InitializeClass(ManagedThread * thread,Class * klass)1463 bool ClassLinker::InitializeClass(ManagedThread *thread, Class *klass)
1464 {
1465 ASSERT(klass != nullptr);
1466 if (klass->IsInitialized()) {
1467 return true;
1468 }
1469
1470 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(*klass);
1471 return ctx.InitializeClass(this, thread, klass);
1472 }
1473
NumLoadedClasses()1474 size_t ClassLinker::NumLoadedClasses()
1475 {
1476 size_t sum = 0;
1477
1478 for (auto &ext : extensions_) {
1479 if (ext == nullptr) {
1480 continue;
1481 }
1482
1483 sum += ext->NumLoadedClasses();
1484 }
1485
1486 return sum;
1487 }
1488
VisitLoadedClasses(size_t flag)1489 void ClassLinker::VisitLoadedClasses(size_t flag)
1490 {
1491 for (auto &ext : extensions_) {
1492 if (ext == nullptr) {
1493 continue;
1494 }
1495 ext->VisitLoadedClasses(flag);
1496 }
1497 }
1498
OnError(ClassLinkerErrorHandler * errorHandler,ClassLinker::Error error,const PandaString & msg)1499 void ClassLinker::OnError(ClassLinkerErrorHandler *errorHandler, ClassLinker::Error error, const PandaString &msg)
1500 {
1501 if (errorHandler != nullptr) {
1502 errorHandler->OnError(error, msg);
1503 }
1504 }
1505
GetField(const Method & caller,panda_file::File::EntityId id,ClassLinkerErrorHandler * errorHandler)1506 Field *ClassLinker::GetField(const Method &caller, panda_file::File::EntityId id,
1507 ClassLinkerErrorHandler *errorHandler /* = nullptr */)
1508 {
1509 Field *field = caller.GetPandaFile()->GetPandaCache()->GetFieldFromCache(id);
1510 if (field != nullptr) {
1511 return field;
1512 }
1513 auto *ext = GetExtension(caller.GetClass()->GetSourceLang());
1514 field = GetField(*caller.GetPandaFile(), id, caller.GetClass()->GetLoadContext(),
1515 (errorHandler == nullptr) ? ext->GetErrorHandler() : errorHandler);
1516 if (LIKELY(field != nullptr)) {
1517 caller.GetPandaFile()->GetPandaCache()->SetFieldCache(id, field);
1518 }
1519 return field;
1520 }
1521
RemoveCreatedClassInExtension(Class * klass)1522 void ClassLinker::RemoveCreatedClassInExtension(Class *klass)
1523 {
1524 if (klass == nullptr) {
1525 return;
1526 }
1527 auto ext = GetExtension(klass->GetSourceLang());
1528 if (ext != nullptr) {
1529 ext->OnClassPrepared(klass);
1530 }
1531 }
1532
1533 } // namespace panda
1534