/** * 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. */ #ifndef PANDA_RUNTIME_CLASS_LINKER_H_ #define PANDA_RUNTIME_CLASS_LINKER_H_ #include #include #include #include #include #include "compiler/aot/aot_manager.h" #include "libpandabase/mem/arena_allocator.h" #include "libpandabase/os/mutex.h" #include "libpandabase/utils/utf.h" #include "libpandafile/class_data_accessor-inl.h" #include "libpandafile/file.h" #include "libpandafile/file_items.h" #include "runtime/class_linker_context.h" #include "runtime/include/class.h" #include "runtime/include/field.h" #include "runtime/include/itable_builder.h" #include "runtime/include/imtable_builder.h" #include "runtime/include/language_context.h" #include "runtime/include/mem/panda_containers.h" #include "runtime/include/mem/panda_smart_pointers.h" #include "runtime/include/mem/panda_string.h" #include "runtime/include/method.h" #include "runtime/include/vtable_builder.h" namespace panda { using compiler::AotManager; class ClassLinkerErrorHandler; class ClassLinker { public: enum class Error { CLASS_NOT_FOUND, FIELD_NOT_FOUND, METHOD_NOT_FOUND, NO_CLASS_DEF, CLASS_CIRCULARITY, }; ClassLinker(mem::InternalAllocatorPtr allocator, std::vector> &&extensions); ~ClassLinker(); bool Initialize(bool compressed_string_enabled = true); bool InitializeRoots(ManagedThread *thread); Class *GetClass(const uint8_t *descriptor, bool need_copy_descriptor, ClassLinkerContext *context, ClassLinkerErrorHandler *error_handler = nullptr); Class *GetClass(const panda_file::File &pf, panda_file::File::EntityId id, ClassLinkerContext *context, ClassLinkerErrorHandler *error_handler = nullptr); Class *GetClass(const Method &caller, panda_file::File::EntityId id, ClassLinkerErrorHandler *error_handler = nullptr); Class *LoadClass(const panda_file::File &pf, panda_file::File::EntityId class_id, ClassLinkerContext *context, ClassLinkerErrorHandler *error_handler = nullptr, bool add_to_runtime = true) { return LoadClass(&pf, class_id, pf.GetStringData(class_id).data, context, error_handler, add_to_runtime); } Method *GetMethod(const panda_file::File &pf, panda_file::File::EntityId id, ClassLinkerContext *context = nullptr, ClassLinkerErrorHandler *error_handler = nullptr); Method *GetMethod(const Method &caller, panda_file::File::EntityId id, ClassLinkerErrorHandler *error_handler = nullptr); Field *GetField(const panda_file::File &pf, panda_file::File::EntityId id, ClassLinkerContext *context = nullptr, ClassLinkerErrorHandler *error_handler = nullptr); Field *GetField(const Method &caller, panda_file::File::EntityId id, ClassLinkerErrorHandler *error_handler = nullptr); void AddPandaFile(std::unique_ptr &&pf, ClassLinkerContext *context = nullptr); template void EnumeratePandaFiles(Callback cb, bool skip_intrinsics = true) const { os::memory::LockHolder lock(panda_files_lock_); for (const auto &file_data : panda_files_) { if (skip_intrinsics && file_data.pf->GetFilename().empty()) { continue; } if (!cb(*(file_data.pf.get()))) { break; } } } template void EnumerateBootPandaFiles(Callback cb) const { os::memory::LockHolder lock {boot_panda_files_lock_}; for (const auto &file : boot_panda_files_) { if (!cb(*file)) { break; } } } const PandaVector &GetBootPandaFiles() const { return boot_panda_files_; } AotManager *GetAotManager() { return aot_manager_.get(); } PandaString GetClassContextForAot() { PandaString aot_ctx; EnumeratePandaFiles(compiler::AotClassContextCollector(&aot_ctx)); return aot_ctx; } template void EnumerateClasses(const Callback &cb, mem::VisitGCRootFlags flags = mem::VisitGCRootFlags::ACCESS_ROOT_ALL) { for (auto &ext : extensions_) { if (ext == nullptr) { continue; } if (!ext->EnumerateClasses(cb, flags)) { return; } } } template void EnumerateContexts(const Callback &cb) { for (auto &ext : extensions_) { if (ext == nullptr) { continue; } ext->EnumerateContexts(cb); } } template void EnumerateContextsForDump(const Callback &cb, std::ostream &os) { size_t register_index = 0; ClassLinkerContext *parent = nullptr; ClassLinkerExtension *ext = nullptr; auto enum_callback = [®ister_index, &parent, &cb, &os, &ext](ClassLinkerContext *ctx) { os << "#" << register_index << " "; if (!cb(ctx, os, parent)) { return true; } if (parent != nullptr) { size_t parent_index = 0; bool founded = false; ext->EnumerateContexts([parent, &parent_index, &founded](ClassLinkerContext *ctx_ptr) { if (parent == ctx_ptr) { founded = true; return false; } parent_index++; return true; }); if (founded) { os << "|Parent class loader: #" << parent_index << "\n"; } else { os << "|Parent class loader: unknown\n"; } } else { os << "|Parent class loader: empty\n"; } register_index++; return true; }; for (auto &ext_ptr : extensions_) { if (ext_ptr == nullptr) { continue; } ext = ext_ptr.get(); ext->EnumerateContexts(enum_callback); } } bool InitializeClass(ManagedThread *thread, Class *klass); bool HasExtension(const LanguageContext &ctx) { return extensions_[panda::panda_file::GetLangArrIndex(ctx.GetLanguage())].get() != nullptr; } bool HasExtension(panda_file::SourceLang lang) { return extensions_[panda::panda_file::GetLangArrIndex(lang)].get() != nullptr; } void ResetExtension(panda_file::SourceLang lang); ClassLinkerExtension *GetExtension(const LanguageContext &ctx) { ClassLinkerExtension *extension = extensions_[panda::panda_file::GetLangArrIndex(ctx.GetLanguage())].get(); ASSERT(extension != nullptr); return extension; }; ClassLinkerExtension *GetExtension(panda_file::SourceLang lang) { ClassLinkerExtension *extension = extensions_[panda::panda_file::GetLangArrIndex(lang)].get(); ASSERT(extension != nullptr); return extension; }; Class *ObjectToClass(const ObjectHeader *object) { ASSERT(object->ClassAddr()->IsClassClass()); return extensions_[panda::panda_file::GetLangArrIndex(object->ClassAddr()->GetSourceLang())] ->FromClassObject(const_cast(object)); } size_t GetClassObjectSize(Class *cls) { return extensions_[panda::panda_file::GetLangArrIndex(cls->GetSourceLang())]->GetClassObjectSizeFromClassSize( cls->GetClassSize()); } void AddClassRoot(ClassRoot root, Class *klass); Class *CreateArrayClass(ClassLinkerExtension *ext, const uint8_t *descriptor, bool need_copy_descriptor, Class *component_class); void FreeClassData(Class *class_ptr); void FreeClass(Class *class_ptr); mem::InternalAllocatorPtr GetAllocator() const { return allocator_; } bool IsInitialized() const { return is_initialized_; } Class *FindLoadedClass(const uint8_t *descriptor, ClassLinkerContext *context = nullptr); size_t NumLoadedClasses(); void VisitLoadedClasses(size_t flag); Class *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); bool IsPandaFileRegistered(const panda_file::File *file) { os::memory::LockHolder lock(panda_files_lock_); for (const auto &data : panda_files_) { if (data.pf.get() == file) { return true; } } return false; } ClassLinkerContext *GetAppContext(std::string_view panda_file) { ClassLinkerContext *app_context = nullptr; EnumerateContexts([panda_file, &app_context](ClassLinkerContext *context) -> bool { auto file_paths = context->GetPandaFilePaths(); for (auto &file : file_paths) { if (file == panda_file) { app_context = context; return false; } } return true; }); return app_context; } void RemoveCreatedClassInExtension(Class *klass); Class *LoadClass(const panda_file::File *pf, const uint8_t *descriptor, panda_file::SourceLang lang); private: struct ClassInfo { size_t size; size_t num_sfields; PandaUniquePtr vtable_builder; PandaUniquePtr itable_builder; PandaUniquePtr imtable_builder; }; Field *GetFieldById(Class *klass, const panda_file::FieldDataAccessor &field_data_accessor, ClassLinkerErrorHandler *error_handler); Field *GetFieldBySignature(Class *klass, const panda_file::FieldDataAccessor &field_data_accessor, ClassLinkerErrorHandler *error_handler); Method *GetMethod(const Class *klass, const panda_file::MethodDataAccessor &method_data_accessor, ClassLinkerErrorHandler *error_handler); bool LinkBootClass(Class *klass); Class *LoadArrayClass(const uint8_t *descriptor, bool need_copy_descriptor, ClassLinkerContext *context, ClassLinkerErrorHandler *error_handler); Class *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); Class *LoadClass(panda_file::ClassDataAccessor *class_data_accessor, const uint8_t *descriptor, Class *base_class, Span interfaces, ClassLinkerContext *context, ClassLinkerExtension *ext, ClassLinkerErrorHandler *error_handler); Class *LoadBaseClass(panda_file::ClassDataAccessor *cda, const LanguageContext &ctx, ClassLinkerContext *context, ClassLinkerErrorHandler *error_handler); std::optional> LoadInterfaces(panda_file::ClassDataAccessor *cda, ClassLinkerContext *context, ClassLinkerErrorHandler *error_handler); bool LinkFields(Class *klass, ClassLinkerErrorHandler *error_handler); bool LoadFields(Class *klass, panda_file::ClassDataAccessor *data_accessor, ClassLinkerErrorHandler *error_handler); bool LinkMethods(Class *klass, ClassInfo *class_info, ClassLinkerErrorHandler *error_handler); bool LoadMethods(Class *klass, ClassInfo *class_info, panda_file::ClassDataAccessor *data_accessor, ClassLinkerErrorHandler *error_handler); ClassInfo GetClassInfo(panda_file::ClassDataAccessor *data_accessor, Class *base, Span interfaces, ClassLinkerContext *context); ClassInfo GetClassInfo(Span methods, Span fields, Class *base, Span interfaces, bool is_interface); void OnError(ClassLinkerErrorHandler *error_handler, Error error, const PandaString &msg); static bool LayoutFields(Class *klass, Span fields, bool is_static, ClassLinkerErrorHandler *error_handler); mem::InternalAllocatorPtr allocator_; PandaVector boot_panda_files_ GUARDED_BY(boot_panda_files_lock_); struct PandaFileLoadData { ClassLinkerContext *context; std::unique_ptr pf; }; mutable os::memory::Mutex panda_files_lock_; mutable os::memory::Mutex boot_panda_files_lock_; PandaVector panda_files_ GUARDED_BY(panda_files_lock_); PandaUniquePtr aot_manager_; // Just to free them at destroy os::memory::Mutex copied_names_lock_; PandaList copied_names_ GUARDED_BY(copied_names_lock_); std::array, panda::panda_file::LANG_COUNT> extensions_; bool is_initialized_ {false}; NO_COPY_SEMANTIC(ClassLinker); NO_MOVE_SEMANTIC(ClassLinker); }; class ClassLinkerErrorHandler { public: virtual void OnError(ClassLinker::Error error, const PandaString &message) = 0; public: ClassLinkerErrorHandler() = default; virtual ~ClassLinkerErrorHandler() = default; NO_MOVE_SEMANTIC(ClassLinkerErrorHandler); NO_COPY_SEMANTIC(ClassLinkerErrorHandler); }; } // namespace panda #endif // PANDA_RUNTIME_CLASS_LINKER_H_