| //===-- NSIndexPath.cpp ---------------------------------------------------===// | 
 | // | 
 | // 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 "Cocoa.h" | 
 |  | 
 | #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" | 
 | #include "lldb/Core/ValueObject.h" | 
 | #include "lldb/Core/ValueObjectConstResult.h" | 
 | #include "lldb/DataFormatters/FormattersHelpers.h" | 
 | #include "lldb/DataFormatters/TypeSynthetic.h" | 
 | #include "lldb/Target/Process.h" | 
 | #include "lldb/Target/Target.h" | 
 |  | 
 | #include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h" | 
 | using namespace lldb; | 
 | using namespace lldb_private; | 
 | using namespace lldb_private::formatters; | 
 |  | 
 | static constexpr size_t PACKED_INDEX_SHIFT_64(size_t i) { | 
 |   return (60 - (13 * (4 - i))); | 
 | } | 
 |  | 
 | static constexpr size_t PACKED_INDEX_SHIFT_32(size_t i) { | 
 |   return (32 - (13 * (2 - i))); | 
 | } | 
 |  | 
 | class NSIndexPathSyntheticFrontEnd : public SyntheticChildrenFrontEnd { | 
 | public: | 
 |   NSIndexPathSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) | 
 |       : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_descriptor_sp(nullptr), | 
 |         m_impl(), 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; | 
 |  | 
 |     TypeSystemClang *ast = ScratchTypeSystemClang::GetForTarget( | 
 |         *m_backend.GetExecutionContextRef().GetTargetSP()); | 
 |     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::Get(*process_sp); | 
 |  | 
 |     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 { return m_impl.m_mode != Mode::Invalid; } | 
 |  | 
 |   size_t GetIndexOfChildWithName(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 { | 
 |     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) { | 
 |         if (!m_process) | 
 |           return nullptr; | 
 |  | 
 |         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.GetString())); | 
 |       } | 
 |  | 
 |       void Clear() { | 
 |         m_indexes = 0; | 
 |         m_count = 0; | 
 |         m_ptr_size = 0; | 
 |         m_process = nullptr; | 
 |       } | 
 |  | 
 |       InlinedIndexes() | 
 |           : 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) { | 
 |         static const uint64_t PACKED_INDEX_MASK = ((1 << 13) - 1); | 
 |         if (m_ptr_size == 8) { | 
 |           switch (pos) { | 
 |           case 3: | 
 |           case 2: | 
 |           case 1: | 
 |           case 0: | 
 |             return {(m_indexes >> PACKED_INDEX_SHIFT_64(pos)) & | 
 |                         PACKED_INDEX_MASK, | 
 |                     true}; | 
 |           default: | 
 |             return {0, false}; | 
 |           } | 
 |         } else { | 
 |           switch (pos) { | 
 |           case 0: | 
 |           case 1: | 
 |             return {(m_indexes >> PACKED_INDEX_SHIFT_32(pos)) & | 
 |                         PACKED_INDEX_MASK, | 
 |                     true}; | 
 |           default: | 
 |             return {0, false}; | 
 |           } | 
 |         } | 
 |         return {0, false}; | 
 |       } | 
 |     }; | 
 |  | 
 |     struct OutsourcedIndexes { | 
 |       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; | 
 |       } | 
 |  | 
 |       OutsourcedIndexes() : m_indexes(nullptr), m_count(0) {} | 
 |  | 
 |       ValueObject *m_indexes; | 
 |       size_t m_count; | 
 |     }; | 
 |  | 
 |     union { | 
 |       struct InlinedIndexes m_inlined; | 
 |       struct OutsourcedIndexes m_outsourced; | 
 |     }; | 
 |  | 
 |     void Clear() { | 
 |       m_mode = Mode::Invalid; | 
 |       m_inlined.Clear(); | 
 |       m_outsourced.Clear(); | 
 |     } | 
 |  | 
 |     Impl() : m_mode(Mode::Invalid) {} | 
 |  | 
 |     Mode m_mode; | 
 |   } 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 |