//===-- NSArray.cpp ---------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/lldb-python.h" #include "lldb/DataFormatters/CXXFormatterFunctions.h" #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/Error.h" #include "lldb/Core/Stream.h" #include "lldb/Core/ValueObject.h" #include "lldb/Core/ValueObjectConstResult.h" #include "lldb/Host/Endian.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Target/ObjCLanguageRuntime.h" #include "lldb/Target/Target.h" using namespace lldb; using namespace lldb_private; using namespace lldb_private::formatters; bool lldb_private::formatters::NSArraySummaryProvider (ValueObject& valobj, Stream& stream) { ProcessSP process_sp = valobj.GetProcessSP(); if (!process_sp) return false; ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); if (!runtime) return false; ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); if (!descriptor.get() || !descriptor->IsValid()) return false; uint32_t ptr_size = process_sp->GetAddressByteSize(); lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); if (!valobj_addr) return false; uint64_t value = 0; const char* class_name = descriptor->GetClassName().GetCString(); if (!class_name || !*class_name) return false; if (!strcmp(class_name,"__NSArrayI")) { Error error; value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, ptr_size, 0, error); if (error.Fail()) return false; } else if (!strcmp(class_name,"__NSArrayM")) { Error error; value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, ptr_size, 0, error); if (error.Fail()) return false; } else if (!strcmp(class_name,"__NSCFArray")) { Error error; value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + 2 * ptr_size, ptr_size, 0, error); if (error.Fail()) return false; } else { if (!ExtractValueFromObjCExpression(valobj, "int", "count", value)) return false; } stream.Printf("@\"%" PRIu64 " object%s\"", value, value == 1 ? "" : "s"); return true; } lldb_private::formatters::NSArrayMSyntheticFrontEnd::NSArrayMSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_exe_ctx_ref(), m_ptr_size(8), m_data_32(NULL), m_data_64(NULL) { if (valobj_sp) { clang::ASTContext *ast = valobj_sp->GetClangType().GetASTContext(); if (ast) m_id_type = ClangASTType(ast, ast->ObjCBuiltinIdTy); } } size_t lldb_private::formatters::NSArrayMSyntheticFrontEnd::CalculateNumChildren () { if (m_data_32) return m_data_32->_used; if (m_data_64) return m_data_64->_used; return 0; } lldb::ValueObjectSP lldb_private::formatters::NSArrayMSyntheticFrontEnd::GetChildAtIndex (size_t idx) { if (!m_data_32 && !m_data_64) return lldb::ValueObjectSP(); if (idx >= CalculateNumChildren()) return lldb::ValueObjectSP(); lldb::addr_t object_at_idx = (m_data_32 ? m_data_32->_data : m_data_64->_data); size_t pyhs_idx = idx; pyhs_idx += (m_data_32 ? m_data_32->offset : m_data_64->offset); if ((m_data_32 ? m_data_32->_size : m_data_64->_size) <= pyhs_idx) pyhs_idx -= (m_data_32 ? m_data_32->_size : m_data_64->_size); object_at_idx += (pyhs_idx * m_ptr_size); StreamString idx_name; idx_name.Printf("[%zu]",idx); lldb::ValueObjectSP retval_sp = ValueObject::CreateValueObjectFromAddress(idx_name.GetData(), object_at_idx, m_exe_ctx_ref, m_id_type); m_children.push_back(retval_sp); return retval_sp; } bool lldb_private::formatters::NSArrayMSyntheticFrontEnd::Update() { m_children.clear(); ValueObjectSP valobj_sp = m_backend.GetSP(); m_ptr_size = 0; delete m_data_32; m_data_32 = NULL; delete m_data_64; m_data_64 = NULL; if (!valobj_sp) return false; m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); Error error; error.Clear(); lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); if (!process_sp) return false; m_ptr_size = process_sp->GetAddressByteSize(); uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size; if (m_ptr_size == 4) { m_data_32 = new DataDescriptor_32(); process_sp->ReadMemory (data_location, m_data_32, sizeof(DataDescriptor_32), error); } else { m_data_64 = new DataDescriptor_64(); process_sp->ReadMemory (data_location, m_data_64, sizeof(DataDescriptor_64), error); } if (error.Fail()) return false; return false; } bool lldb_private::formatters::NSArrayMSyntheticFrontEnd::MightHaveChildren () { return true; } size_t lldb_private::formatters::NSArrayMSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) { if (!m_data_32 && !m_data_64) return UINT32_MAX; const char* item_name = name.GetCString(); uint32_t idx = ExtractIndexFromString(item_name); if (idx < UINT32_MAX && idx >= CalculateNumChildren()) return UINT32_MAX; return idx; } lldb_private::formatters::NSArrayMSyntheticFrontEnd::~NSArrayMSyntheticFrontEnd () { delete m_data_32; m_data_32 = NULL; delete m_data_64; m_data_64 = NULL; } lldb_private::formatters::NSArrayISyntheticFrontEnd::NSArrayISyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : SyntheticChildrenFrontEnd (*valobj_sp.get()), m_exe_ctx_ref (), m_ptr_size (8), m_items (0), m_data_ptr (0) { if (valobj_sp) { clang::ASTContext *ast = valobj_sp->GetClangType().GetASTContext(); if (ast) m_id_type = ClangASTType(ast, ast->ObjCBuiltinIdTy); } } lldb_private::formatters::NSArrayISyntheticFrontEnd::~NSArrayISyntheticFrontEnd () { } size_t lldb_private::formatters::NSArrayISyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) { const char* item_name = name.GetCString(); uint32_t idx = ExtractIndexFromString(item_name); if (idx < UINT32_MAX && idx >= CalculateNumChildren()) return UINT32_MAX; return idx; } size_t lldb_private::formatters::NSArrayISyntheticFrontEnd::CalculateNumChildren () { return m_items; } bool lldb_private::formatters::NSArrayISyntheticFrontEnd::Update() { m_ptr_size = 0; m_items = 0; m_data_ptr = 0; m_children.clear(); ValueObjectSP valobj_sp = m_backend.GetSP(); if (!valobj_sp) return false; m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); Error error; error.Clear(); lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); if (!process_sp) return false; m_ptr_size = process_sp->GetAddressByteSize(); uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size; m_items = process_sp->ReadPointerFromMemory(data_location, error); if (error.Fail()) return false; m_data_ptr = data_location+m_ptr_size; return false; } bool lldb_private::formatters::NSArrayISyntheticFrontEnd::MightHaveChildren () { return true; } lldb::ValueObjectSP lldb_private::formatters::NSArrayISyntheticFrontEnd::GetChildAtIndex (size_t idx) { if (idx >= CalculateNumChildren()) return lldb::ValueObjectSP(); lldb::addr_t object_at_idx = m_data_ptr; object_at_idx += (idx * m_ptr_size); ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); if (!process_sp) return lldb::ValueObjectSP(); Error error; if (error.Fail()) return lldb::ValueObjectSP(); StreamString idx_name; idx_name.Printf("[%zu]",idx); lldb::ValueObjectSP retval_sp = ValueObject::CreateValueObjectFromAddress(idx_name.GetData(), object_at_idx, m_exe_ctx_ref, m_id_type); m_children.push_back(retval_sp); return retval_sp; } SyntheticChildrenFrontEnd* lldb_private::formatters::NSArraySyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) { lldb::ProcessSP process_sp (valobj_sp->GetProcessSP()); if (!process_sp) return NULL; ObjCLanguageRuntime *runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); if (!runtime) return NULL; if (!valobj_sp->IsPointerType()) { Error error; valobj_sp = valobj_sp->AddressOf(error); if (error.Fail() || !valobj_sp) return NULL; } ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(*valobj_sp.get())); if (!descriptor.get() || !descriptor->IsValid()) return NULL; const char* class_name = descriptor->GetClassName().GetCString(); if (!class_name || !*class_name) return NULL; if (!strcmp(class_name,"__NSArrayI")) { return (new NSArrayISyntheticFrontEnd(valobj_sp)); } else if (!strcmp(class_name,"__NSArrayM")) { return (new NSArrayMSyntheticFrontEnd(valobj_sp)); } else { return (new NSArrayCodeRunningSyntheticFrontEnd(valobj_sp)); } } lldb_private::formatters::NSArrayCodeRunningSyntheticFrontEnd::NSArrayCodeRunningSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : SyntheticChildrenFrontEnd(*valobj_sp.get()) {} size_t lldb_private::formatters::NSArrayCodeRunningSyntheticFrontEnd::CalculateNumChildren () { uint64_t count = 0; if (ExtractValueFromObjCExpression(m_backend, "int", "count", count)) return count; return 0; } lldb::ValueObjectSP lldb_private::formatters::NSArrayCodeRunningSyntheticFrontEnd::GetChildAtIndex (size_t idx) { StreamString idx_name; idx_name.Printf("[%zu]",idx); lldb::ValueObjectSP valobj_sp = CallSelectorOnObject(m_backend,"id","objectAtIndex:",idx); if (valobj_sp) valobj_sp->SetName(ConstString(idx_name.GetData())); return valobj_sp; } bool lldb_private::formatters::NSArrayCodeRunningSyntheticFrontEnd::Update() { return false; } bool lldb_private::formatters::NSArrayCodeRunningSyntheticFrontEnd::MightHaveChildren () { return true; } size_t lldb_private::formatters::NSArrayCodeRunningSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) { return 0; } lldb_private::formatters::NSArrayCodeRunningSyntheticFrontEnd::~NSArrayCodeRunningSyntheticFrontEnd () {}