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