//===-- 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 #include #include // Other libraries and framework includes // Project includes #include "lldb/lldb-private.h" #include "lldb/Core/PluginInterface.h" #include "lldb/Symbol/Type.h" #include "lldb/Symbol/TypeVendor.h" #include "lldb/Target/LanguageRuntime.h" namespace lldb_private { class ClangUtilityFunction; class ObjCLanguageRuntime : public LanguageRuntime { public: class MethodName { public: enum Type { eTypeUnspecified, eTypeClassMethod, eTypeInstanceMethod }; MethodName () : m_full(), m_class(), m_category(), m_selector(), m_type (eTypeUnspecified), m_category_is_valid (false) { } MethodName (const char *name, bool strict) : m_full(), m_class(), m_category(), m_selector(), m_type (eTypeUnspecified), m_category_is_valid (false) { SetName (name, strict); } void Clear(); bool IsValid (bool strict) const { // If "strict" is true, the name must have everything specified including // the leading "+" or "-" on the method name if (strict && m_type == eTypeUnspecified) return false; // Other than that, m_full will only be filled in if the objective C // name is valid. return (bool)m_full; } bool HasCategory() { return (bool)GetCategory(); } Type GetType () const { return m_type; } const ConstString & GetFullName () const { return m_full; } ConstString GetFullNameWithoutCategory (bool empty_if_no_category); bool SetName (const char *name, bool strict); const ConstString & GetClassName (); const ConstString & GetClassNameWithCategory (); const ConstString & GetCategory (); const ConstString & GetSelector (); // Get all possible names for a method. Examples: // If name is "+[NSString(my_additions) myStringWithCString:]" // names[0] => "+[NSString(my_additions) myStringWithCString:]" // names[1] => "+[NSString myStringWithCString:]" // If name is specified without the leading '+' or '-' like "[NSString(my_additions) myStringWithCString:]" // names[0] => "+[NSString(my_additions) myStringWithCString:]" // names[1] => "-[NSString(my_additions) myStringWithCString:]" // names[2] => "+[NSString myStringWithCString:]" // names[3] => "-[NSString myStringWithCString:]" size_t GetFullNames (std::vector &names, bool append); protected: ConstString m_full; // Full name: "+[NSString(my_additions) myStringWithCString:]" ConstString m_class; // Class name: "NSString" ConstString m_class_category; // Class with category: "NSString(my_additions)" ConstString m_category; // Category: "my_additions" ConstString m_selector; // Selector: "myStringWithCString:" Type m_type; bool m_category_is_valid; }; typedef lldb::addr_t ObjCISA; class ClassDescriptor; typedef std::shared_ptr 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 () { } virtual ConstString GetClassName () = 0; virtual ClassDescriptorSP GetSuperclass () = 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 = NULL, uint64_t* value_bits = NULL, uint64_t* payload = NULL) = 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 const &superclass_func, std::function const &instance_method_func, std::function const &class_method_func, std::function const &ivar_func) { return false; } lldb::TypeSP GetType () { return m_type_wp.lock(); } void SetType (const lldb::TypeSP &type_sp) { m_type_wp = type_sp; } 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; }; 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); virtual ~ObjCLanguageRuntime(); virtual lldb::LanguageType GetLanguageType () const { 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 ClangUtilityFunction * CreateObjectChecker (const char *) = 0; virtual ObjCRuntimeVersions GetRuntimeVersion () { return 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 TypeVendor * GetTypeVendor() { return NULL; } // 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 (ClangASTType &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; } //------------------------------------------------------------------ /// Chop up an objective C function prototype. /// /// Chop up an objective C function fullname and optionally fill in /// any non-NULL ConstString objects. If a ConstString * is NULL, /// then this name doesn't get filled in /// /// @param[in] name /// A fully specified objective C function name. The string might /// contain a category and it includes the leading "+" or "-" and /// the square brackets, no types for the arguments, just the plain /// selector. A few examples: /// "-[NSStringDrawingContext init]" /// "-[NSStringDrawingContext addString:inRect:]" /// "-[NSString(NSStringDrawing) sizeWithAttributes:]" /// "+[NSString(NSStringDrawing) usesFontLeading]" /// /// @param[out] class_name /// If non-NULL, this string will be filled in with the class /// name including the category. The examples above would return: /// "NSStringDrawingContext" /// "NSStringDrawingContext" /// "NSString(NSStringDrawing)" /// "NSString(NSStringDrawing)" /// /// @param[out] selector_name /// If non-NULL, this string will be filled in with the selector /// name. The examples above would return: /// "init" /// "addString:inRect:" /// "sizeWithAttributes:" /// "usesFontLeading" /// /// @param[out] name_sans_category /// If non-NULL, this string will be filled in with the class /// name _without_ the category. If there is no category, and empty /// string will be returned (as the result would be normally returned /// in the "class_name" argument). The examples above would return: /// /// /// "-[NSString sizeWithAttributes:]" /// "+[NSString usesFontLeading]" /// /// @param[out] class_name_sans_category /// If non-NULL, this string will be filled in with the prototype /// name _without_ the category. If there is no category, and empty /// string will be returned (as this is already the value that was /// passed in). The examples above would return: /// /// /// "NSString" /// "NSString" /// /// @return /// Returns the number of strings that were successfully filled /// in. //------------------------------------------------------------------ // static uint32_t // ParseMethodName (const char *name, // ConstString *class_name, // Class name (with category if there is one) // ConstString *selector_name, // selector only // ConstString *name_sans_category, // full function name with no category (empty if no category) // ConstString *class_name_sans_category);// Class name without category (empty if no category) static bool IsPossibleObjCMethodName (const char *name) { if (!name) return false; bool starts_right = (name[0] == '+' || name[0] == '-') && name[1] == '['; bool ends_right = (name[strlen(name) - 1] == ']'); return (starts_right && ends_right); } static bool IsPossibleObjCSelector (const char *name) { if (!name) return false; if (strchr(name, ':') == NULL) return true; else if (name[strlen(name) - 1] == ':') return true; else return false; } 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(); } 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 ->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 MsgImplMap; typedef std::map ISAToDescriptorMap; typedef std::multimap HashToISAMap; typedef ISAToDescriptorMap::iterator ISAToDescriptorIterator; typedef HashToISAMap::iterator HashToISAIterator; MsgImplMap m_impl_cache; LazyBool m_has_new_literals_and_indexing; ISAToDescriptorMap m_isa_to_descriptor; HashToISAMap m_hash_to_isa_map; protected: uint32_t m_isa_to_descriptor_stop_id; typedef std::map 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 CompleteClassSet; CompleteClassSet m_negative_complete_class_cache; ISAToDescriptorIterator GetDescriptorIterator (const ConstString &name); DISALLOW_COPY_AND_ASSIGN (ObjCLanguageRuntime); }; } // namespace lldb_private #endif // liblldb_ObjCLanguageRuntime_h_