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