| //===-- NSIndexPath.cpp -----------------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| // C Includes |
| // C++ Includes |
| // Other libraries and framework includes |
| // Project includes |
| #include "Cocoa.h" |
| |
| #include "lldb/Core/ValueObject.h" |
| #include "lldb/Core/ValueObjectConstResult.h" |
| #include "lldb/DataFormatters/FormattersHelpers.h" |
| #include "lldb/DataFormatters/TypeSynthetic.h" |
| #include "lldb/Symbol/ClangASTContext.h" |
| #include "lldb/Target/ObjCLanguageRuntime.h" |
| #include "lldb/Target/Process.h" |
| #include "lldb/Target/Target.h" |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| using namespace lldb_private::formatters; |
| |
| class NSIndexPathSyntheticFrontEnd : public SyntheticChildrenFrontEnd |
| { |
| public: |
| NSIndexPathSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : |
| SyntheticChildrenFrontEnd (*valobj_sp.get()), |
| m_ptr_size(0), |
| m_uint_star_type() |
| { |
| m_ptr_size = m_backend.GetTargetSP()->GetArchitecture().GetAddressByteSize(); |
| } |
| |
| ~NSIndexPathSyntheticFrontEnd() override = default; |
| |
| size_t |
| CalculateNumChildren() override |
| { |
| return m_impl.GetNumIndexes(); |
| } |
| |
| lldb::ValueObjectSP |
| GetChildAtIndex(size_t idx) override |
| { |
| return m_impl.GetIndexAtIndex(idx, m_uint_star_type); |
| } |
| |
| bool |
| Update() override |
| { |
| m_impl.Clear(); |
| |
| TypeSystem* type_system = m_backend.GetCompilerType().GetTypeSystem(); |
| if (!type_system) |
| return false; |
| |
| ClangASTContext *ast = m_backend.GetExecutionContextRef().GetTargetSP()->GetScratchClangASTContext(); |
| if (!ast) |
| return false; |
| |
| m_uint_star_type = ast->GetPointerSizedIntType(false); |
| |
| static ConstString g__indexes("_indexes"); |
| static ConstString g__length("_length"); |
| |
| ProcessSP process_sp = m_backend.GetProcessSP(); |
| if (!process_sp) |
| return false; |
| |
| ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); |
| |
| if (!runtime) |
| return false; |
| |
| ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(m_backend)); |
| |
| if (!descriptor.get() || !descriptor->IsValid()) |
| return false; |
| |
| uint64_t info_bits(0),value_bits(0),payload(0); |
| |
| if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits, &payload)) |
| { |
| m_impl.m_inlined.SetIndexes(payload, *process_sp); |
| m_impl.m_mode = Mode::Inlined; |
| } |
| else |
| { |
| ObjCLanguageRuntime::ClassDescriptor::iVarDescriptor _indexes_id; |
| ObjCLanguageRuntime::ClassDescriptor::iVarDescriptor _length_id; |
| |
| bool has_indexes(false),has_length(false); |
| |
| for (size_t x = 0; |
| x < descriptor->GetNumIVars(); |
| x++) |
| { |
| const auto& ivar = descriptor->GetIVarAtIndex(x); |
| if (ivar.m_name == g__indexes) |
| { |
| _indexes_id = ivar; |
| has_indexes = true; |
| } |
| else if (ivar.m_name == g__length) |
| { |
| _length_id = ivar; |
| has_length = true; |
| } |
| |
| if (has_length && has_indexes) |
| break; |
| } |
| |
| if (has_length && has_indexes) |
| { |
| m_impl.m_outsourced.m_indexes = m_backend.GetSyntheticChildAtOffset(_indexes_id.m_offset, |
| m_uint_star_type.GetPointerType(), |
| true).get(); |
| ValueObjectSP length_sp(m_backend.GetSyntheticChildAtOffset(_length_id.m_offset, |
| m_uint_star_type, |
| true)); |
| if (length_sp) |
| { |
| m_impl.m_outsourced.m_count = length_sp->GetValueAsUnsigned(0); |
| if (m_impl.m_outsourced.m_indexes) |
| m_impl.m_mode = Mode::Outsourced; |
| } |
| } |
| } |
| return false; |
| } |
| |
| bool |
| MightHaveChildren() override |
| { |
| if (m_impl.m_mode == Mode::Invalid) |
| return false; |
| return true; |
| } |
| |
| size_t |
| GetIndexOfChildWithName(const ConstString &name) override |
| { |
| const char* item_name = name.GetCString(); |
| uint32_t idx = ExtractIndexFromString(item_name); |
| if (idx < UINT32_MAX && idx >= CalculateNumChildren()) |
| return UINT32_MAX; |
| return idx; |
| } |
| |
| lldb::ValueObjectSP |
| GetSyntheticValue() override |
| { |
| return nullptr; |
| } |
| |
| protected: |
| ObjCLanguageRuntime::ClassDescriptorSP m_descriptor_sp; |
| |
| enum class Mode { |
| Inlined, |
| Outsourced, |
| Invalid |
| }; |
| |
| struct Impl { |
| Mode m_mode; |
| |
| size_t |
| GetNumIndexes () |
| { |
| switch (m_mode) |
| { |
| case Mode::Inlined: |
| return m_inlined.GetNumIndexes(); |
| case Mode::Outsourced: |
| return m_outsourced.m_count; |
| default: |
| return 0; |
| } |
| } |
| |
| lldb::ValueObjectSP |
| GetIndexAtIndex (size_t idx, const CompilerType& desired_type) |
| { |
| if (idx >= GetNumIndexes()) |
| return nullptr; |
| switch (m_mode) |
| { |
| default: return nullptr; |
| case Mode::Inlined: |
| return m_inlined.GetIndexAtIndex (idx, desired_type); |
| case Mode::Outsourced: |
| return m_outsourced.GetIndexAtIndex (idx); |
| } |
| } |
| |
| struct InlinedIndexes { |
| public: |
| void SetIndexes(uint64_t value, Process& p) |
| { |
| m_indexes = value; |
| _lengthForInlinePayload(p.GetAddressByteSize()); |
| m_process = &p; |
| } |
| |
| size_t |
| GetNumIndexes () |
| { |
| return m_count; |
| } |
| |
| lldb::ValueObjectSP |
| GetIndexAtIndex (size_t idx, const CompilerType& desired_type) |
| { |
| std::pair<uint64_t, bool> value(_indexAtPositionForInlinePayload(idx)); |
| if (!value.second) |
| return nullptr; |
| |
| Value v; |
| if (m_ptr_size == 8) |
| { |
| Scalar scalar( (unsigned long long)value.first ); |
| v = Value(scalar); |
| } |
| else |
| { |
| Scalar scalar( (unsigned int)value.first ); |
| v = Value(scalar); |
| } |
| |
| v.SetCompilerType(desired_type); |
| |
| StreamString idx_name; |
| idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); |
| |
| return ValueObjectConstResult::Create(m_process, v, ConstString(idx_name.GetData())); |
| } |
| |
| void |
| Clear () |
| { |
| m_indexes = 0; |
| m_count = 0; |
| m_ptr_size = 0; |
| m_process = nullptr; |
| } |
| |
| private: |
| uint64_t m_indexes; |
| size_t m_count; |
| uint32_t m_ptr_size; |
| Process *m_process; |
| |
| // cfr. Foundation for the details of this code |
| size_t _lengthForInlinePayload(uint32_t ptr_size) { |
| m_ptr_size = ptr_size; |
| if (m_ptr_size == 8) |
| m_count = ((m_indexes >> 3) & 0x7); |
| else |
| m_count = ((m_indexes >> 3) & 0x3); |
| return m_count; |
| } |
| |
| std::pair<uint64_t, bool> |
| _indexAtPositionForInlinePayload(size_t pos) |
| { |
| if (m_ptr_size == 8) |
| { |
| switch (pos) { |
| case 5: return {((m_indexes >> 51) & 0x1ff),true}; |
| case 4: return {((m_indexes >> 42) & 0x1ff),true}; |
| case 3: return {((m_indexes >> 33) & 0x1ff),true}; |
| case 2: return {((m_indexes >> 24) & 0x1ff),true}; |
| case 1: return {((m_indexes >> 15) & 0x1ff),true}; |
| case 0: return {((m_indexes >> 6) & 0x1ff),true}; |
| } |
| } |
| else |
| { |
| switch (pos) { |
| case 2: return {((m_indexes >> 23) & 0x1ff),true}; |
| case 1: return {((m_indexes >> 14) & 0x1ff),true}; |
| case 0: return {((m_indexes >> 5) & 0x1ff),true}; |
| } |
| } |
| return {0,false}; |
| } |
| |
| }; |
| struct OutsourcedIndexes { |
| ValueObject *m_indexes; |
| size_t m_count; |
| |
| lldb::ValueObjectSP |
| GetIndexAtIndex (size_t idx) |
| { |
| if (m_indexes) |
| { |
| ValueObjectSP index_sp(m_indexes->GetSyntheticArrayMember(idx, true)); |
| return index_sp; |
| } |
| return nullptr; |
| } |
| |
| void |
| Clear () |
| { |
| m_indexes = nullptr; |
| m_count = 0; |
| } |
| }; |
| |
| union { |
| struct InlinedIndexes m_inlined; |
| struct OutsourcedIndexes m_outsourced; |
| }; |
| |
| void |
| Clear () |
| { |
| m_mode = Mode::Invalid; |
| m_inlined.Clear(); |
| m_outsourced.Clear(); |
| } |
| } m_impl; |
| |
| uint32_t m_ptr_size; |
| CompilerType m_uint_star_type; |
| }; |
| |
| namespace lldb_private { |
| namespace formatters { |
| |
| SyntheticChildrenFrontEnd* NSIndexPathSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) |
| { |
| if (valobj_sp) |
| return new NSIndexPathSyntheticFrontEnd(valobj_sp); |
| return nullptr; |
| } |
| |
| } // namespace formatters |
| } // namespace lldb_private |