/** * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "runtime/include/class_linker.h" #include "runtime/bridge/bridge.h" #include "runtime/cha.h" #include "runtime/class_initializer.h" #include "runtime/include/coretypes/array.h" #include "runtime/include/coretypes/string.h" #include "runtime/include/field.h" #include "runtime/include/itable_builder.h" #include "runtime/include/method.h" #include "runtime/include/panda_vm.h" #include "runtime/include/runtime.h" #include "runtime/include/runtime_notification.h" #include "libpandabase/macros.h" #include "libpandabase/mem/mem.h" #include "libpandabase/utils/bit_utils.h" #include "libpandabase/utils/span.h" #include "libpandabase/utils/utf.h" #include "libpandafile/class_data_accessor-inl.h" #include "libpandafile/code_data_accessor-inl.h" #include "libpandafile/field_data_accessor-inl.h" #include "libpandafile/method_data_accessor-inl.h" #include "libpandafile/modifiers.h" #include "libpandafile/panda_cache.h" #include "libpandafile/proto_data_accessor-inl.h" #include "runtime/include/tooling/debug_inf.h" #include "trace/trace.h" namespace panda { using Type = panda_file::Type; using SourceLang = panda_file::SourceLang; void ClassLinker::AddPandaFile(std::unique_ptr &&pf, ClassLinkerContext *context) { ASSERT(pf != nullptr); const panda_file::File *file = pf.get(); SCOPED_TRACE_STREAM << __FUNCTION__ << " " << file->GetFilename(); { os::memory::LockHolder lock {panda_files_lock_}; panda_files_.push_back({context, std::forward>(pf)}); } if (context == nullptr || context->IsBootContext()) { os::memory::LockHolder lock {boot_panda_files_lock_}; boot_panda_files_.push_back(file); } if (Runtime::GetCurrent()->IsInitialized()) { // LoadModule for initial boot files is called in runtime Runtime::GetCurrent()->GetNotificationManager()->LoadModuleEvent(file->GetFilename()); } tooling::DebugInf::AddCodeMetaInfo(file); } void ClassLinker::FreeClassData(Class *class_ptr) { Span fields = class_ptr->GetFields(); if (fields.Size() > 0) { allocator_->Free(fields.begin()); class_ptr->SetFields(Span(), 0); } Span methods = class_ptr->GetMethods(); size_t n = methods.Size() + class_ptr->GetNumCopiedMethods(); if (n > 0) { mem::InternalAllocatorPtr allocator = Runtime::GetCurrent()->GetInternalAllocator(); for (auto &method : methods) { // We create Profiling data in method class via InternalAllocator. // Therefore, we should delete it via InternalAllocator too. allocator->Free(method.GetProfilingData()); } allocator_->Free(methods.begin()); class_ptr->SetMethods(Span(), 0, 0); } bool has_own_itable = !class_ptr->IsArrayClass(); auto itable = class_ptr->GetITable().Get(); if (has_own_itable && !itable.Empty()) { for (size_t i = 0; i < itable.Size(); i++) { Span imethods = itable[i].GetMethods(); if (!imethods.Empty()) { allocator_->Free(imethods.begin()); } } allocator_->Free(itable.begin()); class_ptr->SetITable(ITable()); } Span interfaces = class_ptr->GetInterfaces(); if (!interfaces.Empty()) { allocator_->Free(interfaces.begin()); class_ptr->SetInterfaces(Span()); } } void ClassLinker::FreeClass(Class *class_ptr) { FreeClassData(class_ptr); GetExtension(class_ptr->GetSourceLang())->FreeClass(class_ptr); } ClassLinker::~ClassLinker() { for (auto &copied_name : copied_names_) { allocator_->Free(reinterpret_cast(const_cast(copied_name))); } } ClassLinker::ClassLinker(mem::InternalAllocatorPtr allocator, std::vector> &&extensions) : allocator_(allocator), aot_manager_(MakePandaUnique()), copied_names_(allocator->Adapter()) { for (auto &ext : extensions) { extensions_[panda::panda_file::GetLangArrIndex(ext->GetLanguage())] = std::move(ext); } } void ClassLinker::ResetExtension(panda_file::SourceLang lang) { extensions_[panda::panda_file::GetLangArrIndex(lang)] = Runtime::GetCurrent()->GetLanguageContext(lang).CreateClassLinkerExtension(); } template static T *InitializeMemory(T *mem, Args... args) { return new (mem) T(std::forward(args)...); } bool ClassLinker::Initialize(bool compressed_string_enabled) { if (is_initialized_) { return true; } for (auto &ext : extensions_) { if (ext == nullptr) { continue; } if (!ext->Initialize(this, compressed_string_enabled)) { return false; } } is_initialized_ = true; return true; } bool ClassLinker::InitializeRoots(ManagedThread *thread) { for (auto &ext : extensions_) { if (ext == nullptr) { continue; } if (!ext->InitializeRoots(thread)) { return false; } } return true; } using ClassEntry = std::pair; using PandaFiles = PandaVector; static ClassEntry FindClassInPandaFiles(const uint8_t *descriptor, const PandaFiles &panda_files) { for (auto *pf : panda_files) { auto class_id = pf->GetClassId(descriptor); if (class_id.IsValid() && !pf->IsExternal(class_id)) { return {class_id, pf}; } } return {}; } Class *ClassLinker::FindLoadedClass(const uint8_t *descriptor, ClassLinkerContext *context) { ASSERT(context != nullptr); return context->FindClass(descriptor); } template static size_t GetClassSize(ClassDataAccessorT data_accessor, size_t vtable_size, size_t imt_size, size_t *out_num_sfields) { size_t num_8bit_sfields = 0; size_t num_16bit_sfields = 0; size_t num_32bit_sfields = 0; size_t num_64bit_sfields = 0; size_t num_ref_sfields = 0; size_t num_tagged_sfields = 0; size_t num_sfields = 0; data_accessor.template EnumerateStaticFieldTypes([&num_8bit_sfields, &num_16bit_sfields, &num_32bit_sfields, &num_64bit_sfields, &num_ref_sfields, &num_tagged_sfields, &num_sfields](Type field_type) { ++num_sfields; switch (field_type.GetId()) { case Type::TypeId::U1: case Type::TypeId::I8: case Type::TypeId::U8: ++num_8bit_sfields; break; case Type::TypeId::I16: case Type::TypeId::U16: ++num_16bit_sfields; break; case Type::TypeId::I32: case Type::TypeId::U32: case Type::TypeId::F32: ++num_32bit_sfields; break; case Type::TypeId::I64: case Type::TypeId::U64: case Type::TypeId::F64: ++num_64bit_sfields; break; case Type::TypeId::REFERENCE: ++num_ref_sfields; break; case Type::TypeId::TAGGED: ++num_tagged_sfields; break; default: UNREACHABLE(); break; } }); *out_num_sfields = num_sfields; return Class::ComputeClassSize(vtable_size, imt_size, num_8bit_sfields, num_16bit_sfields, num_32bit_sfields, num_64bit_sfields, num_ref_sfields, num_tagged_sfields); } class ClassDataAccessorWrapper { public: explicit ClassDataAccessorWrapper(panda_file::ClassDataAccessor *data_accessor = nullptr) : data_accessor_(data_accessor) { } template void EnumerateStaticFieldTypes(const Callback &cb) const { data_accessor_->EnumerateFields([cb](panda_file::FieldDataAccessor &fda) { if (!fda.IsStatic()) { return; } cb(Type::GetTypeFromFieldEncoding(fda.GetType())); }); } ~ClassDataAccessorWrapper() = default; DEFAULT_COPY_SEMANTIC(ClassDataAccessorWrapper); DEFAULT_MOVE_SEMANTIC(ClassDataAccessorWrapper); private: panda_file::ClassDataAccessor *data_accessor_; }; ClassLinker::ClassInfo ClassLinker::GetClassInfo(panda_file::ClassDataAccessor *data_accessor, Class *base, Span interfaces, ClassLinkerContext *context) { LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(data_accessor); auto vtable_builder = ctx.CreateVTableBuilder(); auto itable_builder = ctx.CreateITableBuilder(); auto imtable_builder = ctx.CreateIMTableBuilder(); itable_builder->Build(this, base, interfaces, data_accessor->IsInterface()); vtable_builder->Build(data_accessor, base, itable_builder->GetITable(), context); imtable_builder->Build(data_accessor, itable_builder->GetITable()); ClassDataAccessorWrapper data_accessor_wrapper(data_accessor); size_t num_sfields = 0; size_t size = GetClassSize(data_accessor_wrapper, vtable_builder->GetVTableSize(), imtable_builder->GetIMTSize(), &num_sfields); return {size, num_sfields, std::move(vtable_builder), std::move(itable_builder), std::move(imtable_builder)}; } class ClassDataAccessor { public: explicit ClassDataAccessor(Span fields) : fields_(fields) {} template void EnumerateStaticFieldTypes(const Callback &cb) const { for (const auto &field : fields_) { if (!field.IsStatic()) { continue; } cb(field.GetType()); } } ~ClassDataAccessor() = default; DEFAULT_COPY_SEMANTIC(ClassDataAccessor); DEFAULT_MOVE_SEMANTIC(ClassDataAccessor); private: Span fields_; }; ClassLinker::ClassInfo ClassLinker::GetClassInfo(Span methods, Span fields, Class *base, Span interfaces, bool is_interface) { LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(*base); auto vtable_builder = ctx.CreateVTableBuilder(); auto itable_builder = ctx.CreateITableBuilder(); auto imtable_builder = ctx.CreateIMTableBuilder(); itable_builder->Build(this, base, interfaces, is_interface); vtable_builder->Build(methods, base, itable_builder->GetITable(), is_interface); imtable_builder->Build(itable_builder->GetITable(), is_interface); ClassDataAccessor data_accessor(fields); size_t num_sfields = 0; size_t size = GetClassSize(data_accessor, vtable_builder->GetVTableSize(), imtable_builder->GetIMTSize(), &num_sfields); return {size, num_sfields, std::move(vtable_builder), std::move(itable_builder), std::move(imtable_builder)}; } static void LoadMethod(Method *method, panda_file::MethodDataAccessor *method_data_accessor, Class *klass, const LanguageContext &ctx, const ClassLinkerExtension *ext) { const auto &pf = method_data_accessor->GetPandaFile(); panda_file::ProtoDataAccessor pda(pf, method_data_accessor->GetProtoId()); uint32_t access_flags = method_data_accessor->GetAccessFlags(); auto *method_name = pf.GetStringData(method_data_accessor->GetNameId()).data; if (utf::IsEqual(method_name, ctx.GetCtorName()) || utf::IsEqual(method_name, ctx.GetCctorName())) { access_flags |= ACC_CONSTRUCTOR; } auto code_id = method_data_accessor->GetCodeId(); size_t num_args = (method_data_accessor->IsStatic()) ? pda.GetNumArgs() : (pda.GetNumArgs() + 1); if (!code_id.has_value()) { InitializeMemory(method, klass, &pf, method_data_accessor->GetMethodId(), panda_file::File::EntityId(0), access_flags, num_args, reinterpret_cast(pda.GetShorty().Data())); if (method_data_accessor->IsNative()) { method->SetCompiledEntryPoint(ext->GetNativeEntryPointFor(method)); } else { method->SetInterpreterEntryPoint(); } } else { InitializeMemory(method, klass, &pf, method_data_accessor->GetMethodId(), code_id.value(), access_flags, num_args, reinterpret_cast(pda.GetShorty().Data())); method->SetCompiledEntryPoint(GetCompiledCodeToInterpreterBridge(method)); } } void MaybeLinkMethodToAotCode(Method *method, const compiler::AotClass &aot_class, size_t method_index) { ASSERT(aot_class.IsValid()); if (method->IsIntrinsic()) { return; } auto entry = aot_class.FindMethodCodeEntry(method_index); if (entry != nullptr) { method->SetCompiledEntryPoint(entry); LOG(DEBUG, AOT) << "Found AOT entrypoint [" << reinterpret_cast(aot_class.FindMethodCodeSpan(method_index).data()) << ":" << reinterpret_cast(aot_class.FindMethodCodeSpan(method_index).end()) << "] for method: " << method->GetFullName(); EVENT_AOT_ENTRYPOINT_FOUND(method->GetFullName()); ASSERT(aot_class.FindMethodHeader(method_index)->method_id == method->GetFileId().GetOffset()); } } bool ClassLinker::LoadMethods(Class *klass, ClassInfo *class_info, panda_file::ClassDataAccessor *data_accessor, [[maybe_unused]] ClassLinkerErrorHandler *error_handler) { uint32_t num_methods = data_accessor->GetMethodsNumber(); uint32_t num_vmethods = klass->GetNumVirtualMethods(); uint32_t num_smethods = num_methods - num_vmethods; auto &copied_methods = class_info->vtable_builder->GetCopiedMethods(); uint32_t n = num_methods + copied_methods.size(); if (n == 0) { return true; } Span methods {allocator_->AllocArray(n), n}; size_t smethod_idx = num_vmethods; size_t vmethod_idx = 0; LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(*klass); auto *ext = GetExtension(ctx); ASSERT(ext != nullptr); auto aot_pfile = aot_manager_->FindPandaFile(klass->GetPandaFile()->GetFullFileName()); if (aot_pfile != nullptr) { EVENT_AOT_LOADED_FOR_CLASS(PandaString(aot_pfile->GetFileName()), PandaString(klass->GetName())); } compiler::AotClass aot_class = (aot_pfile != nullptr) ? aot_pfile->GetClass(klass->GetFileId().GetOffset()) : compiler::AotClass::Invalid(); size_t method_index = 0; data_accessor->EnumerateMethods([klass, &smethod_idx, &vmethod_idx, &methods, aot_class, ctx, ext, &method_index](panda_file::MethodDataAccessor &method_data_accessor) { Method *method = method_data_accessor.IsStatic() ? &methods[smethod_idx++] : &methods[vmethod_idx++]; LoadMethod(method, &method_data_accessor, klass, ctx, ext); if (aot_class.IsValid()) { MaybeLinkMethodToAotCode(method, aot_class, method_index); } // Instead of checking if the method is abstract before every virtual call // the special stub throwing AbstractMethodError is registered as compiled entry point. if (method->IsAbstract()) { method->SetCompiledEntryPoint(GetAbstractMethodStub()); } method_index++; }); for (size_t i = 0; i < copied_methods.size(); i++) { size_t idx = num_methods + i; InitializeMemory(&methods[idx], copied_methods[i].method_); methods[idx].SetIsDefaultInterfaceMethod(); if (copied_methods[i].default_abstract_) { methods[idx].SetCompiledEntryPoint(GetAbstractMethodStub()); } if (copied_methods[i].default_conflict_) { methods[idx].SetCompiledEntryPoint(GetDefaultConflictMethodStub()); } } klass->SetMethods(methods, num_vmethods, num_smethods); return true; } bool ClassLinker::LoadFields(Class *klass, panda_file::ClassDataAccessor *data_accessor, [[maybe_unused]] ClassLinkerErrorHandler *error_handler) { uint32_t num_fields = data_accessor->GetFieldsNumber(); if (num_fields == 0) { return true; } uint32_t num_sfields = klass->GetNumStaticFields(); Span fields {allocator_->AllocArray(num_fields), num_fields}; size_t sfields_idx = 0; size_t ifields_idx = num_sfields; data_accessor->EnumerateFields( [klass, &sfields_idx, &ifields_idx, &fields](panda_file::FieldDataAccessor &field_data_accessor) { Field *field = field_data_accessor.IsStatic() ? &fields[sfields_idx++] : &fields[ifields_idx++]; InitializeMemory(field, klass, field_data_accessor.GetFieldId(), field_data_accessor.GetAccessFlags(), panda_file::Type::GetTypeFromFieldEncoding(field_data_accessor.GetType())); }); klass->SetFields(fields, num_sfields); return true; } template static void LayoutFieldsWithoutAlignment(size_t size, size_t *offset, size_t *space, PandaList *fields) { while ((space == nullptr || *space >= size) && !fields->empty()) { Field *field = fields->front(); // NOLINTNEXTLINE(readability-braces-around-statements) if constexpr (reverse_layout) { *offset -= size; field->SetOffset(*offset); // NOLINTNEXTLINE(readability-misleading-indentation) } else { field->SetOffset(*offset); *offset += size; } if (space != nullptr) { *space -= size; } fields->pop_front(); } } static uint32_t LayoutReferenceFields(size_t size, size_t *offset, const PandaList &fields) { uint32_t volatile_fields_num = 0; // layout volatile fields firstly for (auto *field : fields) { if (field->IsVolatile()) { volatile_fields_num++; field->SetOffset(*offset); *offset += size; } } for (auto *field : fields) { if (!field->IsVolatile()) { field->SetOffset(*offset); *offset += size; } } return volatile_fields_num; } static size_t LayoutFieldsInBaseClassPadding(Class *klass, PandaList *tagged_fields, PandaList *fields64, PandaList *fields32, PandaList *fields16, PandaList *fields8, PandaList *ref_fields, bool is_static) { constexpr size_t SIZE_64 = sizeof(uint64_t); constexpr size_t SIZE_32 = sizeof(uint32_t); constexpr size_t SIZE_16 = sizeof(uint16_t); constexpr size_t SIZE_8 = sizeof(uint8_t); size_t offset; if (is_static) { offset = klass->GetStaticFieldsOffset(); } else { offset = (klass->GetBase() != nullptr) ? klass->GetBase()->GetObjectSize() : ObjectHeader::ObjectHeaderSize(); } size_t align_offset = offset; if (!ref_fields->empty()) { align_offset = AlignUp(offset, ClassHelper::OBJECT_POINTER_SIZE); } else if (!(fields64->empty()) || !(tagged_fields->empty())) { align_offset = AlignUp(offset, SIZE_64); } else if (!fields32->empty()) { align_offset = AlignUp(offset, SIZE_32); } else if (!fields16->empty()) { align_offset = AlignUp(offset, SIZE_16); } if (align_offset != offset) { size_t end_offset = align_offset; size_t padding = end_offset - offset; // try to put short fields of child class at end of free space of base class LayoutFieldsWithoutAlignment(SIZE_32, &end_offset, &padding, fields32); LayoutFieldsWithoutAlignment(SIZE_16, &end_offset, &padding, fields16); LayoutFieldsWithoutAlignment(SIZE_8, &end_offset, &padding, fields8); } return align_offset; } static size_t LayoutFields(Class *klass, PandaList *tagged_fields, PandaList *fields64, PandaList *fields32, PandaList *fields16, PandaList *fields8, PandaList *ref_fields, bool is_static) { constexpr size_t SIZE_64 = sizeof(uint64_t); constexpr size_t SIZE_32 = sizeof(uint32_t); constexpr size_t SIZE_16 = sizeof(uint16_t); constexpr size_t SIZE_8 = sizeof(uint8_t); size_t offset = LayoutFieldsInBaseClassPadding(klass, tagged_fields, fields64, fields32, fields16, fields8, ref_fields, is_static); if (!ref_fields->empty()) { offset = AlignUp(offset, ClassHelper::OBJECT_POINTER_SIZE); klass->SetRefFieldsNum(ref_fields->size(), is_static); klass->SetRefFieldsOffset(offset, is_static); auto volatile_num = LayoutReferenceFields(ClassHelper::OBJECT_POINTER_SIZE, &offset, *ref_fields); klass->SetVolatileRefFieldsNum(volatile_num, is_static); } static_assert(coretypes::TaggedValue::TaggedTypeSize() == SIZE_64, "Please fix alignment of the fields of type \"TaggedValue\""); if (!IsAligned(offset) && (!fields64->empty() || !tagged_fields->empty())) { size_t padding = AlignUp(offset, SIZE_64) - offset; LayoutFieldsWithoutAlignment(SIZE_32, &offset, &padding, fields32); LayoutFieldsWithoutAlignment(SIZE_16, &offset, &padding, fields16); LayoutFieldsWithoutAlignment(SIZE_8, &offset, &padding, fields8); offset += padding; } LayoutFieldsWithoutAlignment(coretypes::TaggedValue::TaggedTypeSize(), &offset, nullptr, tagged_fields); LayoutFieldsWithoutAlignment(SIZE_64, &offset, nullptr, fields64); if (!IsAligned(offset) && !fields32->empty()) { size_t padding = AlignUp(offset, SIZE_32) - offset; LayoutFieldsWithoutAlignment(SIZE_16, &offset, &padding, fields16); LayoutFieldsWithoutAlignment(SIZE_8, &offset, &padding, fields8); offset += padding; } LayoutFieldsWithoutAlignment(SIZE_32, &offset, nullptr, fields32); if (!IsAligned(offset) && !fields16->empty()) { size_t padding = AlignUp(offset, SIZE_16) - offset; LayoutFieldsWithoutAlignment(SIZE_8, &offset, &padding, fields8); offset += padding; } LayoutFieldsWithoutAlignment(SIZE_16, &offset, nullptr, fields16); LayoutFieldsWithoutAlignment(SIZE_8, &offset, nullptr, fields8); return offset; } /* static */ bool ClassLinker::LayoutFields(Class *klass, Span fields, bool is_static, [[maybe_unused]] ClassLinkerErrorHandler *error_handler) { PandaList tagged_fields; PandaList fields64; PandaList fields32; PandaList fields16; PandaList fields8; PandaList ref_fields; for (auto &field : fields) { auto type = field.GetType(); if (!type.IsPrimitive()) { ref_fields.push_back(&field); continue; } switch (type.GetId()) { case Type::TypeId::U1: case Type::TypeId::I8: case Type::TypeId::U8: fields8.push_back(&field); break; case Type::TypeId::I16: case Type::TypeId::U16: fields16.push_back(&field); break; case Type::TypeId::I32: case Type::TypeId::U32: case Type::TypeId::F32: fields32.push_back(&field); break; case Type::TypeId::I64: case Type::TypeId::U64: case Type::TypeId::F64: fields64.push_back(&field); break; case Type::TypeId::TAGGED: tagged_fields.push_back(&field); break; default: UNREACHABLE(); break; } } size_t size = panda::LayoutFields(klass, &tagged_fields, &fields64, &fields32, &fields16, &fields8, &ref_fields, is_static); if (!is_static && !klass->IsVariableSize()) { klass->SetObjectSize(size); } return true; } bool ClassLinker::LinkMethods(Class *klass, ClassInfo *class_info, [[maybe_unused]] ClassLinkerErrorHandler *error_handler) { class_info->vtable_builder->UpdateClass(klass); class_info->itable_builder->Resolve(klass); class_info->itable_builder->UpdateClass(klass); class_info->imtable_builder->UpdateClass(klass); return true; } bool ClassLinker::LinkFields(Class *klass, ClassLinkerErrorHandler *error_handler) { if (!LayoutFields(klass, klass->GetStaticFields(), true, error_handler)) { LOG(ERROR, CLASS_LINKER) << "Cannot layout static fields of class '" << klass->GetName() << "'"; return false; } if (!LayoutFields(klass, klass->GetInstanceFields(), false, error_handler)) { LOG(ERROR, CLASS_LINKER) << "Cannot layout instance fields of class '" << klass->GetName() << "'"; return false; } return true; } Class *ClassLinker::LoadBaseClass(panda_file::ClassDataAccessor *cda, const LanguageContext &ctx, ClassLinkerContext *context, ClassLinkerErrorHandler *error_handler) { auto base_class_id = cda->GetSuperClassId(); auto *ext = GetExtension(ctx); ASSERT(ext != nullptr); if (base_class_id.GetOffset() == 0) { return ext->GetClassRoot(ClassRoot::OBJECT); } auto &pf = cda->GetPandaFile(); auto *base_class = ext->GetClass(pf, base_class_id, context, error_handler); if (base_class == nullptr) { LOG(INFO, CLASS_LINKER) << "Cannot find base class '" << utf::Mutf8AsCString(pf.GetStringData(base_class_id).data) << "' of class '" << utf::Mutf8AsCString(pf.GetStringData(cda->GetClassId()).data) << "' in ctx " << context; return nullptr; } return base_class; } std::optional> ClassLinker::LoadInterfaces(panda_file::ClassDataAccessor *cda, ClassLinkerContext *context, ClassLinkerErrorHandler *error_handler) { ASSERT(context != nullptr); size_t ifaces_num = cda->GetIfacesNumber(); if (ifaces_num == 0) { return Span(nullptr, ifaces_num); } Span ifaces {allocator_->AllocArray(ifaces_num), ifaces_num}; for (size_t i = 0; i < ifaces_num; i++) { auto id = cda->GetInterfaceId(i); auto &pf = cda->GetPandaFile(); auto *iface = GetClass(pf, id, context, error_handler); if (iface == nullptr) { LOG(INFO, CLASS_LINKER) << "Cannot find interface '" << utf::Mutf8AsCString(pf.GetStringData(id).data) << "' of class '" << utf::Mutf8AsCString(pf.GetStringData(cda->GetClassId()).data) << "' in ctx " << context; ASSERT(!ifaces.Empty()); allocator_->Free(ifaces.begin()); return {}; } ifaces[i] = iface; } return ifaces; } using ClassLoadingSet = std::unordered_set; // This class is required to clear static unordered_set on return class ClassScopeStaticSetAutoCleaner { public: ClassScopeStaticSetAutoCleaner() = default; explicit ClassScopeStaticSetAutoCleaner(ClassLoadingSet *set_ptr, ClassLoadingSet **tl_set_ptr) : set_ptr_(set_ptr), tl_set_ptr_(tl_set_ptr) { } ~ClassScopeStaticSetAutoCleaner() { set_ptr_->clear(); if (tl_set_ptr_ != nullptr) { *tl_set_ptr_ = nullptr; } } NO_COPY_SEMANTIC(ClassScopeStaticSetAutoCleaner); NO_MOVE_SEMANTIC(ClassScopeStaticSetAutoCleaner); private: ClassLoadingSet *set_ptr_; ClassLoadingSet **tl_set_ptr_; }; static uint64_t GetClassUniqueHash(uint32_t panda_file_hash, uint32_t class_id) { const uint8_t BITS_TO_SHUFFLE = 32; return (static_cast(panda_file_hash) << BITS_TO_SHUFFLE) | static_cast(class_id); } Class *ClassLinker::LoadClass(panda_file::ClassDataAccessor *class_data_accessor, const uint8_t *descriptor, Class *base_class, Span interfaces, ClassLinkerContext *context, ClassLinkerExtension *ext, ClassLinkerErrorHandler *error_handler) { ASSERT(context != nullptr); ClassInfo class_info = GetClassInfo(class_data_accessor, base_class, interfaces, context); auto *klass = ext->CreateClass(descriptor, class_info.vtable_builder->GetVTableSize(), class_info.imtable_builder->GetIMTSize(), class_info.size); if (UNLIKELY(klass == nullptr)) { return nullptr; } klass->SetLoadContext(context); klass->SetBase(base_class); klass->SetInterfaces(interfaces); klass->SetFileId(class_data_accessor->GetClassId()); klass->SetPandaFile(&class_data_accessor->GetPandaFile()); klass->SetAccessFlags(class_data_accessor->GetAccessFlags()); auto &pf = class_data_accessor->GetPandaFile(); auto class_id = class_data_accessor->GetClassId(); klass->SetClassIndex(pf.GetClassIndex(class_id)); klass->SetMethodIndex(pf.GetMethodIndex(class_id)); klass->SetFieldIndex(pf.GetFieldIndex(class_id)); klass->SetNumVirtualMethods(class_info.vtable_builder->GetNumVirtualMethods()); klass->SetNumCopiedMethods(class_info.vtable_builder->GetCopiedMethods().size()); klass->SetNumStaticFields(class_info.num_sfields); if (!LoadMethods(klass, &class_info, class_data_accessor, error_handler)) { FreeClass(klass); LOG(ERROR, CLASS_LINKER) << "Cannot load methods of class '" << descriptor << "'"; return nullptr; } if (!LoadFields(klass, class_data_accessor, error_handler)) { FreeClass(klass); LOG(ERROR, CLASS_LINKER) << "Cannot load fields of class '" << descriptor << "'"; return nullptr; } if (!LinkMethods(klass, &class_info, error_handler)) { FreeClass(klass); LOG(ERROR, CLASS_LINKER) << "Cannot link methods of class '" << descriptor << "'"; return nullptr; } if (!LinkFields(klass, error_handler)) { FreeClass(klass); LOG(ERROR, CLASS_LINKER) << "Cannot link fields of class '" << descriptor << "'"; return nullptr; } return klass; } Class *ClassLinker::LoadClass(const panda_file::File *pf, const uint8_t *descriptor, panda_file::SourceLang lang) { panda_file::File::EntityId class_id = pf->GetClassId(descriptor); if (!class_id.IsValid() || pf->IsExternal(class_id)) { return nullptr; } ClassLinkerContext *context = GetExtension(lang)->GetBootContext(); return LoadClass(pf, class_id, descriptor, context, nullptr); } Class *ClassLinker::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 /* = true */) { ASSERT(!pf->IsExternal(class_id)); ASSERT(context != nullptr); panda_file::ClassDataAccessor class_data_accessor(*pf, class_id); LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(&class_data_accessor); if (ctx.GetLanguage() != context->GetSourceLang()) { LanguageContext current_ctx = Runtime::GetCurrent()->GetLanguageContext(context->GetSourceLang()); PandaStringStream ss; ss << "Cannot load " << ctx << " class " << descriptor << " into " << current_ctx << " context"; LOG(ERROR, CLASS_LINKER) << ss.str(); OnError(error_handler, Error::CLASS_NOT_FOUND, ss.str()); return nullptr; } if (!HasExtension(ctx)) { PandaStringStream ss; ss << "Cannot load class '" << descriptor << "' as class linker hasn't " << ctx << " language extension"; LOG(ERROR, CLASS_LINKER) << ss.str(); OnError(error_handler, Error::CLASS_NOT_FOUND, ss.str()); return nullptr; } // This set is used to find out if the class is its own superclass ClassLoadingSet loading_set; static thread_local ClassLoadingSet *thread_local_set = nullptr; ClassLoadingSet **thread_local_set_ptr = nullptr; if (thread_local_set == nullptr) { thread_local_set = &loading_set; thread_local_set_ptr = &thread_local_set; } ClassScopeStaticSetAutoCleaner class_set_auto_cleaner_on_return(thread_local_set, thread_local_set_ptr); auto *ext = GetExtension(ctx); Class *base_class = nullptr; bool need_load_base = IsInitialized() || !utf::IsEqual(ctx.GetObjectClassDescriptor(), descriptor); if (need_load_base) { uint32_t class_id_int = class_id.GetOffset(); uint32_t panda_file_hash = pf->GetFilenameHash(); if (!thread_local_set->insert(GetClassUniqueHash(panda_file_hash, class_id_int)).second) { const PandaString &class_name = utf::Mutf8AsCString(pf->GetStringData(class_data_accessor.GetClassId()).data); PandaString msg = "Class or interface \"" + class_name + "\" is its own superclass or superinterface"; OnError(error_handler, Error::CLASS_CIRCULARITY, msg); return nullptr; } base_class = LoadBaseClass(&class_data_accessor, ctx, context, error_handler); if (base_class == nullptr) { LOG(INFO, CLASS_LINKER) << "Cannot load base class of class '" << descriptor << "'"; return nullptr; } } auto res = LoadInterfaces(&class_data_accessor, context, error_handler); if (!res) { LOG(INFO, CLASS_LINKER) << "Cannot load interfaces of class '" << descriptor << "'"; return nullptr; } auto *klass = LoadClass(&class_data_accessor, descriptor, base_class, res.value(), context, ext, error_handler); if (klass == nullptr) { return nullptr; } Runtime::GetCurrent()->GetCha()->Update(klass); if (LIKELY(ext->CanInitializeClasses())) { ext->InitializeClass(klass); klass->SetState(Class::State::LOADED); } if (LIKELY(add_to_runtime)) { Runtime::GetCurrent()->GetNotificationManager()->ClassLoadEvent(klass); auto *other_klass = context->InsertClass(klass); if (other_klass != nullptr) { // Someone has created the class in the other thread (increase the critical section?) FreeClass(klass); return other_klass; } RemoveCreatedClassInExtension(klass); Runtime::GetCurrent()->GetNotificationManager()->ClassPrepareEvent(klass); } return klass; } static const uint8_t *CopyMutf8String(mem::InternalAllocatorPtr allocator, const uint8_t *descriptor) { size_t size = utf::Mutf8Size(descriptor) + 1; // + 1 - null terminate auto *ptr = allocator->AllocArray(size); memcpy_s(ptr, size, descriptor, size); return ptr; } Class *ClassLinker::BuildClass(const uint8_t *descriptor, bool need_copy_descriptor, uint32_t access_flags, Span methods, Span fields, Class *base_class, Span interfaces, ClassLinkerContext *context, bool is_interface) { ASSERT(context != nullptr); if (need_copy_descriptor) { descriptor = CopyMutf8String(allocator_, descriptor); os::memory::LockHolder lock(copied_names_lock_); copied_names_.push_front(descriptor); } auto *ext = GetExtension(base_class->GetSourceLang()); ASSERT(ext != nullptr); ClassInfo class_info = GetClassInfo(methods, fields, base_class, interfaces, is_interface); // Need to protect ArenaAllocator and loaded_classes_ auto *klass = ext->CreateClass(descriptor, class_info.vtable_builder->GetVTableSize(), class_info.imtable_builder->GetIMTSize(), class_info.size); if (UNLIKELY(klass == nullptr)) { return nullptr; } klass->SetLoadContext(context); klass->SetBase(base_class); klass->SetInterfaces(interfaces); klass->SetAccessFlags(access_flags); klass->SetNumVirtualMethods(class_info.vtable_builder->GetNumVirtualMethods()); klass->SetNumCopiedMethods(class_info.vtable_builder->GetCopiedMethods().size()); klass->SetNumStaticFields(class_info.num_sfields); ASSERT(klass->GetNumCopiedMethods() == 0); size_t num_smethods = methods.size() - klass->GetNumVirtualMethods(); klass->SetMethods(methods, klass->GetNumVirtualMethods(), num_smethods); klass->SetFields(fields, klass->GetNumStaticFields()); for (auto &method : methods) { method.SetClass(klass); } for (auto &field : fields) { field.SetClass(klass); } if (!LinkMethods(klass, &class_info, ext->GetErrorHandler())) { LOG(ERROR, CLASS_LINKER) << "Cannot link class methods '" << descriptor << "'"; return nullptr; } if (!LinkFields(klass, ext->GetErrorHandler())) { LOG(ERROR, CLASS_LINKER) << "Cannot link class fields '" << descriptor << "'"; return nullptr; } ext->InitializeClass(klass); klass->SetState(Class::State::LOADED); Runtime::GetCurrent()->GetNotificationManager()->ClassLoadEvent(klass); auto *other_klass = context->InsertClass(klass); if (other_klass != nullptr) { // Someone has created the class in the other thread (increase the critical section?) FreeClass(klass); return other_klass; } RemoveCreatedClassInExtension(klass); Runtime::GetCurrent()->GetNotificationManager()->ClassPrepareEvent(klass); return klass; } Class *ClassLinker::CreateArrayClass(ClassLinkerExtension *ext, const uint8_t *descriptor, bool need_copy_descriptor, Class *component_class) { if (need_copy_descriptor) { descriptor = CopyMutf8String(allocator_, descriptor); os::memory::LockHolder lock(copied_names_lock_); copied_names_.push_front(descriptor); } auto *array_class = ext->CreateClass(descriptor, ext->GetArrayClassVTableSize(), ext->GetArrayClassIMTSize(), ext->GetArrayClassSize()); if (UNLIKELY(array_class == nullptr)) { return nullptr; } array_class->SetLoadContext(component_class->GetLoadContext()); ext->InitializeArrayClass(array_class, component_class); return array_class; } Class *ClassLinker::LoadArrayClass(const uint8_t *descriptor, bool need_copy_descriptor, ClassLinkerContext *context, ClassLinkerErrorHandler *error_handler) { Span sp(descriptor, 1); Class *component_class = GetClass(sp.cend(), need_copy_descriptor, context, error_handler); if (component_class == nullptr) { return nullptr; } if (UNLIKELY(component_class->GetType().GetId() == panda_file::Type::TypeId::VOID)) { OnError(error_handler, Error::NO_CLASS_DEF, "Try to create array with void component type"); return nullptr; } auto *ext = GetExtension(component_class->GetSourceLang()); ASSERT(ext != nullptr); auto *component_class_context = component_class->GetLoadContext(); ASSERT(component_class_context != nullptr); if (component_class_context != context) { auto *loaded_class = FindLoadedClass(descriptor, component_class_context); if (loaded_class != nullptr) { return loaded_class; } } auto *array_class = CreateArrayClass(ext, descriptor, need_copy_descriptor, component_class); if (UNLIKELY(array_class == nullptr)) { return nullptr; } Runtime::GetCurrent()->GetNotificationManager()->ClassLoadEvent(array_class); auto *other_klass = component_class_context->InsertClass(array_class); if (other_klass != nullptr) { FreeClass(array_class); return other_klass; } RemoveCreatedClassInExtension(array_class); Runtime::GetCurrent()->GetNotificationManager()->ClassPrepareEvent(array_class); return array_class; } static PandaString PandaFilesToString(const PandaVector &panda_files) { PandaStringStream ss; ss << "["; size_t n = panda_files.size(); for (size_t i = 0; i < n; i++) { ss << panda_files[i]->GetFilename(); if (i < n - 1) { ss << ", "; } } ss << "]"; return ss.str(); } Class *ClassLinker::GetClass(const uint8_t *descriptor, bool need_copy_descriptor, ClassLinkerContext *context, ClassLinkerErrorHandler *error_handler /* = nullptr */) { ASSERT(context != nullptr); ASSERT(!MTManagedThread::ThreadIsMTManagedThread(Thread::GetCurrent()) || !PandaVM::GetCurrent()->GetGC()->IsGCRunning() || Locks::mutator_lock->HasLock()); Class *cls = FindLoadedClass(descriptor, context); if (cls != nullptr) { return cls; } if (ClassHelper::IsArrayDescriptor(descriptor)) { return LoadArrayClass(descriptor, need_copy_descriptor, context, error_handler); } if (context->IsBootContext()) { panda_file::File::EntityId class_id; const panda_file::File *panda_file {nullptr}; { { os::memory::LockHolder lock {boot_panda_files_lock_}; std::tie(class_id, panda_file) = FindClassInPandaFiles(descriptor, boot_panda_files_); } if (!class_id.IsValid()) { PandaStringStream ss; { // can't make a wider scope for lock here - will get recursion os::memory::LockHolder lock {boot_panda_files_lock_}; ss << "Cannot find class " << descriptor << " in boot panda files: " << PandaFilesToString(boot_panda_files_); } OnError(error_handler, Error::CLASS_NOT_FOUND, ss.str()); return nullptr; } } return LoadClass(panda_file, class_id, panda_file->GetStringData(class_id).data, context, error_handler); } return context->LoadClass(descriptor, need_copy_descriptor, error_handler); } Class *ClassLinker::GetClass(const panda_file::File &pf, panda_file::File::EntityId id, ClassLinkerContext *context, ClassLinkerErrorHandler *error_handler /* = nullptr */) { ASSERT(context != nullptr); ASSERT(!MTManagedThread::ThreadIsMTManagedThread(Thread::GetCurrent()) || !PandaVM::GetCurrent()->GetGC()->IsGCRunning() || Locks::mutator_lock->HasLock()); Class *cls = pf.GetPandaCache()->GetClassFromCache(id); if (cls != nullptr) { return cls; } const uint8_t *descriptor = pf.GetStringData(id).data; cls = FindLoadedClass(descriptor, context); if (cls != nullptr) { pf.GetPandaCache()->SetClassCache(id, cls); return cls; } if (ClassHelper::IsArrayDescriptor(descriptor)) { cls = LoadArrayClass(descriptor, false, context, error_handler); if (LIKELY(cls != nullptr)) { pf.GetPandaCache()->SetClassCache(id, cls); } return cls; } if (context->IsBootContext()) { const panda_file::File *pf_ptr = nullptr; panda_file::File::EntityId ext_id; { os::memory::LockHolder lock {boot_panda_files_lock_}; std::tie(ext_id, pf_ptr) = FindClassInPandaFiles(descriptor, boot_panda_files_); } if (!ext_id.IsValid()) { PandaStringStream ss; { // can't make a wider scope for lock here - will get recursion os::memory::LockHolder lock {boot_panda_files_lock_}; ss << "Cannot find class " << descriptor << " in boot panda files: " << PandaFilesToString(boot_panda_files_); } OnError(error_handler, Error::CLASS_NOT_FOUND, ss.str()); return nullptr; } cls = LoadClass(pf_ptr, ext_id, descriptor, context, error_handler); if (LIKELY(cls != nullptr)) { pf.GetPandaCache()->SetClassCache(id, cls); } return cls; } return context->LoadClass(descriptor, false, error_handler); } Method *ClassLinker::GetMethod(const panda_file::File &pf, panda_file::File::EntityId id, ClassLinkerContext *context /* = nullptr */, ClassLinkerErrorHandler *error_handler /* = nullptr */) { Method *method = pf.GetPandaCache()->GetMethodFromCache(id); if (method != nullptr) { return method; } panda_file::MethodDataAccessor method_data_accessor(pf, id); auto class_id = method_data_accessor.GetClassId(); if (context == nullptr) { panda_file::ClassDataAccessor class_data_accessor(pf, class_id); auto lang = class_data_accessor.GetSourceLang(); if (!lang) { LOG(INFO, CLASS_LINKER) << "Cannot resolve language context for class_id " << class_id << " in file " << pf.GetFilename(); return nullptr; } auto *extension = GetExtension(lang.value()); context = extension->GetBootContext(); } Class *klass = GetClass(pf, class_id, context, error_handler); if (klass == nullptr) { auto class_name = pf.GetStringData(class_id).data; LOG(INFO, CLASS_LINKER) << "Cannot find class '" << class_name << "' in ctx " << context; return nullptr; } method = GetMethod(klass, method_data_accessor, error_handler); if (LIKELY(method != nullptr)) { pf.GetPandaCache()->SetMethodCache(id, method); } return method; } Method *ClassLinker::GetMethod(const Method &caller, panda_file::File::EntityId id, ClassLinkerErrorHandler *error_handler /* = nullptr */) { auto *pf = caller.GetPandaFile(); Method *method = pf->GetPandaCache()->GetMethodFromCache(id); if (method != nullptr) { return method; } panda_file::MethodDataAccessor method_data_accessor(*pf, id); auto class_id = method_data_accessor.GetClassId(); auto *context = caller.GetClass()->GetLoadContext(); auto *ext = GetExtension(caller.GetClass()->GetSourceLang()); Class *klass = ext->GetClass(*pf, class_id, context, error_handler); if (klass == nullptr) { auto class_name = pf->GetStringData(class_id).data; LOG(INFO, CLASS_LINKER) << "Cannot find class '" << class_name << "' in ctx " << context; return nullptr; } method = GetMethod(klass, method_data_accessor, (error_handler == nullptr) ? ext->GetErrorHandler() : error_handler); if (LIKELY(method != nullptr)) { pf->GetPandaCache()->SetMethodCache(id, method); } return method; } Method *ClassLinker::GetMethod(const Class *klass, const panda_file::MethodDataAccessor &method_data_accessor, ClassLinkerErrorHandler *error_handler) { Method *method; auto id = method_data_accessor.GetMethodId(); const auto &pf = method_data_accessor.GetPandaFile(); bool is_static = method_data_accessor.IsStatic(); if (!method_data_accessor.IsExternal() && klass->GetPandaFile() == &pf) { if (klass->IsInterface()) { method = is_static ? klass->GetStaticInterfaceMethod(id) : klass->GetVirtualInterfaceMethod(id); } else { method = is_static ? klass->GetStaticClassMethod(id) : klass->GetVirtualClassMethod(id); } if (method == nullptr) { PandaStringStream ss; ss << "Cannot find method '" << method_data_accessor.GetName().data << "' in class '" << klass->GetName() << "'"; OnError(error_handler, Error::METHOD_NOT_FOUND, ss.str()); return nullptr; } return method; } auto name = method_data_accessor.GetName(); Method::Proto proto(pf, method_data_accessor.GetProtoId()); if (klass->IsInterface()) { method = is_static ? klass->GetStaticInterfaceMethodByName(name, proto) : klass->GetVirtualInterfaceMethodByName(name, proto); } else { method = is_static ? klass->GetStaticClassMethodByName(name, proto) : klass->GetVirtualClassMethodByName(name, proto); if (method == nullptr && klass->IsAbstract()) { method = klass->GetInterfaceMethod(name, proto); } } if (method == nullptr) { PandaStringStream ss; ss << "Cannot find method '" << name.data << "' in class '" << klass->GetName() << "'"; OnError(error_handler, Error::METHOD_NOT_FOUND, ss.str()); return nullptr; } LOG_IF(method->IsStatic() != method_data_accessor.IsStatic(), FATAL, CLASS_LINKER) << "Expected ACC_STATIC for method " << name.data << " in class " << klass->GetName() << " does not match loaded value"; return method; } Field *ClassLinker::GetFieldById(Class *klass, const panda_file::FieldDataAccessor &field_data_accessor, ClassLinkerErrorHandler *error_handler) { bool is_static = field_data_accessor.IsStatic(); auto &pf = field_data_accessor.GetPandaFile(); auto id = field_data_accessor.GetFieldId(); Field *field = is_static ? klass->FindStaticFieldById(id) : klass->FindInstanceFieldById(id); if (field == nullptr) { PandaStringStream ss; ss << "Cannot find field '" << pf.GetStringData(field_data_accessor.GetNameId()).data << "' in class '" << klass->GetName() << "'"; OnError(error_handler, Error::FIELD_NOT_FOUND, ss.str()); return nullptr; } pf.GetPandaCache()->SetFieldCache(id, field); return field; } Field *ClassLinker::GetFieldBySignature(Class *klass, const panda_file::FieldDataAccessor &field_data_accessor, ClassLinkerErrorHandler *error_handler) { auto &pf = field_data_accessor.GetPandaFile(); auto id = field_data_accessor.GetFieldId(); auto field_name = pf.GetStringData(field_data_accessor.GetNameId()); auto field_type = panda_file::Type::GetTypeFromFieldEncoding(field_data_accessor.GetType()); Field *field = klass->FindField([&](const Field &fld) { if (field_type == fld.GetType() && field_name == fld.GetName()) { if (!field_type.IsReference()) { return true; } // compare field class type if (&pf == fld.GetPandaFile() && id == fld.GetFileId()) { return true; } auto type_id = panda_file::FieldDataAccessor::GetTypeId(*fld.GetPandaFile(), fld.GetFileId()); if (pf.GetStringData(panda_file::File::EntityId(field_data_accessor.GetType())) == fld.GetPandaFile()->GetStringData(type_id)) { return true; } } return false; }); if (field == nullptr) { PandaStringStream ss; ss << "Cannot find field '" << field_name.data << "' in class '" << klass->GetName() << "'"; OnError(error_handler, Error::FIELD_NOT_FOUND, ss.str()); return nullptr; } pf.GetPandaCache()->SetFieldCache(id, field); return field; } Field *ClassLinker::GetField(const panda_file::File &pf, panda_file::File::EntityId id, ClassLinkerContext *context /* = nullptr */, ClassLinkerErrorHandler *error_handler /* = nullptr */) { Field *field = pf.GetPandaCache()->GetFieldFromCache(id); if (field != nullptr) { return field; } panda_file::FieldDataAccessor field_data_accessor(pf, id); Class *klass = GetClass(pf, field_data_accessor.GetClassId(), context, error_handler); if (klass == nullptr) { auto class_name = pf.GetStringData(field_data_accessor.GetClassId()).data; LOG(INFO, CLASS_LINKER) << "Cannot find class '" << class_name << "' in ctx " << context; return nullptr; } if (!field_data_accessor.IsExternal() && klass->GetPandaFile() == &pf) { field = GetFieldById(klass, field_data_accessor, error_handler); } else { field = GetFieldBySignature(klass, field_data_accessor, error_handler); } return field; } bool ClassLinker::InitializeClass(ManagedThread *thread, Class *klass) { ASSERT(klass != nullptr); if (klass->IsInitialized()) { return true; } LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(*klass); return ctx.InitializeClass(this, thread, klass); } size_t ClassLinker::NumLoadedClasses() { size_t sum = 0; for (auto &ext : extensions_) { if (ext == nullptr) { continue; } sum += ext->NumLoadedClasses(); } return sum; } void ClassLinker::VisitLoadedClasses(size_t flag) { for (auto &ext : extensions_) { if (ext == nullptr) { continue; } ext->VisitLoadedClasses(flag); } } void ClassLinker::OnError(ClassLinkerErrorHandler *error_handler, ClassLinker::Error error, const PandaString &msg) { if (error_handler != nullptr) { error_handler->OnError(error, msg); } } Field *ClassLinker::GetField(const Method &caller, panda_file::File::EntityId id, ClassLinkerErrorHandler *error_handler /* = nullptr */) { Field *field = caller.GetPandaFile()->GetPandaCache()->GetFieldFromCache(id); if (field != nullptr) { return field; } auto *ext = GetExtension(caller.GetClass()->GetSourceLang()); field = GetField(*caller.GetPandaFile(), id, caller.GetClass()->GetLoadContext(), (error_handler == nullptr) ? ext->GetErrorHandler() : error_handler); if (LIKELY(field != nullptr)) { caller.GetPandaFile()->GetPandaCache()->SetFieldCache(id, field); } return field; } void ClassLinker::RemoveCreatedClassInExtension(Class *klass) { if (klass == nullptr) { return; } auto ext = GetExtension(klass->GetSourceLang()); if (ext != nullptr) { ext->OnClassPrepared(klass); } } } // namespace panda