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 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 auto type_system = m_backend.GetCompilerType().GetTypeSystem(); 53 if (!type_system) 54 return false; 55 56 auto 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 214 private: 215 uint64_t m_indexes = 0; 216 size_t m_count = 0; 217 uint32_t m_ptr_size = 0; 218 Process *m_process = nullptr; 219 220 // cfr. Foundation for the details of this code 221 size_t _lengthForInlinePayload(uint32_t ptr_size) { 222 m_ptr_size = ptr_size; 223 if (m_ptr_size == 8) 224 m_count = ((m_indexes >> 3) & 0x7); 225 else 226 m_count = ((m_indexes >> 3) & 0x3); 227 return m_count; 228 } 229 230 std::pair<uint64_t, bool> _indexAtPositionForInlinePayload(size_t pos) { 231 static const uint64_t PACKED_INDEX_MASK = ((1 << 13) - 1); 232 if (m_ptr_size == 8) { 233 switch (pos) { 234 case 3: 235 case 2: 236 case 1: 237 case 0: 238 return {(m_indexes >> PACKED_INDEX_SHIFT_64(pos)) & 239 PACKED_INDEX_MASK, 240 true}; 241 default: 242 return {0, false}; 243 } 244 } else { 245 switch (pos) { 246 case 0: 247 case 1: 248 return {(m_indexes >> PACKED_INDEX_SHIFT_32(pos)) & 249 PACKED_INDEX_MASK, 250 true}; 251 default: 252 return {0, false}; 253 } 254 } 255 return {0, false}; 256 } 257 }; 258 259 struct OutsourcedIndexes { 260 lldb::ValueObjectSP GetIndexAtIndex(size_t idx) { 261 if (m_indexes) { 262 ValueObjectSP index_sp(m_indexes->GetSyntheticArrayMember(idx, true)); 263 return index_sp; 264 } 265 return nullptr; 266 } 267 268 void Clear() { 269 m_indexes = nullptr; 270 m_count = 0; 271 } 272 273 OutsourcedIndexes() {} 274 275 ValueObject *m_indexes = nullptr; 276 size_t m_count = 0; 277 }; 278 279 union { 280 struct InlinedIndexes m_inlined; 281 struct OutsourcedIndexes m_outsourced; 282 }; 283 284 void Clear() { 285 switch (m_mode) { 286 case Mode::Inlined: 287 m_inlined.Clear(); 288 break; 289 case Mode::Outsourced: 290 m_outsourced.Clear(); 291 break; 292 case Mode::Invalid: 293 break; 294 } 295 m_mode = Mode::Invalid; 296 } 297 298 Impl() {} 299 300 Mode m_mode = Mode::Invalid; 301 } m_impl; 302 303 uint32_t m_ptr_size = 0; 304 CompilerType m_uint_star_type; 305 }; 306 307 namespace lldb_private { 308 namespace formatters { 309 310 SyntheticChildrenFrontEnd * 311 NSIndexPathSyntheticFrontEndCreator(CXXSyntheticChildren *, 312 lldb::ValueObjectSP valobj_sp) { 313 if (valobj_sp) 314 return new NSIndexPathSyntheticFrontEnd(valobj_sp); 315 return nullptr; 316 } 317 318 } // namespace formatters 319 } // namespace lldb_private 320