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