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/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 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 = ObjCLanguageRuntime::Get(*process_sp); 72 73 if (!runtime) 74 return false; 75 76 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 77 runtime->GetClassDescriptor(m_backend)); 78 79 if (!descriptor.get() || !descriptor->IsValid()) 80 return false; 81 82 uint64_t info_bits(0), value_bits(0), payload(0); 83 84 if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits, &payload)) { 85 m_impl.m_inlined.SetIndexes(payload, *process_sp); 86 m_impl.m_mode = Mode::Inlined; 87 } else { 88 ObjCLanguageRuntime::ClassDescriptor::iVarDescriptor _indexes_id; 89 ObjCLanguageRuntime::ClassDescriptor::iVarDescriptor _length_id; 90 91 bool has_indexes(false), has_length(false); 92 93 for (size_t x = 0; x < descriptor->GetNumIVars(); x++) { 94 const auto &ivar = descriptor->GetIVarAtIndex(x); 95 if (ivar.m_name == g__indexes) { 96 _indexes_id = ivar; 97 has_indexes = true; 98 } else if (ivar.m_name == g__length) { 99 _length_id = ivar; 100 has_length = true; 101 } 102 103 if (has_length && has_indexes) 104 break; 105 } 106 107 if (has_length && has_indexes) { 108 m_impl.m_outsourced.m_indexes = 109 m_backend 110 .GetSyntheticChildAtOffset(_indexes_id.m_offset, 111 m_uint_star_type.GetPointerType(), 112 true) 113 .get(); 114 ValueObjectSP length_sp(m_backend.GetSyntheticChildAtOffset( 115 _length_id.m_offset, m_uint_star_type, true)); 116 if (length_sp) { 117 m_impl.m_outsourced.m_count = length_sp->GetValueAsUnsigned(0); 118 if (m_impl.m_outsourced.m_indexes) 119 m_impl.m_mode = Mode::Outsourced; 120 } 121 } 122 } 123 return false; 124 } 125 126 bool MightHaveChildren() override { return m_impl.m_mode != Mode::Invalid; } 127 128 size_t GetIndexOfChildWithName(ConstString name) override { 129 const char *item_name = name.GetCString(); 130 uint32_t idx = ExtractIndexFromString(item_name); 131 if (idx < UINT32_MAX && idx >= CalculateNumChildren()) 132 return UINT32_MAX; 133 return idx; 134 } 135 136 lldb::ValueObjectSP GetSyntheticValue() override { return nullptr; } 137 138 protected: 139 ObjCLanguageRuntime::ClassDescriptorSP m_descriptor_sp; 140 141 enum class Mode { Inlined, Outsourced, Invalid }; 142 143 struct Impl { 144 size_t GetNumIndexes() { 145 switch (m_mode) { 146 case Mode::Inlined: 147 return m_inlined.GetNumIndexes(); 148 case Mode::Outsourced: 149 return m_outsourced.m_count; 150 default: 151 return 0; 152 } 153 } 154 155 lldb::ValueObjectSP GetIndexAtIndex(size_t idx, 156 const CompilerType &desired_type) { 157 if (idx >= GetNumIndexes()) 158 return nullptr; 159 switch (m_mode) { 160 default: 161 return nullptr; 162 case Mode::Inlined: 163 return m_inlined.GetIndexAtIndex(idx, desired_type); 164 case Mode::Outsourced: 165 return m_outsourced.GetIndexAtIndex(idx); 166 } 167 } 168 169 struct InlinedIndexes { 170 public: 171 void SetIndexes(uint64_t value, Process &p) { 172 m_indexes = value; 173 _lengthForInlinePayload(p.GetAddressByteSize()); 174 m_process = &p; 175 } 176 177 size_t GetNumIndexes() { return m_count; } 178 179 lldb::ValueObjectSP GetIndexAtIndex(size_t idx, 180 const CompilerType &desired_type) { 181 if (!m_process) 182 return nullptr; 183 184 std::pair<uint64_t, bool> value(_indexAtPositionForInlinePayload(idx)); 185 if (!value.second) 186 return nullptr; 187 188 Value v; 189 if (m_ptr_size == 8) { 190 Scalar scalar((unsigned long long)value.first); 191 v = Value(scalar); 192 } else { 193 Scalar scalar((unsigned int)value.first); 194 v = Value(scalar); 195 } 196 197 v.SetCompilerType(desired_type); 198 199 StreamString idx_name; 200 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); 201 202 return ValueObjectConstResult::Create( 203 m_process, v, ConstString(idx_name.GetString())); 204 } 205 206 void Clear() { 207 m_indexes = 0; 208 m_count = 0; 209 m_ptr_size = 0; 210 m_process = nullptr; 211 } 212 213 InlinedIndexes() 214 : m_indexes(0), m_count(0), m_ptr_size(0), m_process(nullptr) {} 215 216 private: 217 uint64_t m_indexes; 218 size_t m_count; 219 uint32_t m_ptr_size; 220 Process *m_process; 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() : m_indexes(nullptr), m_count(0) {} 276 277 ValueObject *m_indexes; 278 size_t m_count; 279 }; 280 281 union { 282 struct InlinedIndexes m_inlined; 283 struct OutsourcedIndexes m_outsourced; 284 }; 285 286 void Clear() { 287 m_mode = Mode::Invalid; 288 m_inlined.Clear(); 289 m_outsourced.Clear(); 290 } 291 292 Impl() : m_mode(Mode::Invalid) {} 293 294 Mode m_mode; 295 } m_impl; 296 297 uint32_t m_ptr_size; 298 CompilerType m_uint_star_type; 299 }; 300 301 namespace lldb_private { 302 namespace formatters { 303 304 SyntheticChildrenFrontEnd * 305 NSIndexPathSyntheticFrontEndCreator(CXXSyntheticChildren *, 306 lldb::ValueObjectSP valobj_sp) { 307 if (valobj_sp) 308 return new NSIndexPathSyntheticFrontEnd(valobj_sp); 309 return nullptr; 310 } 311 312 } // namespace formatters 313 } // namespace lldb_private 314