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