• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "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