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