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 { 130 if (m_impl.m_mode == Mode::Invalid) 131 return false; 132 return true; 133 } 134 135 size_t GetIndexOfChildWithName(const ConstString &name) override { 136 const char *item_name = name.GetCString(); 137 uint32_t idx = ExtractIndexFromString(item_name); 138 if (idx < UINT32_MAX && idx >= CalculateNumChildren()) 139 return UINT32_MAX; 140 return idx; 141 } 142 143 lldb::ValueObjectSP GetSyntheticValue() override { return nullptr; } 144 145 protected: 146 ObjCLanguageRuntime::ClassDescriptorSP m_descriptor_sp; 147 148 enum class Mode { Inlined, Outsourced, Invalid }; 149 150 struct Impl { 151 size_t GetNumIndexes() { 152 switch (m_mode) { 153 case Mode::Inlined: 154 return m_inlined.GetNumIndexes(); 155 case Mode::Outsourced: 156 return m_outsourced.m_count; 157 default: 158 return 0; 159 } 160 } 161 162 lldb::ValueObjectSP GetIndexAtIndex(size_t idx, 163 const CompilerType &desired_type) { 164 if (idx >= GetNumIndexes()) 165 return nullptr; 166 switch (m_mode) { 167 default: 168 return nullptr; 169 case Mode::Inlined: 170 return m_inlined.GetIndexAtIndex(idx, desired_type); 171 case Mode::Outsourced: 172 return m_outsourced.GetIndexAtIndex(idx); 173 } 174 } 175 176 struct InlinedIndexes { 177 public: 178 void SetIndexes(uint64_t value, Process &p) { 179 m_indexes = value; 180 _lengthForInlinePayload(p.GetAddressByteSize()); 181 m_process = &p; 182 } 183 184 size_t GetNumIndexes() { return m_count; } 185 186 lldb::ValueObjectSP GetIndexAtIndex(size_t idx, 187 const CompilerType &desired_type) { 188 if (!m_process) 189 return nullptr; 190 191 std::pair<uint64_t, bool> value(_indexAtPositionForInlinePayload(idx)); 192 if (!value.second) 193 return nullptr; 194 195 Value v; 196 if (m_ptr_size == 8) { 197 Scalar scalar((unsigned long long)value.first); 198 v = Value(scalar); 199 } else { 200 Scalar scalar((unsigned int)value.first); 201 v = Value(scalar); 202 } 203 204 v.SetCompilerType(desired_type); 205 206 StreamString idx_name; 207 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); 208 209 return ValueObjectConstResult::Create( 210 m_process, v, ConstString(idx_name.GetString())); 211 } 212 213 void Clear() { 214 m_indexes = 0; 215 m_count = 0; 216 m_ptr_size = 0; 217 m_process = nullptr; 218 } 219 220 InlinedIndexes() 221 : m_indexes(0), m_count(0), m_ptr_size(0), m_process(nullptr) {} 222 223 private: 224 uint64_t m_indexes; 225 size_t m_count; 226 uint32_t m_ptr_size; 227 Process *m_process; 228 229 // cfr. Foundation for the details of this code 230 size_t _lengthForInlinePayload(uint32_t ptr_size) { 231 m_ptr_size = ptr_size; 232 if (m_ptr_size == 8) 233 m_count = ((m_indexes >> 3) & 0x7); 234 else 235 m_count = ((m_indexes >> 3) & 0x3); 236 return m_count; 237 } 238 239 std::pair<uint64_t, bool> _indexAtPositionForInlinePayload(size_t pos) { 240 static const uint64_t PACKED_INDEX_MASK = ((1 << 13) - 1); 241 if (m_ptr_size == 8) { 242 switch (pos) { 243 case 3: 244 case 2: 245 case 1: 246 case 0: 247 return {(m_indexes >> PACKED_INDEX_SHIFT_64(pos)) & 248 PACKED_INDEX_MASK, 249 true}; 250 default: 251 return {0, false}; 252 } 253 } else { 254 switch (pos) { 255 case 0: 256 case 1: 257 return {(m_indexes >> PACKED_INDEX_SHIFT_32(pos)) & 258 PACKED_INDEX_MASK, 259 true}; 260 default: 261 return {0, false}; 262 } 263 } 264 return {0, false}; 265 } 266 }; 267 268 struct OutsourcedIndexes { 269 lldb::ValueObjectSP GetIndexAtIndex(size_t idx) { 270 if (m_indexes) { 271 ValueObjectSP index_sp(m_indexes->GetSyntheticArrayMember(idx, true)); 272 return index_sp; 273 } 274 return nullptr; 275 } 276 277 void Clear() { 278 m_indexes = nullptr; 279 m_count = 0; 280 } 281 282 OutsourcedIndexes() : m_indexes(nullptr), m_count(0) {} 283 284 ValueObject *m_indexes; 285 size_t m_count; 286 }; 287 288 union { 289 struct InlinedIndexes m_inlined; 290 struct OutsourcedIndexes m_outsourced; 291 }; 292 293 void Clear() { 294 m_mode = Mode::Invalid; 295 m_inlined.Clear(); 296 m_outsourced.Clear(); 297 } 298 299 Impl() : m_mode(Mode::Invalid) {} 300 301 Mode m_mode; 302 } m_impl; 303 304 uint32_t m_ptr_size; 305 CompilerType m_uint_star_type; 306 }; 307 308 namespace lldb_private { 309 namespace formatters { 310 311 SyntheticChildrenFrontEnd * 312 NSIndexPathSyntheticFrontEndCreator(CXXSyntheticChildren *, 313 lldb::ValueObjectSP valobj_sp) { 314 if (valobj_sp) 315 return new NSIndexPathSyntheticFrontEnd(valobj_sp); 316 return nullptr; 317 } 318 319 } // namespace formatters 320 } // namespace lldb_private 321