diff options
Diffstat (limited to 'source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp')
-rw-r--r-- | source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp | 436 |
1 files changed, 436 insertions, 0 deletions
diff --git a/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp b/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp new file mode 100644 index 0000000000000..631c15c46ce85 --- /dev/null +++ b/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp @@ -0,0 +1,436 @@ +//===-- ObjCLanguageRuntime.cpp ---------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +#include "clang/AST/Type.h" + +#include "ObjCLanguageRuntime.h" + +#include "lldb/Core/MappedHash.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Timer.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/DJB.h" + +using namespace lldb; +using namespace lldb_private; + +char ObjCLanguageRuntime::ID = 0; + +// Destructor +ObjCLanguageRuntime::~ObjCLanguageRuntime() {} + +ObjCLanguageRuntime::ObjCLanguageRuntime(Process *process) + : LanguageRuntime(process), m_impl_cache(), + m_has_new_literals_and_indexing(eLazyBoolCalculate), + m_isa_to_descriptor(), m_hash_to_isa_map(), m_type_size_cache(), + m_isa_to_descriptor_stop_id(UINT32_MAX), m_complete_class_cache(), + m_negative_complete_class_cache() {} + +bool ObjCLanguageRuntime::IsWhitelistedRuntimeValue(ConstString name) { + static ConstString g_self = ConstString("self"); + static ConstString g_cmd = ConstString("_cmd"); + return name == g_self || name == g_cmd; +} + +bool ObjCLanguageRuntime::AddClass(ObjCISA isa, + const ClassDescriptorSP &descriptor_sp, + const char *class_name) { + if (isa != 0) { + m_isa_to_descriptor[isa] = descriptor_sp; + // class_name is assumed to be valid + m_hash_to_isa_map.insert(std::make_pair(llvm::djbHash(class_name), isa)); + return true; + } + return false; +} + +void ObjCLanguageRuntime::AddToMethodCache(lldb::addr_t class_addr, + lldb::addr_t selector, + lldb::addr_t impl_addr) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + if (log) { + log->Printf("Caching: class 0x%" PRIx64 " selector 0x%" PRIx64 + " implementation 0x%" PRIx64 ".", + class_addr, selector, impl_addr); + } + m_impl_cache.insert(std::pair<ClassAndSel, lldb::addr_t>( + ClassAndSel(class_addr, selector), impl_addr)); +} + +lldb::addr_t ObjCLanguageRuntime::LookupInMethodCache(lldb::addr_t class_addr, + lldb::addr_t selector) { + MsgImplMap::iterator pos, end = m_impl_cache.end(); + pos = m_impl_cache.find(ClassAndSel(class_addr, selector)); + if (pos != end) + return (*pos).second; + return LLDB_INVALID_ADDRESS; +} + +lldb::TypeSP +ObjCLanguageRuntime::LookupInCompleteClassCache(ConstString &name) { + CompleteClassMap::iterator complete_class_iter = + m_complete_class_cache.find(name); + + if (complete_class_iter != m_complete_class_cache.end()) { + // Check the weak pointer to make sure the type hasn't been unloaded + TypeSP complete_type_sp(complete_class_iter->second.lock()); + + if (complete_type_sp) + return complete_type_sp; + else + m_complete_class_cache.erase(name); + } + + if (m_negative_complete_class_cache.count(name) > 0) + return TypeSP(); + + const ModuleList &modules = m_process->GetTarget().GetImages(); + + SymbolContextList sc_list; + const size_t matching_symbols = + modules.FindSymbolsWithNameAndType(name, eSymbolTypeObjCClass, sc_list); + + if (matching_symbols) { + SymbolContext sc; + + sc_list.GetContextAtIndex(0, sc); + + ModuleSP module_sp(sc.module_sp); + + if (!module_sp) + return TypeSP(); + + const bool exact_match = true; + const uint32_t max_matches = UINT32_MAX; + TypeList types; + + llvm::DenseSet<SymbolFile *> searched_symbol_files; + const uint32_t num_types = module_sp->FindTypes( + name, exact_match, max_matches, searched_symbol_files, types); + + if (num_types) { + uint32_t i; + for (i = 0; i < num_types; ++i) { + TypeSP type_sp(types.GetTypeAtIndex(i)); + + if (ClangASTContext::IsObjCObjectOrInterfaceType( + type_sp->GetForwardCompilerType())) { + if (type_sp->IsCompleteObjCClass()) { + m_complete_class_cache[name] = type_sp; + return type_sp; + } + } + } + } + } + m_negative_complete_class_cache.insert(name); + return TypeSP(); +} + +size_t ObjCLanguageRuntime::GetByteOffsetForIvar(CompilerType &parent_qual_type, + const char *ivar_name) { + return LLDB_INVALID_IVAR_OFFSET; +} + +bool ObjCLanguageRuntime::ClassDescriptor::IsPointerValid( + lldb::addr_t value, uint32_t ptr_size, bool allow_NULLs, bool allow_tagged, + bool check_version_specific) const { + if (!value) + return allow_NULLs; + if ((value % 2) == 1 && allow_tagged) + return true; + if ((value % ptr_size) == 0) + return (check_version_specific ? CheckPointer(value, ptr_size) : true); + else + return false; +} + +ObjCLanguageRuntime::ObjCISA +ObjCLanguageRuntime::GetISA(ConstString name) { + ISAToDescriptorIterator pos = GetDescriptorIterator(name); + if (pos != m_isa_to_descriptor.end()) + return pos->first; + return 0; +} + +ObjCLanguageRuntime::ISAToDescriptorIterator +ObjCLanguageRuntime::GetDescriptorIterator(ConstString name) { + ISAToDescriptorIterator end = m_isa_to_descriptor.end(); + + if (name) { + UpdateISAToDescriptorMap(); + if (m_hash_to_isa_map.empty()) { + // No name hashes were provided, we need to just linearly power through + // the names and find a match + for (ISAToDescriptorIterator pos = m_isa_to_descriptor.begin(); + pos != end; ++pos) { + if (pos->second->GetClassName() == name) + return pos; + } + } else { + // Name hashes were provided, so use them to efficiently lookup name to + // isa/descriptor + const uint32_t name_hash = llvm::djbHash(name.GetStringRef()); + std::pair<HashToISAIterator, HashToISAIterator> range = + m_hash_to_isa_map.equal_range(name_hash); + for (HashToISAIterator range_pos = range.first; range_pos != range.second; + ++range_pos) { + ISAToDescriptorIterator pos = + m_isa_to_descriptor.find(range_pos->second); + if (pos != m_isa_to_descriptor.end()) { + if (pos->second->GetClassName() == name) + return pos; + } + } + } + } + return end; +} + +std::pair<ObjCLanguageRuntime::ISAToDescriptorIterator, + ObjCLanguageRuntime::ISAToDescriptorIterator> +ObjCLanguageRuntime::GetDescriptorIteratorPair(bool update_if_needed) { + if (update_if_needed) + UpdateISAToDescriptorMapIfNeeded(); + + return std::pair<ObjCLanguageRuntime::ISAToDescriptorIterator, + ObjCLanguageRuntime::ISAToDescriptorIterator>( + m_isa_to_descriptor.begin(), m_isa_to_descriptor.end()); +} + +ObjCLanguageRuntime::ObjCISA +ObjCLanguageRuntime::GetParentClass(ObjCLanguageRuntime::ObjCISA isa) { + ClassDescriptorSP objc_class_sp(GetClassDescriptorFromISA(isa)); + if (objc_class_sp) { + ClassDescriptorSP objc_super_class_sp(objc_class_sp->GetSuperclass()); + if (objc_super_class_sp) + return objc_super_class_sp->GetISA(); + } + return 0; +} + +ConstString +ObjCLanguageRuntime::GetActualTypeName(ObjCLanguageRuntime::ObjCISA isa) { + ClassDescriptorSP objc_class_sp(GetNonKVOClassDescriptor(isa)); + if (objc_class_sp) + return objc_class_sp->GetClassName(); + return ConstString(); +} + +ObjCLanguageRuntime::ClassDescriptorSP +ObjCLanguageRuntime::GetClassDescriptorFromClassName( + ConstString class_name) { + ISAToDescriptorIterator pos = GetDescriptorIterator(class_name); + if (pos != m_isa_to_descriptor.end()) + return pos->second; + return ClassDescriptorSP(); +} + +ObjCLanguageRuntime::ClassDescriptorSP +ObjCLanguageRuntime::GetClassDescriptor(ValueObject &valobj) { + ClassDescriptorSP objc_class_sp; + // if we get an invalid VO (which might still happen when playing around with + // pointers returned by the expression parser, don't consider this a valid + // ObjC object) + if (valobj.GetCompilerType().IsValid()) { + addr_t isa_pointer = valobj.GetPointerValue(); + if (isa_pointer != LLDB_INVALID_ADDRESS) { + ExecutionContext exe_ctx(valobj.GetExecutionContextRef()); + + Process *process = exe_ctx.GetProcessPtr(); + if (process) { + Status error; + ObjCISA isa = process->ReadPointerFromMemory(isa_pointer, error); + if (isa != LLDB_INVALID_ADDRESS) + objc_class_sp = GetClassDescriptorFromISA(isa); + } + } + } + return objc_class_sp; +} + +ObjCLanguageRuntime::ClassDescriptorSP +ObjCLanguageRuntime::GetNonKVOClassDescriptor(ValueObject &valobj) { + ObjCLanguageRuntime::ClassDescriptorSP objc_class_sp( + GetClassDescriptor(valobj)); + if (objc_class_sp) { + if (!objc_class_sp->IsKVO()) + return objc_class_sp; + + ClassDescriptorSP non_kvo_objc_class_sp(objc_class_sp->GetSuperclass()); + if (non_kvo_objc_class_sp && non_kvo_objc_class_sp->IsValid()) + return non_kvo_objc_class_sp; + } + return ClassDescriptorSP(); +} + +ObjCLanguageRuntime::ClassDescriptorSP +ObjCLanguageRuntime::GetClassDescriptorFromISA(ObjCISA isa) { + if (isa) { + UpdateISAToDescriptorMap(); + ObjCLanguageRuntime::ISAToDescriptorIterator pos = + m_isa_to_descriptor.find(isa); + if (pos != m_isa_to_descriptor.end()) + return pos->second; + } + return ClassDescriptorSP(); +} + +ObjCLanguageRuntime::ClassDescriptorSP +ObjCLanguageRuntime::GetNonKVOClassDescriptor(ObjCISA isa) { + if (isa) { + ClassDescriptorSP objc_class_sp = GetClassDescriptorFromISA(isa); + if (objc_class_sp && objc_class_sp->IsValid()) { + if (!objc_class_sp->IsKVO()) + return objc_class_sp; + + ClassDescriptorSP non_kvo_objc_class_sp(objc_class_sp->GetSuperclass()); + if (non_kvo_objc_class_sp && non_kvo_objc_class_sp->IsValid()) + return non_kvo_objc_class_sp; + } + } + return ClassDescriptorSP(); +} + +CompilerType +ObjCLanguageRuntime::EncodingToType::RealizeType(const char *name, + bool for_expression) { + if (m_scratch_ast_ctx_up) + return RealizeType(*m_scratch_ast_ctx_up, name, for_expression); + return CompilerType(); +} + +CompilerType ObjCLanguageRuntime::EncodingToType::RealizeType( + ClangASTContext &ast_ctx, const char *name, bool for_expression) { + clang::ASTContext *clang_ast = ast_ctx.getASTContext(); + if (!clang_ast) + return CompilerType(); + return RealizeType(*clang_ast, name, for_expression); +} + +ObjCLanguageRuntime::EncodingToType::~EncodingToType() {} + +ObjCLanguageRuntime::EncodingToTypeSP ObjCLanguageRuntime::GetEncodingToType() { + return nullptr; +} + +bool ObjCLanguageRuntime::GetTypeBitSize(const CompilerType &compiler_type, + uint64_t &size) { + void *opaque_ptr = compiler_type.GetOpaqueQualType(); + size = m_type_size_cache.Lookup(opaque_ptr); + // an ObjC object will at least have an ISA, so 0 is definitely not OK + if (size > 0) + return true; + + ClassDescriptorSP class_descriptor_sp = + GetClassDescriptorFromClassName(compiler_type.GetTypeName()); + if (!class_descriptor_sp) + return false; + + int32_t max_offset = INT32_MIN; + uint64_t sizeof_max = 0; + bool found = false; + + for (size_t idx = 0; idx < class_descriptor_sp->GetNumIVars(); idx++) { + const auto &ivar = class_descriptor_sp->GetIVarAtIndex(idx); + int32_t cur_offset = ivar.m_offset; + if (cur_offset > max_offset) { + max_offset = cur_offset; + sizeof_max = ivar.m_size; + found = true; + } + } + + size = 8 * (max_offset + sizeof_max); + if (found) + m_type_size_cache.Insert(opaque_ptr, size); + + return found; +} + +lldb::BreakpointPreconditionSP +ObjCLanguageRuntime::GetBreakpointExceptionPrecondition(LanguageType language, + bool throw_bp) { + if (language != eLanguageTypeObjC) + return lldb::BreakpointPreconditionSP(); + if (!throw_bp) + return lldb::BreakpointPreconditionSP(); + BreakpointPreconditionSP precondition_sp( + new ObjCLanguageRuntime::ObjCExceptionPrecondition()); + return precondition_sp; +} + +// Exception breakpoint Precondition class for ObjC: +void ObjCLanguageRuntime::ObjCExceptionPrecondition::AddClassName( + const char *class_name) { + m_class_names.insert(class_name); +} + +ObjCLanguageRuntime::ObjCExceptionPrecondition::ObjCExceptionPrecondition() {} + +bool ObjCLanguageRuntime::ObjCExceptionPrecondition::EvaluatePrecondition( + StoppointCallbackContext &context) { + return true; +} + +void ObjCLanguageRuntime::ObjCExceptionPrecondition::GetDescription( + Stream &stream, lldb::DescriptionLevel level) {} + +Status ObjCLanguageRuntime::ObjCExceptionPrecondition::ConfigurePrecondition( + Args &args) { + Status error; + if (args.GetArgumentCount() > 0) + error.SetErrorString( + "The ObjC Exception breakpoint doesn't support extra options."); + return error; +} + +llvm::Optional<CompilerType> +ObjCLanguageRuntime::GetRuntimeType(CompilerType base_type) { + CompilerType class_type; + bool is_pointer_type = false; + + if (ClangASTContext::IsObjCObjectPointerType(base_type, &class_type)) + is_pointer_type = true; + else if (ClangASTContext::IsObjCObjectOrInterfaceType(base_type)) + class_type = base_type; + else + return llvm::None; + + if (!class_type) + return llvm::None; + + ConstString class_name(class_type.GetConstTypeName()); + if (!class_name) + return llvm::None; + + TypeSP complete_objc_class_type_sp = LookupInCompleteClassCache(class_name); + if (!complete_objc_class_type_sp) + return llvm::None; + + CompilerType complete_class( + complete_objc_class_type_sp->GetFullCompilerType()); + if (complete_class.GetCompleteType()) { + if (is_pointer_type) + return complete_class.GetPointerType(); + else + return complete_class; + } + + return llvm::None; +} |