1 //===-- NSIndexPath.cpp ---------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "Cocoa.h" 10 11 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" 12 #include "lldb/Core/ValueObject.h" 13 #include "lldb/Core/ValueObjectConstResult.h" 14 #include "lldb/DataFormatters/FormattersHelpers.h" 15 #include "lldb/DataFormatters/TypeSynthetic.h" 16 #include "lldb/Target/Process.h" 17 #include "lldb/Target/Target.h" 18 19 #include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h" 20 using namespace lldb; 21 using namespace lldb_private; 22 using namespace lldb_private::formatters; 23 24 static constexpr size_t PACKED_INDEX_SHIFT_64(size_t i) { 25 return (60 - (13 * (4 - i))); 26 } 27 28 static constexpr size_t PACKED_INDEX_SHIFT_32(size_t i) { 29 return (32 - (13 * (2 - i))); 30 } 31 32 class NSIndexPathSyntheticFrontEnd : public SyntheticChildrenFrontEnd { 33 public: 34 NSIndexPathSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) 35 : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_descriptor_sp(nullptr), 36 m_impl(), m_ptr_size(0), m_uint_star_type() { 37 m_ptr_size = 38 m_backend.GetTargetSP()->GetArchitecture().GetAddressByteSize(); 39 } 40 41 ~NSIndexPathSyntheticFrontEnd() override = default; 42 43 size_t CalculateNumChildren() override { return m_impl.GetNumIndexes(); } 44 45 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override { 46 return m_impl.GetIndexAtIndex(idx, m_uint_star_type); 47 } 48 49 bool Update() override { 50 m_impl.Clear(); 51 52 TypeSystem *type_system = m_backend.GetCompilerType().GetTypeSystem(); 53 if (!type_system) 54 return false; 55 56 TypeSystemClang *ast = ScratchTypeSystemClang::GetForTarget( 57 *m_backend.GetExecutionContextRef().GetTargetSP()); 58 if (!ast) 59 return false; 60 61 m_uint_star_type = ast->GetPointerSizedIntType(false); 62 63 static ConstString g__indexes("_indexes"); 64 static ConstString g__length("_length"); 65 66 ProcessSP process_sp = m_backend.GetProcessSP(); 67 if (!process_sp) 68 return false; 69 70 ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); 71 72 if (!runtime) 73 return false; 74 75 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 76 runtime->GetClassDescriptor(m_backend)); 77 78 if (!descriptor.get() || !descriptor->IsValid()) 79 return false; 80 81 uint64_t info_bits(0), value_bits(0), payload(0); 82 83 if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits, &payload)) { 84 m_impl.m_inlined.SetIndexes(payload, *process_sp); 85 m_impl.m_mode = Mode::Inlined; 86 } else { 87 ObjCLanguageRuntime::ClassDescriptor::iVarDescriptor _indexes_id; 88 ObjCLanguageRuntime::ClassDescriptor::iVarDescriptor _length_id; 89 90 bool has_indexes(false), has_length(false); 91 92 for (size_t x = 0; x < descriptor->GetNumIVars(); x++) { 93 const auto &ivar = descriptor->GetIVarAtIndex(x); 94 if (ivar.m_name == g__indexes) { 95 _indexes_id = ivar; 96 has_indexes = true; 97 } else if (ivar.m_name == g__length) { 98 _length_id = ivar; 99 has_length = true; 100 } 101 102 if (has_length && has_indexes) 103 break; 104 } 105 106 if (has_length && has_indexes) { 107 m_impl.m_outsourced.m_indexes = 108 m_backend 109 .GetSyntheticChildAtOffset(_indexes_id.m_offset, 110 m_uint_star_type.GetPointerType(), 111 true) 112 .get(); 113 ValueObjectSP length_sp(m_backend.GetSyntheticChildAtOffset( 114 _length_id.m_offset, m_uint_star_type, true)); 115 if (length_sp) { 116 m_impl.m_outsourced.m_count = length_sp->GetValueAsUnsigned(0); 117 if (m_impl.m_outsourced.m_indexes) 118 m_impl.m_mode = Mode::Outsourced; 119 } 120 } 121 } 122 return false; 123 } 124 125 bool MightHaveChildren() override { return m_impl.m_mode != Mode::Invalid; } 126 127 size_t GetIndexOfChildWithName(ConstString name) override { 128 const char *item_name = name.GetCString(); 129 uint32_t idx = ExtractIndexFromString(item_name); 130 if (idx < UINT32_MAX && idx >= CalculateNumChildren()) 131 return UINT32_MAX; 132 return idx; 133 } 134 135 lldb::ValueObjectSP GetSyntheticValue() override { return nullptr; } 136 137 protected: 138 ObjCLanguageRuntime::ClassDescriptorSP m_descriptor_sp; 139 140 enum class Mode { Inlined, Outsourced, Invalid }; 141 142 struct Impl { 143 size_t GetNumIndexes() { 144 switch (m_mode) { 145 case Mode::Inlined: 146 return m_inlined.GetNumIndexes(); 147 case Mode::Outsourced: 148 return m_outsourced.m_count; 149 default: 150 return 0; 151 } 152 } 153 154 lldb::ValueObjectSP GetIndexAtIndex(size_t idx, 155 const CompilerType &desired_type) { 156 if (idx >= GetNumIndexes()) 157 return nullptr; 158 switch (m_mode) { 159 default: 160 return nullptr; 161 case Mode::Inlined: 162 return m_inlined.GetIndexAtIndex(idx, desired_type); 163 case Mode::Outsourced: 164 return m_outsourced.GetIndexAtIndex(idx); 165 } 166 } 167 168 struct InlinedIndexes { 169 public: 170 void SetIndexes(uint64_t value, Process &p) { 171 m_indexes = value; 172 _lengthForInlinePayload(p.GetAddressByteSize()); 173 m_process = &p; 174 } 175 176 size_t GetNumIndexes() { return m_count; } 177 178 lldb::ValueObjectSP GetIndexAtIndex(size_t idx, 179 const CompilerType &desired_type) { 180 if (!m_process) 181 return nullptr; 182 183 std::pair<uint64_t, bool> value(_indexAtPositionForInlinePayload(idx)); 184 if (!value.second) 185 return nullptr; 186 187 Value v; 188 if (m_ptr_size == 8) { 189 Scalar scalar((unsigned long long)value.first); 190 v = Value(scalar); 191 } else { 192 Scalar scalar((unsigned int)value.first); 193 v = Value(scalar); 194 } 195 196 v.SetCompilerType(desired_type); 197 198 StreamString idx_name; 199 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); 200 201 return ValueObjectConstResult::Create( 202 m_process, v, ConstString(idx_name.GetString())); 203 } 204 205 void Clear() { 206 m_indexes = 0; 207 m_count = 0; 208 m_ptr_size = 0; 209 m_process = nullptr; 210 } 211 212 InlinedIndexes() 213 : m_indexes(0), m_count(0), m_ptr_size(0), m_process(nullptr) {} 214 215 private: 216 uint64_t m_indexes; 217 size_t m_count; 218 uint32_t m_ptr_size; 219 Process *m_process; 220 221 // cfr. Foundation for the details of this code 222 size_t _lengthForInlinePayload(uint32_t ptr_size) { 223 m_ptr_size = ptr_size; 224 if (m_ptr_size == 8) 225 m_count = ((m_indexes >> 3) & 0x7); 226 else 227 m_count = ((m_indexes >> 3) & 0x3); 228 return m_count; 229 } 230 231 std::pair<uint64_t, bool> _indexAtPositionForInlinePayload(size_t pos) { 232 static const uint64_t PACKED_INDEX_MASK = ((1 << 13) - 1); 233 if (m_ptr_size == 8) { 234 switch (pos) { 235 case 3: 236 case 2: 237 case 1: 238 case 0: 239 return {(m_indexes >> PACKED_INDEX_SHIFT_64(pos)) & 240 PACKED_INDEX_MASK, 241 true}; 242 default: 243 return {0, false}; 244 } 245 } else { 246 switch (pos) { 247 case 0: 248 case 1: 249 return {(m_indexes >> PACKED_INDEX_SHIFT_32(pos)) & 250 PACKED_INDEX_MASK, 251 true}; 252 default: 253 return {0, false}; 254 } 255 } 256 return {0, false}; 257 } 258 }; 259 260 struct OutsourcedIndexes { 261 lldb::ValueObjectSP GetIndexAtIndex(size_t idx) { 262 if (m_indexes) { 263 ValueObjectSP index_sp(m_indexes->GetSyntheticArrayMember(idx, true)); 264 return index_sp; 265 } 266 return nullptr; 267 } 268 269 void Clear() { 270 m_indexes = nullptr; 271 m_count = 0; 272 } 273 274 OutsourcedIndexes() : m_indexes(nullptr), m_count(0) {} 275 276 ValueObject *m_indexes; 277 size_t m_count; 278 }; 279 280 union { 281 struct InlinedIndexes m_inlined; 282 struct OutsourcedIndexes m_outsourced; 283 }; 284 285 void Clear() { 286 m_mode = Mode::Invalid; 287 m_inlined.Clear(); 288 m_outsourced.Clear(); 289 } 290 291 Impl() : m_mode(Mode::Invalid) {} 292 293 Mode m_mode; 294 } m_impl; 295 296 uint32_t m_ptr_size; 297 CompilerType m_uint_star_type; 298 }; 299 300 namespace lldb_private { 301 namespace formatters { 302 303 SyntheticChildrenFrontEnd * 304 NSIndexPathSyntheticFrontEndCreator(CXXSyntheticChildren *, 305 lldb::ValueObjectSP valobj_sp) { 306 if (valobj_sp) 307 return new NSIndexPathSyntheticFrontEnd(valobj_sp); 308 return nullptr; 309 } 310 311 } // namespace formatters 312 } // namespace lldb_private 313