| //===-- ObjCLanguageRuntime.h -----------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef liblldb_ObjCLanguageRuntime_h_ |
| #define liblldb_ObjCLanguageRuntime_h_ |
| |
| // C Includes |
| // C++ Includes |
| #include <functional> |
| #include <map> |
| #include <memory> |
| #include <unordered_set> |
| |
| // Other libraries and framework includes |
| #include "llvm/Support/Casting.h" |
| |
| // Project includes |
| #include "lldb/Core/PluginInterface.h" |
| #include "lldb/Core/ThreadSafeDenseMap.h" |
| #include "lldb/Symbol/CompilerType.h" |
| #include "lldb/Symbol/DeclVendor.h" |
| #include "lldb/Symbol/Type.h" |
| #include "lldb/Target/LanguageRuntime.h" |
| #include "lldb/lldb-private.h" |
| |
| class CommandObjectObjC_ClassTable_Dump; |
| |
| namespace lldb_private { |
| |
| class UtilityFunction; |
| |
| class ObjCLanguageRuntime : public LanguageRuntime { |
| public: |
| enum class ObjCRuntimeVersions { |
| eObjC_VersionUnknown = 0, |
| eAppleObjC_V1 = 1, |
| eAppleObjC_V2 = 2 |
| }; |
| |
| typedef lldb::addr_t ObjCISA; |
| |
| class ClassDescriptor; |
| typedef std::shared_ptr<ClassDescriptor> ClassDescriptorSP; |
| |
| // the information that we want to support retrieving from an ObjC class this |
| // needs to be pure virtual since there are at least 2 different |
| // implementations of the runtime, and more might come |
| class ClassDescriptor { |
| public: |
| ClassDescriptor() |
| : m_is_kvo(eLazyBoolCalculate), m_is_cf(eLazyBoolCalculate), |
| m_type_wp() {} |
| |
| virtual ~ClassDescriptor() = default; |
| |
| virtual ConstString GetClassName() = 0; |
| |
| virtual ClassDescriptorSP GetSuperclass() = 0; |
| |
| virtual ClassDescriptorSP GetMetaclass() const = 0; |
| |
| // virtual if any implementation has some other version-specific rules but |
| // for the known v1/v2 this is all that needs to be done |
| virtual bool IsKVO() { |
| if (m_is_kvo == eLazyBoolCalculate) { |
| const char *class_name = GetClassName().AsCString(); |
| if (class_name && *class_name) |
| m_is_kvo = |
| (LazyBool)(strstr(class_name, "NSKVONotifying_") == class_name); |
| } |
| return (m_is_kvo == eLazyBoolYes); |
| } |
| |
| // virtual if any implementation has some other version-specific rules but |
| // for the known v1/v2 this is all that needs to be done |
| virtual bool IsCFType() { |
| if (m_is_cf == eLazyBoolCalculate) { |
| const char *class_name = GetClassName().AsCString(); |
| if (class_name && *class_name) |
| m_is_cf = (LazyBool)(strcmp(class_name, "__NSCFType") == 0 || |
| strcmp(class_name, "NSCFType") == 0); |
| } |
| return (m_is_cf == eLazyBoolYes); |
| } |
| |
| virtual bool IsValid() = 0; |
| |
| virtual bool GetTaggedPointerInfo(uint64_t *info_bits = nullptr, |
| uint64_t *value_bits = nullptr, |
| uint64_t *payload = nullptr) = 0; |
| |
| virtual uint64_t GetInstanceSize() = 0; |
| |
| // use to implement version-specific additional constraints on pointers |
| virtual bool CheckPointer(lldb::addr_t value, uint32_t ptr_size) const { |
| return true; |
| } |
| |
| virtual ObjCISA GetISA() = 0; |
| |
| // This should return true iff the interface could be completed |
| virtual bool |
| Describe(std::function<void(ObjCISA)> const &superclass_func, |
| std::function<bool(const char *, const char *)> const |
| &instance_method_func, |
| std::function<bool(const char *, const char *)> const |
| &class_method_func, |
| std::function<bool(const char *, const char *, lldb::addr_t, |
| uint64_t)> const &ivar_func) const { |
| return false; |
| } |
| |
| lldb::TypeSP GetType() { return m_type_wp.lock(); } |
| |
| void SetType(const lldb::TypeSP &type_sp) { m_type_wp = type_sp; } |
| |
| struct iVarDescriptor { |
| ConstString m_name; |
| CompilerType m_type; |
| uint64_t m_size; |
| int32_t m_offset; |
| }; |
| |
| virtual size_t GetNumIVars() { return 0; } |
| |
| virtual iVarDescriptor GetIVarAtIndex(size_t idx) { |
| return iVarDescriptor(); |
| } |
| |
| protected: |
| bool IsPointerValid(lldb::addr_t value, uint32_t ptr_size, |
| bool allow_NULLs = false, bool allow_tagged = false, |
| bool check_version_specific = false) const; |
| |
| private: |
| LazyBool m_is_kvo; |
| LazyBool m_is_cf; |
| lldb::TypeWP m_type_wp; |
| }; |
| |
| class EncodingToType { |
| public: |
| virtual ~EncodingToType(); |
| |
| virtual CompilerType RealizeType(ClangASTContext &ast_ctx, const char *name, |
| bool for_expression); |
| virtual CompilerType RealizeType(const char *name, bool for_expression); |
| |
| virtual CompilerType RealizeType(clang::ASTContext &ast_ctx, |
| const char *name, bool for_expression) = 0; |
| |
| protected: |
| std::unique_ptr<ClangASTContext> m_scratch_ast_ctx_ap; |
| }; |
| |
| class ObjCExceptionPrecondition : public Breakpoint::BreakpointPrecondition { |
| public: |
| ObjCExceptionPrecondition(); |
| |
| ~ObjCExceptionPrecondition() override = default; |
| |
| bool EvaluatePrecondition(StoppointCallbackContext &context) override; |
| void GetDescription(Stream &stream, lldb::DescriptionLevel level) override; |
| Status ConfigurePrecondition(Args &args) override; |
| |
| protected: |
| void AddClassName(const char *class_name); |
| |
| private: |
| std::unordered_set<std::string> m_class_names; |
| }; |
| |
| class TaggedPointerVendor { |
| public: |
| virtual ~TaggedPointerVendor() = default; |
| |
| virtual bool IsPossibleTaggedPointer(lldb::addr_t ptr) = 0; |
| |
| virtual ObjCLanguageRuntime::ClassDescriptorSP |
| GetClassDescriptor(lldb::addr_t ptr) = 0; |
| |
| protected: |
| TaggedPointerVendor() = default; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(TaggedPointerVendor); |
| }; |
| |
| ~ObjCLanguageRuntime() override; |
| |
| virtual TaggedPointerVendor *GetTaggedPointerVendor() { return nullptr; } |
| |
| typedef std::shared_ptr<EncodingToType> EncodingToTypeSP; |
| |
| virtual EncodingToTypeSP GetEncodingToType(); |
| |
| virtual ClassDescriptorSP GetClassDescriptor(ValueObject &in_value); |
| |
| ClassDescriptorSP GetNonKVOClassDescriptor(ValueObject &in_value); |
| |
| virtual ClassDescriptorSP |
| GetClassDescriptorFromClassName(const ConstString &class_name); |
| |
| virtual ClassDescriptorSP GetClassDescriptorFromISA(ObjCISA isa); |
| |
| ClassDescriptorSP GetNonKVOClassDescriptor(ObjCISA isa); |
| |
| lldb::LanguageType GetLanguageType() const override { |
| return lldb::eLanguageTypeObjC; |
| } |
| |
| virtual bool IsModuleObjCLibrary(const lldb::ModuleSP &module_sp) = 0; |
| |
| virtual bool ReadObjCLibrary(const lldb::ModuleSP &module_sp) = 0; |
| |
| virtual bool HasReadObjCLibrary() = 0; |
| |
| virtual lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread, |
| bool stop_others) = 0; |
| |
| lldb::addr_t LookupInMethodCache(lldb::addr_t class_addr, lldb::addr_t sel); |
| |
| void AddToMethodCache(lldb::addr_t class_addr, lldb::addr_t sel, |
| lldb::addr_t impl_addr); |
| |
| TypeAndOrName LookupInClassNameCache(lldb::addr_t class_addr); |
| |
| void AddToClassNameCache(lldb::addr_t class_addr, const char *name, |
| lldb::TypeSP type_sp); |
| |
| void AddToClassNameCache(lldb::addr_t class_addr, |
| const TypeAndOrName &class_or_type_name); |
| |
| lldb::TypeSP LookupInCompleteClassCache(ConstString &name); |
| |
| virtual UtilityFunction *CreateObjectChecker(const char *) = 0; |
| |
| virtual ObjCRuntimeVersions GetRuntimeVersion() const { |
| return ObjCRuntimeVersions::eObjC_VersionUnknown; |
| } |
| |
| bool IsValidISA(ObjCISA isa) { |
| UpdateISAToDescriptorMap(); |
| return m_isa_to_descriptor.count(isa) > 0; |
| } |
| |
| virtual void UpdateISAToDescriptorMapIfNeeded() = 0; |
| |
| void UpdateISAToDescriptorMap() { |
| if (m_process && m_process->GetStopID() != m_isa_to_descriptor_stop_id) { |
| UpdateISAToDescriptorMapIfNeeded(); |
| } |
| } |
| |
| virtual ObjCISA GetISA(const ConstString &name); |
| |
| virtual ConstString GetActualTypeName(ObjCISA isa); |
| |
| virtual ObjCISA GetParentClass(ObjCISA isa); |
| |
| virtual DeclVendor *GetDeclVendor() { return nullptr; } |
| |
| // Finds the byte offset of the child_type ivar in parent_type. If it can't |
| // find the offset, returns LLDB_INVALID_IVAR_OFFSET. |
| |
| virtual size_t GetByteOffsetForIvar(CompilerType &parent_qual_type, |
| const char *ivar_name); |
| |
| // Given the name of an Objective-C runtime symbol (e.g., ivar offset |
| // symbol), try to determine from the runtime what the value of that symbol |
| // would be. Useful when the underlying binary is stripped. |
| virtual lldb::addr_t LookupRuntimeSymbol(const ConstString &name) { |
| return LLDB_INVALID_ADDRESS; |
| } |
| |
| bool HasNewLiteralsAndIndexing() { |
| if (m_has_new_literals_and_indexing == eLazyBoolCalculate) { |
| if (CalculateHasNewLiteralsAndIndexing()) |
| m_has_new_literals_and_indexing = eLazyBoolYes; |
| else |
| m_has_new_literals_and_indexing = eLazyBoolNo; |
| } |
| |
| return (m_has_new_literals_and_indexing == eLazyBoolYes); |
| } |
| |
| virtual void SymbolsDidLoad(const ModuleList &module_list) { |
| m_negative_complete_class_cache.clear(); |
| } |
| |
| bool GetTypeBitSize(const CompilerType &compiler_type, |
| uint64_t &size) override; |
| |
| protected: |
| //------------------------------------------------------------------ |
| // Classes that inherit from ObjCLanguageRuntime can see and modify these |
| //------------------------------------------------------------------ |
| ObjCLanguageRuntime(Process *process); |
| |
| virtual bool CalculateHasNewLiteralsAndIndexing() { return false; } |
| |
| bool ISAIsCached(ObjCISA isa) const { |
| return m_isa_to_descriptor.find(isa) != m_isa_to_descriptor.end(); |
| } |
| |
| bool AddClass(ObjCISA isa, const ClassDescriptorSP &descriptor_sp) { |
| if (isa != 0) { |
| m_isa_to_descriptor[isa] = descriptor_sp; |
| return true; |
| } |
| return false; |
| } |
| |
| bool AddClass(ObjCISA isa, const ClassDescriptorSP &descriptor_sp, |
| const char *class_name); |
| |
| bool AddClass(ObjCISA isa, const ClassDescriptorSP &descriptor_sp, |
| uint32_t class_name_hash) { |
| if (isa != 0) { |
| m_isa_to_descriptor[isa] = descriptor_sp; |
| m_hash_to_isa_map.insert(std::make_pair(class_name_hash, isa)); |
| return true; |
| } |
| return false; |
| } |
| |
| private: |
| // We keep a map of <Class,Selector>->Implementation so we don't have to call |
| // the resolver function over and over. |
| |
| // FIXME: We need to watch for the loading of Protocols, and flush the cache |
| // for any |
| // class that we see so changed. |
| |
| struct ClassAndSel { |
| ClassAndSel() { |
| sel_addr = LLDB_INVALID_ADDRESS; |
| class_addr = LLDB_INVALID_ADDRESS; |
| } |
| |
| ClassAndSel(lldb::addr_t in_sel_addr, lldb::addr_t in_class_addr) |
| : class_addr(in_class_addr), sel_addr(in_sel_addr) {} |
| |
| bool operator==(const ClassAndSel &rhs) { |
| if (class_addr == rhs.class_addr && sel_addr == rhs.sel_addr) |
| return true; |
| else |
| return false; |
| } |
| |
| bool operator<(const ClassAndSel &rhs) const { |
| if (class_addr < rhs.class_addr) |
| return true; |
| else if (class_addr > rhs.class_addr) |
| return false; |
| else { |
| if (sel_addr < rhs.sel_addr) |
| return true; |
| else |
| return false; |
| } |
| } |
| |
| lldb::addr_t class_addr; |
| lldb::addr_t sel_addr; |
| }; |
| |
| typedef std::map<ClassAndSel, lldb::addr_t> MsgImplMap; |
| typedef std::map<ObjCISA, ClassDescriptorSP> ISAToDescriptorMap; |
| typedef std::multimap<uint32_t, ObjCISA> HashToISAMap; |
| typedef ISAToDescriptorMap::iterator ISAToDescriptorIterator; |
| typedef HashToISAMap::iterator HashToISAIterator; |
| typedef ThreadSafeDenseMap<void *, uint64_t> TypeSizeCache; |
| |
| MsgImplMap m_impl_cache; |
| LazyBool m_has_new_literals_and_indexing; |
| ISAToDescriptorMap m_isa_to_descriptor; |
| HashToISAMap m_hash_to_isa_map; |
| TypeSizeCache m_type_size_cache; |
| |
| protected: |
| uint32_t m_isa_to_descriptor_stop_id; |
| |
| typedef std::map<ConstString, lldb::TypeWP> CompleteClassMap; |
| CompleteClassMap m_complete_class_cache; |
| |
| struct ConstStringSetHelpers { |
| size_t operator()(const ConstString &arg) const // for hashing |
| { |
| return (size_t)arg.GetCString(); |
| } |
| bool operator()(const ConstString &arg1, |
| const ConstString &arg2) const // for equality |
| { |
| return arg1.operator==(arg2); |
| } |
| }; |
| typedef std::unordered_set<ConstString, ConstStringSetHelpers, |
| ConstStringSetHelpers> |
| CompleteClassSet; |
| CompleteClassSet m_negative_complete_class_cache; |
| |
| ISAToDescriptorIterator GetDescriptorIterator(const ConstString &name); |
| |
| friend class ::CommandObjectObjC_ClassTable_Dump; |
| |
| std::pair<ISAToDescriptorIterator, ISAToDescriptorIterator> |
| GetDescriptorIteratorPair(bool update_if_needed = true); |
| |
| void ReadObjCLibraryIfNeeded(const ModuleList &module_list); |
| |
| DISALLOW_COPY_AND_ASSIGN(ObjCLanguageRuntime); |
| }; |
| |
| } // namespace lldb_private |
| |
| #endif // liblldb_ObjCLanguageRuntime_h_ |