|  | //===-- ObjCLanguageRuntime.h -----------------------------------*- C++ -*-===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #ifndef liblldb_ObjCLanguageRuntime_h_ | 
|  | #define liblldb_ObjCLanguageRuntime_h_ | 
|  |  | 
|  | #include <functional> | 
|  | #include <map> | 
|  | #include <memory> | 
|  | #include <unordered_set> | 
|  |  | 
|  | #include "llvm/Support/Casting.h" | 
|  |  | 
|  | #include "lldb/Breakpoint/BreakpointPrecondition.h" | 
|  | #include "lldb/Core/ClangForward.h" | 
|  | #include "lldb/Core/PluginInterface.h" | 
|  | #include "lldb/Core/ThreadSafeDenseMap.h" | 
|  | #include "lldb/Symbol/CompilerType.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(TypeSystemClang &ast_ctx, const char *name, | 
|  | bool for_expression) = 0; | 
|  | virtual CompilerType RealizeType(const char *name, bool for_expression); | 
|  |  | 
|  | protected: | 
|  | std::unique_ptr<TypeSystemClang> m_scratch_ast_ctx_up; | 
|  | }; | 
|  |  | 
|  | class ObjCExceptionPrecondition : public 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; | 
|  | }; | 
|  |  | 
|  | static lldb::BreakpointPreconditionSP | 
|  | GetBreakpointExceptionPrecondition(lldb::LanguageType language, | 
|  | bool throw_bp); | 
|  |  | 
|  | 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; | 
|  |  | 
|  | static char ID; | 
|  |  | 
|  | bool isA(const void *ClassID) const override { | 
|  | return ClassID == &ID || LanguageRuntime::isA(ClassID); | 
|  | } | 
|  |  | 
|  | static bool classof(const LanguageRuntime *runtime) { | 
|  | return runtime->isA(&ID); | 
|  | } | 
|  |  | 
|  | static ObjCLanguageRuntime *Get(Process &process) { | 
|  | return llvm::cast_or_null<ObjCLanguageRuntime>( | 
|  | process.GetLanguageRuntime(lldb::eLanguageTypeObjC)); | 
|  | } | 
|  |  | 
|  | 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(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; | 
|  |  | 
|  | 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); | 
|  |  | 
|  | llvm::Optional<CompilerType> GetRuntimeType(CompilerType base_type) override; | 
|  |  | 
|  | 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(ConstString name); | 
|  |  | 
|  | virtual ObjCISA GetParentClass(ObjCISA isa); | 
|  |  | 
|  | // 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); | 
|  |  | 
|  | 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); | 
|  | } | 
|  |  | 
|  | void SymbolsDidLoad(const ModuleList &module_list) override { | 
|  | m_negative_complete_class_cache.clear(); | 
|  | } | 
|  |  | 
|  | bool GetTypeBitSize(const CompilerType &compiler_type, | 
|  | uint64_t &size) override; | 
|  |  | 
|  | /// Check whether the name is "self" or "_cmd" and should show up in | 
|  | /// "frame variable". | 
|  | bool IsWhitelistedRuntimeValue(ConstString name) 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()(ConstString arg) const // for hashing | 
|  | { | 
|  | return (size_t)arg.GetCString(); | 
|  | } | 
|  | bool operator()(ConstString arg1, | 
|  | 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(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_ |