forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			444 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			444 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===-- ObjCLanguageRuntime.cpp ---------------------------------*- C++ -*-===//
 | |
| //
 | |
| //                     The LLVM Compiler Infrastructure
 | |
| //
 | |
| // This file is distributed under the University of Illinois Open Source
 | |
| // License. See LICENSE.TXT for details.
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| #include "clang/AST/Type.h"
 | |
| 
 | |
| #include "lldb/Core/Log.h"
 | |
| #include "lldb/Core/MappedHash.h"
 | |
| #include "lldb/Core/Module.h"
 | |
| #include "lldb/Core/PluginManager.h"
 | |
| #include "lldb/Core/Timer.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/Target/ObjCLanguageRuntime.h"
 | |
| #include "lldb/Target/Target.h"
 | |
| 
 | |
| #include "llvm/ADT/StringRef.h"
 | |
| 
 | |
| using namespace lldb;
 | |
| using namespace lldb_private;
 | |
| 
 | |
| //----------------------------------------------------------------------
 | |
| // 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::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(MappedHash::HashStringUsingDJB(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 SymbolContext null_sc;
 | |
|         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 (null_sc,
 | |
|                                                          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(const ConstString &name)
 | |
| {
 | |
|     ISAToDescriptorIterator pos = GetDescriptorIterator (name);
 | |
|     if (pos != m_isa_to_descriptor.end())
 | |
|         return pos->first;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| ObjCLanguageRuntime::ISAToDescriptorIterator
 | |
| ObjCLanguageRuntime::GetDescriptorIterator (const 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 = MappedHash::HashStringUsingDJB (name.GetCString());
 | |
|             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 (const 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)
 | |
|             {
 | |
|                 Error 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_ap)
 | |
|         return RealizeType(*m_scratch_ast_ctx_ap, 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;
 | |
| }
 | |
| 
 | |
| //------------------------------------------------------------------
 | |
| // 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)
 | |
| {
 | |
| }
 | |
| 
 | |
| Error
 | |
| ObjCLanguageRuntime::ObjCExceptionPrecondition::ConfigurePrecondition(Args &args)
 | |
| {
 | |
|     Error error;
 | |
|     if (args.GetArgumentCount() > 0)
 | |
|         error.SetErrorString("The ObjC Exception breakpoint doesn't support extra options.");
 | |
|     return error;
 | |
| }
 |