mirror of
https://github.com/opnsense/src.git
synced 2026-02-28 20:30:57 -05:00
(A number of files not required for the FreeBSD build have been removed.) Sponsored by: DARPA, AFRL
604 lines
18 KiB
C++
604 lines
18 KiB
C++
//===-- 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 <unordered_set>
|
|
|
|
// 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<ConstString> &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<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 ()
|
|
{
|
|
}
|
|
|
|
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 <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)
|
|
{
|
|
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:
|
|
/// <empty>
|
|
/// <empty>
|
|
/// "-[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:
|
|
/// <empty>
|
|
/// <empty>
|
|
/// "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 <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;
|
|
|
|
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<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);
|
|
|
|
DISALLOW_COPY_AND_ASSIGN (ObjCLanguageRuntime);
|
|
};
|
|
|
|
} // namespace lldb_private
|
|
|
|
#endif // liblldb_ObjCLanguageRuntime_h_
|