/* * 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_INCLUDE_CLASS_H_ #define PANDA_RUNTIME_INCLUDE_CLASS_H_ #include #include #include #include #include #include "libpandafile/file.h" #include "libpandafile/file_items.h" #include "runtime/include/field.h" #include "runtime/include/itable.h" #include "runtime/include/method.h" #include "runtime/include/object_header.h" #include "libpandabase/macros.h" namespace panda { class ClassLinkerContext; class Field; class ManagedThread; class BaseClass { public: static constexpr uint32_t DYNAMIC_CLASS = 1U; public: explicit BaseClass(panda_file::SourceLang lang) : lang_(lang) {} ~BaseClass() = default; DEFAULT_COPY_SEMANTIC(BaseClass); DEFAULT_MOVE_SEMANTIC(BaseClass); uint32_t GetFlags() const { return flags_; } void SetFlags(uint32_t flags) { flags_ = flags; } bool IsDynamicClass() const { return (flags_ & DYNAMIC_CLASS) != 0; } uint32_t GetObjectSize() const { return object_size_; } void SetObjectSize(uint32_t size) { object_size_ = size; } void SetManagedObject(ObjectHeader *obj) { managed_object_ = obj; } ObjectHeader *GetManagedObject() const { return managed_object_; } panda_file::SourceLang GetSourceLang() const { return lang_; } void SetSourceLang(panda_file::SourceLang lang) { lang_ = lang; } static constexpr size_t OffsetOfManageObject() { return offsetof(BaseClass, managed_object_); } private: uint32_t flags_ {0}; // Size of the object of this class. In case of static classes it is 0 // for abstract classes, interfaces and classes whose objects // have variable size (for example strings). uint32_t object_size_ {0}; ObjectHeader *managed_object_ {nullptr}; panda_file::SourceLang lang_; }; class Class : public BaseClass { public: using UniqId = uint64_t; static constexpr uint32_t STRING_CLASS = 1U << 1U; static constexpr size_t IMTABLE_SIZE = 32; enum { DUMPCLASSFULLDETAILS = 1, DUMPCLASSCLASSLODER = 2, DUMPCLASSINITIALIZED = 4, }; enum class State : uint8_t { INITIAL = 0, LOADED, VERIFIED, INITIALIZING, ERRONEOUS, INITIALIZED }; Class(const uint8_t *descriptor, panda_file::SourceLang lang, uint32_t vtable_size, uint32_t imt_size, uint32_t size); Class *GetBase() const { return base_; } void SetBase(Class *base) { base_ = base; } panda_file::File::EntityId GetFileId() const { return file_id_; } void SetFileId(panda_file::File::EntityId file_id) { file_id_ = file_id; } const panda_file::File *GetPandaFile() const { return panda_file_; } void SetPandaFile(const panda_file::File *pf) { panda_file_ = pf; } const uint8_t *GetDescriptor() const { return descriptor_; } void SetMethods(Span methods, uint32_t num_vmethods, uint32_t num_smethods) { methods_ = methods.data(); num_methods_ = num_vmethods + num_smethods; num_vmethods_ = num_vmethods; num_copied_methods_ = methods.size() - num_methods_; } Span GetMethods() const { return {methods_, num_methods_}; } Span GetStaticMethods() const { return GetMethods().SubSpan(num_vmethods_); } Span GetVirtualMethods() const { return {methods_, num_vmethods_}; } Span GetCopiedMethods() const { Span res {methods_, num_methods_ + num_copied_methods_}; return res.SubSpan(num_methods_); } Span GetFields() const { return {fields_, num_fields_}; } Span GetStaticFields() const { return {fields_, num_sfields_}; } Span GetInstanceFields() const { return GetFields().SubSpan(num_sfields_); } void SetFields(Span fields, uint32_t num_sfields) { fields_ = fields.data(); num_fields_ = fields.size(); num_sfields_ = num_sfields; } Span GetVTable() { return GetClassSpan().SubSpan(GetVTableOffset(), vtable_size_); } Span GetVTable() const { return GetClassSpan().SubSpan(GetVTableOffset(), vtable_size_); } Span GetInterfaces() const { return {ifaces_, num_ifaces_}; } void SetInterfaces(Span ifaces) { ifaces_ = ifaces.data(); num_ifaces_ = ifaces.size(); } Span GetIMT() { return GetClassSpan().SubSpan(GetIMTOffset(), imt_size_); } Span GetIMT() const { return GetClassSpan().SubSpan(GetIMTOffset(), imt_size_); } uint32_t GetIMTableIndex(uint32_t method_offset) const { ASSERT(imt_size_ != 0); return method_offset % imt_size_; } uint32_t GetAccessFlags() const { return access_flags_; } void SetAccessFlags(uint32_t access_flags) { access_flags_ = access_flags; } bool IsPublic() const { return (access_flags_ & ACC_PUBLIC) != 0; } bool IsFinal() const { return (access_flags_ & ACC_FINAL) != 0; } bool IsAnnotation() const { return (access_flags_ & ACC_ANNOTATION) != 0; } bool IsEnum() const { return (access_flags_ & ACC_ENUM) != 0; } uint32_t GetVTableSize() const { return vtable_size_; } uint32_t GetIMTSize() const { return imt_size_; } uint32_t GetClassSize() const { return class_size_; } uint32_t GetObjectSize() const { ASSERT(!IsVariableSize()); return BaseClass::GetObjectSize(); } void SetObjectSize(uint32_t size) { ASSERT(!IsVariableSize()); BaseClass::SetObjectSize(size); } static uint32_t GetTypeSize(panda_file::Type type); uint32_t GetComponentSize() const; Class *GetComponentType() const { return component_type_; } void SetComponentType(Class *type) { component_type_ = type; } bool IsArrayClass() const { return component_type_ != nullptr; } bool IsObjectArrayClass() const { return IsArrayClass() && !component_type_->IsPrimitive(); } bool IsStringClass() const { return (GetFlags() & STRING_CLASS) != 0; } bool IsVariableSize() const { return IsArrayClass() || IsStringClass(); } size_t GetStaticFieldsOffset() const { return ClassHelper::ComputeClassSize(vtable_size_, imt_size_, 0, 0, 0, 0, 0, 0); } panda_file::Type GetType() const { return type_; } void SetType(panda_file::Type type) { type_ = type; } bool IsPrimitive() const { return type_.IsPrimitive(); } bool IsAbstract() const { return (access_flags_ & ACC_ABSTRACT) != 0; } bool IsInterface() const { return (access_flags_ & ACC_INTERFACE) != 0; } bool IsInstantiable() const { return (!IsPrimitive() && !IsAbstract() && !IsInterface()) || IsArrayClass(); } bool IsObjectClass() const { return !IsPrimitive() && GetBase() == nullptr; } /** * Check if the object is Class instance * @return true if the object is Class instance */ bool IsClassClass() const { return GetManagedObject()->ClassAddr() == this; } bool IsSubClassOf(const Class *klass) const; /** * Check whether an instance of this class can be assigned from an instance of class "klass". * Object of type O is instance of type T if O is the same as T or is subtype of T. For arrays T should be a root * type in type hierarchy or T is such array that O array elements are the same or subtype of T array elements. */ bool IsAssignableFrom(const Class *klass) const; bool IsProxy() const { return (GetAccessFlags() & ACC_PROXY) != 0; } bool Implements(const Class *klass) const; void SetITable(ITable itable) { itable_ = itable; } ITable GetITable() const { return itable_; } State GetState() const { return state_; } void SetState(State state); bool IsVerified() const { return state_ >= State::VERIFIED; } bool IsInitializing() const { return state_ == State::INITIALIZING; } bool IsInitialized() const { return state_ == State::INITIALIZED; } bool IsLoaded() const { return state_ >= State::LOADED; } bool IsErroneous() const { return state_ == State::ERRONEOUS; } static constexpr uint32_t GetStateOffset() { return MEMBER_OFFSET(Class, state_); } static constexpr uint32_t GetComponentTypeOffset() { return MEMBER_OFFSET(Class, component_type_); } static constexpr uint32_t GetTypeOffset() { return MEMBER_OFFSET(Class, type_); } static constexpr uint32_t GetBaseOffset() { return MEMBER_OFFSET(Class, base_); } uint8_t GetInitializedValue() { return static_cast(State::INITIALIZED); } bool IsVerifiedSuccess() const { return (IsVerified() && (!IsErroneous())); } void SetInitTid(uint32_t id) { init_tid_ = id; } uint32_t GetInitTid() const { return init_tid_; } static size_t GetVTableOffset() { return ClassHelper::ComputeClassSize(0, 0, 0, 0, 0, 0, 0, 0); } uint32_t GetNumVirtualMethods() const { return num_vmethods_; } void SetNumVirtualMethods(uint32_t n) { num_vmethods_ = n; } uint32_t GetNumCopiedMethods() const { return num_copied_methods_; } void SetNumCopiedMethods(uint32_t n) { num_copied_methods_ = n; } uint32_t GetNumStaticFields() const { return num_sfields_; } void SetNumStaticFields(uint32_t n) { num_sfields_ = n; } void SetHasDefaultMethods() { access_flags_ |= ACC_HAS_DEFAULT_METHODS; } bool HasDefaultMethods() const { return (access_flags_ & ACC_HAS_DEFAULT_METHODS) != 0; } size_t GetIMTOffset() { return GetVTableOffset() + vtable_size_ * sizeof(uintptr_t); } size_t GetIMTOffset() const { return GetVTableOffset() + vtable_size_ * sizeof(uintptr_t); } std::string GetName() const; ClassLinkerContext *GetLoadContext() const { ASSERT(load_context_ != nullptr); return load_context_; } void SetLoadContext(ClassLinkerContext *context) { ASSERT(context != nullptr); load_context_ = context; } template Field *FindInstanceField(Pred pred) const; template Field *FindStaticField(Pred pred) const; template Field *FindField(Pred pred) const; template Field *FindDeclaredField(Pred pred) const; Field *GetInstanceFieldByName(const uint8_t *mutf8_name) const; Field *GetStaticFieldByName(const uint8_t *mutf8_name) const; Field *GetDeclaredFieldByName(const uint8_t *mutf8_name) const; template Method *FindVirtualClassMethod(Pred pred) const; template Method *FindInterfaceMethod(Pred pred) const; template Method *FindVirtualInterfaceMethod(Pred pred) const; template Method *FindStaticInterfaceMethod(Pred pred) const; template Method *FindStaticClassMethod(Pred pred) const; template Method *FindClassMethod(Pred pred) const; Method *GetDirectMethod(const uint8_t *mutf8_name, const Method::Proto &proto) const; Method *GetClassMethod(const uint8_t *mutf8_name, const Method::Proto &proto) const; Method *GetInterfaceMethod(const uint8_t *mutf8_name, const Method::Proto &proto) const; Method *GetDirectMethod(const uint8_t *mutf8_name) const; Method *GetClassMethod(const uint8_t *mutf8_name) const; Method *ResolveVirtualMethod(const Method *method) const; template T GetFieldPrimitive(size_t offset) const; template void SetFieldPrimitive(size_t offset, T value); template ObjectHeader *GetFieldObject(size_t offset) const; template void SetFieldObject(size_t offset, ObjectHeader *value); template T GetFieldPrimitive(const Field &field) const; template void SetFieldPrimitive(const Field &field, T value); template ObjectHeader *GetFieldObject(const Field &field) const; template void SetFieldObject(const Field &field, ObjectHeader *value); // Pass thread parameter to speed up interpreter template ObjectHeader *GetFieldObject(ManagedThread *thread, const Field &field) const; template void SetFieldObject(ManagedThread *thread, const Field &field, ObjectHeader *value); template T GetFieldPrimitive(size_t offset, std::memory_order memory_order) const; template void SetFieldPrimitive(size_t offset, T value, std::memory_order memory_order); template ObjectHeader *GetFieldObject(size_t offset, std::memory_order memory_order) const; template void SetFieldObject(size_t offset, ObjectHeader *value, std::memory_order memory_order); template bool CompareAndSetFieldPrimitive(size_t offset, T old_value, T new_value, std::memory_order memory_order, bool strong); template bool CompareAndSetFieldObject(size_t offset, ObjectHeader *old_value, ObjectHeader *new_value, std::memory_order memory_order, bool strong); template T CompareAndExchangeFieldPrimitive(size_t offset, T old_value, T new_value, std::memory_order memory_order, bool strong); template ObjectHeader *CompareAndExchangeFieldObject(size_t offset, ObjectHeader *old_value, ObjectHeader *new_value, std::memory_order memory_order, bool strong); template T GetAndSetFieldPrimitive(size_t offset, T value, std::memory_order memory_order); template ObjectHeader *GetAndSetFieldObject(size_t offset, ObjectHeader *value, std::memory_order memory_order); template T GetAndAddFieldPrimitive(size_t offset, T value, std::memory_order memory_order); template T GetAndBitwiseOrFieldPrimitive(size_t offset, T value, std::memory_order memory_order); template T GetAndBitwiseAndFieldPrimitive(size_t offset, T value, std::memory_order memory_order); template T GetAndBitwiseXorFieldPrimitive(size_t offset, T value, std::memory_order memory_order); void DumpClass(std::ostream &os, size_t flags); static UniqId CalcUniqId(const panda_file::File *file, panda_file::File::EntityId file_id); // for synthetic classes, like arrays static UniqId CalcUniqId(const uint8_t *descriptor); UniqId GetUniqId() const { auto id = uniq_id_.load(); if (id == 0) { id = CalcUniqId(); uniq_id_.store(id); } return id; } void SetRefFieldsNum(uint32_t num, bool is_static) { if (is_static) { num_refsfields_ = num; } else { num_reffields_ = num; } } void SetRefFieldsOffset(uint32_t offset, bool is_static) { if (is_static) { offset_refsfields_ = offset; } else { offset_reffields_ = offset; } } void SetVolatileRefFieldsNum(uint32_t num, bool is_static) { if (is_static) { volatile_refsfields_num_ = num; } else { volatile_reffields_num_ = num; } } template uint32_t GetRefFieldsNum() const { return is_static ? num_refsfields_ : num_reffields_; } template uint32_t GetRefFieldsOffset() const { return is_static ? offset_refsfields_ : offset_reffields_; } template uint32_t GetVolatileRefFieldsNum() const { return is_static ? volatile_refsfields_num_ : volatile_reffields_num_; } panda_file::File::EntityId ResolveClassIndex(panda_file::File::Index idx) const { return class_idx_[idx]; } panda_file::File::EntityId ResolveMethodIndex(panda_file::File::Index idx) const { return method_idx_[idx]; } panda_file::File::EntityId ResolveFieldIndex(panda_file::File::Index idx) const { return field_idx_[idx]; } Span GetClassIndex() const { return class_idx_; } void SetClassIndex(Span index) { class_idx_ = index; } Span GetMethodIndex() const { return method_idx_; } void SetMethodIndex(Span index) { method_idx_ = index; } Span GetFieldIndex() const { return field_idx_; } void SetFieldIndex(Span index) { field_idx_ = index; } static Class *FromClassObject(const ObjectHeader *obj); static size_t GetClassObjectSizeFromClass(Class *cls); ~Class() = default; NO_COPY_SEMANTIC(Class); NO_MOVE_SEMANTIC(Class); private: enum class FindFilter { STATIC, INSTANCE, ALL, COPIED }; template Span GetFields() const; template Field *FindDeclaredField(Pred pred) const; template Field *FindField(Pred pred) const; template Span GetMethods() const; template Method *FindDirectMethod(Pred pred) const; template Method *FindClassMethod(Pred pred) const; template Method *FindInterfaceMethod(Pred pred) const; Span GetClassSpan() { return Span(reinterpret_cast(this), class_size_); } Span GetClassSpan() const { return Span(reinterpret_cast(this), class_size_); } Class *base_ {nullptr}; const panda_file::File *panda_file_ {nullptr}; // Decscriptor is a valid MUTF8 string. See docs/file_format.md#typedescriptor for more information. const uint8_t *descriptor_; Method *methods_ {nullptr}; Field *fields_ {nullptr}; Class **ifaces_ {nullptr}; panda_file::File::EntityId file_id_ {}; uint32_t vtable_size_; uint32_t imt_size_; uint32_t class_size_; uint32_t access_flags_ {0}; uint32_t num_methods_ {0}; uint32_t num_vmethods_ {0}; uint32_t num_copied_methods_ {0}; uint32_t num_fields_ {0}; uint32_t num_sfields_ {0}; uint32_t num_ifaces_ {0}; uint32_t init_tid_ {0}; ITable itable_; // For array types this field contains array's element size, for non-array type it should be zero. Class *component_type_ {nullptr}; ClassLinkerContext *load_context_ {nullptr}; panda_file::Type type_ {panda_file::Type::TypeId::REFERENCE}; std::atomic state_ {State::INITIAL}; UniqId CalcUniqId() const; mutable std::atomic uniq_id_ {0}; uint32_t num_reffields_ {0}; // instance reference fields num uint32_t num_refsfields_ {0}; // static reference fields num uint32_t offset_reffields_ {0}; // first instance reference fields offset in object layout uint32_t offset_refsfields_ {0}; // first static reference fields offset in object layout uint32_t volatile_reffields_num_ {0}; uint32_t volatile_refsfields_num_ {0}; Span class_idx_ {nullptr, nullptr}; Span method_idx_ {nullptr, nullptr}; Span field_idx_ {nullptr, nullptr}; }; std::ostream &operator<<(std::ostream &os, const Class::State &state); #ifdef PANDA_TARGET_64 constexpr uint32_t CLASS_MANAGE_OBJECT_OFFSET = 8U; static_assert(CLASS_MANAGE_OBJECT_OFFSET == Class::OffsetOfManageObject()); constexpr uint32_t CLASS_STATE_OFFSET = 153U; static_assert(CLASS_STATE_OFFSET == panda::Class::GetStateOffset()); constexpr uint32_t CLASS_COMPONENT_TYPE_OFFSET = 136U; static_assert(CLASS_COMPONENT_TYPE_OFFSET == panda::Class::GetComponentTypeOffset()); constexpr uint32_t CLASS_TYPE_OFFSET = 152U; static_assert(CLASS_TYPE_OFFSET == panda::Class::GetTypeOffset()); constexpr uint32_t CLASS_BASE_OFFSET = 24U; static_assert(CLASS_BASE_OFFSET == panda::Class::GetBaseOffset()); #endif } // namespace panda #endif // PANDA_RUNTIME_INCLUDE_CLASS_H_