• 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 {panda_files_lock_};
58         panda_files_.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 {boot_panda_files_lock_};
63         boot_panda_files_.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 * class_ptr)74 void ClassLinker::FreeClassData(Class *class_ptr)
75 {
76     Span<Field> fields = class_ptr->GetFields();
77     if (fields.Size() > 0) {
78         allocator_->Free(fields.begin());
79         class_ptr->SetFields(Span<Field>(), 0);
80     }
81     Span<Method> methods = class_ptr->GetMethods();
82     size_t n = methods.Size() + class_ptr->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         class_ptr->SetMethods(Span<Method>(), 0, 0);
92     }
93     bool has_own_itable = !class_ptr->IsArrayClass();
94     auto itable = class_ptr->GetITable().Get();
95     if (has_own_itable && !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         class_ptr->SetITable(ITable());
104     }
105     Span<Class *> interfaces = class_ptr->GetInterfaces();
106     if (!interfaces.Empty()) {
107         allocator_->Free(interfaces.begin());
108         class_ptr->SetInterfaces(Span<Class *>());
109     }
110 }
111 
FreeClass(Class * class_ptr)112 void ClassLinker::FreeClass(Class *class_ptr)
113 {
114     FreeClassData(class_ptr);
115     GetExtension(class_ptr->GetSourceLang())->FreeClass(class_ptr);
116 }
117 
~ClassLinker()118 ClassLinker::~ClassLinker()
119 {
120     for (auto &copied_name : copied_names_) {
121         allocator_->Free(reinterpret_cast<void *>(const_cast<uint8_t *>(copied_name)));
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), aot_manager_(MakePandaUnique<AotManager>()), copied_names_(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 compressed_string_enabled)146 bool ClassLinker::Initialize(bool compressed_string_enabled)
147 {
148     if (is_initialized_) {
149         return true;
150     }
151 
152     for (auto &ext : extensions_) {
153         if (ext == nullptr) {
154             continue;
155         }
156 
157         if (!ext->Initialize(this, compressed_string_enabled)) {
158             return false;
159         }
160     }
161 
162     is_initialized_ = 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 & panda_files)185 static ClassEntry FindClassInPandaFiles(const uint8_t *descriptor, const PandaFiles &panda_files)
186 {
187     for (auto *pf : panda_files) {
188         auto class_id = pf->GetClassId(descriptor);
189         if (class_id.IsValid() && !pf->IsExternal(class_id)) {
190             return {class_id, 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 data_accessor,size_t vtable_size,size_t imt_size,size_t * out_num_sfields)204 static size_t GetClassSize(ClassDataAccessorT data_accessor, size_t vtable_size, size_t imt_size,
205                            size_t *out_num_sfields)
206 {
207     size_t num_8bit_sfields = 0;
208     size_t num_16bit_sfields = 0;
209     size_t num_32bit_sfields = 0;
210     size_t num_64bit_sfields = 0;
211     size_t num_ref_sfields = 0;
212     size_t num_tagged_sfields = 0;
213 
214     size_t num_sfields = 0;
215 
216     data_accessor.template EnumerateStaticFieldTypes([&num_8bit_sfields, &num_16bit_sfields, &num_32bit_sfields,
217                                                       &num_64bit_sfields, &num_ref_sfields, &num_tagged_sfields,
218                                                       &num_sfields](Type field_type) {
219         ++num_sfields;
220 
221         switch (field_type.GetId()) {
222             case Type::TypeId::U1:
223             case Type::TypeId::I8:
224             case Type::TypeId::U8:
225                 ++num_8bit_sfields;
226                 break;
227             case Type::TypeId::I16:
228             case Type::TypeId::U16:
229                 ++num_16bit_sfields;
230                 break;
231             case Type::TypeId::I32:
232             case Type::TypeId::U32:
233             case Type::TypeId::F32:
234                 ++num_32bit_sfields;
235                 break;
236             case Type::TypeId::I64:
237             case Type::TypeId::U64:
238             case Type::TypeId::F64:
239                 ++num_64bit_sfields;
240                 break;
241             case Type::TypeId::REFERENCE:
242                 ++num_ref_sfields;
243                 break;
244             case Type::TypeId::TAGGED:
245                 ++num_tagged_sfields;
246                 break;
247             default:
248                 UNREACHABLE();
249                 break;
250         }
251     });
252 
253     *out_num_sfields = num_sfields;
254 
255     return Class::ComputeClassSize(vtable_size, imt_size, num_8bit_sfields, num_16bit_sfields, num_32bit_sfields,
256                                    num_64bit_sfields, num_ref_sfields, num_tagged_sfields);
257 }
258 
259 class ClassDataAccessorWrapper {
260 public:
ClassDataAccessorWrapper(panda_file::ClassDataAccessor * data_accessor=nullptr)261     explicit ClassDataAccessorWrapper(panda_file::ClassDataAccessor *data_accessor = nullptr)
262         : data_accessor_(data_accessor)
263     {
264     }
265 
266     template <class Callback>
EnumerateStaticFieldTypes(const Callback & cb) const267     void EnumerateStaticFieldTypes(const Callback &cb) const
268     {
269         data_accessor_->EnumerateFields([cb](panda_file::FieldDataAccessor &fda) {
270             if (!fda.IsStatic()) {
271                 return;
272             }
273 
274             cb(Type::GetTypeFromFieldEncoding(fda.GetType()));
275         });
276     }
277 
278     ~ClassDataAccessorWrapper() = default;
279 
280     DEFAULT_COPY_SEMANTIC(ClassDataAccessorWrapper);
281     DEFAULT_MOVE_SEMANTIC(ClassDataAccessorWrapper);
282 
283 private:
284     panda_file::ClassDataAccessor *data_accessor_;
285 };
286 
GetClassInfo(panda_file::ClassDataAccessor * data_accessor,Class * base,Span<Class * > interfaces,ClassLinkerContext * context)287 ClassLinker::ClassInfo ClassLinker::GetClassInfo(panda_file::ClassDataAccessor *data_accessor, Class *base,
288                                                  Span<Class *> interfaces, ClassLinkerContext *context)
289 {
290     LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(data_accessor);
291 
292     auto vtable_builder = ctx.CreateVTableBuilder();
293     auto itable_builder = ctx.CreateITableBuilder();
294     auto imtable_builder = ctx.CreateIMTableBuilder();
295 
296     itable_builder->Build(this, base, interfaces, data_accessor->IsInterface());
297     vtable_builder->Build(data_accessor, base, itable_builder->GetITable(), context);
298     imtable_builder->Build(data_accessor, itable_builder->GetITable());
299 
300     ClassDataAccessorWrapper data_accessor_wrapper(data_accessor);
301     size_t num_sfields = 0;
302     size_t size = GetClassSize(data_accessor_wrapper, vtable_builder->GetVTableSize(), imtable_builder->GetIMTSize(),
303                                &num_sfields);
304 
305     return {size, num_sfields, std::move(vtable_builder), std::move(itable_builder), std::move(imtable_builder)};
306 }
307 
308 class ClassDataAccessor {
309 public:
ClassDataAccessor(Span<Field> fields)310     explicit ClassDataAccessor(Span<Field> fields) : fields_(fields) {}
311 
312     template <class Callback>
EnumerateStaticFieldTypes(const Callback & cb) const313     void EnumerateStaticFieldTypes(const Callback &cb) const
314     {
315         for (const auto &field : fields_) {
316             if (!field.IsStatic()) {
317                 continue;
318             }
319 
320             cb(field.GetType());
321         }
322     }
323 
324     ~ClassDataAccessor() = default;
325 
326     DEFAULT_COPY_SEMANTIC(ClassDataAccessor);
327     DEFAULT_MOVE_SEMANTIC(ClassDataAccessor);
328 
329 private:
330     Span<Field> fields_;
331 };
332 
GetClassInfo(Span<Method> methods,Span<Field> fields,Class * base,Span<Class * > interfaces,bool is_interface)333 ClassLinker::ClassInfo ClassLinker::GetClassInfo(Span<Method> methods, Span<Field> fields, Class *base,
334                                                  Span<Class *> interfaces, bool is_interface)
335 {
336     LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(*base);
337 
338     auto vtable_builder = ctx.CreateVTableBuilder();
339     auto itable_builder = ctx.CreateITableBuilder();
340     auto imtable_builder = ctx.CreateIMTableBuilder();
341 
342     itable_builder->Build(this, base, interfaces, is_interface);
343     vtable_builder->Build(methods, base, itable_builder->GetITable(), is_interface);
344     imtable_builder->Build(itable_builder->GetITable(), is_interface);
345 
346     ClassDataAccessor data_accessor(fields);
347     size_t num_sfields = 0;
348     size_t size =
349         GetClassSize(data_accessor, vtable_builder->GetVTableSize(), imtable_builder->GetIMTSize(), &num_sfields);
350 
351     return {size, num_sfields, std::move(vtable_builder), std::move(itable_builder), std::move(imtable_builder)};
352 }
353 
LoadMethod(Method * method,panda_file::MethodDataAccessor * method_data_accessor,Class * klass,const LanguageContext & ctx,const ClassLinkerExtension * ext)354 static void LoadMethod(Method *method, panda_file::MethodDataAccessor *method_data_accessor, Class *klass,
355                        const LanguageContext &ctx, const ClassLinkerExtension *ext)
356 {
357     const auto &pf = method_data_accessor->GetPandaFile();
358     panda_file::ProtoDataAccessor pda(pf, method_data_accessor->GetProtoId());
359 
360     uint32_t access_flags = method_data_accessor->GetAccessFlags();
361 
362     auto *method_name = pf.GetStringData(method_data_accessor->GetNameId()).data;
363     if (utf::IsEqual(method_name, ctx.GetCtorName()) || utf::IsEqual(method_name, ctx.GetCctorName())) {
364         access_flags |= ACC_CONSTRUCTOR;
365     }
366 
367     auto code_id = method_data_accessor->GetCodeId();
368     size_t num_args = (method_data_accessor->IsStatic()) ? pda.GetNumArgs() : (pda.GetNumArgs() + 1);
369 
370     if (!code_id.has_value()) {
371         InitializeMemory(method, klass, &pf, method_data_accessor->GetMethodId(), panda_file::File::EntityId(0),
372                          access_flags, num_args, reinterpret_cast<const uint16_t *>(pda.GetShorty().Data()));
373 
374         if (method_data_accessor->IsNative()) {
375             method->SetCompiledEntryPoint(ext->GetNativeEntryPointFor(method));
376         } else {
377             method->SetInterpreterEntryPoint();
378         }
379     } else {
380         InitializeMemory(method, klass, &pf, method_data_accessor->GetMethodId(), code_id.value(), access_flags,
381                          num_args, reinterpret_cast<const uint16_t *>(pda.GetShorty().Data()));
382         method->SetCompiledEntryPoint(GetCompiledCodeToInterpreterBridge(method));
383     }
384 }
385 
MaybeLinkMethodToAotCode(Method * method,const compiler::AotClass & aot_class,size_t method_index)386 void MaybeLinkMethodToAotCode(Method *method, const compiler::AotClass &aot_class, size_t method_index)
387 {
388     ASSERT(aot_class.IsValid());
389     if (method->IsIntrinsic()) {
390         return;
391     }
392     auto entry = aot_class.FindMethodCodeEntry(method_index);
393     if (entry != nullptr) {
394         method->SetCompiledEntryPoint(entry);
395         LOG(DEBUG, AOT) << "Found AOT entrypoint ["
396                         << reinterpret_cast<const void *>(aot_class.FindMethodCodeSpan(method_index).data()) << ":"
397                         << reinterpret_cast<const void *>(aot_class.FindMethodCodeSpan(method_index).end())
398                         << "] for method: " << method->GetFullName();
399 
400         EVENT_AOT_ENTRYPOINT_FOUND(method->GetFullName());
401         ASSERT(aot_class.FindMethodHeader(method_index)->method_id == method->GetFileId().GetOffset());
402     }
403 }
404 
LoadMethods(Class * klass,ClassInfo * class_info,panda_file::ClassDataAccessor * data_accessor,ClassLinkerErrorHandler * error_handler)405 bool ClassLinker::LoadMethods(Class *klass, ClassInfo *class_info, panda_file::ClassDataAccessor *data_accessor,
406                               [[maybe_unused]] ClassLinkerErrorHandler *error_handler)
407 {
408     uint32_t num_methods = data_accessor->GetMethodsNumber();
409 
410     uint32_t num_vmethods = klass->GetNumVirtualMethods();
411     uint32_t num_smethods = num_methods - num_vmethods;
412 
413     auto &copied_methods = class_info->vtable_builder->GetCopiedMethods();
414     uint32_t n = num_methods + copied_methods.size();
415 
416     if (n == 0) {
417         return true;
418     }
419 
420     Span<Method> methods {allocator_->AllocArray<Method>(n), n};
421 
422     size_t smethod_idx = num_vmethods;
423     size_t vmethod_idx = 0;
424 
425     LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(*klass);
426     auto *ext = GetExtension(ctx);
427     ASSERT(ext != nullptr);
428 
429     auto aot_pfile = aot_manager_->FindPandaFile(klass->GetPandaFile()->GetFullFileName());
430     if (aot_pfile != nullptr) {
431         EVENT_AOT_LOADED_FOR_CLASS(PandaString(aot_pfile->GetFileName()), PandaString(klass->GetName()));
432     }
433 
434     compiler::AotClass aot_class =
435         (aot_pfile != nullptr) ? aot_pfile->GetClass(klass->GetFileId().GetOffset()) : compiler::AotClass::Invalid();
436 
437     size_t method_index = 0;
438     data_accessor->EnumerateMethods([klass, &smethod_idx, &vmethod_idx, &methods, aot_class, ctx, ext,
439                                      &method_index](panda_file::MethodDataAccessor &method_data_accessor) {
440         Method *method = method_data_accessor.IsStatic() ? &methods[smethod_idx++] : &methods[vmethod_idx++];
441         LoadMethod(method, &method_data_accessor, klass, ctx, ext);
442         if (aot_class.IsValid()) {
443             MaybeLinkMethodToAotCode(method, aot_class, method_index);
444         }
445         // Instead of checking if the method is abstract before every virtual call
446         // the special stub throwing AbstractMethodError is registered as compiled entry point.
447         if (method->IsAbstract()) {
448             method->SetCompiledEntryPoint(GetAbstractMethodStub());
449         }
450         method_index++;
451     });
452 
453     for (size_t i = 0; i < copied_methods.size(); i++) {
454         size_t idx = num_methods + i;
455         InitializeMemory(&methods[idx], copied_methods[i].method_);
456         methods[idx].SetIsDefaultInterfaceMethod();
457         if (copied_methods[i].default_abstract_) {
458             methods[idx].SetCompiledEntryPoint(GetAbstractMethodStub());
459         }
460         if (copied_methods[i].default_conflict_) {
461             methods[idx].SetCompiledEntryPoint(GetDefaultConflictMethodStub());
462         }
463     }
464 
465     klass->SetMethods(methods, num_vmethods, num_smethods);
466 
467     return true;
468 }
469 
LoadFields(Class * klass,panda_file::ClassDataAccessor * data_accessor,ClassLinkerErrorHandler * error_handler)470 bool ClassLinker::LoadFields(Class *klass, panda_file::ClassDataAccessor *data_accessor,
471                              [[maybe_unused]] ClassLinkerErrorHandler *error_handler)
472 {
473     uint32_t num_fields = data_accessor->GetFieldsNumber();
474     if (num_fields == 0) {
475         return true;
476     }
477 
478     uint32_t num_sfields = klass->GetNumStaticFields();
479 
480     Span<Field> fields {allocator_->AllocArray<Field>(num_fields), num_fields};
481 
482     size_t sfields_idx = 0;
483     size_t ifields_idx = num_sfields;
484     data_accessor->EnumerateFields(
485         [klass, &sfields_idx, &ifields_idx, &fields](panda_file::FieldDataAccessor &field_data_accessor) {
486             Field *field = field_data_accessor.IsStatic() ? &fields[sfields_idx++] : &fields[ifields_idx++];
487             InitializeMemory(field, klass, field_data_accessor.GetFieldId(), field_data_accessor.GetAccessFlags(),
488                              panda_file::Type::GetTypeFromFieldEncoding(field_data_accessor.GetType()));
489         });
490 
491     klass->SetFields(fields, num_sfields);
492 
493     return true;
494 }
495 
496 template <bool reverse_layout = false>
LayoutFieldsWithoutAlignment(size_t size,size_t * offset,size_t * space,PandaList<Field * > * fields)497 static void LayoutFieldsWithoutAlignment(size_t size, size_t *offset, size_t *space, PandaList<Field *> *fields)
498 {
499     while ((space == nullptr || *space >= size) && !fields->empty()) {
500         Field *field = fields->front();
501         // NOLINTNEXTLINE(readability-braces-around-statements)
502         if constexpr (reverse_layout) {
503             *offset -= size;
504             field->SetOffset(*offset);
505             // NOLINTNEXTLINE(readability-misleading-indentation)
506         } else {
507             field->SetOffset(*offset);
508             *offset += size;
509         }
510         if (space != nullptr) {
511             *space -= size;
512         }
513         fields->pop_front();
514     }
515 }
516 
LayoutReferenceFields(size_t size,size_t * offset,const PandaList<Field * > & fields)517 static uint32_t LayoutReferenceFields(size_t size, size_t *offset, const PandaList<Field *> &fields)
518 {
519     uint32_t volatile_fields_num = 0;
520     // layout volatile fields firstly
521     for (auto *field : fields) {
522         if (field->IsVolatile()) {
523             volatile_fields_num++;
524             field->SetOffset(*offset);
525             *offset += size;
526         }
527     }
528     for (auto *field : fields) {
529         if (!field->IsVolatile()) {
530             field->SetOffset(*offset);
531             *offset += size;
532         }
533     }
534     return volatile_fields_num;
535 }
536 
LayoutFieldsInBaseClassPadding(Class * klass,PandaList<Field * > * tagged_fields,PandaList<Field * > * fields64,PandaList<Field * > * fields32,PandaList<Field * > * fields16,PandaList<Field * > * fields8,PandaList<Field * > * ref_fields,bool is_static)537 static size_t LayoutFieldsInBaseClassPadding(Class *klass, PandaList<Field *> *tagged_fields,
538                                              PandaList<Field *> *fields64, PandaList<Field *> *fields32,
539                                              PandaList<Field *> *fields16, PandaList<Field *> *fields8,
540                                              PandaList<Field *> *ref_fields, bool is_static)
541 {
542     constexpr size_t SIZE_64 = sizeof(uint64_t);
543     constexpr size_t SIZE_32 = sizeof(uint32_t);
544     constexpr size_t SIZE_16 = sizeof(uint16_t);
545     constexpr size_t SIZE_8 = sizeof(uint8_t);
546 
547     size_t offset;
548 
549     if (is_static) {
550         offset = klass->GetStaticFieldsOffset();
551     } else {
552         offset = (klass->GetBase() != nullptr) ? klass->GetBase()->GetObjectSize() : ObjectHeader::ObjectHeaderSize();
553     }
554 
555     size_t align_offset = offset;
556     if (!ref_fields->empty()) {
557         align_offset = AlignUp(offset, ClassHelper::OBJECT_POINTER_SIZE);
558     } else if (!(fields64->empty()) || !(tagged_fields->empty())) {
559         align_offset = AlignUp(offset, SIZE_64);
560     } else if (!fields32->empty()) {
561         align_offset = AlignUp(offset, SIZE_32);
562     } else if (!fields16->empty()) {
563         align_offset = AlignUp(offset, SIZE_16);
564     }
565     if (align_offset != offset) {
566         size_t end_offset = align_offset;
567         size_t padding = end_offset - offset;
568         // try to put short fields of child class at end of free space of base class
569         LayoutFieldsWithoutAlignment<true>(SIZE_32, &end_offset, &padding, fields32);
570         LayoutFieldsWithoutAlignment<true>(SIZE_16, &end_offset, &padding, fields16);
571         LayoutFieldsWithoutAlignment<true>(SIZE_8, &end_offset, &padding, fields8);
572     }
573     return align_offset;
574 }
575 
LayoutFields(Class * klass,PandaList<Field * > * tagged_fields,PandaList<Field * > * fields64,PandaList<Field * > * fields32,PandaList<Field * > * fields16,PandaList<Field * > * fields8,PandaList<Field * > * ref_fields,bool is_static)576 static size_t LayoutFields(Class *klass, PandaList<Field *> *tagged_fields, PandaList<Field *> *fields64,
577                            PandaList<Field *> *fields32, PandaList<Field *> *fields16, PandaList<Field *> *fields8,
578                            PandaList<Field *> *ref_fields, bool is_static)
579 {
580     constexpr size_t SIZE_64 = sizeof(uint64_t);
581     constexpr size_t SIZE_32 = sizeof(uint32_t);
582     constexpr size_t SIZE_16 = sizeof(uint16_t);
583     constexpr size_t SIZE_8 = sizeof(uint8_t);
584 
585     size_t offset = LayoutFieldsInBaseClassPadding(klass, tagged_fields, fields64, fields32, fields16, fields8,
586                                                    ref_fields, is_static);
587     if (!ref_fields->empty()) {
588         offset = AlignUp(offset, ClassHelper::OBJECT_POINTER_SIZE);
589         klass->SetRefFieldsNum(ref_fields->size(), is_static);
590         klass->SetRefFieldsOffset(offset, is_static);
591         auto volatile_num = LayoutReferenceFields(ClassHelper::OBJECT_POINTER_SIZE, &offset, *ref_fields);
592         klass->SetVolatileRefFieldsNum(volatile_num, is_static);
593     }
594 
595     static_assert(coretypes::TaggedValue::TaggedTypeSize() == SIZE_64,
596                   "Please fix alignment of the fields of type \"TaggedValue\"");
597     if (!IsAligned<SIZE_64>(offset) && (!fields64->empty() || !tagged_fields->empty())) {
598         size_t padding = AlignUp(offset, SIZE_64) - offset;
599 
600         LayoutFieldsWithoutAlignment(SIZE_32, &offset, &padding, fields32);
601         LayoutFieldsWithoutAlignment(SIZE_16, &offset, &padding, fields16);
602         LayoutFieldsWithoutAlignment(SIZE_8, &offset, &padding, fields8);
603 
604         offset += padding;
605     }
606 
607     LayoutFieldsWithoutAlignment(coretypes::TaggedValue::TaggedTypeSize(), &offset, nullptr, tagged_fields);
608     LayoutFieldsWithoutAlignment(SIZE_64, &offset, nullptr, fields64);
609 
610     if (!IsAligned<SIZE_32>(offset) && !fields32->empty()) {
611         size_t padding = AlignUp(offset, SIZE_32) - offset;
612 
613         LayoutFieldsWithoutAlignment(SIZE_16, &offset, &padding, fields16);
614         LayoutFieldsWithoutAlignment(SIZE_8, &offset, &padding, fields8);
615 
616         offset += padding;
617     }
618 
619     LayoutFieldsWithoutAlignment(SIZE_32, &offset, nullptr, fields32);
620 
621     if (!IsAligned<SIZE_16>(offset) && !fields16->empty()) {
622         size_t padding = AlignUp(offset, SIZE_16) - offset;
623 
624         LayoutFieldsWithoutAlignment(SIZE_8, &offset, &padding, fields8);
625 
626         offset += padding;
627     }
628 
629     LayoutFieldsWithoutAlignment(SIZE_16, &offset, nullptr, fields16);
630 
631     LayoutFieldsWithoutAlignment(SIZE_8, &offset, nullptr, fields8);
632 
633     return offset;
634 }
635 
636 /* static */
LayoutFields(Class * klass,Span<Field> fields,bool is_static,ClassLinkerErrorHandler * error_handler)637 bool ClassLinker::LayoutFields(Class *klass, Span<Field> fields, bool is_static,
638                                [[maybe_unused]] ClassLinkerErrorHandler *error_handler)
639 {
640     PandaList<Field *> tagged_fields;
641     PandaList<Field *> fields64;
642     PandaList<Field *> fields32;
643     PandaList<Field *> fields16;
644     PandaList<Field *> fields8;
645     PandaList<Field *> ref_fields;
646 
647     for (auto &field : fields) {
648         auto type = field.GetType();
649 
650         if (!type.IsPrimitive()) {
651             ref_fields.push_back(&field);
652             continue;
653         }
654 
655         switch (type.GetId()) {
656             case Type::TypeId::U1:
657             case Type::TypeId::I8:
658             case Type::TypeId::U8:
659                 fields8.push_back(&field);
660                 break;
661             case Type::TypeId::I16:
662             case Type::TypeId::U16:
663                 fields16.push_back(&field);
664                 break;
665             case Type::TypeId::I32:
666             case Type::TypeId::U32:
667             case Type::TypeId::F32:
668                 fields32.push_back(&field);
669                 break;
670             case Type::TypeId::I64:
671             case Type::TypeId::U64:
672             case Type::TypeId::F64:
673                 fields64.push_back(&field);
674                 break;
675             case Type::TypeId::TAGGED:
676                 tagged_fields.push_back(&field);
677                 break;
678             default:
679                 UNREACHABLE();
680                 break;
681         }
682     }
683 
684     size_t size =
685         panda::LayoutFields(klass, &tagged_fields, &fields64, &fields32, &fields16, &fields8, &ref_fields, is_static);
686 
687     if (!is_static && !klass->IsVariableSize()) {
688         klass->SetObjectSize(size);
689     }
690 
691     return true;
692 }
693 
LinkMethods(Class * klass,ClassInfo * class_info,ClassLinkerErrorHandler * error_handler)694 bool ClassLinker::LinkMethods(Class *klass, ClassInfo *class_info,
695                               [[maybe_unused]] ClassLinkerErrorHandler *error_handler)
696 {
697     class_info->vtable_builder->UpdateClass(klass);
698     class_info->itable_builder->Resolve(klass);
699     class_info->itable_builder->UpdateClass(klass);
700     class_info->imtable_builder->UpdateClass(klass);
701 
702     return true;
703 }
704 
LinkFields(Class * klass,ClassLinkerErrorHandler * error_handler)705 bool ClassLinker::LinkFields(Class *klass, ClassLinkerErrorHandler *error_handler)
706 {
707     if (!LayoutFields(klass, klass->GetStaticFields(), true, error_handler)) {
708         LOG(ERROR, CLASS_LINKER) << "Cannot layout static fields of class '" << klass->GetName() << "'";
709         return false;
710     }
711 
712     if (!LayoutFields(klass, klass->GetInstanceFields(), false, error_handler)) {
713         LOG(ERROR, CLASS_LINKER) << "Cannot layout instance fields of class '" << klass->GetName() << "'";
714         return false;
715     }
716 
717     return true;
718 }
719 
LoadBaseClass(panda_file::ClassDataAccessor * cda,const LanguageContext & ctx,ClassLinkerContext * context,ClassLinkerErrorHandler * error_handler)720 Class *ClassLinker::LoadBaseClass(panda_file::ClassDataAccessor *cda, const LanguageContext &ctx,
721                                   ClassLinkerContext *context, ClassLinkerErrorHandler *error_handler)
722 {
723     auto base_class_id = cda->GetSuperClassId();
724     auto *ext = GetExtension(ctx);
725     ASSERT(ext != nullptr);
726     if (base_class_id.GetOffset() == 0) {
727         return ext->GetClassRoot(ClassRoot::OBJECT);
728     }
729 
730     auto &pf = cda->GetPandaFile();
731     auto *base_class = ext->GetClass(pf, base_class_id, context, error_handler);
732     if (base_class == nullptr) {
733         LOG(INFO, CLASS_LINKER) << "Cannot find base class '"
734                                 << utf::Mutf8AsCString(pf.GetStringData(base_class_id).data) << "' of class '"
735                                 << utf::Mutf8AsCString(pf.GetStringData(cda->GetClassId()).data) << "' in ctx "
736                                 << context;
737         return nullptr;
738     }
739 
740     return base_class;
741 }
742 
LoadInterfaces(panda_file::ClassDataAccessor * cda,ClassLinkerContext * context,ClassLinkerErrorHandler * error_handler)743 std::optional<Span<Class *>> ClassLinker::LoadInterfaces(panda_file::ClassDataAccessor *cda,
744                                                          ClassLinkerContext *context,
745                                                          ClassLinkerErrorHandler *error_handler)
746 {
747     ASSERT(context != nullptr);
748     size_t ifaces_num = cda->GetIfacesNumber();
749 
750     if (ifaces_num == 0) {
751         return Span<Class *>(nullptr, ifaces_num);
752     }
753 
754     Span<Class *> ifaces {allocator_->AllocArray<Class *>(ifaces_num), ifaces_num};
755 
756     for (size_t i = 0; i < ifaces_num; i++) {
757         auto id = cda->GetInterfaceId(i);
758         auto &pf = cda->GetPandaFile();
759         auto *iface = GetClass(pf, id, context, error_handler);
760         if (iface == nullptr) {
761             LOG(INFO, CLASS_LINKER) << "Cannot find interface '" << utf::Mutf8AsCString(pf.GetStringData(id).data)
762                                     << "' of class '" << utf::Mutf8AsCString(pf.GetStringData(cda->GetClassId()).data)
763                                     << "' in ctx " << context;
764             ASSERT(!ifaces.Empty());
765             allocator_->Free(ifaces.begin());
766             return {};
767         }
768 
769         ifaces[i] = iface;
770     }
771 
772     return ifaces;
773 }
774 
775 using ClassLoadingSet = std::unordered_set<uint64_t>;
776 
777 // This class is required to clear static unordered_set on return
778 class ClassScopeStaticSetAutoCleaner {
779 public:
780     ClassScopeStaticSetAutoCleaner() = default;
ClassScopeStaticSetAutoCleaner(ClassLoadingSet * set_ptr,ClassLoadingSet ** tl_set_ptr)781     explicit ClassScopeStaticSetAutoCleaner(ClassLoadingSet *set_ptr, ClassLoadingSet **tl_set_ptr)
782         : set_ptr_(set_ptr), tl_set_ptr_(tl_set_ptr)
783     {
784     }
~ClassScopeStaticSetAutoCleaner()785     ~ClassScopeStaticSetAutoCleaner()
786     {
787         set_ptr_->clear();
788         if (tl_set_ptr_ != nullptr) {
789             *tl_set_ptr_ = nullptr;
790         }
791     }
792 
793     NO_COPY_SEMANTIC(ClassScopeStaticSetAutoCleaner);
794     NO_MOVE_SEMANTIC(ClassScopeStaticSetAutoCleaner);
795 
796 private:
797     ClassLoadingSet *set_ptr_;
798     ClassLoadingSet **tl_set_ptr_;
799 };
800 
GetClassUniqueHash(uint32_t panda_file_hash,uint32_t class_id)801 static uint64_t GetClassUniqueHash(uint32_t panda_file_hash, uint32_t class_id)
802 {
803     const uint8_t BITS_TO_SHUFFLE = 32;
804     return (static_cast<uint64_t>(panda_file_hash) << BITS_TO_SHUFFLE) | static_cast<uint64_t>(class_id);
805 }
806 
LoadClass(panda_file::ClassDataAccessor * class_data_accessor,const uint8_t * descriptor,Class * base_class,Span<Class * > interfaces,ClassLinkerContext * context,ClassLinkerExtension * ext,ClassLinkerErrorHandler * error_handler)807 Class *ClassLinker::LoadClass(panda_file::ClassDataAccessor *class_data_accessor, const uint8_t *descriptor,
808                               Class *base_class, Span<Class *> interfaces, ClassLinkerContext *context,
809                               ClassLinkerExtension *ext, ClassLinkerErrorHandler *error_handler)
810 {
811     ASSERT(context != nullptr);
812     ClassInfo class_info = GetClassInfo(class_data_accessor, base_class, interfaces, context);
813 
814     auto *klass = ext->CreateClass(descriptor, class_info.vtable_builder->GetVTableSize(),
815                                    class_info.imtable_builder->GetIMTSize(), class_info.size);
816 
817     if (UNLIKELY(klass == nullptr)) {
818         return nullptr;
819     }
820 
821     klass->SetLoadContext(context);
822     klass->SetBase(base_class);
823     klass->SetInterfaces(interfaces);
824     klass->SetFileId(class_data_accessor->GetClassId());
825     klass->SetPandaFile(&class_data_accessor->GetPandaFile());
826     klass->SetAccessFlags(class_data_accessor->GetAccessFlags());
827 
828     auto &pf = class_data_accessor->GetPandaFile();
829     auto class_id = class_data_accessor->GetClassId();
830     klass->SetClassIndex(pf.GetClassIndex(class_id));
831     klass->SetMethodIndex(pf.GetMethodIndex(class_id));
832     klass->SetFieldIndex(pf.GetFieldIndex(class_id));
833 
834     klass->SetNumVirtualMethods(class_info.vtable_builder->GetNumVirtualMethods());
835     klass->SetNumCopiedMethods(class_info.vtable_builder->GetCopiedMethods().size());
836     klass->SetNumStaticFields(class_info.num_sfields);
837 
838     if (!LoadMethods(klass, &class_info, class_data_accessor, error_handler)) {
839         FreeClass(klass);
840         LOG(ERROR, CLASS_LINKER) << "Cannot load methods of class '" << descriptor << "'";
841         return nullptr;
842     }
843 
844     if (!LoadFields(klass, class_data_accessor, error_handler)) {
845         FreeClass(klass);
846         LOG(ERROR, CLASS_LINKER) << "Cannot load fields of class '" << descriptor << "'";
847         return nullptr;
848     }
849 
850     if (!LinkMethods(klass, &class_info, error_handler)) {
851         FreeClass(klass);
852         LOG(ERROR, CLASS_LINKER) << "Cannot link methods of class '" << descriptor << "'";
853         return nullptr;
854     }
855 
856     if (!LinkFields(klass, error_handler)) {
857         FreeClass(klass);
858         LOG(ERROR, CLASS_LINKER) << "Cannot link fields of class '" << descriptor << "'";
859         return nullptr;
860     }
861 
862     return klass;
863 }
864 
LoadClass(const panda_file::File * pf,const uint8_t * descriptor,panda_file::SourceLang lang)865 Class *ClassLinker::LoadClass(const panda_file::File *pf, const uint8_t *descriptor, panda_file::SourceLang lang)
866 {
867     panda_file::File::EntityId class_id = pf->GetClassId(descriptor);
868     if (!class_id.IsValid() || pf->IsExternal(class_id)) {
869         return nullptr;
870     }
871     ClassLinkerContext *context = GetExtension(lang)->GetBootContext();
872     return LoadClass(pf, class_id, descriptor, context, nullptr);
873 }
874 
LoadClass(const panda_file::File * pf,panda_file::File::EntityId class_id,const uint8_t * descriptor,ClassLinkerContext * context,ClassLinkerErrorHandler * error_handler,bool add_to_runtime)875 Class *ClassLinker::LoadClass(const panda_file::File *pf, panda_file::File::EntityId class_id,
876                               const uint8_t *descriptor, ClassLinkerContext *context,
877                               ClassLinkerErrorHandler *error_handler, bool add_to_runtime /* = true */)
878 {
879     ASSERT(!pf->IsExternal(class_id));
880     ASSERT(context != nullptr);
881     panda_file::ClassDataAccessor class_data_accessor(*pf, class_id);
882     LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(&class_data_accessor);
883 
884     if (ctx.GetLanguage() != context->GetSourceLang()) {
885         LanguageContext current_ctx = Runtime::GetCurrent()->GetLanguageContext(context->GetSourceLang());
886         PandaStringStream ss;
887         ss << "Cannot load " << ctx << " class " << descriptor << " into " << current_ctx << " context";
888         LOG(ERROR, CLASS_LINKER) << ss.str();
889         OnError(error_handler, Error::CLASS_NOT_FOUND, ss.str());
890         return nullptr;
891     }
892 
893     if (!HasExtension(ctx)) {
894         PandaStringStream ss;
895         ss << "Cannot load class '" << descriptor << "' as class linker hasn't " << ctx << " language extension";
896         LOG(ERROR, CLASS_LINKER) << ss.str();
897         OnError(error_handler, Error::CLASS_NOT_FOUND, ss.str());
898         return nullptr;
899     }
900 
901     // This set is used to find out if the class is its own superclass
902     ClassLoadingSet loading_set;
903     static thread_local ClassLoadingSet *thread_local_set = nullptr;
904     ClassLoadingSet **thread_local_set_ptr = nullptr;
905     if (thread_local_set == nullptr) {
906         thread_local_set = &loading_set;
907         thread_local_set_ptr = &thread_local_set;
908     }
909     ClassScopeStaticSetAutoCleaner class_set_auto_cleaner_on_return(thread_local_set, thread_local_set_ptr);
910 
911     auto *ext = GetExtension(ctx);
912     Class *base_class = nullptr;
913     bool need_load_base = IsInitialized() || !utf::IsEqual(ctx.GetObjectClassDescriptor(), descriptor);
914     if (need_load_base) {
915         uint32_t class_id_int = class_id.GetOffset();
916         uint32_t panda_file_hash = pf->GetFilenameHash();
917         if (!thread_local_set->insert(GetClassUniqueHash(panda_file_hash, class_id_int)).second) {
918             const PandaString &class_name =
919                 utf::Mutf8AsCString(pf->GetStringData(class_data_accessor.GetClassId()).data);
920             PandaString msg = "Class or interface \"" + class_name + "\" is its own superclass or superinterface";
921             OnError(error_handler, Error::CLASS_CIRCULARITY, msg);
922             return nullptr;
923         }
924 
925         base_class = LoadBaseClass(&class_data_accessor, ctx, context, error_handler);
926         if (base_class == nullptr) {
927             LOG(INFO, CLASS_LINKER) << "Cannot load base class of class '" << descriptor << "'";
928             return nullptr;
929         }
930     }
931 
932     auto res = LoadInterfaces(&class_data_accessor, context, error_handler);
933     if (!res) {
934         LOG(INFO, CLASS_LINKER) << "Cannot load interfaces of class '" << descriptor << "'";
935         return nullptr;
936     }
937 
938     auto *klass = LoadClass(&class_data_accessor, descriptor, base_class, res.value(), context, ext, error_handler);
939     if (klass == nullptr) {
940         return nullptr;
941     }
942 
943     Runtime::GetCurrent()->GetCha()->Update(klass);
944 
945     if (LIKELY(ext->CanInitializeClasses())) {
946         ext->InitializeClass(klass);
947         klass->SetState(Class::State::LOADED);
948     }
949 
950     if (LIKELY(add_to_runtime)) {
951         Runtime::GetCurrent()->GetNotificationManager()->ClassLoadEvent(klass);
952 
953         auto *other_klass = context->InsertClass(klass);
954         if (other_klass != nullptr) {
955             // Someone has created the class in the other thread (increase the critical section?)
956             FreeClass(klass);
957             return other_klass;
958         }
959 
960         RemoveCreatedClassInExtension(klass);
961         Runtime::GetCurrent()->GetNotificationManager()->ClassPrepareEvent(klass);
962     }
963     return klass;
964 }
965 
CopyMutf8String(mem::InternalAllocatorPtr allocator,const uint8_t * descriptor)966 static const uint8_t *CopyMutf8String(mem::InternalAllocatorPtr allocator, const uint8_t *descriptor)
967 {
968     size_t size = utf::Mutf8Size(descriptor) + 1;  // + 1 - null terminate
969     auto *ptr = allocator->AllocArray<uint8_t>(size);
970     memcpy_s(ptr, size, descriptor, size);
971     return ptr;
972 }
973 
BuildClass(const uint8_t * descriptor,bool need_copy_descriptor,uint32_t access_flags,Span<Method> methods,Span<Field> fields,Class * base_class,Span<Class * > interfaces,ClassLinkerContext * context,bool is_interface)974 Class *ClassLinker::BuildClass(const uint8_t *descriptor, bool need_copy_descriptor, uint32_t access_flags,
975                                Span<Method> methods, Span<Field> fields, Class *base_class, Span<Class *> interfaces,
976                                ClassLinkerContext *context, bool is_interface)
977 {
978     ASSERT(context != nullptr);
979     if (need_copy_descriptor) {
980         descriptor = CopyMutf8String(allocator_, descriptor);
981         os::memory::LockHolder lock(copied_names_lock_);
982         copied_names_.push_front(descriptor);
983     }
984 
985     auto *ext = GetExtension(base_class->GetSourceLang());
986     ASSERT(ext != nullptr);
987 
988     ClassInfo class_info = GetClassInfo(methods, fields, base_class, interfaces, is_interface);
989 
990     // Need to protect ArenaAllocator and loaded_classes_
991     auto *klass = ext->CreateClass(descriptor, class_info.vtable_builder->GetVTableSize(),
992                                    class_info.imtable_builder->GetIMTSize(), class_info.size);
993 
994     if (UNLIKELY(klass == nullptr)) {
995         return nullptr;
996     }
997 
998     klass->SetLoadContext(context);
999     klass->SetBase(base_class);
1000     klass->SetInterfaces(interfaces);
1001     klass->SetAccessFlags(access_flags);
1002 
1003     klass->SetNumVirtualMethods(class_info.vtable_builder->GetNumVirtualMethods());
1004     klass->SetNumCopiedMethods(class_info.vtable_builder->GetCopiedMethods().size());
1005     klass->SetNumStaticFields(class_info.num_sfields);
1006 
1007     ASSERT(klass->GetNumCopiedMethods() == 0);
1008 
1009     size_t num_smethods = methods.size() - klass->GetNumVirtualMethods();
1010     klass->SetMethods(methods, klass->GetNumVirtualMethods(), num_smethods);
1011     klass->SetFields(fields, klass->GetNumStaticFields());
1012 
1013     for (auto &method : methods) {
1014         method.SetClass(klass);
1015     }
1016 
1017     for (auto &field : fields) {
1018         field.SetClass(klass);
1019     }
1020 
1021     if (!LinkMethods(klass, &class_info, ext->GetErrorHandler())) {
1022         LOG(ERROR, CLASS_LINKER) << "Cannot link class methods '" << descriptor << "'";
1023         return nullptr;
1024     }
1025 
1026     if (!LinkFields(klass, ext->GetErrorHandler())) {
1027         LOG(ERROR, CLASS_LINKER) << "Cannot link class fields '" << descriptor << "'";
1028         return nullptr;
1029     }
1030 
1031     ext->InitializeClass(klass);
1032     klass->SetState(Class::State::LOADED);
1033 
1034     Runtime::GetCurrent()->GetNotificationManager()->ClassLoadEvent(klass);
1035 
1036     auto *other_klass = context->InsertClass(klass);
1037     if (other_klass != nullptr) {
1038         // Someone has created the class in the other thread (increase the critical section?)
1039         FreeClass(klass);
1040         return other_klass;
1041     }
1042 
1043     RemoveCreatedClassInExtension(klass);
1044     Runtime::GetCurrent()->GetNotificationManager()->ClassPrepareEvent(klass);
1045 
1046     return klass;
1047 }
1048 
CreateArrayClass(ClassLinkerExtension * ext,const uint8_t * descriptor,bool need_copy_descriptor,Class * component_class)1049 Class *ClassLinker::CreateArrayClass(ClassLinkerExtension *ext, const uint8_t *descriptor, bool need_copy_descriptor,
1050                                      Class *component_class)
1051 {
1052     if (need_copy_descriptor) {
1053         descriptor = CopyMutf8String(allocator_, descriptor);
1054         os::memory::LockHolder lock(copied_names_lock_);
1055         copied_names_.push_front(descriptor);
1056     }
1057 
1058     auto *array_class = ext->CreateClass(descriptor, ext->GetArrayClassVTableSize(), ext->GetArrayClassIMTSize(),
1059                                          ext->GetArrayClassSize());
1060 
1061     if (UNLIKELY(array_class == nullptr)) {
1062         return nullptr;
1063     }
1064 
1065     array_class->SetLoadContext(component_class->GetLoadContext());
1066 
1067     ext->InitializeArrayClass(array_class, component_class);
1068 
1069     return array_class;
1070 }
1071 
LoadArrayClass(const uint8_t * descriptor,bool need_copy_descriptor,ClassLinkerContext * context,ClassLinkerErrorHandler * error_handler)1072 Class *ClassLinker::LoadArrayClass(const uint8_t *descriptor, bool need_copy_descriptor, ClassLinkerContext *context,
1073                                    ClassLinkerErrorHandler *error_handler)
1074 {
1075     Span<const uint8_t> sp(descriptor, 1);
1076 
1077     Class *component_class = GetClass(sp.cend(), need_copy_descriptor, context, error_handler);
1078 
1079     if (component_class == nullptr) {
1080         return nullptr;
1081     }
1082 
1083     if (UNLIKELY(component_class->GetType().GetId() == panda_file::Type::TypeId::VOID)) {
1084         OnError(error_handler, Error::NO_CLASS_DEF, "Try to create array with void component type");
1085         return nullptr;
1086     }
1087 
1088     auto *ext = GetExtension(component_class->GetSourceLang());
1089     ASSERT(ext != nullptr);
1090 
1091     auto *component_class_context = component_class->GetLoadContext();
1092     ASSERT(component_class_context != nullptr);
1093     if (component_class_context != context) {
1094         auto *loaded_class = FindLoadedClass(descriptor, component_class_context);
1095         if (loaded_class != nullptr) {
1096             return loaded_class;
1097         }
1098     }
1099 
1100     auto *array_class = CreateArrayClass(ext, descriptor, need_copy_descriptor, component_class);
1101 
1102     if (UNLIKELY(array_class == nullptr)) {
1103         return nullptr;
1104     }
1105 
1106     Runtime::GetCurrent()->GetNotificationManager()->ClassLoadEvent(array_class);
1107 
1108     auto *other_klass = component_class_context->InsertClass(array_class);
1109     if (other_klass != nullptr) {
1110         FreeClass(array_class);
1111         return other_klass;
1112     }
1113 
1114     RemoveCreatedClassInExtension(array_class);
1115     Runtime::GetCurrent()->GetNotificationManager()->ClassPrepareEvent(array_class);
1116 
1117     return array_class;
1118 }
1119 
PandaFilesToString(const PandaVector<const panda_file::File * > & panda_files)1120 static PandaString PandaFilesToString(const PandaVector<const panda_file::File *> &panda_files)
1121 {
1122     PandaStringStream ss;
1123     ss << "[";
1124 
1125     size_t n = panda_files.size();
1126     for (size_t i = 0; i < n; i++) {
1127         ss << panda_files[i]->GetFilename();
1128 
1129         if (i < n - 1) {
1130             ss << ", ";
1131         }
1132     }
1133 
1134     ss << "]";
1135     return ss.str();
1136 }
1137 
GetClass(const uint8_t * descriptor,bool need_copy_descriptor,ClassLinkerContext * context,ClassLinkerErrorHandler * error_handler)1138 Class *ClassLinker::GetClass(const uint8_t *descriptor, bool need_copy_descriptor, ClassLinkerContext *context,
1139                              ClassLinkerErrorHandler *error_handler /* = nullptr */)
1140 {
1141     ASSERT(context != nullptr);
1142     ASSERT(!MTManagedThread::ThreadIsMTManagedThread(Thread::GetCurrent()) ||
1143            !PandaVM::GetCurrent()->GetGC()->IsGCRunning() || Locks::mutator_lock->HasLock());
1144 
1145     Class *cls = FindLoadedClass(descriptor, context);
1146     if (cls != nullptr) {
1147         return cls;
1148     }
1149 
1150     if (ClassHelper::IsArrayDescriptor(descriptor)) {
1151         return LoadArrayClass(descriptor, need_copy_descriptor, context, error_handler);
1152     }
1153 
1154     if (context->IsBootContext()) {
1155         panda_file::File::EntityId class_id;
1156         const panda_file::File *panda_file {nullptr};
1157         {
1158             {
1159                 os::memory::LockHolder lock {boot_panda_files_lock_};
1160                 std::tie(class_id, panda_file) = FindClassInPandaFiles(descriptor, boot_panda_files_);
1161             }
1162 
1163             if (!class_id.IsValid()) {
1164                 PandaStringStream ss;
1165                 {
1166                     // can't make a wider scope for lock here - will get recursion
1167                     os::memory::LockHolder lock {boot_panda_files_lock_};
1168                     ss << "Cannot find class " << descriptor
1169                        << " in boot panda files: " << PandaFilesToString(boot_panda_files_);
1170                 }
1171                 OnError(error_handler, Error::CLASS_NOT_FOUND, ss.str());
1172                 return nullptr;
1173             }
1174         }
1175 
1176         return LoadClass(panda_file, class_id, panda_file->GetStringData(class_id).data, context, error_handler);
1177     }
1178 
1179     return context->LoadClass(descriptor, need_copy_descriptor, error_handler);
1180 }
1181 
GetClass(const panda_file::File & pf,panda_file::File::EntityId id,ClassLinkerContext * context,ClassLinkerErrorHandler * error_handler)1182 Class *ClassLinker::GetClass(const panda_file::File &pf, panda_file::File::EntityId id, ClassLinkerContext *context,
1183                              ClassLinkerErrorHandler *error_handler /* = nullptr */)
1184 {
1185     ASSERT(context != nullptr);
1186     ASSERT(!MTManagedThread::ThreadIsMTManagedThread(Thread::GetCurrent()) ||
1187            !PandaVM::GetCurrent()->GetGC()->IsGCRunning() || Locks::mutator_lock->HasLock());
1188 
1189     Class *cls = pf.GetPandaCache()->GetClassFromCache(id);
1190     if (cls != nullptr) {
1191         return cls;
1192     }
1193     const uint8_t *descriptor = pf.GetStringData(id).data;
1194 
1195     cls = FindLoadedClass(descriptor, context);
1196     if (cls != nullptr) {
1197         pf.GetPandaCache()->SetClassCache(id, cls);
1198         return cls;
1199     }
1200 
1201     if (ClassHelper::IsArrayDescriptor(descriptor)) {
1202         cls = LoadArrayClass(descriptor, false, context, error_handler);
1203         if (LIKELY(cls != nullptr)) {
1204             pf.GetPandaCache()->SetClassCache(id, cls);
1205         }
1206         return cls;
1207     }
1208 
1209     if (context->IsBootContext()) {
1210         const panda_file::File *pf_ptr = nullptr;
1211         panda_file::File::EntityId ext_id;
1212         {
1213             os::memory::LockHolder lock {boot_panda_files_lock_};
1214             std::tie(ext_id, pf_ptr) = FindClassInPandaFiles(descriptor, boot_panda_files_);
1215         }
1216 
1217         if (!ext_id.IsValid()) {
1218             PandaStringStream ss;
1219             {
1220                 // can't make a wider scope for lock here - will get recursion
1221                 os::memory::LockHolder lock {boot_panda_files_lock_};
1222                 ss << "Cannot find class " << descriptor
1223                    << " in boot panda files: " << PandaFilesToString(boot_panda_files_);
1224             }
1225             OnError(error_handler, Error::CLASS_NOT_FOUND, ss.str());
1226             return nullptr;
1227         }
1228 
1229         cls = LoadClass(pf_ptr, ext_id, descriptor, context, error_handler);
1230         if (LIKELY(cls != nullptr)) {
1231             pf.GetPandaCache()->SetClassCache(id, cls);
1232         }
1233         return cls;
1234     }
1235 
1236     return context->LoadClass(descriptor, false, error_handler);
1237 }
1238 
GetMethod(const panda_file::File & pf,panda_file::File::EntityId id,ClassLinkerContext * context,ClassLinkerErrorHandler * error_handler)1239 Method *ClassLinker::GetMethod(const panda_file::File &pf, panda_file::File::EntityId id,
1240                                ClassLinkerContext *context /* = nullptr */,
1241                                ClassLinkerErrorHandler *error_handler /* = nullptr */)
1242 {
1243     Method *method = pf.GetPandaCache()->GetMethodFromCache(id);
1244     if (method != nullptr) {
1245         return method;
1246     }
1247     panda_file::MethodDataAccessor method_data_accessor(pf, id);
1248 
1249     auto class_id = method_data_accessor.GetClassId();
1250     if (context == nullptr) {
1251         panda_file::ClassDataAccessor class_data_accessor(pf, class_id);
1252         auto lang = class_data_accessor.GetSourceLang();
1253         if (!lang) {
1254             LOG(INFO, CLASS_LINKER) << "Cannot resolve language context for class_id " << class_id << " in file "
1255                                     << pf.GetFilename();
1256             return nullptr;
1257         }
1258         auto *extension = GetExtension(lang.value());
1259         context = extension->GetBootContext();
1260     }
1261 
1262     Class *klass = GetClass(pf, class_id, context, error_handler);
1263 
1264     if (klass == nullptr) {
1265         auto class_name = pf.GetStringData(class_id).data;
1266         LOG(INFO, CLASS_LINKER) << "Cannot find class '" << class_name << "' in ctx " << context;
1267         return nullptr;
1268     }
1269     method = GetMethod(klass, method_data_accessor, error_handler);
1270     if (LIKELY(method != nullptr)) {
1271         pf.GetPandaCache()->SetMethodCache(id, method);
1272     }
1273     return method;
1274 }
1275 
GetMethod(const Method & caller,panda_file::File::EntityId id,ClassLinkerErrorHandler * error_handler)1276 Method *ClassLinker::GetMethod(const Method &caller, panda_file::File::EntityId id,
1277                                ClassLinkerErrorHandler *error_handler /* = nullptr */)
1278 {
1279     auto *pf = caller.GetPandaFile();
1280     Method *method = pf->GetPandaCache()->GetMethodFromCache(id);
1281     if (method != nullptr) {
1282         return method;
1283     }
1284 
1285     panda_file::MethodDataAccessor method_data_accessor(*pf, id);
1286     auto class_id = method_data_accessor.GetClassId();
1287 
1288     auto *context = caller.GetClass()->GetLoadContext();
1289     auto *ext = GetExtension(caller.GetClass()->GetSourceLang());
1290     Class *klass = ext->GetClass(*pf, class_id, context, error_handler);
1291 
1292     if (klass == nullptr) {
1293         auto class_name = pf->GetStringData(class_id).data;
1294         LOG(INFO, CLASS_LINKER) << "Cannot find class '" << class_name << "' in ctx " << context;
1295         return nullptr;
1296     }
1297 
1298     method =
1299         GetMethod(klass, method_data_accessor, (error_handler == nullptr) ? ext->GetErrorHandler() : error_handler);
1300     if (LIKELY(method != nullptr)) {
1301         pf->GetPandaCache()->SetMethodCache(id, method);
1302     }
1303     return method;
1304 }
1305 
GetMethod(const Class * klass,const panda_file::MethodDataAccessor & method_data_accessor,ClassLinkerErrorHandler * error_handler)1306 Method *ClassLinker::GetMethod(const Class *klass, const panda_file::MethodDataAccessor &method_data_accessor,
1307                                ClassLinkerErrorHandler *error_handler)
1308 {
1309     Method *method;
1310     auto id = method_data_accessor.GetMethodId();
1311     const auto &pf = method_data_accessor.GetPandaFile();
1312 
1313     bool is_static = method_data_accessor.IsStatic();
1314     if (!method_data_accessor.IsExternal() && klass->GetPandaFile() == &pf) {
1315         if (klass->IsInterface()) {
1316             method = is_static ? klass->GetStaticInterfaceMethod(id) : klass->GetVirtualInterfaceMethod(id);
1317         } else {
1318             method = is_static ? klass->GetStaticClassMethod(id) : klass->GetVirtualClassMethod(id);
1319         }
1320 
1321         if (method == nullptr) {
1322             PandaStringStream ss;
1323             ss << "Cannot find method '" << method_data_accessor.GetName().data << "' in class '" << klass->GetName()
1324                << "'";
1325             OnError(error_handler, Error::METHOD_NOT_FOUND, ss.str());
1326             return nullptr;
1327         }
1328 
1329         return method;
1330     }
1331 
1332     auto name = method_data_accessor.GetName();
1333     Method::Proto proto(pf, method_data_accessor.GetProtoId());
1334     if (klass->IsInterface()) {
1335         method = is_static ? klass->GetStaticInterfaceMethodByName(name, proto)
1336                            : klass->GetVirtualInterfaceMethodByName(name, proto);
1337     } else {
1338         method = is_static ? klass->GetStaticClassMethodByName(name, proto)
1339                            : klass->GetVirtualClassMethodByName(name, proto);
1340         if (method == nullptr && klass->IsAbstract()) {
1341             method = klass->GetInterfaceMethod(name, proto);
1342         }
1343     }
1344 
1345     if (method == nullptr) {
1346         PandaStringStream ss;
1347         ss << "Cannot find method '" << name.data << "' in class '" << klass->GetName() << "'";
1348         OnError(error_handler, Error::METHOD_NOT_FOUND, ss.str());
1349         return nullptr;
1350     }
1351 
1352     LOG_IF(method->IsStatic() != method_data_accessor.IsStatic(), FATAL, CLASS_LINKER)
1353         << "Expected ACC_STATIC for method " << name.data << " in class " << klass->GetName()
1354         << " does not match loaded value";
1355 
1356     return method;
1357 }
1358 
GetFieldById(Class * klass,const panda_file::FieldDataAccessor & field_data_accessor,ClassLinkerErrorHandler * error_handler)1359 Field *ClassLinker::GetFieldById(Class *klass, const panda_file::FieldDataAccessor &field_data_accessor,
1360                                  ClassLinkerErrorHandler *error_handler)
1361 {
1362     bool is_static = field_data_accessor.IsStatic();
1363     auto &pf = field_data_accessor.GetPandaFile();
1364     auto id = field_data_accessor.GetFieldId();
1365 
1366     Field *field = is_static ? klass->FindStaticFieldById(id) : klass->FindInstanceFieldById(id);
1367 
1368     if (field == nullptr) {
1369         PandaStringStream ss;
1370         ss << "Cannot find field '" << pf.GetStringData(field_data_accessor.GetNameId()).data << "' in class '"
1371            << klass->GetName() << "'";
1372         OnError(error_handler, Error::FIELD_NOT_FOUND, ss.str());
1373         return nullptr;
1374     }
1375 
1376     pf.GetPandaCache()->SetFieldCache(id, field);
1377     return field;
1378 }
1379 
GetFieldBySignature(Class * klass,const panda_file::FieldDataAccessor & field_data_accessor,ClassLinkerErrorHandler * error_handler)1380 Field *ClassLinker::GetFieldBySignature(Class *klass, const panda_file::FieldDataAccessor &field_data_accessor,
1381                                         ClassLinkerErrorHandler *error_handler)
1382 {
1383     auto &pf = field_data_accessor.GetPandaFile();
1384     auto id = field_data_accessor.GetFieldId();
1385     auto field_name = pf.GetStringData(field_data_accessor.GetNameId());
1386     auto field_type = panda_file::Type::GetTypeFromFieldEncoding(field_data_accessor.GetType());
1387     Field *field = klass->FindField([&](const Field &fld) {
1388         if (field_type == fld.GetType() && field_name == fld.GetName()) {
1389             if (!field_type.IsReference()) {
1390                 return true;
1391             }
1392 
1393             // compare field class type
1394             if (&pf == fld.GetPandaFile() && id == fld.GetFileId()) {
1395                 return true;
1396             }
1397             auto type_id = panda_file::FieldDataAccessor::GetTypeId(*fld.GetPandaFile(), fld.GetFileId());
1398             if (pf.GetStringData(panda_file::File::EntityId(field_data_accessor.GetType())) ==
1399                 fld.GetPandaFile()->GetStringData(type_id)) {
1400                 return true;
1401             }
1402         }
1403         return false;
1404     });
1405 
1406     if (field == nullptr) {
1407         PandaStringStream ss;
1408         ss << "Cannot find field '" << field_name.data << "' in class '" << klass->GetName() << "'";
1409         OnError(error_handler, Error::FIELD_NOT_FOUND, ss.str());
1410         return nullptr;
1411     }
1412 
1413     pf.GetPandaCache()->SetFieldCache(id, field);
1414     return field;
1415 }
1416 
GetField(const panda_file::File & pf,panda_file::File::EntityId id,ClassLinkerContext * context,ClassLinkerErrorHandler * error_handler)1417 Field *ClassLinker::GetField(const panda_file::File &pf, panda_file::File::EntityId id,
1418                              ClassLinkerContext *context /* = nullptr */,
1419                              ClassLinkerErrorHandler *error_handler /* = nullptr */)
1420 {
1421     Field *field = pf.GetPandaCache()->GetFieldFromCache(id);
1422     if (field != nullptr) {
1423         return field;
1424     }
1425     panda_file::FieldDataAccessor field_data_accessor(pf, id);
1426 
1427     Class *klass = GetClass(pf, field_data_accessor.GetClassId(), context, error_handler);
1428 
1429     if (klass == nullptr) {
1430         auto class_name = pf.GetStringData(field_data_accessor.GetClassId()).data;
1431         LOG(INFO, CLASS_LINKER) << "Cannot find class '" << class_name << "' in ctx " << context;
1432         return nullptr;
1433     }
1434 
1435     if (!field_data_accessor.IsExternal() && klass->GetPandaFile() == &pf) {
1436         field = GetFieldById(klass, field_data_accessor, error_handler);
1437     } else {
1438         field = GetFieldBySignature(klass, field_data_accessor, error_handler);
1439     }
1440     return field;
1441 }
1442 
InitializeClass(ManagedThread * thread,Class * klass)1443 bool ClassLinker::InitializeClass(ManagedThread *thread, Class *klass)
1444 {
1445     ASSERT(klass != nullptr);
1446     if (klass->IsInitialized()) {
1447         return true;
1448     }
1449 
1450     LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(*klass);
1451     return ctx.InitializeClass(this, thread, klass);
1452 }
1453 
NumLoadedClasses()1454 size_t ClassLinker::NumLoadedClasses()
1455 {
1456     size_t sum = 0;
1457 
1458     for (auto &ext : extensions_) {
1459         if (ext == nullptr) {
1460             continue;
1461         }
1462 
1463         sum += ext->NumLoadedClasses();
1464     }
1465 
1466     return sum;
1467 }
1468 
VisitLoadedClasses(size_t flag)1469 void ClassLinker::VisitLoadedClasses(size_t flag)
1470 {
1471     for (auto &ext : extensions_) {
1472         if (ext == nullptr) {
1473             continue;
1474         }
1475         ext->VisitLoadedClasses(flag);
1476     }
1477 }
1478 
OnError(ClassLinkerErrorHandler * error_handler,ClassLinker::Error error,const PandaString & msg)1479 void ClassLinker::OnError(ClassLinkerErrorHandler *error_handler, ClassLinker::Error error, const PandaString &msg)
1480 {
1481     if (error_handler != nullptr) {
1482         error_handler->OnError(error, msg);
1483     }
1484 }
1485 
GetField(const Method & caller,panda_file::File::EntityId id,ClassLinkerErrorHandler * error_handler)1486 Field *ClassLinker::GetField(const Method &caller, panda_file::File::EntityId id,
1487                              ClassLinkerErrorHandler *error_handler /* = nullptr */)
1488 {
1489     Field *field = caller.GetPandaFile()->GetPandaCache()->GetFieldFromCache(id);
1490     if (field != nullptr) {
1491         return field;
1492     }
1493     auto *ext = GetExtension(caller.GetClass()->GetSourceLang());
1494     field = GetField(*caller.GetPandaFile(), id, caller.GetClass()->GetLoadContext(),
1495                      (error_handler == nullptr) ? ext->GetErrorHandler() : error_handler);
1496     if (LIKELY(field != nullptr)) {
1497         caller.GetPandaFile()->GetPandaCache()->SetFieldCache(id, field);
1498     }
1499     return field;
1500 }
1501 
RemoveCreatedClassInExtension(Class * klass)1502 void ClassLinker::RemoveCreatedClassInExtension(Class *klass)
1503 {
1504     if (klass == nullptr) {
1505         return;
1506     }
1507     auto ext = GetExtension(klass->GetSourceLang());
1508     if (ext != nullptr) {
1509         ext->OnClassPrepared(klass);
1510     }
1511 }
1512 
1513 }  // namespace panda
1514