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