diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/Language/ObjC')
21 files changed, 7659 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CF.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CF.cpp new file mode 100644 index 000000000000..0926192a4f38 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CF.cpp @@ -0,0 +1,286 @@ +//===-- CF.cpp ------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "CF.h" + +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Target/Language.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" + +#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +bool lldb_private::formatters::CFAbsoluteTimeSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + time_t epoch = GetOSXEpoch(); + epoch = epoch + (time_t)valobj.GetValueAsSigned(0); + tm *tm_date = localtime(&epoch); + if (!tm_date) + return false; + std::string buffer(1024, 0); + if (strftime(&buffer[0], 1023, "%Z", tm_date) == 0) + return false; + stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year + 1900, + tm_date->tm_mon + 1, tm_date->tm_mday, tm_date->tm_hour, + tm_date->tm_min, tm_date->tm_sec, buffer.c_str()); + return true; +} + +bool lldb_private::formatters::CFBagSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + static constexpr llvm::StringLiteral g_TypeHint("CFBag"); + + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor( + runtime->GetClassDescriptor(valobj)); + + if (!descriptor.get() || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + uint32_t count = 0; + + bool is_type_ok = false; // check to see if this is a CFBag we know about + if (descriptor->IsCFType()) { + ConstString type_name(valobj.GetTypeName()); + + static ConstString g_CFBag("__CFBag"); + static ConstString g_conststruct__CFBag("const struct __CFBag"); + + if (type_name == g_CFBag || type_name == g_conststruct__CFBag) { + if (valobj.IsPointerType()) + is_type_ok = true; + } + } + + if (is_type_ok) { + lldb::addr_t offset = 2 * ptr_size + 4 + valobj_addr; + Status error; + count = process_sp->ReadUnsignedIntegerFromMemory(offset, 4, 0, error); + if (error.Fail()) + return false; + } else + return false; + + llvm::StringRef prefix, suffix; + if (Language *language = Language::FindPlugin(options.GetLanguage())) + std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint); + + stream << prefix; + stream.Printf("\"%u value%s\"", count, (count == 1 ? "" : "s")); + stream << suffix; + return true; +} + +bool lldb_private::formatters::CFBitVectorSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor( + runtime->GetClassDescriptor(valobj)); + + if (!descriptor.get() || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + uint32_t count = 0; + + bool is_type_ok = false; // check to see if this is a CFBag we know about + if (descriptor->IsCFType()) { + ConstString type_name(valobj.GetTypeName()); + if (type_name == "__CFMutableBitVector" || type_name == "__CFBitVector" || + type_name == "CFMutableBitVectorRef" || type_name == "CFBitVectorRef") { + if (valobj.IsPointerType()) + is_type_ok = true; + } + } + + if (!is_type_ok) + return false; + + Status error; + count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + 2 * ptr_size, + ptr_size, 0, error); + if (error.Fail()) + return false; + uint64_t num_bytes = count / 8 + ((count & 7) ? 1 : 0); + addr_t data_ptr = process_sp->ReadPointerFromMemory( + valobj_addr + 2 * ptr_size + 2 * ptr_size, error); + if (error.Fail()) + return false; + // make sure we do not try to read huge amounts of data + if (num_bytes > 1024) + num_bytes = 1024; + WritableDataBufferSP buffer_sp(new DataBufferHeap(num_bytes, 0)); + num_bytes = + process_sp->ReadMemory(data_ptr, buffer_sp->GetBytes(), num_bytes, error); + if (error.Fail() || num_bytes == 0) + return false; + uint8_t *bytes = buffer_sp->GetBytes(); + for (uint64_t byte_idx = 0; byte_idx < num_bytes - 1; byte_idx++) { + uint8_t byte = bytes[byte_idx]; + bool bit0 = (byte & 1) == 1; + bool bit1 = (byte & 2) == 2; + bool bit2 = (byte & 4) == 4; + bool bit3 = (byte & 8) == 8; + bool bit4 = (byte & 16) == 16; + bool bit5 = (byte & 32) == 32; + bool bit6 = (byte & 64) == 64; + bool bit7 = (byte & 128) == 128; + stream.Printf("%c%c%c%c %c%c%c%c ", (bit7 ? '1' : '0'), (bit6 ? '1' : '0'), + (bit5 ? '1' : '0'), (bit4 ? '1' : '0'), (bit3 ? '1' : '0'), + (bit2 ? '1' : '0'), (bit1 ? '1' : '0'), (bit0 ? '1' : '0')); + count -= 8; + } + { + // print the last byte ensuring we do not print spurious bits + uint8_t byte = bytes[num_bytes - 1]; + bool bit0 = (byte & 1) == 1; + bool bit1 = (byte & 2) == 2; + bool bit2 = (byte & 4) == 4; + bool bit3 = (byte & 8) == 8; + bool bit4 = (byte & 16) == 16; + bool bit5 = (byte & 32) == 32; + bool bit6 = (byte & 64) == 64; + bool bit7 = (byte & 128) == 128; + if (count) { + stream.Printf("%c", bit7 ? '1' : '0'); + count -= 1; + } + if (count) { + stream.Printf("%c", bit6 ? '1' : '0'); + count -= 1; + } + if (count) { + stream.Printf("%c", bit5 ? '1' : '0'); + count -= 1; + } + if (count) { + stream.Printf("%c", bit4 ? '1' : '0'); + count -= 1; + } + if (count) { + stream.Printf("%c", bit3 ? '1' : '0'); + count -= 1; + } + if (count) { + stream.Printf("%c", bit2 ? '1' : '0'); + count -= 1; + } + if (count) { + stream.Printf("%c", bit1 ? '1' : '0'); + count -= 1; + } + if (count) + stream.Printf("%c", bit0 ? '1' : '0'); + } + return true; +} + +bool lldb_private::formatters::CFBinaryHeapSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + static constexpr llvm::StringLiteral g_TypeHint("CFBinaryHeap"); + + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor( + runtime->GetClassDescriptor(valobj)); + + if (!descriptor.get() || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + uint32_t count = 0; + + bool is_type_ok = + false; // check to see if this is a CFBinaryHeap we know about + if (descriptor->IsCFType()) { + ConstString type_name(valobj.GetTypeName()); + + static ConstString g_CFBinaryHeap("__CFBinaryHeap"); + static ConstString g_conststruct__CFBinaryHeap( + "const struct __CFBinaryHeap"); + static ConstString g_CFBinaryHeapRef("CFBinaryHeapRef"); + + if (type_name == g_CFBinaryHeap || + type_name == g_conststruct__CFBinaryHeap || + type_name == g_CFBinaryHeapRef) { + if (valobj.IsPointerType()) + is_type_ok = true; + } + } + + if (is_type_ok) { + lldb::addr_t offset = 2 * ptr_size + valobj_addr; + Status error; + count = process_sp->ReadUnsignedIntegerFromMemory(offset, 4, 0, error); + if (error.Fail()) + return false; + } else + return false; + + llvm::StringRef prefix, suffix; + if (Language *language = Language::FindPlugin(options.GetLanguage())) + std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint); + + stream << prefix; + stream.Printf("\"%u item%s\"", count, (count == 1 ? "" : "s")); + stream << suffix; + return true; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CF.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CF.h new file mode 100644 index 000000000000..6165e1c235bc --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CF.h @@ -0,0 +1,32 @@ +//===-- CF.h ---------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_CF_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_CF_H + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/Utility/Stream.h" + +namespace lldb_private { +namespace formatters { +bool CFBagSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +bool CFBinaryHeapSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +bool CFBitVectorSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +bool CFAbsoluteTimeSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); +} // namespace formatters +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_CF_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CFBasicHash.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CFBasicHash.cpp new file mode 100644 index 000000000000..42cda0146f2e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CFBasicHash.cpp @@ -0,0 +1,114 @@ +#include "CFBasicHash.h" + +#include "lldb/Utility/Endian.h" + +using namespace lldb; +using namespace lldb_private; + +bool CFBasicHash::IsValid() const { + if (m_address != LLDB_INVALID_ADDRESS) { + if (m_ptr_size == 4 && m_ht_32) + return true; + else if (m_ptr_size == 8 && m_ht_64) + return true; + else + return false; + } + return false; +} + +bool CFBasicHash::Update(addr_t addr, ExecutionContextRef exe_ctx_rf) { + if (addr == LLDB_INVALID_ADDRESS || !addr) + return false; + + m_address = addr; + m_exe_ctx_ref = exe_ctx_rf; + m_ptr_size = + m_exe_ctx_ref.GetTargetSP()->GetArchitecture().GetAddressByteSize(); + m_byte_order = m_exe_ctx_ref.GetTargetSP()->GetArchitecture().GetByteOrder(); + + if (m_ptr_size == 4) + return UpdateFor(m_ht_32); + else if (m_ptr_size == 8) + return UpdateFor(m_ht_64); + return false; + + llvm_unreachable( + "Unsupported architecture. Only 32bits and 64bits supported."); +} + +template <typename T> +bool CFBasicHash::UpdateFor(std::unique_ptr<__CFBasicHash<T>> &m_ht) { + if (m_byte_order != endian::InlHostByteOrder()) + return false; + + Status error; + Target *target = m_exe_ctx_ref.GetTargetSP().get(); + addr_t addr = m_address.GetLoadAddress(target); + size_t size = sizeof(typename __CFBasicHash<T>::RuntimeBase) + + sizeof(typename __CFBasicHash<T>::Bits); + + m_ht = std::make_unique<__CFBasicHash<T>>(); + m_exe_ctx_ref.GetProcessSP()->ReadMemory(addr, m_ht.get(), + size, error); + if (error.Fail()) + return false; + + m_mutable = !(m_ht->base.cfinfoa & (1 << 6)); + m_multi = m_ht->bits.counts_offset; + m_type = static_cast<HashType>(m_ht->bits.keys_offset); + addr_t ptr_offset = addr + size; + size_t ptr_count = GetPointerCount(); + size = ptr_count * sizeof(T); + + m_exe_ctx_ref.GetProcessSP()->ReadMemory(ptr_offset, m_ht->pointers, size, + error); + + if (error.Fail()) { + m_ht = nullptr; + return false; + } + + return true; +} + +size_t CFBasicHash::GetCount() const { + if (!IsValid()) + return 0; + + if (!m_multi) + return (m_ptr_size == 4) ? m_ht_32->bits.used_buckets + : m_ht_64->bits.used_buckets; + + // FIXME: Add support for multi + return 0; +} + +size_t CFBasicHash::GetPointerCount() const { + if (!IsValid()) + return 0; + + if (m_multi) + return 3; // Bits::counts_offset; + return (m_type == HashType::dict) + 1; +} + +addr_t CFBasicHash::GetKeyPointer() const { + if (!IsValid()) + return LLDB_INVALID_ADDRESS; + + if (m_ptr_size == 4) + return m_ht_32->pointers[m_ht_32->bits.keys_offset]; + + return m_ht_64->pointers[m_ht_64->bits.keys_offset]; +} + +addr_t CFBasicHash::GetValuePointer() const { + if (!IsValid()) + return LLDB_INVALID_ADDRESS; + + if (m_ptr_size == 4) + return m_ht_32->pointers[0]; + + return m_ht_64->pointers[0]; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CFBasicHash.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CFBasicHash.h new file mode 100644 index 000000000000..f850c50342a3 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CFBasicHash.h @@ -0,0 +1,76 @@ +//===-- CFBasicHash.h -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_CFBASICHASH_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_CFBASICHASH_H + +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +namespace lldb_private { + +class CFBasicHash { +public: + enum class HashType { set = 0, dict }; + + CFBasicHash() = default; + ~CFBasicHash() = default; + + bool Update(lldb::addr_t addr, ExecutionContextRef exe_ctx_rf); + + bool IsValid() const; + + bool IsMutable() const { return m_mutable; }; + bool IsMultiVariant() const { return m_multi; } + HashType GetType() const { return m_type; } + + size_t GetCount() const; + lldb::addr_t GetKeyPointer() const; + lldb::addr_t GetValuePointer() const; + +private: + template <typename T> struct __CFBasicHash { + struct RuntimeBase { + T cfisa; + T cfinfoa; + } base; + + struct Bits { + uint16_t __reserved0; + uint16_t __reserved1 : 2; + uint16_t keys_offset : 1; + uint16_t counts_offset : 2; + uint16_t counts_width : 2; + uint16_t __reserved2 : 9; + uint32_t used_buckets; // number of used buckets + uint64_t deleted : 16; // number of elements deleted + uint64_t num_buckets_idx : 8; // index to number of buckets + uint64_t __reserved3 : 40; + uint64_t __reserved4; + } bits; + + T pointers[3]; + }; + template <typename T> bool UpdateFor(std::unique_ptr<__CFBasicHash<T>> &m_ht); + + size_t GetPointerCount() const; + + uint32_t m_ptr_size = UINT32_MAX; + lldb::ByteOrder m_byte_order = lldb::eByteOrderInvalid; + Address m_address = LLDB_INVALID_ADDRESS; + std::unique_ptr<__CFBasicHash<uint32_t>> m_ht_32 = nullptr; + std::unique_ptr<__CFBasicHash<uint64_t>> m_ht_64 = nullptr; + ExecutionContextRef m_exe_ctx_ref; + bool m_mutable = true; + bool m_multi = false; + HashType m_type = HashType::set; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_CFBASICHASH_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/Cocoa.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/Cocoa.cpp new file mode 100644 index 000000000000..341923108e32 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/Cocoa.cpp @@ -0,0 +1,1258 @@ +//===-- Cocoa.cpp ---------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Cocoa.h" +#include "NSString.h" +#include "ObjCConstants.h" + +#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/Core/Mangled.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/DataFormatters/StringPrinter.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/Host/Time.h" +#include "lldb/Target/Language.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" + +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/bit.h" + + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +bool lldb_private::formatters::NSBundleSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor( + runtime->GetClassDescriptor(valobj)); + + if (!descriptor || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + llvm::StringRef class_name(descriptor->GetClassName().GetCString()); + + if (class_name.empty()) + return false; + + if (class_name == "NSBundle") { + uint64_t offset = 5 * ptr_size; + ValueObjectSP text(valobj.GetSyntheticChildAtOffset( + offset, + valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID), + true)); + + if (!text) + return false; + + StreamString summary_stream; + bool was_nsstring_ok = + NSStringSummaryProvider(*text, summary_stream, options); + if (was_nsstring_ok && summary_stream.GetSize() > 0) { + stream.Printf("%s", summary_stream.GetData()); + return true; + } + } + + return false; +} + +bool lldb_private::formatters::NSTimeZoneSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor( + runtime->GetClassDescriptor(valobj)); + + if (!descriptor || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + llvm::StringRef class_name(descriptor->GetClassName().GetCString()); + + if (class_name.empty()) + return false; + + if (class_name == "__NSTimeZone") { + uint64_t offset = ptr_size; + ValueObjectSP text(valobj.GetSyntheticChildAtOffset( + offset, valobj.GetCompilerType(), true)); + + if (!text) + return false; + + StreamString summary_stream; + bool was_nsstring_ok = + NSStringSummaryProvider(*text, summary_stream, options); + if (was_nsstring_ok && summary_stream.GetSize() > 0) { + stream.Printf("%s", summary_stream.GetData()); + return true; + } + } + + return false; +} + +bool lldb_private::formatters::NSNotificationSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor( + runtime->GetClassDescriptor(valobj)); + + if (!descriptor || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + llvm::StringRef class_name(descriptor->GetClassName().GetCString()); + + if (class_name.empty()) + return false; + + if (class_name == "NSConcreteNotification") { + uint64_t offset = ptr_size; + ValueObjectSP text(valobj.GetSyntheticChildAtOffset( + offset, valobj.GetCompilerType(), true)); + + if (!text) + return false; + + StreamString summary_stream; + bool was_nsstring_ok = + NSStringSummaryProvider(*text, summary_stream, options); + if (was_nsstring_ok && summary_stream.GetSize() > 0) { + stream.Printf("%s", summary_stream.GetData()); + return true; + } + } + + return false; +} + +bool lldb_private::formatters::NSMachPortSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor( + runtime->GetClassDescriptor(valobj)); + + if (!descriptor || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + llvm::StringRef class_name(descriptor->GetClassName().GetCString()); + + if (class_name.empty()) + return false; + + uint64_t port_number = 0; + + if (class_name == "NSMachPort") { + uint64_t offset = (ptr_size == 4 ? 12 : 20); + Status error; + port_number = process_sp->ReadUnsignedIntegerFromMemory( + offset + valobj_addr, 4, 0, error); + if (error.Success()) { + stream.Printf("mach port: %u", + (uint32_t)(port_number & 0x00000000FFFFFFFF)); + return true; + } + } + + return false; +} + +bool lldb_private::formatters::NSIndexSetSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>( + ObjCLanguageRuntime::Get(*process_sp)); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor( + runtime->GetClassDescriptor(valobj)); + + if (!descriptor || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + llvm::StringRef class_name(descriptor->GetClassName().GetCString()); + + if (class_name.empty()) + return false; + + uint64_t count = 0; + + do { + if (class_name == "NSIndexSet" || class_name == "NSMutableIndexSet") { + // Foundation version 2000 added a bitmask if the index set fit in 64 bits + // and a Tagged Pointer version if the bitmask is small enough to fit in + // the tagged pointer payload. + // It also changed the layout (but not the size) of the set descriptor. + + // First check whether this is a tagged pointer. The bitmask will be in + // the payload of the tagged pointer. + uint64_t payload; + if (runtime->GetFoundationVersion() >= 2000 + && descriptor->GetTaggedPointerInfo(nullptr, nullptr, &payload)) { + count = llvm::popcount(payload); + break; + } + // The first 32 bits describe the index set in all cases: + Status error; + uint32_t mode = process_sp->ReadUnsignedIntegerFromMemory( + valobj_addr + ptr_size, 4, 0, error); + if (error.Fail()) + return false; + // Now check if the index is held in a bitmask in the object: + if (runtime->GetFoundationVersion() >= 2000) { + // The first two bits are "isSingleRange" and "isBitfield". If this is + // a bitfield we handle it here, otherwise set mode appropriately and + // the rest of the treatment is in common. + if ((mode & 2) == 2) { + // The bitfield is a 64 bit uint at the beginning of the data var. + uint64_t bitfield = process_sp->ReadUnsignedIntegerFromMemory( + valobj_addr + 2 * ptr_size, 8, 0, error); + if (error.Fail()) + return false; + count = llvm::popcount(bitfield); + break; + } + // It wasn't a bitfield, so read the isSingleRange from its new loc: + if ((mode & 1) == 1) + mode = 1; // this means the set only has one range + else + mode = 2; // this means the set has multiple ranges + } else { + // this means the set is empty - count = 0 + if ((mode & 1) == 1) { + count = 0; + break; + } + + if ((mode & 2) == 2) + mode = 1; // this means the set only has one range + else + mode = 2; // this means the set has multiple ranges + } + if (mode == 1) { + count = process_sp->ReadUnsignedIntegerFromMemory( + valobj_addr + 3 * ptr_size, ptr_size, 0, error); + if (error.Fail()) + return false; + } else { + // read a pointer to the data at 2*ptr_size + count = process_sp->ReadUnsignedIntegerFromMemory( + valobj_addr + 2 * ptr_size, ptr_size, 0, error); + if (error.Fail()) + return false; + // read the data at 2*ptr_size from the first location + count = process_sp->ReadUnsignedIntegerFromMemory(count + 2 * ptr_size, + ptr_size, 0, error); + if (error.Fail()) + return false; + } + } else + return false; + } while (false); + stream.Printf("%" PRIu64 " index%s", count, (count == 1 ? "" : "es")); + return true; +} + +static void NSNumber_FormatChar(ValueObject &valobj, Stream &stream, char value, + lldb::LanguageType lang) { + static constexpr llvm::StringLiteral g_TypeHint("NSNumber:char"); + + llvm::StringRef prefix, suffix; + if (Language *language = Language::FindPlugin(lang)) + std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint); + + stream << prefix; + stream.Printf("%hhd", value); + stream << suffix; +} + +static void NSNumber_FormatShort(ValueObject &valobj, Stream &stream, + short value, lldb::LanguageType lang) { + static constexpr llvm::StringLiteral g_TypeHint("NSNumber:short"); + + llvm::StringRef prefix, suffix; + if (Language *language = Language::FindPlugin(lang)) + std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint); + + stream << prefix; + stream.Printf("%hd", value); + stream << suffix; +} + +static void NSNumber_FormatInt(ValueObject &valobj, Stream &stream, int value, + lldb::LanguageType lang) { + static constexpr llvm::StringLiteral g_TypeHint("NSNumber:int"); + + llvm::StringRef prefix, suffix; + if (Language *language = Language::FindPlugin(lang)) + std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint); + + stream << prefix; + stream.Printf("%d", value); + stream << suffix; +} + +static void NSNumber_FormatLong(ValueObject &valobj, Stream &stream, + int64_t value, lldb::LanguageType lang) { + static constexpr llvm::StringLiteral g_TypeHint("NSNumber:long"); + + llvm::StringRef prefix, suffix; + if (Language *language = Language::FindPlugin(lang)) + std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint); + + stream << prefix; + stream.Printf("%" PRId64 "", value); + stream << suffix; +} + +static void NSNumber_FormatInt128(ValueObject &valobj, Stream &stream, + const llvm::APInt &value, + lldb::LanguageType lang) { + static constexpr llvm::StringLiteral g_TypeHint("NSNumber:int128_t"); + + llvm::StringRef prefix, suffix; + if (Language *language = Language::FindPlugin(lang)) + std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint); + + stream << prefix; + const int radix = 10; + const bool isSigned = true; + std::string str = llvm::toString(value, radix, isSigned); + stream.PutCString(str.c_str()); + stream << suffix; +} + +static void NSNumber_FormatFloat(ValueObject &valobj, Stream &stream, + float value, lldb::LanguageType lang) { + static constexpr llvm::StringLiteral g_TypeHint("NSNumber:float"); + + llvm::StringRef prefix, suffix; + if (Language *language = Language::FindPlugin(lang)) + std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint); + + stream << prefix; + stream.Printf("%f", value); + stream << suffix; +} + +static void NSNumber_FormatDouble(ValueObject &valobj, Stream &stream, + double value, lldb::LanguageType lang) { + static constexpr llvm::StringLiteral g_TypeHint("NSNumber:double"); + + llvm::StringRef prefix, suffix; + if (Language *language = Language::FindPlugin(lang)) + std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint); + + stream << prefix; + stream.Printf("%g", value); + stream << suffix; +} + +bool lldb_private::formatters::NSNumberSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + Log *log = GetLog(LLDBLog::DataFormatters); + ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor( + runtime->GetClassDescriptor(valobj)); + + if (!descriptor || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + llvm::StringRef class_name(descriptor->GetClassName().GetCString()); + + if (class_name.empty()) + return false; + + if (class_name == "__NSCFBoolean") + return ObjCBooleanSummaryProvider(valobj, stream, options); + + if (class_name == "NSDecimalNumber") + return NSDecimalNumberSummaryProvider(valobj, stream, options); + + if (class_name == "NSConstantIntegerNumber") { + Status error; + int64_t value = process_sp->ReadSignedIntegerFromMemory( + valobj_addr + 2 * ptr_size, 8, 0, error); + if (error.Fail()) + return false; + uint64_t encoding_addr = process_sp->ReadUnsignedIntegerFromMemory( + valobj_addr + ptr_size, ptr_size, 0, error); + if (error.Fail()) + return false; + char encoding = + process_sp->ReadUnsignedIntegerFromMemory(encoding_addr, 1, 0, error); + if (error.Fail()) + return false; + + switch (encoding) { + case _C_CHR: + NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage()); + return true; + case _C_SHT: + NSNumber_FormatShort(valobj, stream, (short)value, options.GetLanguage()); + return true; + case _C_INT: + NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage()); + return true; + case _C_LNG: + case _C_LNG_LNG: + NSNumber_FormatLong(valobj, stream, value, options.GetLanguage()); + return true; + + case _C_UCHR: + case _C_USHT: + case _C_UINT: + case _C_ULNG: + case _C_ULNG_LNG: + stream.Printf("%" PRIu64, value); + return true; + } + + return false; + } + + if (class_name == "NSConstantFloatNumber") { + Status error; + uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory( + valobj_addr + ptr_size, 4, 0, error); + if (error.Fail()) + return false; + float flt_value = 0.0f; + memcpy(&flt_value, &flt_as_int, sizeof(flt_as_int)); + NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage()); + return true; + } + + if (class_name == "NSConstantDoubleNumber") { + Status error; + uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory( + valobj_addr + ptr_size, 8, 0, error); + if (error.Fail()) + return false; + double dbl_value = 0.0; + memcpy(&dbl_value, &dbl_as_lng, sizeof(dbl_as_lng)); + NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage()); + return true; + } + + if (class_name == "NSNumber" || class_name == "__NSCFNumber") { + int64_t value = 0; + uint64_t i_bits = 0; + if (descriptor->GetTaggedPointerInfoSigned(&i_bits, &value)) { + // Check for "preserved" numbers. We still don't support them yet. + if (i_bits & 0x8) { + if (log) + log->Printf( + "Unsupported (preserved) NSNumber tagged pointer 0x%" PRIu64, + valobj_addr); + return false; + } + + switch (i_bits) { + case 0: + NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage()); + break; + case 1: + case 4: + NSNumber_FormatShort(valobj, stream, (short)value, + options.GetLanguage()); + break; + case 2: + case 8: + NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage()); + break; + case 3: + case 12: + NSNumber_FormatLong(valobj, stream, value, options.GetLanguage()); + break; + default: + return false; + } + return true; + } else { + Status error; + + AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>( + ObjCLanguageRuntime::Get(*process_sp)); + + const bool new_format = + (runtime && runtime->GetFoundationVersion() >= 1400); + + enum class TypeCodes : int { + sint8 = 0x0, + sint16 = 0x1, + sint32 = 0x2, + sint64 = 0x3, + f32 = 0x4, + f64 = 0x5, + sint128 = 0x6 + }; + + uint64_t data_location = valobj_addr + 2 * ptr_size; + TypeCodes type_code; + + if (new_format) { + uint64_t cfinfoa = process_sp->ReadUnsignedIntegerFromMemory( + valobj_addr + ptr_size, ptr_size, 0, error); + + if (error.Fail()) + return false; + + bool is_preserved_number = cfinfoa & 0x8; + if (is_preserved_number) { + if (log) + log->Printf( + "Unsupported preserved NSNumber tagged pointer 0x%" PRIu64, + valobj_addr); + return false; + } + + type_code = static_cast<TypeCodes>(cfinfoa & 0x7); + } else { + uint8_t data_type = process_sp->ReadUnsignedIntegerFromMemory( + valobj_addr + ptr_size, 1, 0, error) & + 0x1F; + + if (error.Fail()) + return false; + + switch (data_type) { + case 1: + type_code = TypeCodes::sint8; + break; + case 2: + type_code = TypeCodes::sint16; + break; + case 3: + type_code = TypeCodes::sint32; + break; + case 17: + data_location += 8; + [[fallthrough]]; + case 4: + type_code = TypeCodes::sint64; + break; + case 5: + type_code = TypeCodes::f32; + break; + case 6: + type_code = TypeCodes::f64; + break; + default: + return false; + } + } + + uint64_t value = 0; + bool success = false; + switch (type_code) { + case TypeCodes::sint8: + value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0, + error); + if (error.Fail()) + return false; + NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage()); + success = true; + break; + case TypeCodes::sint16: + value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0, + error); + if (error.Fail()) + return false; + NSNumber_FormatShort(valobj, stream, (short)value, + options.GetLanguage()); + success = true; + break; + case TypeCodes::sint32: + value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, + error); + if (error.Fail()) + return false; + NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage()); + success = true; + break; + case TypeCodes::sint64: + value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, + error); + if (error.Fail()) + return false; + NSNumber_FormatLong(valobj, stream, value, options.GetLanguage()); + success = true; + break; + case TypeCodes::f32: { + uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory( + data_location, 4, 0, error); + if (error.Fail()) + return false; + float flt_value = 0.0f; + memcpy(&flt_value, &flt_as_int, sizeof(flt_as_int)); + NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage()); + success = true; + break; + } + case TypeCodes::f64: { + uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory( + data_location, 8, 0, error); + if (error.Fail()) + return false; + double dbl_value = 0.0; + memcpy(&dbl_value, &dbl_as_lng, sizeof(dbl_as_lng)); + NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage()); + success = true; + break; + } + case TypeCodes::sint128: // internally, this is the same + { + uint64_t words[2]; + words[1] = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, + 0, error); + if (error.Fail()) + return false; + words[0] = process_sp->ReadUnsignedIntegerFromMemory(data_location + 8, + 8, 0, error); + if (error.Fail()) + return false; + llvm::APInt i128_value(128, words); + NSNumber_FormatInt128(valobj, stream, i128_value, + options.GetLanguage()); + success = true; + break; + } + } + return success; + } + } + + return false; +} + +bool lldb_private::formatters::NSDecimalNumberSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + Status error; + int8_t exponent = process_sp->ReadUnsignedIntegerFromMemory( + valobj_addr + ptr_size, 1, 0, error); + if (error.Fail()) + return false; + + uint8_t length_and_negative = process_sp->ReadUnsignedIntegerFromMemory( + valobj_addr + ptr_size + 1, 1, 0, error); + if (error.Fail()) + return false; + + // Fifth bit marks negativity. + const bool is_negative = (length_and_negative >> 4) & 1; + + // Zero length and negative means NaN. + uint8_t length = length_and_negative & 0xf; + const bool is_nan = is_negative && (length == 0); + + if (is_nan) { + stream.Printf("NaN"); + return true; + } + + if (length == 0) { + stream.Printf("0"); + return true; + } + + uint64_t mantissa = process_sp->ReadUnsignedIntegerFromMemory( + valobj_addr + ptr_size + 4, 8, 0, error); + if (error.Fail()) + return false; + + if (is_negative) + stream.Printf("-"); + + stream.Printf("%" PRIu64 " x 10^%" PRIi8, mantissa, exponent); + return true; +} + +bool lldb_private::formatters::NSURLSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor( + runtime->GetClassDescriptor(valobj)); + + if (!descriptor || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + llvm::StringRef class_name = descriptor->GetClassName().GetStringRef(); + + if (class_name != "NSURL") + return false; + + uint64_t offset_text = ptr_size + ptr_size + + 8; // ISA + pointer + 8 bytes of data (even on 32bit) + uint64_t offset_base = offset_text + ptr_size; + CompilerType type(valobj.GetCompilerType()); + ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset_text, type, true)); + ValueObjectSP base(valobj.GetSyntheticChildAtOffset(offset_base, type, true)); + if (!text || text->GetValueAsUnsigned(0) == 0) + return false; + + StreamString base_summary; + if (base && base->GetValueAsUnsigned(0)) { + if (!NSURLSummaryProvider(*base, base_summary, options)) + base_summary.Clear(); + } + if (base_summary.Empty()) + return NSStringSummaryProvider(*text, stream, options); + + StreamString summary; + if (!NSStringSummaryProvider(*text, summary, options) || summary.Empty()) + return false; + + static constexpr llvm::StringLiteral quote_char("\""); + static constexpr llvm::StringLiteral g_TypeHint("NSString"); + llvm::StringRef prefix, suffix; + if (Language *language = Language::FindPlugin(options.GetLanguage())) + std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint); + + // @"A" -> @"A + llvm::StringRef summary_str = summary.GetString(); + bool back_consumed = + summary_str.consume_back(suffix) && summary_str.consume_back(quote_char); + assert(back_consumed); + UNUSED_IF_ASSERT_DISABLED(back_consumed); + // @"B" -> B" + llvm::StringRef base_summary_str = base_summary.GetString(); + bool front_consumed = base_summary_str.consume_front(prefix) && + base_summary_str.consume_front(quote_char); + assert(front_consumed); + UNUSED_IF_ASSERT_DISABLED(front_consumed); + // @"A -- B" + if (!summary_str.empty() && !base_summary_str.empty()) { + stream << summary_str << " -- " << base_summary_str; + return true; + } + + return false; +} + +/// Bias value for tagged pointer exponents. +/// Recommended values: +/// 0x3e3: encodes all dates between distantPast and distantFuture +/// except for the range within about 1e-28 second of the reference date. +/// 0x3ef: encodes all dates for a few million years beyond distantPast and +/// distantFuture, except within about 1e-25 second of the reference date. +const int TAGGED_DATE_EXPONENT_BIAS = 0x3ef; + +struct DoubleBits { + uint64_t fraction : 52; // unsigned + uint64_t exponent : 11; // signed + uint64_t sign : 1; +}; + +struct TaggedDoubleBits { + uint64_t fraction : 52; // unsigned + uint64_t exponent : 7; // signed + uint64_t sign : 1; + uint64_t unused : 4; // placeholder for pointer tag bits +}; + +static uint64_t decodeExponent(uint64_t exp) { + // Tagged exponent field is 7-bit signed. Sign-extend the value to 64 bits + // before performing arithmetic. + return llvm::SignExtend64<7>(exp) + TAGGED_DATE_EXPONENT_BIAS; +} + +static double decodeTaggedTimeInterval(uint64_t encodedTimeInterval) { + if (encodedTimeInterval == 0) + return 0.0; + if (encodedTimeInterval == std::numeric_limits<uint64_t>::max()) + return (uint64_t)-0.0; + + TaggedDoubleBits encodedBits = + llvm::bit_cast<TaggedDoubleBits>(encodedTimeInterval); + assert(encodedBits.unused == 0); + + // Sign and fraction are represented exactly. + // Exponent is encoded. + DoubleBits decodedBits; + decodedBits.sign = encodedBits.sign; + decodedBits.fraction = encodedBits.fraction; + decodedBits.exponent = decodeExponent(encodedBits.exponent); + + return llvm::bit_cast<double>(decodedBits); +} + +bool lldb_private::formatters::NSDateSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor( + runtime->GetClassDescriptor(valobj)); + + if (!descriptor || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + uint64_t date_value_bits = 0; + double date_value = 0.0; + + ConstString class_name = descriptor->GetClassName(); + + static const ConstString g_NSDate("NSDate"); + static const ConstString g_dunder_NSDate("__NSDate"); + static const ConstString g_NSTaggedDate("__NSTaggedDate"); + static const ConstString g_NSCalendarDate("NSCalendarDate"); + static const ConstString g_NSConstantDate("NSConstantDate"); + + if (class_name.IsEmpty()) + return false; + + uint64_t info_bits = 0, value_bits = 0; + if ((class_name == g_NSDate) || (class_name == g_dunder_NSDate) || + (class_name == g_NSTaggedDate) || (class_name == g_NSConstantDate)) { + if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits)) { + date_value_bits = ((value_bits << 8) | (info_bits << 4)); + memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); + } else { + llvm::Triple triple( + process_sp->GetTarget().GetArchitecture().GetTriple()); + uint32_t delta = + (triple.isWatchOS() && triple.isWatchABI()) ? 8 : ptr_size; + Status error; + date_value_bits = process_sp->ReadUnsignedIntegerFromMemory( + valobj_addr + delta, 8, 0, error); + memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); + if (error.Fail()) + return false; + } + } else if (class_name == g_NSCalendarDate) { + Status error; + date_value_bits = process_sp->ReadUnsignedIntegerFromMemory( + valobj_addr + 2 * ptr_size, 8, 0, error); + memcpy(&date_value, &date_value_bits, sizeof(date_value_bits)); + if (error.Fail()) + return false; + } else + return false; + + // FIXME: It seems old dates are not formatted according to NSDate's calendar + // so we hardcode distantPast's value so that it looks like LLDB is doing + // the right thing. + + // The relative time in seconds from Cocoa Epoch to [NSDate distantPast]. + const double RelSecondsFromCocoaEpochToNSDateDistantPast = -63114076800; + if (date_value == RelSecondsFromCocoaEpochToNSDateDistantPast) { + stream.Printf("0001-01-01 00:00:00 UTC"); + return true; + } + + // Accomodate for the __NSTaggedDate format introduced in Foundation 1600. + if (class_name == g_NSTaggedDate) { + auto *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>( + ObjCLanguageRuntime::Get(*process_sp)); + if (runtime && runtime->GetFoundationVersion() >= 1600) + date_value = decodeTaggedTimeInterval(value_bits << 4); + } + + // this snippet of code assumes that time_t == seconds since Jan-1-1970 this + // is generally true and POSIXly happy, but might break if a library vendor + // decides to get creative + time_t epoch = GetOSXEpoch(); + epoch = epoch + static_cast<time_t>(std::floor(date_value)); + tm *tm_date = gmtime(&epoch); + if (!tm_date) + return false; + std::string buffer(1024, 0); + if (strftime(&buffer[0], 1023, "%Z", tm_date) == 0) + return false; + stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year + 1900, + tm_date->tm_mon + 1, tm_date->tm_mday, tm_date->tm_hour, + tm_date->tm_min, tm_date->tm_sec, buffer.c_str()); + return true; +} + +bool lldb_private::formatters::ObjCClassSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor( + runtime->GetClassDescriptorFromISA(valobj.GetValueAsUnsigned(0))); + + if (!descriptor || !descriptor->IsValid()) + return false; + + ConstString class_name = descriptor->GetClassName(); + + if (class_name.IsEmpty()) + return false; + + if (ConstString cs = Mangled(class_name).GetDemangledName()) + class_name = cs; + + stream.Printf("%s", class_name.AsCString("<unknown class>")); + return true; +} + +class ObjCClassSyntheticChildrenFrontEnd : public SyntheticChildrenFrontEnd { +public: + ObjCClassSyntheticChildrenFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) {} + + ~ObjCClassSyntheticChildrenFrontEnd() override = default; + + llvm::Expected<uint32_t> CalculateNumChildren() override { return 0; } + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override { + return lldb::ValueObjectSP(); + } + + lldb::ChildCacheState Update() override { + return lldb::ChildCacheState::eRefetch; + } + + bool MightHaveChildren() override { return false; } + + size_t GetIndexOfChildWithName(ConstString name) override { + return UINT32_MAX; + } +}; + +SyntheticChildrenFrontEnd * +lldb_private::formatters::ObjCClassSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return new ObjCClassSyntheticChildrenFrontEnd(valobj_sp); +} + +template <bool needs_at> +bool lldb_private::formatters::NSDataSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor( + runtime->GetClassDescriptor(valobj)); + + if (!descriptor || !descriptor->IsValid()) + return false; + + bool is_64bit = (process_sp->GetAddressByteSize() == 8); + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + uint64_t value = 0; + + llvm::StringRef class_name = descriptor->GetClassName().GetCString(); + + if (class_name.empty()) + return false; + + bool isNSConcreteData = class_name == "NSConcreteData"; + bool isNSConcreteMutableData = class_name == "NSConcreteMutableData"; + bool isNSCFData = class_name == "__NSCFData"; + if (isNSConcreteData || isNSConcreteMutableData || isNSCFData) { + uint32_t offset; + if (isNSConcreteData) + offset = is_64bit ? 8 : 4; + else + offset = is_64bit ? 16 : 8; + + Status error; + value = process_sp->ReadUnsignedIntegerFromMemory( + valobj_addr + offset, is_64bit ? 8 : 4, 0, error); + if (error.Fail()) + return false; + } else if (class_name == "_NSInlineData") { + uint32_t offset = (is_64bit ? 8 : 4); + Status error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + offset, 2, + 0, error); + if (error.Fail()) + return false; + } else if (class_name == "_NSZeroData") { + value = 0; + } else + return false; + + stream.Printf("%s%" PRIu64 " byte%s%s", (needs_at ? "@\"" : ""), value, + (value != 1 ? "s" : ""), (needs_at ? "\"" : "")); + + return true; +} + +bool lldb_private::formatters::ObjCBOOLSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + const uint32_t type_info = valobj.GetCompilerType().GetTypeInfo(); + + ValueObjectSP real_guy_sp = valobj.GetSP(); + + if (type_info & eTypeIsPointer) { + Status err; + real_guy_sp = valobj.Dereference(err); + if (err.Fail() || !real_guy_sp) + return false; + } else if (type_info & eTypeIsReference) { + real_guy_sp = valobj.GetChildAtIndex(0); + if (!real_guy_sp) + return false; + } + int8_t value = (real_guy_sp->GetValueAsSigned(0) & 0xFF); + switch (value) { + case 0: + stream.Printf("NO"); + break; + case 1: + stream.Printf("YES"); + break; + default: + stream.Printf("%d", value); + break; + } + return true; +} + +bool lldb_private::formatters::ObjCBooleanSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + lldb::addr_t valobj_ptr_value = + valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS); + if (valobj_ptr_value == LLDB_INVALID_ADDRESS) + return false; + + ProcessSP process_sp(valobj.GetProcessSP()); + if (!process_sp) + return false; + + if (AppleObjCRuntime *objc_runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>( + ObjCLanguageRuntime::Get(*process_sp))) { + lldb::addr_t cf_true = LLDB_INVALID_ADDRESS, + cf_false = LLDB_INVALID_ADDRESS; + objc_runtime->GetValuesForGlobalCFBooleans(cf_true, cf_false); + if (valobj_ptr_value == cf_true) { + stream.PutCString("YES"); + return true; + } + if (valobj_ptr_value == cf_false) { + stream.PutCString("NO"); + return true; + } + } + + return false; +} + +template <bool is_sel_ptr> +bool lldb_private::formatters::ObjCSELSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + lldb::ValueObjectSP valobj_sp; + + CompilerType charstar(valobj.GetCompilerType() + .GetBasicTypeFromAST(eBasicTypeChar) + .GetPointerType()); + + if (!charstar) + return false; + + ExecutionContext exe_ctx(valobj.GetExecutionContextRef()); + + if (is_sel_ptr) { + lldb::addr_t data_address = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS); + if (data_address == LLDB_INVALID_ADDRESS) + return false; + valobj_sp = ValueObject::CreateValueObjectFromAddress("text", data_address, + exe_ctx, charstar); + } else { + DataExtractor data; + Status error; + valobj.GetData(data, error); + if (error.Fail()) + return false; + valobj_sp = + ValueObject::CreateValueObjectFromData("text", data, exe_ctx, charstar); + } + + if (!valobj_sp) + return false; + + stream.Printf("%s", valobj_sp->GetSummaryAsCString()); + return true; +} + +// POSIX has an epoch on Jan-1-1970, but Cocoa prefers Jan-1-2001 +// this call gives the POSIX equivalent of the Cocoa epoch +time_t lldb_private::formatters::GetOSXEpoch() { + static time_t epoch = 0; + if (!epoch) { +#ifndef _WIN32 + tzset(); + tm tm_epoch; + tm_epoch.tm_sec = 0; + tm_epoch.tm_hour = 0; + tm_epoch.tm_min = 0; + tm_epoch.tm_mon = 0; + tm_epoch.tm_mday = 1; + tm_epoch.tm_year = 2001 - 1900; + tm_epoch.tm_isdst = -1; + tm_epoch.tm_gmtoff = 0; + tm_epoch.tm_zone = nullptr; + epoch = timegm(&tm_epoch); +#endif + } + return epoch; +} + +template bool lldb_private::formatters::NSDataSummaryProvider<true>( + ValueObject &, Stream &, const TypeSummaryOptions &); + +template bool lldb_private::formatters::NSDataSummaryProvider<false>( + ValueObject &, Stream &, const TypeSummaryOptions &); + +template bool lldb_private::formatters::ObjCSELSummaryProvider<true>( + ValueObject &, Stream &, const TypeSummaryOptions &); + +template bool lldb_private::formatters::ObjCSELSummaryProvider<false>( + ValueObject &, Stream &, const TypeSummaryOptions &); diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/Cocoa.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/Cocoa.h new file mode 100644 index 000000000000..a195d622ce58 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/Cocoa.h @@ -0,0 +1,116 @@ +//===-- Cocoa.h ---------------------------------------------------*- C++ +//-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_COCOA_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_COCOA_H + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/Utility/Stream.h" + +#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h" + +namespace lldb_private { +namespace formatters { +bool NSIndexSetSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +bool NSArraySummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +template <bool needs_at> +bool NSDataSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +bool NSNumberSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +bool NSDecimalNumberSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +bool NSNotificationSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +bool NSTimeZoneSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +bool NSMachPortSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +bool NSDateSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +bool NSBundleSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +bool NSURLSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +extern template bool NSDataSummaryProvider<true>(ValueObject &, Stream &, + const TypeSummaryOptions &); + +extern template bool NSDataSummaryProvider<false>(ValueObject &, Stream &, + const TypeSummaryOptions &); + +SyntheticChildrenFrontEnd * +NSArraySyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +NSIndexPathSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +bool ObjCClassSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +SyntheticChildrenFrontEnd * +ObjCClassSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); + +bool ObjCBOOLSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +bool ObjCBooleanSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +template <bool is_sel_ptr> +bool ObjCSELSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +extern template bool ObjCSELSummaryProvider<true>(ValueObject &, Stream &, + const TypeSummaryOptions &); + +extern template bool ObjCSELSummaryProvider<false>(ValueObject &, Stream &, + const TypeSummaryOptions &); + +bool NSError_SummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +bool NSException_SummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +SyntheticChildrenFrontEnd * +NSErrorSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp); + +SyntheticChildrenFrontEnd * +NSExceptionSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp); + +class NSArray_Additionals { +public: + static std::map<ConstString, CXXFunctionSummaryFormat::Callback> & + GetAdditionalSummaries(); + + static std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback> & + GetAdditionalSynthetics(); +}; +} // namespace formatters +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_COCOA_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CoreMedia.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CoreMedia.cpp new file mode 100644 index 000000000000..1f4991bbfda2 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CoreMedia.cpp @@ -0,0 +1,88 @@ +//===-- CoreMedia.cpp -----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "CoreMedia.h" + +#include "lldb/Utility/Flags.h" +#include "lldb/Utility/Log.h" + +#include "lldb/Symbol/TypeSystem.h" +#include "lldb/Target/Target.h" +#include <cinttypes> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +bool lldb_private::formatters::CMTimeSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + CompilerType type = valobj.GetCompilerType(); + if (!type.IsValid()) + return false; + + auto type_system = type.GetTypeSystem(); + if (!type_system) + return false; + // fetch children by offset to compensate for potential lack of debug info + auto int64_ty = + type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingSint, 64); + auto int32_ty = + type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingSint, 32); + + auto value_sp(valobj.GetSyntheticChildAtOffset(0, int64_ty, true)); + auto timescale_sp(valobj.GetSyntheticChildAtOffset(8, int32_ty, true)); + auto flags_sp(valobj.GetSyntheticChildAtOffset(12, int32_ty, true)); + + if (!value_sp || !timescale_sp || !flags_sp) + return false; + + auto value = value_sp->GetValueAsUnsigned(0); + auto timescale = (int32_t)timescale_sp->GetValueAsUnsigned( + 0); // the timescale specifies the fraction of a second each unit in the + // numerator occupies + auto flags = Flags(flags_sp->GetValueAsUnsigned(0) & + 0x00000000000000FF); // the flags I need sit in the LSB + + const unsigned int FlagPositiveInf = 4; + const unsigned int FlagNegativeInf = 8; + const unsigned int FlagIndefinite = 16; + + if (flags.AnySet(FlagIndefinite)) { + stream.Printf("indefinite"); + return true; + } + + if (flags.AnySet(FlagPositiveInf)) { + stream.Printf("+oo"); + return true; + } + + if (flags.AnySet(FlagNegativeInf)) { + stream.Printf("-oo"); + return true; + } + + switch (timescale) { + case 0: + return false; + case 1: + stream.Printf("%" PRId64 " seconds", value); + return true; + case 2: + stream.Printf("%" PRId64 " half seconds", value); + return true; + case 3: + stream.Printf("%" PRId64 " third%sof a second", value, + value == 1 ? " " : "s "); + return true; + default: + stream.Printf("%" PRId64 " %" PRId32 "th%sof a second", value, timescale, + value == 1 ? " " : "s "); + return true; + } +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CoreMedia.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CoreMedia.h new file mode 100644 index 000000000000..7fd8560d20e1 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CoreMedia.h @@ -0,0 +1,25 @@ +//===-- CoreMedia.h -----------------------------------------------*- C++ +//-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_COREMEDIA_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_COREMEDIA_H + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/Utility/Stream.h" + +namespace lldb_private { +namespace formatters { + +bool CMTimeSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); +} // namespace formatters +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_COREMEDIA_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSArray.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSArray.cpp new file mode 100644 index 000000000000..67d0cd08f51a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSArray.cpp @@ -0,0 +1,868 @@ +//===-- NSArray.cpp -------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/Basic/TargetInfo.h" + +#include "Cocoa.h" + +#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Expression/FunctionCaller.h" +#include "lldb/Target/Language.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace lldb_private { +namespace formatters { +std::map<ConstString, CXXFunctionSummaryFormat::Callback> & +NSArray_Additionals::GetAdditionalSummaries() { + static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map; + return g_map; +} + +std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback> & +NSArray_Additionals::GetAdditionalSynthetics() { + static std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback> + g_map; + return g_map; +} + +class NSArrayMSyntheticFrontEndBase : public SyntheticChildrenFrontEnd { +public: + NSArrayMSyntheticFrontEndBase(lldb::ValueObjectSP valobj_sp); + + ~NSArrayMSyntheticFrontEndBase() override = default; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override = 0; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +protected: + virtual lldb::addr_t GetDataAddress() = 0; + + virtual uint64_t GetUsedCount() = 0; + + virtual uint64_t GetOffset() = 0; + + virtual uint64_t GetSize() = 0; + + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size = 8; + CompilerType m_id_type; +}; + +template <typename D32, typename D64> +class GenericNSArrayMSyntheticFrontEnd : public NSArrayMSyntheticFrontEndBase { +public: + GenericNSArrayMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~GenericNSArrayMSyntheticFrontEnd() override; + + lldb::ChildCacheState Update() override; + +protected: + lldb::addr_t GetDataAddress() override; + + uint64_t GetUsedCount() override; + + uint64_t GetOffset() override; + + uint64_t GetSize() override; + +private: + D32 *m_data_32; + D64 *m_data_64; +}; + +namespace Foundation1010 { + namespace { + struct DataDescriptor_32 { + uint32_t _used; + uint32_t _offset; + uint32_t _size : 28; + uint64_t _priv1 : 4; + uint32_t _priv2; + uint32_t _data; + }; + + struct DataDescriptor_64 { + uint64_t _used; + uint64_t _offset; + uint64_t _size : 60; + uint64_t _priv1 : 4; + uint32_t _priv2; + uint64_t _data; + }; + } + + using NSArrayMSyntheticFrontEnd = + GenericNSArrayMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>; +} + +namespace Foundation1428 { + namespace { + struct DataDescriptor_32 { + uint32_t _used; + uint32_t _offset; + uint32_t _size; + uint32_t _data; + }; + + struct DataDescriptor_64 { + uint64_t _used; + uint64_t _offset; + uint64_t _size; + uint64_t _data; + }; + } + + using NSArrayMSyntheticFrontEnd = + GenericNSArrayMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>; +} + +namespace Foundation1437 { + template <typename PtrType> + struct DataDescriptor { + PtrType _cow; + // __deque + PtrType _data; + uint32_t _offset; + uint32_t _size; + uint32_t _muts; + uint32_t _used; + }; + + using NSArrayMSyntheticFrontEnd = + GenericNSArrayMSyntheticFrontEnd< + DataDescriptor<uint32_t>, DataDescriptor<uint64_t>>; + + template <typename DD> + uint64_t + __NSArrayMSize_Impl(lldb_private::Process &process, + lldb::addr_t valobj_addr, Status &error) { + const lldb::addr_t start_of_descriptor = + valobj_addr + process.GetAddressByteSize(); + DD descriptor = DD(); + process.ReadMemory(start_of_descriptor, &descriptor, + sizeof(descriptor), error); + if (error.Fail()) { + return 0; + } + return descriptor._used; + } + + uint64_t + __NSArrayMSize(lldb_private::Process &process, lldb::addr_t valobj_addr, + Status &error) { + if (process.GetAddressByteSize() == 4) { + return __NSArrayMSize_Impl<DataDescriptor<uint32_t>>(process, valobj_addr, + error); + } else { + return __NSArrayMSize_Impl<DataDescriptor<uint64_t>>(process, valobj_addr, + error); + } + } + +} + +namespace CallStackArray { +struct DataDescriptor_32 { + uint32_t _data; + uint32_t _used; + uint32_t _offset; + const uint32_t _size = 0; +}; + +struct DataDescriptor_64 { + uint64_t _data; + uint64_t _used; + uint64_t _offset; + const uint64_t _size = 0; +}; + +using NSCallStackArraySyntheticFrontEnd = + GenericNSArrayMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>; +} // namespace CallStackArray + +template <typename D32, typename D64, bool Inline> +class GenericNSArrayISyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + GenericNSArrayISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~GenericNSArrayISyntheticFrontEnd() override; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size = 8; + + D32 *m_data_32; + D64 *m_data_64; + CompilerType m_id_type; +}; + +namespace Foundation1300 { + struct IDD32 { + uint32_t used; + uint32_t list; + }; + + struct IDD64 { + uint64_t used; + uint64_t list; + }; + + using NSArrayISyntheticFrontEnd = + GenericNSArrayISyntheticFrontEnd<IDD32, IDD64, true>; +} + +namespace Foundation1430 { + using NSArrayISyntheticFrontEnd = + Foundation1428::NSArrayMSyntheticFrontEnd; +} + +namespace Foundation1436 { + struct IDD32 { + uint32_t used; + uint32_t list; // in Inline cases, this is the first element + }; + + struct IDD64 { + uint64_t used; + uint64_t list; // in Inline cases, this is the first element + }; + + using NSArrayI_TransferSyntheticFrontEnd = + GenericNSArrayISyntheticFrontEnd<IDD32, IDD64, false>; + + using NSArrayISyntheticFrontEnd = + GenericNSArrayISyntheticFrontEnd<IDD32, IDD64, true>; + + using NSFrozenArrayMSyntheticFrontEnd = + Foundation1437::NSArrayMSyntheticFrontEnd; + + uint64_t + __NSFrozenArrayMSize(lldb_private::Process &process, lldb::addr_t valobj_addr, + Status &error) { + return Foundation1437::__NSArrayMSize(process, valobj_addr, error); + } +} + +namespace ConstantArray { + +struct ConstantArray32 { + uint64_t used; + uint32_t list; +}; + +struct ConstantArray64 { + uint64_t used; + uint64_t list; +}; + +using NSConstantArraySyntheticFrontEnd = + GenericNSArrayISyntheticFrontEnd<ConstantArray32, ConstantArray64, false>; +} // namespace ConstantArray + +class NSArray0SyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + NSArray0SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~NSArray0SyntheticFrontEnd() override = default; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; +}; + +class NSArray1SyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + NSArray1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~NSArray1SyntheticFrontEnd() override = default; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; +}; +} // namespace formatters +} // namespace lldb_private + +bool lldb_private::formatters::NSArraySummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + static constexpr llvm::StringLiteral g_TypeHint("NSArray"); + + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor( + runtime->GetClassDescriptor(valobj)); + + if (!descriptor || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + uint64_t value = 0; + + ConstString class_name(descriptor->GetClassName()); + + static const ConstString g_NSArrayI("__NSArrayI"); + static const ConstString g_NSArrayM("__NSArrayM"); + static const ConstString g_NSArrayI_Transfer("__NSArrayI_Transfer"); + static const ConstString g_NSFrozenArrayM("__NSFrozenArrayM"); + static const ConstString g_NSArray0("__NSArray0"); + static const ConstString g_NSArray1("__NSSingleObjectArrayI"); + static const ConstString g_NSArrayCF("__NSCFArray"); + static const ConstString g_NSArrayMLegacy("__NSArrayM_Legacy"); + static const ConstString g_NSArrayMImmutable("__NSArrayM_Immutable"); + static const ConstString g_NSCallStackArray("_NSCallStackArray"); + static const ConstString g_NSConstantArray("NSConstantArray"); + + if (class_name.IsEmpty()) + return false; + + if (class_name == g_NSArrayI) { + Status error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, + ptr_size, 0, error); + if (error.Fail()) + return false; + } else if (class_name == g_NSConstantArray) { + Status error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 8, + 0, error); + if (error.Fail()) + return false; + } else if (class_name == g_NSArrayM) { + AppleObjCRuntime *apple_runtime = + llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime); + Status error; + if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) { + value = Foundation1437::__NSArrayMSize(*process_sp, valobj_addr, error); + } else { + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, + ptr_size, 0, error); + } + if (error.Fail()) + return false; + } else if (class_name == g_NSArrayI_Transfer) { + Status error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, + ptr_size, 0, error); + if (error.Fail()) + return false; + } else if (class_name == g_NSFrozenArrayM) { + Status error; + value = Foundation1436::__NSFrozenArrayMSize(*process_sp, valobj_addr, error); + if (error.Fail()) + return false; + } else if (class_name == g_NSArrayMLegacy) { + Status error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, + ptr_size, 0, error); + if (error.Fail()) + return false; + } else if (class_name == g_NSArrayMImmutable) { + Status error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, + ptr_size, 0, error); + if (error.Fail()) + return false; + } else if (class_name == g_NSArray0) { + value = 0; + } else if (class_name == g_NSArray1) { + value = 1; + } else if (class_name == g_NSArrayCF || class_name == g_NSCallStackArray) { + // __NSCFArray and _NSCallStackArray store the number of elements as a + // pointer-sized value at offset `2 * ptr_size`. + Status error; + value = process_sp->ReadUnsignedIntegerFromMemory( + valobj_addr + 2 * ptr_size, ptr_size, 0, error); + if (error.Fail()) + return false; + } else { + auto &map(NSArray_Additionals::GetAdditionalSummaries()); + auto iter = map.find(class_name), end = map.end(); + if (iter != end) + return iter->second(valobj, stream, options); + else + return false; + } + + llvm::StringRef prefix, suffix; + if (Language *language = Language::FindPlugin(options.GetLanguage())) + std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint); + + stream << prefix; + stream.Printf("%" PRIu64 " %s%s", value, "element", value == 1 ? "" : "s"); + stream << suffix; + return true; +} + +lldb_private::formatters::NSArrayMSyntheticFrontEndBase:: + NSArrayMSyntheticFrontEndBase(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_id_type() { + if (valobj_sp) { + TypeSystemClangSP scratch_ts_sp = ScratchTypeSystemClang::GetForTarget( + *valobj_sp->GetExecutionContextRef().GetTargetSP()); + if (scratch_ts_sp) + m_id_type = CompilerType( + scratch_ts_sp->weak_from_this(), + scratch_ts_sp->getASTContext().ObjCBuiltinIdTy.getAsOpaquePtr()); + if (valobj_sp->GetProcessSP()) + m_ptr_size = valobj_sp->GetProcessSP()->GetAddressByteSize(); + } +} + +template <typename D32, typename D64> +lldb_private::formatters:: + GenericNSArrayMSyntheticFrontEnd<D32, D64>:: + GenericNSArrayMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : NSArrayMSyntheticFrontEndBase(valobj_sp), m_data_32(nullptr), + m_data_64(nullptr) {} + +llvm::Expected<uint32_t> lldb_private::formatters:: + NSArrayMSyntheticFrontEndBase::CalculateNumChildren() { + return GetUsedCount(); +} + +lldb::ValueObjectSP +lldb_private::formatters::NSArrayMSyntheticFrontEndBase::GetChildAtIndex( + uint32_t idx) { + if (idx >= CalculateNumChildrenIgnoringErrors()) + return lldb::ValueObjectSP(); + lldb::addr_t object_at_idx = GetDataAddress(); + size_t pyhs_idx = idx; + pyhs_idx += GetOffset(); + if (GetSize() <= pyhs_idx) + pyhs_idx -= GetSize(); + object_at_idx += (pyhs_idx * m_ptr_size); + StreamString idx_name; + idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); + return CreateValueObjectFromAddress(idx_name.GetString(), object_at_idx, + m_exe_ctx_ref, m_id_type); +} + +template <typename D32, typename D64> +lldb::ChildCacheState +lldb_private::formatters::GenericNSArrayMSyntheticFrontEnd<D32, D64>::Update() { + ValueObjectSP valobj_sp = m_backend.GetSP(); + m_ptr_size = 0; + delete m_data_32; + m_data_32 = nullptr; + delete m_data_64; + m_data_64 = nullptr; + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + Status error; + error.Clear(); + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return lldb::ChildCacheState::eRefetch; + m_ptr_size = process_sp->GetAddressByteSize(); + uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size; + if (m_ptr_size == 4) { + m_data_32 = new D32(); + process_sp->ReadMemory(data_location, m_data_32, sizeof(D32), + error); + } else { + m_data_64 = new D64(); + process_sp->ReadMemory(data_location, m_data_64, sizeof(D64), + error); + } + + return error.Success() ? lldb::ChildCacheState::eReuse + : lldb::ChildCacheState::eRefetch; +} + +bool +lldb_private::formatters::NSArrayMSyntheticFrontEndBase::MightHaveChildren() { + return true; +} + +size_t +lldb_private::formatters::NSArrayMSyntheticFrontEndBase::GetIndexOfChildWithName( + ConstString name) { + const char *item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors()) + return UINT32_MAX; + return idx; +} + +template <typename D32, typename D64> +lldb_private::formatters::GenericNSArrayMSyntheticFrontEnd<D32, D64>:: + GenericNSArrayMSyntheticFrontEnd::~GenericNSArrayMSyntheticFrontEnd() { + delete m_data_32; + m_data_32 = nullptr; + delete m_data_64; + m_data_64 = nullptr; +} + +template <typename D32, typename D64> +lldb::addr_t +lldb_private::formatters:: + GenericNSArrayMSyntheticFrontEnd<D32, D64>:: + GenericNSArrayMSyntheticFrontEnd::GetDataAddress() { + if (!m_data_32 && !m_data_64) + return LLDB_INVALID_ADDRESS; + return m_data_32 ? m_data_32->_data : m_data_64->_data; +} + +template <typename D32, typename D64> +uint64_t +lldb_private::formatters:: + GenericNSArrayMSyntheticFrontEnd<D32, D64>:: + GenericNSArrayMSyntheticFrontEnd::GetUsedCount() { + if (!m_data_32 && !m_data_64) + return 0; + return m_data_32 ? m_data_32->_used : m_data_64->_used; +} + +template <typename D32, typename D64> +uint64_t +lldb_private::formatters:: + GenericNSArrayMSyntheticFrontEnd<D32, D64>:: + GenericNSArrayMSyntheticFrontEnd::GetOffset() { + if (!m_data_32 && !m_data_64) + return 0; + return m_data_32 ? m_data_32->_offset : m_data_64->_offset; +} + +template <typename D32, typename D64> +uint64_t +lldb_private::formatters:: + GenericNSArrayMSyntheticFrontEnd<D32, D64>:: + GenericNSArrayMSyntheticFrontEnd::GetSize() { + if (!m_data_32 && !m_data_64) + return 0; + return m_data_32 ? m_data_32->_size : m_data_64->_size; +} + +template <typename D32, typename D64, bool Inline> +lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>:: + GenericNSArrayISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), + m_data_32(nullptr), m_data_64(nullptr) { + if (valobj_sp) { + CompilerType type = valobj_sp->GetCompilerType(); + if (type) { + TypeSystemClangSP scratch_ts_sp = ScratchTypeSystemClang::GetForTarget( + *valobj_sp->GetExecutionContextRef().GetTargetSP()); + if (scratch_ts_sp) + m_id_type = scratch_ts_sp->GetType( + scratch_ts_sp->getASTContext().ObjCBuiltinIdTy); + } + } +} + +template <typename D32, typename D64, bool Inline> +lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>:: + GenericNSArrayISyntheticFrontEnd::~GenericNSArrayISyntheticFrontEnd() { + delete m_data_32; + m_data_32 = nullptr; + delete m_data_64; + m_data_64 = nullptr; +} + +template <typename D32, typename D64, bool Inline> +size_t +lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>:: + GetIndexOfChildWithName(ConstString name) { + const char *item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors()) + return UINT32_MAX; + return idx; +} + +template <typename D32, typename D64, bool Inline> +llvm::Expected<uint32_t> +lldb_private::formatters::GenericNSArrayISyntheticFrontEnd< + D32, D64, Inline>::CalculateNumChildren() { + return m_data_32 ? m_data_32->used : m_data_64->used; +} + +template <typename D32, typename D64, bool Inline> +lldb::ChildCacheState +lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, + Inline>::Update() { + ValueObjectSP valobj_sp = m_backend.GetSP(); + m_ptr_size = 0; + delete m_data_32; + m_data_32 = nullptr; + delete m_data_64; + m_data_64 = nullptr; + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + Status error; + error.Clear(); + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return lldb::ChildCacheState::eRefetch; + m_ptr_size = process_sp->GetAddressByteSize(); + uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size; + if (m_ptr_size == 4) { + m_data_32 = new D32(); + process_sp->ReadMemory(data_location, m_data_32, sizeof(D32), + error); + } else { + m_data_64 = new D64(); + process_sp->ReadMemory(data_location, m_data_64, sizeof(D64), + error); + } + + return error.Success() ? lldb::ChildCacheState::eReuse + : lldb::ChildCacheState::eRefetch; +} + +template <typename D32, typename D64, bool Inline> +bool +lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>:: + MightHaveChildren() { + return true; +} + +template <typename D32, typename D64, bool Inline> +lldb::ValueObjectSP +lldb_private::formatters::GenericNSArrayISyntheticFrontEnd<D32, D64, Inline>:: + GetChildAtIndex(uint32_t idx) { + if (idx >= CalculateNumChildrenIgnoringErrors()) + return lldb::ValueObjectSP(); + lldb::addr_t object_at_idx; + if (Inline) { + object_at_idx = m_backend.GetSP()->GetValueAsUnsigned(0) + m_ptr_size; + object_at_idx += m_ptr_size == 4 ? sizeof(D32) : sizeof(D64); // skip the data header + object_at_idx -= m_ptr_size; // we treat the last entry in the data header as the first pointer + } else { + object_at_idx = m_data_32 ? m_data_32->list : m_data_64->list; + } + object_at_idx += (idx * m_ptr_size); + + ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); + if (!process_sp) + return lldb::ValueObjectSP(); + Status error; + if (error.Fail()) + return lldb::ValueObjectSP(); + StreamString idx_name; + idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); + return CreateValueObjectFromAddress(idx_name.GetString(), object_at_idx, + m_exe_ctx_ref, m_id_type); +} + +lldb_private::formatters::NSArray0SyntheticFrontEnd::NSArray0SyntheticFrontEnd( + lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) {} + +size_t +lldb_private::formatters::NSArray0SyntheticFrontEnd::GetIndexOfChildWithName( + ConstString name) { + return UINT32_MAX; +} + +llvm::Expected<uint32_t> +lldb_private::formatters::NSArray0SyntheticFrontEnd::CalculateNumChildren() { + return 0; +} + +lldb::ChildCacheState +lldb_private::formatters::NSArray0SyntheticFrontEnd::Update() { + return lldb::ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::NSArray0SyntheticFrontEnd::MightHaveChildren() { + return false; +} + +lldb::ValueObjectSP +lldb_private::formatters::NSArray0SyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + return lldb::ValueObjectSP(); +} + +lldb_private::formatters::NSArray1SyntheticFrontEnd::NSArray1SyntheticFrontEnd( + lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp.get()) {} + +size_t +lldb_private::formatters::NSArray1SyntheticFrontEnd::GetIndexOfChildWithName( + ConstString name) { + static const ConstString g_zero("[0]"); + + if (name == g_zero) + return 0; + + return UINT32_MAX; +} + +llvm::Expected<uint32_t> +lldb_private::formatters::NSArray1SyntheticFrontEnd::CalculateNumChildren() { + return 1; +} + +lldb::ChildCacheState +lldb_private::formatters::NSArray1SyntheticFrontEnd::Update() { + return lldb::ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::NSArray1SyntheticFrontEnd::MightHaveChildren() { + return true; +} + +lldb::ValueObjectSP +lldb_private::formatters::NSArray1SyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + static const ConstString g_zero("[0]"); + + if (idx == 0) { + TypeSystemClangSP scratch_ts_sp = + ScratchTypeSystemClang::GetForTarget(*m_backend.GetTargetSP()); + if (scratch_ts_sp) { + CompilerType id_type(scratch_ts_sp->GetBasicType(lldb::eBasicTypeObjCID)); + return m_backend.GetSyntheticChildAtOffset( + m_backend.GetProcessSP()->GetAddressByteSize(), id_type, true, + g_zero); + } + } + return lldb::ValueObjectSP(); +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::NSArraySyntheticFrontEndCreator( + CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return nullptr; + AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>( + ObjCLanguageRuntime::Get(*process_sp)); + if (!runtime) + return nullptr; + + CompilerType valobj_type(valobj_sp->GetCompilerType()); + Flags flags(valobj_type.GetTypeInfo()); + + if (flags.IsClear(eTypeIsPointer)) { + Status error; + valobj_sp = valobj_sp->AddressOf(error); + if (error.Fail() || !valobj_sp) + return nullptr; + } + + ObjCLanguageRuntime::ClassDescriptorSP descriptor( + runtime->GetClassDescriptor(*valobj_sp)); + + if (!descriptor || !descriptor->IsValid()) + return nullptr; + + ConstString class_name(descriptor->GetClassName()); + + static const ConstString g_NSArrayI("__NSArrayI"); + static const ConstString g_NSConstantArray("NSConstantArray"); + static const ConstString g_NSArrayI_Transfer("__NSArrayI_Transfer"); + static const ConstString g_NSFrozenArrayM("__NSFrozenArrayM"); + static const ConstString g_NSArrayM("__NSArrayM"); + static const ConstString g_NSArray0("__NSArray0"); + static const ConstString g_NSArray1("__NSSingleObjectArrayI"); + static const ConstString g_NSArrayMLegacy("__NSArrayM_Legacy"); + static const ConstString g_NSArrayMImmutable("__NSArrayM_Immutable"); + static const ConstString g_NSCallStackArray("_NSCallStackArray"); + + if (class_name.IsEmpty()) + return nullptr; + + if (class_name == g_NSArrayI) { + if (runtime->GetFoundationVersion() >= 1436) + return (new Foundation1436::NSArrayISyntheticFrontEnd(valobj_sp)); + if (runtime->GetFoundationVersion() >= 1430) + return (new Foundation1430::NSArrayISyntheticFrontEnd(valobj_sp)); + return (new Foundation1300::NSArrayISyntheticFrontEnd(valobj_sp)); + } else if (class_name == g_NSArrayI_Transfer) { + return (new Foundation1436::NSArrayI_TransferSyntheticFrontEnd(valobj_sp)); + } else if (class_name == g_NSConstantArray) { + return new ConstantArray::NSConstantArraySyntheticFrontEnd(valobj_sp); + } else if (class_name == g_NSFrozenArrayM) { + return (new Foundation1436::NSFrozenArrayMSyntheticFrontEnd(valobj_sp)); + } else if (class_name == g_NSArray0) { + return (new NSArray0SyntheticFrontEnd(valobj_sp)); + } else if (class_name == g_NSArray1) { + return (new NSArray1SyntheticFrontEnd(valobj_sp)); + } else if (class_name == g_NSArrayM) { + if (runtime->GetFoundationVersion() >= 1437) + return (new Foundation1437::NSArrayMSyntheticFrontEnd(valobj_sp)); + if (runtime->GetFoundationVersion() >= 1428) + return (new Foundation1428::NSArrayMSyntheticFrontEnd(valobj_sp)); + if (runtime->GetFoundationVersion() >= 1100) + return (new Foundation1010::NSArrayMSyntheticFrontEnd(valobj_sp)); + } else if (class_name == g_NSCallStackArray) { + return (new CallStackArray::NSCallStackArraySyntheticFrontEnd(valobj_sp)); + } else { + auto &map(NSArray_Additionals::GetAdditionalSynthetics()); + auto iter = map.find(class_name), end = map.end(); + if (iter != end) + return iter->second(synth, valobj_sp); + } + + return nullptr; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp new file mode 100644 index 000000000000..ec6fd756394a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp @@ -0,0 +1,1388 @@ +//===-- NSDictionary.cpp --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <mutex> + +#include "clang/AST/DeclCXX.h" + +#include "CFBasicHash.h" +#include "NSDictionary.h" + +#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Target/Language.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +NSDictionary_Additionals::AdditionalFormatterMatching::Prefix::Prefix( + ConstString p) + : m_prefix(p) {} + +bool NSDictionary_Additionals::AdditionalFormatterMatching::Prefix::Match( + ConstString class_name) { + return class_name.GetStringRef().starts_with(m_prefix.GetStringRef()); +} + +NSDictionary_Additionals::AdditionalFormatterMatching::Full::Full(ConstString n) + : m_name(n) {} + +bool NSDictionary_Additionals::AdditionalFormatterMatching::Full::Match( + ConstString class_name) { + return (class_name == m_name); +} + +NSDictionary_Additionals::AdditionalFormatters< + CXXFunctionSummaryFormat::Callback> & +NSDictionary_Additionals::GetAdditionalSummaries() { + static AdditionalFormatters<CXXFunctionSummaryFormat::Callback> g_map; + return g_map; +} + +NSDictionary_Additionals::AdditionalFormatters< + CXXSyntheticChildren::CreateFrontEndCallback> & +NSDictionary_Additionals::GetAdditionalSynthetics() { + static AdditionalFormatters<CXXSyntheticChildren::CreateFrontEndCallback> + g_map; + return g_map; +} + +static CompilerType GetLLDBNSPairType(TargetSP target_sp) { + CompilerType compiler_type; + TypeSystemClangSP scratch_ts_sp = + ScratchTypeSystemClang::GetForTarget(*target_sp); + + if (!scratch_ts_sp) + return compiler_type; + + static constexpr llvm::StringLiteral g_lldb_autogen_nspair("__lldb_autogen_nspair"); + + compiler_type = scratch_ts_sp->GetTypeForIdentifier<clang::CXXRecordDecl>(g_lldb_autogen_nspair); + + if (!compiler_type) { + compiler_type = scratch_ts_sp->CreateRecordType( + nullptr, OptionalClangModuleID(), lldb::eAccessPublic, + g_lldb_autogen_nspair, llvm::to_underlying(clang::TagTypeKind::Struct), + lldb::eLanguageTypeC); + + if (compiler_type) { + TypeSystemClang::StartTagDeclarationDefinition(compiler_type); + CompilerType id_compiler_type = + scratch_ts_sp->GetBasicType(eBasicTypeObjCID); + TypeSystemClang::AddFieldToRecordType( + compiler_type, "key", id_compiler_type, lldb::eAccessPublic, 0); + TypeSystemClang::AddFieldToRecordType( + compiler_type, "value", id_compiler_type, lldb::eAccessPublic, 0); + TypeSystemClang::CompleteTagDeclarationDefinition(compiler_type); + } + } + return compiler_type; +} + +namespace lldb_private { +namespace formatters { +class NSDictionaryISyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~NSDictionaryISyntheticFrontEnd() override; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + struct DataDescriptor_32 { + uint32_t _used : 26; + uint32_t _szidx : 6; + }; + + struct DataDescriptor_64 { + uint64_t _used : 58; + uint32_t _szidx : 6; + }; + + struct DictionaryItemDescriptor { + lldb::addr_t key_ptr; + lldb::addr_t val_ptr; + lldb::ValueObjectSP valobj_sp; + }; + + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size = 8; + lldb::ByteOrder m_order = lldb::eByteOrderInvalid; + DataDescriptor_32 *m_data_32 = nullptr; + DataDescriptor_64 *m_data_64 = nullptr; + lldb::addr_t m_data_ptr = LLDB_INVALID_ADDRESS; + CompilerType m_pair_type; + std::vector<DictionaryItemDescriptor> m_children; +}; + +class NSConstantDictionarySyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + NSConstantDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + ExecutionContextRef m_exe_ctx_ref; + CompilerType m_pair_type; + uint8_t m_ptr_size = 8; + lldb::ByteOrder m_order = lldb::eByteOrderInvalid; + unsigned int m_size = 0; + lldb::addr_t m_keys_ptr = LLDB_INVALID_ADDRESS; + lldb::addr_t m_objects_ptr = LLDB_INVALID_ADDRESS; + + struct DictionaryItemDescriptor { + lldb::addr_t key_ptr; + lldb::addr_t val_ptr; + lldb::ValueObjectSP valobj_sp; + }; + + std::vector<DictionaryItemDescriptor> m_children; +}; + +class NSCFDictionarySyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + NSCFDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + struct DictionaryItemDescriptor { + lldb::addr_t key_ptr; + lldb::addr_t val_ptr; + lldb::ValueObjectSP valobj_sp; + }; + + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size = 8; + lldb::ByteOrder m_order = lldb::eByteOrderInvalid; + + CFBasicHash m_hashtable; + + CompilerType m_pair_type; + std::vector<DictionaryItemDescriptor> m_children; +}; + +class NSDictionary1SyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~NSDictionary1SyntheticFrontEnd() override = default; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + ValueObjectSP m_pair; +}; + +template <typename D32, typename D64> +class GenericNSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~GenericNSDictionaryMSyntheticFrontEnd() override; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + struct DictionaryItemDescriptor { + lldb::addr_t key_ptr; + lldb::addr_t val_ptr; + lldb::ValueObjectSP valobj_sp; + }; + + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size = 8; + lldb::ByteOrder m_order = lldb::eByteOrderInvalid; + D32 *m_data_32; + D64 *m_data_64; + CompilerType m_pair_type; + std::vector<DictionaryItemDescriptor> m_children; +}; + +namespace Foundation1100 { + class NSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd { + public: + NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~NSDictionaryMSyntheticFrontEnd() override; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + + private: + struct DataDescriptor_32 { + uint32_t _used : 26; + uint32_t _kvo : 1; + uint32_t _size; + uint32_t _mutations; + uint32_t _objs_addr; + uint32_t _keys_addr; + }; + + struct DataDescriptor_64 { + uint64_t _used : 58; + uint32_t _kvo : 1; + uint64_t _size; + uint64_t _mutations; + uint64_t _objs_addr; + uint64_t _keys_addr; + }; + + struct DictionaryItemDescriptor { + lldb::addr_t key_ptr; + lldb::addr_t val_ptr; + lldb::ValueObjectSP valobj_sp; + }; + + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size = 8; + lldb::ByteOrder m_order = lldb::eByteOrderInvalid; + DataDescriptor_32 *m_data_32 = nullptr; + DataDescriptor_64 *m_data_64 = nullptr; + CompilerType m_pair_type; + std::vector<DictionaryItemDescriptor> m_children; + }; +} + +namespace Foundation1428 { + namespace { + struct DataDescriptor_32 { + uint32_t _used : 26; + uint32_t _kvo : 1; + uint32_t _size; + uint32_t _buffer; + uint64_t GetSize() { return _size; } + }; + + struct DataDescriptor_64 { + uint64_t _used : 58; + uint32_t _kvo : 1; + uint64_t _size; + uint64_t _buffer; + uint64_t GetSize() { return _size; } + }; + } + + using NSDictionaryMSyntheticFrontEnd = + GenericNSDictionaryMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>; +} + +namespace Foundation1437 { + static const uint64_t NSDictionaryCapacities[] = { + 0, 3, 7, 13, 23, 41, 71, 127, 191, 251, 383, 631, 1087, 1723, + 2803, 4523, 7351, 11959, 19447, 31231, 50683, 81919, 132607, + 214519, 346607, 561109, 907759, 1468927, 2376191, 3845119, + 6221311, 10066421, 16287743, 26354171, 42641881, 68996069, + 111638519, 180634607, 292272623, 472907251 + }; + + static const size_t NSDictionaryNumSizeBuckets = + sizeof(NSDictionaryCapacities) / sizeof(uint64_t); + + namespace { + struct DataDescriptor_32 { + uint32_t _buffer; + uint32_t _muts; + uint32_t _used : 25; + uint32_t _kvo : 1; + uint32_t _szidx : 6; + + uint64_t GetSize() { + return (_szidx) >= NSDictionaryNumSizeBuckets ? + 0 : NSDictionaryCapacities[_szidx]; + } + }; + + struct DataDescriptor_64 { + uint64_t _buffer; + uint32_t _muts; + uint32_t _used : 25; + uint32_t _kvo : 1; + uint32_t _szidx : 6; + + uint64_t GetSize() { + return (_szidx) >= NSDictionaryNumSizeBuckets ? + 0 : NSDictionaryCapacities[_szidx]; + } + }; + } // namespace + + using NSDictionaryMSyntheticFrontEnd = + GenericNSDictionaryMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>; + + template <typename DD> + uint64_t + __NSDictionaryMSize_Impl(lldb_private::Process &process, + lldb::addr_t valobj_addr, Status &error) { + const lldb::addr_t start_of_descriptor = + valobj_addr + process.GetAddressByteSize(); + DD descriptor = DD(); + process.ReadMemory(start_of_descriptor, &descriptor, sizeof(descriptor), + error); + if (error.Fail()) { + return 0; + } + return descriptor._used; + } + + uint64_t + __NSDictionaryMSize(lldb_private::Process &process, lldb::addr_t valobj_addr, + Status &error) { + if (process.GetAddressByteSize() == 4) { + return __NSDictionaryMSize_Impl<DataDescriptor_32>(process, valobj_addr, + error); + } else { + return __NSDictionaryMSize_Impl<DataDescriptor_64>(process, valobj_addr, + error); + } + } + +} +} // namespace formatters +} // namespace lldb_private + +template <bool name_entries> +bool lldb_private::formatters::NSDictionarySummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + static constexpr llvm::StringLiteral g_TypeHint("NSDictionary"); + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor( + runtime->GetNonKVOClassDescriptor(valobj)); + + if (!descriptor || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + bool is_64bit = (ptr_size == 8); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + uint64_t value = 0; + + ConstString class_name(descriptor->GetClassName()); + + static const ConstString g_DictionaryI("__NSDictionaryI"); + static const ConstString g_DictionaryM("__NSDictionaryM"); + static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy"); + static const ConstString g_DictionaryMImmutable("__NSDictionaryM_Immutable"); + static const ConstString g_DictionaryMFrozen("__NSFrozenDictionaryM"); + static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI"); + static const ConstString g_Dictionary0("__NSDictionary0"); + static const ConstString g_DictionaryCF("__CFDictionary"); + static const ConstString g_DictionaryNSCF("__NSCFDictionary"); + static const ConstString g_DictionaryCFRef("CFDictionaryRef"); + static const ConstString g_ConstantDictionary("NSConstantDictionary"); + + if (class_name.IsEmpty()) + return false; + + if (class_name == g_DictionaryI || class_name == g_DictionaryMImmutable) { + Status error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, + ptr_size, 0, error); + if (error.Fail()) + return false; + + value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U); + } else if (class_name == g_ConstantDictionary) { + Status error; + value = process_sp->ReadUnsignedIntegerFromMemory( + valobj_addr + 2 * ptr_size, ptr_size, 0, error); + if (error.Fail()) + return false; + } else if (class_name == g_DictionaryM || class_name == g_DictionaryMLegacy || + class_name == g_DictionaryMFrozen) { + AppleObjCRuntime *apple_runtime = + llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime); + Status error; + if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) { + value = Foundation1437::__NSDictionaryMSize(*process_sp, valobj_addr, + error); + } else { + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, + ptr_size, 0, error); + value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U); + } + if (error.Fail()) + return false; + } else if (class_name == g_Dictionary1) { + value = 1; + } else if (class_name == g_Dictionary0) { + value = 0; + } else if (class_name == g_DictionaryCF || class_name == g_DictionaryNSCF || + class_name == g_DictionaryCFRef) { + ExecutionContext exe_ctx(process_sp); + CFBasicHash cfbh; + if (!cfbh.Update(valobj_addr, exe_ctx)) + return false; + value = cfbh.GetCount(); + } else { + auto &map(NSDictionary_Additionals::GetAdditionalSummaries()); + for (auto &candidate : map) { + if (candidate.first && candidate.first->Match(class_name)) + return candidate.second(valobj, stream, options); + } + return false; + } + + llvm::StringRef prefix, suffix; + if (Language *language = Language::FindPlugin(options.GetLanguage())) + std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint); + + stream << prefix; + stream.Printf("%" PRIu64 " %s%s", value, "key/value pair", + value == 1 ? "" : "s"); + stream << suffix; + return true; +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::NSDictionarySyntheticFrontEndCreator( + CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) { + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return nullptr; + AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>( + ObjCLanguageRuntime::Get(*process_sp)); + if (!runtime) + return nullptr; + + CompilerType valobj_type(valobj_sp->GetCompilerType()); + Flags flags(valobj_type.GetTypeInfo()); + + if (flags.IsClear(eTypeIsPointer)) { + Status error; + valobj_sp = valobj_sp->AddressOf(error); + if (error.Fail() || !valobj_sp) + return nullptr; + } + + ObjCLanguageRuntime::ClassDescriptorSP descriptor( + runtime->GetClassDescriptor(*valobj_sp)); + + if (!descriptor || !descriptor->IsValid()) + return nullptr; + + ConstString class_name(descriptor->GetClassName()); + + static const ConstString g_DictionaryI("__NSDictionaryI"); + static const ConstString g_DictionaryM("__NSDictionaryM"); + static const ConstString g_Dictionary1("__NSSingleEntryDictionaryI"); + static const ConstString g_DictionaryImmutable("__NSDictionaryM_Immutable"); + static const ConstString g_DictionaryMFrozen("__NSFrozenDictionaryM"); + static const ConstString g_DictionaryMLegacy("__NSDictionaryM_Legacy"); + static const ConstString g_Dictionary0("__NSDictionary0"); + static const ConstString g_DictionaryCF("__CFDictionary"); + static const ConstString g_DictionaryNSCF("__NSCFDictionary"); + static const ConstString g_DictionaryCFRef("CFDictionaryRef"); + static const ConstString g_ConstantDictionary("NSConstantDictionary"); + + if (class_name.IsEmpty()) + return nullptr; + + if (class_name == g_DictionaryI) { + return (new NSDictionaryISyntheticFrontEnd(valobj_sp)); + } else if (class_name == g_ConstantDictionary) { + return (new NSConstantDictionarySyntheticFrontEnd(valobj_sp)); + } else if (class_name == g_DictionaryM || class_name == g_DictionaryMFrozen) { + if (runtime->GetFoundationVersion() >= 1437) { + return (new Foundation1437::NSDictionaryMSyntheticFrontEnd(valobj_sp)); + } else if (runtime->GetFoundationVersion() >= 1428) { + return (new Foundation1428::NSDictionaryMSyntheticFrontEnd(valobj_sp)); + } else { + return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp)); + } + } else if (class_name == g_DictionaryMLegacy) { + return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp)); + } else if (class_name == g_Dictionary1) { + return (new NSDictionary1SyntheticFrontEnd(valobj_sp)); + } else if (class_name == g_DictionaryCF || class_name == g_DictionaryNSCF || + class_name == g_DictionaryCFRef) { + return (new NSCFDictionarySyntheticFrontEnd(valobj_sp)); + } else { + auto &map(NSDictionary_Additionals::GetAdditionalSynthetics()); + for (auto &candidate : map) { + if (candidate.first && candidate.first->Match((class_name))) + return candidate.second(synth, valobj_sp); + } + } + + return nullptr; +} + +lldb_private::formatters::NSDictionaryISyntheticFrontEnd:: + NSDictionaryISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_pair_type() {} + +lldb_private::formatters::NSDictionaryISyntheticFrontEnd:: + ~NSDictionaryISyntheticFrontEnd() { + delete m_data_32; + m_data_32 = nullptr; + delete m_data_64; + m_data_64 = nullptr; +} + +size_t lldb_private::formatters::NSDictionaryISyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + const char *item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors()) + return UINT32_MAX; + return idx; +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + NSDictionaryISyntheticFrontEnd::CalculateNumChildren() { + if (!m_data_32 && !m_data_64) + return 0; + return (m_data_32 ? m_data_32->_used : m_data_64->_used); +} + +lldb::ChildCacheState +lldb_private::formatters::NSDictionaryISyntheticFrontEnd::Update() { + m_children.clear(); + delete m_data_32; + m_data_32 = nullptr; + delete m_data_64; + m_data_64 = nullptr; + m_ptr_size = 0; + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + Status error; + error.Clear(); + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return lldb::ChildCacheState::eRefetch; + m_ptr_size = process_sp->GetAddressByteSize(); + m_order = process_sp->GetByteOrder(); + uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size; + if (m_ptr_size == 4) { + m_data_32 = new DataDescriptor_32(); + process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32), + error); + } else { + m_data_64 = new DataDescriptor_64(); + process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64), + error); + } + if (error.Fail()) + return lldb::ChildCacheState::eRefetch; + m_data_ptr = data_location + m_ptr_size; + return lldb::ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::NSDictionaryISyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +lldb::ValueObjectSP +lldb_private::formatters::NSDictionaryISyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + uint32_t num_children = CalculateNumChildrenIgnoringErrors(); + + if (idx >= num_children) + return lldb::ValueObjectSP(); + + if (m_children.empty()) { + // do the scan phase + lldb::addr_t key_at_idx = 0, val_at_idx = 0; + + uint32_t tries = 0; + uint32_t test_idx = 0; + + while (tries < num_children) { + key_at_idx = m_data_ptr + (2 * test_idx * m_ptr_size); + val_at_idx = key_at_idx + m_ptr_size; + ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); + if (!process_sp) + return lldb::ValueObjectSP(); + Status error; + key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + + test_idx++; + + if (!key_at_idx || !val_at_idx) + continue; + tries++; + + DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx, + lldb::ValueObjectSP()}; + + m_children.push_back(descriptor); + } + } + + if (idx >= m_children.size()) // should never happen + return lldb::ValueObjectSP(); + + DictionaryItemDescriptor &dict_item = m_children[idx]; + if (!dict_item.valobj_sp) { + if (!m_pair_type.IsValid()) { + TargetSP target_sp(m_backend.GetTargetSP()); + if (!target_sp) + return ValueObjectSP(); + m_pair_type = GetLLDBNSPairType(target_sp); + } + if (!m_pair_type.IsValid()) + return ValueObjectSP(); + + WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0)); + + if (m_ptr_size == 8) { + uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes(); + *data_ptr = dict_item.key_ptr; + *(data_ptr + 1) = dict_item.val_ptr; + } else { + uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes(); + *data_ptr = dict_item.key_ptr; + *(data_ptr + 1) = dict_item.val_ptr; + } + + StreamString idx_name; + idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); + DataExtractor data(buffer_sp, m_order, m_ptr_size); + dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data, + m_exe_ctx_ref, m_pair_type); + } + return dict_item.valobj_sp; +} + +lldb_private::formatters::NSCFDictionarySyntheticFrontEnd:: + NSCFDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_hashtable(), + m_pair_type() {} + +size_t lldb_private::formatters::NSCFDictionarySyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + const char *item_name = name.GetCString(); + const uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors()) + return UINT32_MAX; + return idx; +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + NSCFDictionarySyntheticFrontEnd::CalculateNumChildren() { + if (!m_hashtable.IsValid()) + return 0; + return m_hashtable.GetCount(); +} + +lldb::ChildCacheState +lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::Update() { + m_children.clear(); + ValueObjectSP valobj_sp = m_backend.GetSP(); + m_ptr_size = 0; + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return lldb::ChildCacheState::eRefetch; + m_ptr_size = process_sp->GetAddressByteSize(); + m_order = process_sp->GetByteOrder(); + return m_hashtable.Update(valobj_sp->GetValueAsUnsigned(0), m_exe_ctx_ref) + ? lldb::ChildCacheState::eReuse + : lldb::ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::NSCFDictionarySyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +lldb::ValueObjectSP +lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + lldb::addr_t m_keys_ptr = m_hashtable.GetKeyPointer(); + lldb::addr_t m_values_ptr = m_hashtable.GetValuePointer(); + + const uint32_t num_children = CalculateNumChildrenIgnoringErrors(); + + if (idx >= num_children) + return lldb::ValueObjectSP(); + + if (m_children.empty()) { + ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); + if (!process_sp) + return lldb::ValueObjectSP(); + + Status error; + lldb::addr_t key_at_idx = 0, val_at_idx = 0; + + uint32_t tries = 0; + uint32_t test_idx = 0; + + // Iterate over inferior memory, reading key/value pointers by shifting each + // cursor by test_index * m_ptr_size. Returns an empty ValueObject if a read + // fails, otherwise, continue until the number of tries matches the number + // of childen. + while (tries < num_children) { + key_at_idx = m_keys_ptr + (test_idx * m_ptr_size); + val_at_idx = m_values_ptr + (test_idx * m_ptr_size); + + key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + + test_idx++; + + if (!key_at_idx || !val_at_idx) + continue; + tries++; + + DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx, + lldb::ValueObjectSP()}; + + m_children.push_back(descriptor); + } + } + + if (idx >= m_children.size()) // should never happen + return lldb::ValueObjectSP(); + + DictionaryItemDescriptor &dict_item = m_children[idx]; + if (!dict_item.valobj_sp) { + if (!m_pair_type.IsValid()) { + TargetSP target_sp(m_backend.GetTargetSP()); + if (!target_sp) + return ValueObjectSP(); + m_pair_type = GetLLDBNSPairType(target_sp); + } + if (!m_pair_type.IsValid()) + return ValueObjectSP(); + + WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0)); + + switch (m_ptr_size) { + case 0: // architecture has no clue - fail + return lldb::ValueObjectSP(); + case 4: { + uint32_t *data_ptr = reinterpret_cast<uint32_t *>(buffer_sp->GetBytes()); + *data_ptr = dict_item.key_ptr; + *(data_ptr + 1) = dict_item.val_ptr; + } break; + case 8: { + uint64_t *data_ptr = reinterpret_cast<uint64_t *>(buffer_sp->GetBytes()); + *data_ptr = dict_item.key_ptr; + *(data_ptr + 1) = dict_item.val_ptr; + } break; + default: + lldbassert(false && "pointer size is not 4 nor 8"); + } + + StreamString idx_name; + idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); + DataExtractor data(buffer_sp, m_order, m_ptr_size); + dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data, + m_exe_ctx_ref, m_pair_type); + } + return dict_item.valobj_sp; +} + +lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd:: + NSConstantDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) {} + +size_t lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + const char *item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors()) + return UINT32_MAX; + return idx; +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + NSConstantDictionarySyntheticFrontEnd::CalculateNumChildren() { + return m_size; +} + +lldb::ChildCacheState +lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::Update() { + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + Status error; + error.Clear(); + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return lldb::ChildCacheState::eRefetch; + m_ptr_size = process_sp->GetAddressByteSize(); + m_order = process_sp->GetByteOrder(); + uint64_t valobj_addr = valobj_sp->GetValueAsUnsigned(0); + m_size = process_sp->ReadUnsignedIntegerFromMemory( + valobj_addr + 2 * m_ptr_size, m_ptr_size, 0, error); + if (error.Fail()) + return lldb::ChildCacheState::eRefetch; + m_keys_ptr = + process_sp->ReadPointerFromMemory(valobj_addr + 3 * m_ptr_size, error); + if (error.Fail()) + return lldb::ChildCacheState::eRefetch; + m_objects_ptr = + process_sp->ReadPointerFromMemory(valobj_addr + 4 * m_ptr_size, error); + + return error.Success() ? lldb::ChildCacheState::eReuse + : lldb::ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +lldb::ValueObjectSP lldb_private::formatters:: + NSConstantDictionarySyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { + uint32_t num_children = CalculateNumChildrenIgnoringErrors(); + + if (idx >= num_children) + return lldb::ValueObjectSP(); + + if (m_children.empty()) { + // do the scan phase + lldb::addr_t key_at_idx = 0, val_at_idx = 0; + ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); + if (!process_sp) + return lldb::ValueObjectSP(); + + for (unsigned int child = 0; child < num_children; ++child) { + Status error; + key_at_idx = process_sp->ReadPointerFromMemory( + m_keys_ptr + child * m_ptr_size, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + val_at_idx = process_sp->ReadPointerFromMemory( + m_objects_ptr + child * m_ptr_size, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx, + lldb::ValueObjectSP()}; + m_children.push_back(descriptor); + } + } + + if (idx >= m_children.size()) // should never happen + return lldb::ValueObjectSP(); + + DictionaryItemDescriptor &dict_item = m_children[idx]; + if (!dict_item.valobj_sp) { + if (!m_pair_type.IsValid()) { + TargetSP target_sp(m_backend.GetTargetSP()); + if (!target_sp) + return ValueObjectSP(); + m_pair_type = GetLLDBNSPairType(target_sp); + } + if (!m_pair_type.IsValid()) + return ValueObjectSP(); + + WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0)); + + if (m_ptr_size == 8) { + uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes(); + *data_ptr = dict_item.key_ptr; + *(data_ptr + 1) = dict_item.val_ptr; + } else { + uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes(); + *data_ptr = dict_item.key_ptr; + *(data_ptr + 1) = dict_item.val_ptr; + } + + StreamString idx_name; + idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); + DataExtractor data(buffer_sp, m_order, m_ptr_size); + dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data, + m_exe_ctx_ref, m_pair_type); + } + return dict_item.valobj_sp; +} + +lldb_private::formatters::NSDictionary1SyntheticFrontEnd:: + NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_pair(nullptr) {} + +size_t lldb_private::formatters::NSDictionary1SyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + static const ConstString g_zero("[0]"); + return name == g_zero ? 0 : UINT32_MAX; +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + NSDictionary1SyntheticFrontEnd::CalculateNumChildren() { + return 1; +} + +lldb::ChildCacheState +lldb_private::formatters::NSDictionary1SyntheticFrontEnd::Update() { + m_pair.reset(); + return lldb::ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::NSDictionary1SyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +lldb::ValueObjectSP +lldb_private::formatters::NSDictionary1SyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + if (idx != 0) + return lldb::ValueObjectSP(); + + if (m_pair.get()) + return m_pair; + + auto process_sp(m_backend.GetProcessSP()); + if (!process_sp) + return nullptr; + + auto ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t key_ptr = + m_backend.GetValueAsUnsigned(LLDB_INVALID_ADDRESS) + ptr_size; + lldb::addr_t value_ptr = key_ptr + ptr_size; + + Status error; + + lldb::addr_t value_at_idx = process_sp->ReadPointerFromMemory(key_ptr, error); + if (error.Fail()) + return nullptr; + lldb::addr_t key_at_idx = process_sp->ReadPointerFromMemory(value_ptr, error); + if (error.Fail()) + return nullptr; + + auto pair_type = + GetLLDBNSPairType(process_sp->GetTarget().shared_from_this()); + + WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * ptr_size, 0)); + + if (ptr_size == 8) { + uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes(); + *data_ptr = key_at_idx; + *(data_ptr + 1) = value_at_idx; + } else { + uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes(); + *data_ptr = key_at_idx; + *(data_ptr + 1) = value_at_idx; + } + + DataExtractor data(buffer_sp, process_sp->GetByteOrder(), ptr_size); + m_pair = CreateValueObjectFromData( + "[0]", data, m_backend.GetExecutionContextRef(), pair_type); + + return m_pair; +} + +template <typename D32, typename D64> +lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32, D64>:: + GenericNSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), + m_data_32(nullptr), m_data_64(nullptr), m_pair_type() {} + +template <typename D32, typename D64> +lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd< + D32, D64>::GenericNSDictionaryMSyntheticFrontEnd:: + ~GenericNSDictionaryMSyntheticFrontEnd() { + delete m_data_32; + m_data_32 = nullptr; + delete m_data_64; + m_data_64 = nullptr; +} + +template <typename D32, typename D64> +size_t lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd< + D32, D64>::GetIndexOfChildWithName(ConstString name) { + const char *item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors()) + return UINT32_MAX; + return idx; +} + +template <typename D32, typename D64> +llvm::Expected<uint32_t> +lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd< + D32, D64>::CalculateNumChildren() { + if (!m_data_32 && !m_data_64) + return 0; + return (m_data_32 ? (uint32_t)m_data_32->_used : (uint32_t)m_data_64->_used); +} + +template <typename D32, typename D64> +lldb::ChildCacheState +lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32, + D64>::Update() { + m_children.clear(); + ValueObjectSP valobj_sp = m_backend.GetSP(); + m_ptr_size = 0; + delete m_data_32; + m_data_32 = nullptr; + delete m_data_64; + m_data_64 = nullptr; + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + Status error; + error.Clear(); + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return lldb::ChildCacheState::eRefetch; + m_ptr_size = process_sp->GetAddressByteSize(); + m_order = process_sp->GetByteOrder(); + uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size; + if (m_ptr_size == 4) { + m_data_32 = new D32(); + process_sp->ReadMemory(data_location, m_data_32, sizeof(D32), + error); + } else { + m_data_64 = new D64(); + process_sp->ReadMemory(data_location, m_data_64, sizeof(D64), + error); + } + + return error.Success() ? lldb::ChildCacheState::eReuse + : lldb::ChildCacheState::eRefetch; +} + +template <typename D32, typename D64> +bool +lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd<D32,D64>:: + MightHaveChildren() { + return true; +} + +template <typename D32, typename D64> +lldb::ValueObjectSP +lldb_private::formatters::GenericNSDictionaryMSyntheticFrontEnd< + D32, D64>::GetChildAtIndex(uint32_t idx) { + lldb::addr_t m_keys_ptr; + lldb::addr_t m_values_ptr; + if (m_data_32) { + uint32_t size = m_data_32->GetSize(); + m_keys_ptr = m_data_32->_buffer; + m_values_ptr = m_data_32->_buffer + (m_ptr_size * size); + } else { + uint32_t size = m_data_64->GetSize(); + m_keys_ptr = m_data_64->_buffer; + m_values_ptr = m_data_64->_buffer + (m_ptr_size * size); + } + + uint32_t num_children = CalculateNumChildrenIgnoringErrors(); + + if (idx >= num_children) + return lldb::ValueObjectSP(); + + if (m_children.empty()) { + // do the scan phase + lldb::addr_t key_at_idx = 0, val_at_idx = 0; + + uint32_t tries = 0; + uint32_t test_idx = 0; + + while (tries < num_children) { + key_at_idx = m_keys_ptr + (test_idx * m_ptr_size); + val_at_idx = m_values_ptr + (test_idx * m_ptr_size); + ; + ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); + if (!process_sp) + return lldb::ValueObjectSP(); + Status error; + key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + + test_idx++; + + if (!key_at_idx || !val_at_idx) + continue; + tries++; + + DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx, + lldb::ValueObjectSP()}; + + m_children.push_back(descriptor); + } + } + + if (idx >= m_children.size()) // should never happen + return lldb::ValueObjectSP(); + + DictionaryItemDescriptor &dict_item = m_children[idx]; + if (!dict_item.valobj_sp) { + if (!m_pair_type.IsValid()) { + TargetSP target_sp(m_backend.GetTargetSP()); + if (!target_sp) + return ValueObjectSP(); + m_pair_type = GetLLDBNSPairType(target_sp); + } + if (!m_pair_type.IsValid()) + return ValueObjectSP(); + + WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0)); + + if (m_ptr_size == 8) { + uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes(); + *data_ptr = dict_item.key_ptr; + *(data_ptr + 1) = dict_item.val_ptr; + } else { + uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes(); + *data_ptr = dict_item.key_ptr; + *(data_ptr + 1) = dict_item.val_ptr; + } + + StreamString idx_name; + idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); + DataExtractor data(buffer_sp, m_order, m_ptr_size); + dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data, + m_exe_ctx_ref, m_pair_type); + } + return dict_item.valobj_sp; +} + +lldb_private::formatters::Foundation1100::NSDictionaryMSyntheticFrontEnd:: + NSDictionaryMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_pair_type() {} + +lldb_private::formatters::Foundation1100:: + NSDictionaryMSyntheticFrontEnd::~NSDictionaryMSyntheticFrontEnd() { + delete m_data_32; + m_data_32 = nullptr; + delete m_data_64; + m_data_64 = nullptr; +} + +size_t +lldb_private::formatters::Foundation1100:: + NSDictionaryMSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) { + const char *item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors()) + return UINT32_MAX; + return idx; +} + +llvm::Expected<uint32_t> lldb_private::formatters::Foundation1100:: + NSDictionaryMSyntheticFrontEnd::CalculateNumChildren() { + if (!m_data_32 && !m_data_64) + return 0; + return (m_data_32 ? m_data_32->_used : m_data_64->_used); +} + +lldb::ChildCacheState lldb_private::formatters::Foundation1100:: + NSDictionaryMSyntheticFrontEnd::Update() { + m_children.clear(); + ValueObjectSP valobj_sp = m_backend.GetSP(); + m_ptr_size = 0; + delete m_data_32; + m_data_32 = nullptr; + delete m_data_64; + m_data_64 = nullptr; + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + Status error; + error.Clear(); + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return lldb::ChildCacheState::eRefetch; + m_ptr_size = process_sp->GetAddressByteSize(); + m_order = process_sp->GetByteOrder(); + uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size; + if (m_ptr_size == 4) { + m_data_32 = new DataDescriptor_32(); + process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32), + error); + } else { + m_data_64 = new DataDescriptor_64(); + process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64), + error); + } + + return error.Success() ? lldb::ChildCacheState::eReuse + : lldb::ChildCacheState::eRefetch; +} + +bool +lldb_private::formatters::Foundation1100:: + NSDictionaryMSyntheticFrontEnd::MightHaveChildren() { + return true; +} + +lldb::ValueObjectSP +lldb_private::formatters::Foundation1100:: + NSDictionaryMSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { + lldb::addr_t m_keys_ptr = + (m_data_32 ? m_data_32->_keys_addr : m_data_64->_keys_addr); + lldb::addr_t m_values_ptr = + (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr); + + uint32_t num_children = CalculateNumChildrenIgnoringErrors(); + + if (idx >= num_children) + return lldb::ValueObjectSP(); + + if (m_children.empty()) { + // do the scan phase + lldb::addr_t key_at_idx = 0, val_at_idx = 0; + + uint32_t tries = 0; + uint32_t test_idx = 0; + + while (tries < num_children) { + key_at_idx = m_keys_ptr + (test_idx * m_ptr_size); + val_at_idx = m_values_ptr + (test_idx * m_ptr_size); + ; + ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); + if (!process_sp) + return lldb::ValueObjectSP(); + Status error; + key_at_idx = process_sp->ReadPointerFromMemory(key_at_idx, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + + test_idx++; + + if (!key_at_idx || !val_at_idx) + continue; + tries++; + + DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx, + lldb::ValueObjectSP()}; + + m_children.push_back(descriptor); + } + } + + if (idx >= m_children.size()) // should never happen + return lldb::ValueObjectSP(); + + DictionaryItemDescriptor &dict_item = m_children[idx]; + if (!dict_item.valobj_sp) { + if (!m_pair_type.IsValid()) { + TargetSP target_sp(m_backend.GetTargetSP()); + if (!target_sp) + return ValueObjectSP(); + m_pair_type = GetLLDBNSPairType(target_sp); + } + if (!m_pair_type.IsValid()) + return ValueObjectSP(); + + WritableDataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0)); + + if (m_ptr_size == 8) { + uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes(); + *data_ptr = dict_item.key_ptr; + *(data_ptr + 1) = dict_item.val_ptr; + } else { + uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes(); + *data_ptr = dict_item.key_ptr; + *(data_ptr + 1) = dict_item.val_ptr; + } + + StreamString idx_name; + idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); + DataExtractor data(buffer_sp, m_order, m_ptr_size); + dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data, + m_exe_ctx_ref, m_pair_type); + } + return dict_item.valobj_sp; +} + +template bool lldb_private::formatters::NSDictionarySummaryProvider<true>( + ValueObject &, Stream &, const TypeSummaryOptions &); + +template bool lldb_private::formatters::NSDictionarySummaryProvider<false>( + ValueObject &, Stream &, const TypeSummaryOptions &); diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.h new file mode 100644 index 000000000000..57dacd6759d2 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.h @@ -0,0 +1,93 @@ +//===-- NSDictionary.h ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_NSDICTIONARY_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_NSDICTIONARY_H + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Stream.h" + +#include <map> +#include <memory> + +namespace lldb_private { +namespace formatters { +template <bool name_entries> +bool NSDictionarySummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +extern template bool +NSDictionarySummaryProvider<true>(ValueObject &, Stream &, + const TypeSummaryOptions &); + +extern template bool +NSDictionarySummaryProvider<false>(ValueObject &, Stream &, + const TypeSummaryOptions &); + +SyntheticChildrenFrontEnd * +NSDictionarySyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +class NSDictionary_Additionals { +public: + class AdditionalFormatterMatching { + public: + class Matcher { + public: + virtual ~Matcher() = default; + virtual bool Match(ConstString class_name) = 0; + + typedef std::unique_ptr<Matcher> UP; + }; + class Prefix : public Matcher { + public: + Prefix(ConstString p); + ~Prefix() override = default; + bool Match(ConstString class_name) override; + + private: + ConstString m_prefix; + }; + class Full : public Matcher { + public: + Full(ConstString n); + ~Full() override = default; + bool Match(ConstString class_name) override; + + private: + ConstString m_name; + }; + typedef Matcher::UP MatcherUP; + + MatcherUP GetFullMatch(ConstString n) { return std::make_unique<Full>(n); } + + MatcherUP GetPrefixMatch(ConstString p) { + return std::make_unique<Prefix>(p); + } + }; + + template <typename FormatterType> + using AdditionalFormatter = + std::pair<AdditionalFormatterMatching::MatcherUP, FormatterType>; + + template <typename FormatterType> + using AdditionalFormatters = std::vector<AdditionalFormatter<FormatterType>>; + + static AdditionalFormatters<CXXFunctionSummaryFormat::Callback> & + GetAdditionalSummaries(); + + static AdditionalFormatters<CXXSyntheticChildren::CreateFrontEndCallback> & + GetAdditionalSynthetics(); +}; +} // namespace formatters +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_NSDICTIONARY_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSError.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSError.cpp new file mode 100644 index 000000000000..5ef7edc7e80c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSError.cpp @@ -0,0 +1,215 @@ +//===-- NSError.cpp -------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/DeclCXX.h" + +#include "Cocoa.h" + +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" + +#include "Plugins/Language/ObjC/NSString.h" +#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +static lldb::addr_t DerefToNSErrorPointer(ValueObject &valobj) { + CompilerType valobj_type(valobj.GetCompilerType()); + Flags type_flags(valobj_type.GetTypeInfo()); + if (type_flags.AllClear(eTypeHasValue)) { + if (valobj.IsBaseClass() && valobj.GetParent()) + return valobj.GetParent()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); + } else { + lldb::addr_t ptr_value = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS); + if (type_flags.AllSet(eTypeIsPointer)) { + CompilerType pointee_type(valobj_type.GetPointeeType()); + Flags pointee_flags(pointee_type.GetTypeInfo()); + if (pointee_flags.AllSet(eTypeIsPointer)) { + if (ProcessSP process_sp = valobj.GetProcessSP()) { + Status error; + ptr_value = process_sp->ReadPointerFromMemory(ptr_value, error); + } + } + } + return ptr_value; + } + + return LLDB_INVALID_ADDRESS; +} + +bool lldb_private::formatters::NSError_SummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + ProcessSP process_sp(valobj.GetProcessSP()); + if (!process_sp) + return false; + + lldb::addr_t ptr_value = DerefToNSErrorPointer(valobj); + if (ptr_value == LLDB_INVALID_ADDRESS) + return false; + + size_t ptr_size = process_sp->GetAddressByteSize(); + lldb::addr_t code_location = ptr_value + 2 * ptr_size; + lldb::addr_t domain_location = ptr_value + 3 * ptr_size; + + Status error; + uint64_t code = process_sp->ReadUnsignedIntegerFromMemory(code_location, + ptr_size, 0, error); + if (error.Fail()) + return false; + + lldb::addr_t domain_str_value = + process_sp->ReadPointerFromMemory(domain_location, error); + if (error.Fail() || domain_str_value == LLDB_INVALID_ADDRESS) + return false; + + if (!domain_str_value) { + stream.Printf("domain: nil - code: %" PRIu64, code); + return true; + } + + InferiorSizedWord isw(domain_str_value, *process_sp); + TypeSystemClangSP scratch_ts_sp = + ScratchTypeSystemClang::GetForTarget(process_sp->GetTarget()); + + if (!scratch_ts_sp) + return false; + ValueObjectSP domain_str_sp = ValueObject::CreateValueObjectFromData( + "domain_str", isw.GetAsData(process_sp->GetByteOrder()), + valobj.GetExecutionContextRef(), + scratch_ts_sp->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()); + + if (!domain_str_sp) + return false; + + StreamString domain_str_summary; + if (NSStringSummaryProvider(*domain_str_sp, domain_str_summary, options) && + !domain_str_summary.Empty()) { + stream.Printf("domain: %s - code: %" PRIu64, domain_str_summary.GetData(), + code); + return true; + } else { + stream.Printf("domain: nil - code: %" PRIu64, code); + return true; + } +} + +class NSErrorSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + NSErrorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) {} + + ~NSErrorSyntheticFrontEnd() override = default; + // no need to delete m_child_ptr - it's kept alive by the cluster manager on + // our behalf + + llvm::Expected<uint32_t> CalculateNumChildren() override { + if (m_child_ptr) + return 1; + if (m_child_sp) + return 1; + return 0; + } + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override { + if (idx != 0) + return lldb::ValueObjectSP(); + + if (m_child_ptr) + return m_child_ptr->GetSP(); + return m_child_sp; + } + + lldb::ChildCacheState Update() override { + m_child_ptr = nullptr; + m_child_sp.reset(); + + ProcessSP process_sp(m_backend.GetProcessSP()); + if (!process_sp) + return lldb::ChildCacheState::eRefetch; + + lldb::addr_t userinfo_location = DerefToNSErrorPointer(m_backend); + if (userinfo_location == LLDB_INVALID_ADDRESS) + return lldb::ChildCacheState::eRefetch; + + size_t ptr_size = process_sp->GetAddressByteSize(); + + userinfo_location += 4 * ptr_size; + Status error; + lldb::addr_t userinfo = + process_sp->ReadPointerFromMemory(userinfo_location, error); + if (userinfo == LLDB_INVALID_ADDRESS || error.Fail()) + return lldb::ChildCacheState::eRefetch; + InferiorSizedWord isw(userinfo, *process_sp); + TypeSystemClangSP scratch_ts_sp = + ScratchTypeSystemClang::GetForTarget(process_sp->GetTarget()); + if (!scratch_ts_sp) + return lldb::ChildCacheState::eRefetch; + m_child_sp = CreateValueObjectFromData( + "_userInfo", isw.GetAsData(process_sp->GetByteOrder()), + m_backend.GetExecutionContextRef(), + scratch_ts_sp->GetBasicType(lldb::eBasicTypeObjCID)); + return lldb::ChildCacheState::eRefetch; + } + + bool MightHaveChildren() override { return true; } + + size_t GetIndexOfChildWithName(ConstString name) override { + static ConstString g_userInfo("_userInfo"); + if (name == g_userInfo) + return 0; + return UINT32_MAX; + } + +private: + // the child here can be "real" (i.e. an actual child of the root) or + // synthetized from raw memory if the former, I need to store a plain pointer + // to it - or else a loop of references will cause this entire hierarchy of + // values to leak if the latter, then I need to store a SharedPointer to it - + // so that it only goes away when everyone else in the cluster goes away oh + // joy! + ValueObject *m_child_ptr = nullptr; + ValueObjectSP m_child_sp; +}; + +SyntheticChildrenFrontEnd * +lldb_private::formatters::NSErrorSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return nullptr; + ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); + if (!runtime) + return nullptr; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor( + runtime->GetClassDescriptor(*valobj_sp.get())); + + if (!descriptor.get() || !descriptor->IsValid()) + return nullptr; + + const char *class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return nullptr; + + if (!strcmp(class_name, "NSError")) + return (new NSErrorSyntheticFrontEnd(valobj_sp)); + else if (!strcmp(class_name, "__NSCFError")) + return (new NSErrorSyntheticFrontEnd(valobj_sp)); + + return nullptr; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSException.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSException.cpp new file mode 100644 index 000000000000..e7ce26ea4c6f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSException.cpp @@ -0,0 +1,206 @@ +//===-- NSException.cpp ---------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/DeclCXX.h" + +#include "Cocoa.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" + +#include "Plugins/Language/ObjC/NSString.h" +#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +static bool ExtractFields(ValueObject &valobj, ValueObjectSP *name_sp, + ValueObjectSP *reason_sp, ValueObjectSP *userinfo_sp, + ValueObjectSP *reserved_sp) { + ProcessSP process_sp(valobj.GetProcessSP()); + if (!process_sp) + return false; + + lldb::addr_t ptr = LLDB_INVALID_ADDRESS; + + CompilerType valobj_type(valobj.GetCompilerType()); + Flags type_flags(valobj_type.GetTypeInfo()); + if (type_flags.AllClear(eTypeHasValue)) { + if (valobj.IsBaseClass() && valobj.GetParent()) + ptr = valobj.GetParent()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); + } else { + ptr = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS); + } + + if (ptr == LLDB_INVALID_ADDRESS) + return false; + size_t ptr_size = process_sp->GetAddressByteSize(); + + Status error; + auto name = process_sp->ReadPointerFromMemory(ptr + 1 * ptr_size, error); + if (error.Fail() || name == LLDB_INVALID_ADDRESS) + return false; + auto reason = process_sp->ReadPointerFromMemory(ptr + 2 * ptr_size, error); + if (error.Fail() || reason == LLDB_INVALID_ADDRESS) + return false; + auto userinfo = process_sp->ReadPointerFromMemory(ptr + 3 * ptr_size, error); + if (error.Fail() || userinfo == LLDB_INVALID_ADDRESS) + return false; + auto reserved = process_sp->ReadPointerFromMemory(ptr + 4 * ptr_size, error); + if (error.Fail() || reserved == LLDB_INVALID_ADDRESS) + return false; + + InferiorSizedWord name_isw(name, *process_sp); + InferiorSizedWord reason_isw(reason, *process_sp); + InferiorSizedWord userinfo_isw(userinfo, *process_sp); + InferiorSizedWord reserved_isw(reserved, *process_sp); + + TypeSystemClangSP scratch_ts_sp = + ScratchTypeSystemClang::GetForTarget(process_sp->GetTarget()); + if (!scratch_ts_sp) + return false; + + CompilerType voidstar = + scratch_ts_sp->GetBasicType(lldb::eBasicTypeVoid).GetPointerType(); + + if (name_sp) + *name_sp = ValueObject::CreateValueObjectFromData( + "name", name_isw.GetAsData(process_sp->GetByteOrder()), + valobj.GetExecutionContextRef(), voidstar); + if (reason_sp) + *reason_sp = ValueObject::CreateValueObjectFromData( + "reason", reason_isw.GetAsData(process_sp->GetByteOrder()), + valobj.GetExecutionContextRef(), voidstar); + if (userinfo_sp) + *userinfo_sp = ValueObject::CreateValueObjectFromData( + "userInfo", userinfo_isw.GetAsData(process_sp->GetByteOrder()), + valobj.GetExecutionContextRef(), voidstar); + if (reserved_sp) + *reserved_sp = ValueObject::CreateValueObjectFromData( + "reserved", reserved_isw.GetAsData(process_sp->GetByteOrder()), + valobj.GetExecutionContextRef(), voidstar); + + return true; +} + +bool lldb_private::formatters::NSException_SummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + lldb::ValueObjectSP reason_sp; + if (!ExtractFields(valobj, nullptr, &reason_sp, nullptr, nullptr)) + return false; + + if (!reason_sp) { + stream.Printf("No reason"); + return false; + } + + StreamString reason_str_summary; + if (NSStringSummaryProvider(*reason_sp, reason_str_summary, options) && + !reason_str_summary.Empty()) { + stream.Printf("%s", reason_str_summary.GetData()); + return true; + } else + return false; +} + +class NSExceptionSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + NSExceptionSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) {} + + ~NSExceptionSyntheticFrontEnd() override = default; + + llvm::Expected<uint32_t> CalculateNumChildren() override { return 4; } + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override { + switch (idx) { + case 0: return m_name_sp; + case 1: return m_reason_sp; + case 2: return m_userinfo_sp; + case 3: return m_reserved_sp; + } + return lldb::ValueObjectSP(); + } + + lldb::ChildCacheState Update() override { + m_name_sp.reset(); + m_reason_sp.reset(); + m_userinfo_sp.reset(); + m_reserved_sp.reset(); + + const auto ret = ExtractFields(m_backend, &m_name_sp, &m_reason_sp, + &m_userinfo_sp, &m_reserved_sp); + + return ret ? lldb::ChildCacheState::eReuse + : lldb::ChildCacheState::eRefetch; + } + + bool MightHaveChildren() override { return true; } + + size_t GetIndexOfChildWithName(ConstString name) override { + // NSException has 4 members: + // NSString *name; + // NSString *reason; + // NSDictionary *userInfo; + // id reserved; + static ConstString g_name("name"); + static ConstString g_reason("reason"); + static ConstString g_userInfo("userInfo"); + static ConstString g_reserved("reserved"); + if (name == g_name) return 0; + if (name == g_reason) return 1; + if (name == g_userInfo) return 2; + if (name == g_reserved) return 3; + return UINT32_MAX; + } + +private: + ValueObjectSP m_name_sp; + ValueObjectSP m_reason_sp; + ValueObjectSP m_userinfo_sp; + ValueObjectSP m_reserved_sp; +}; + +SyntheticChildrenFrontEnd * +lldb_private::formatters::NSExceptionSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return nullptr; + ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); + if (!runtime) + return nullptr; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor( + runtime->GetClassDescriptor(*valobj_sp.get())); + + if (!descriptor.get() || !descriptor->IsValid()) + return nullptr; + + const char *class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return nullptr; + + if (!strcmp(class_name, "NSException")) + return (new NSExceptionSyntheticFrontEnd(valobj_sp)); + else if (!strcmp(class_name, "NSCFException")) + return (new NSExceptionSyntheticFrontEnd(valobj_sp)); + else if (!strcmp(class_name, "__NSCFException")) + return (new NSExceptionSyntheticFrontEnd(valobj_sp)); + + return nullptr; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp new file mode 100644 index 000000000000..a434cee09d38 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp @@ -0,0 +1,321 @@ +//===-- NSIndexPath.cpp ---------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Cocoa.h" + +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h" +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +static constexpr size_t PACKED_INDEX_SHIFT_64(size_t i) { + return (60 - (13 * (4 - i))); +} + +static constexpr size_t PACKED_INDEX_SHIFT_32(size_t i) { + return (32 - (13 * (2 - i))); +} + +class NSIndexPathSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + NSIndexPathSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_descriptor_sp(nullptr), + m_impl(), m_uint_star_type() { + m_ptr_size = + m_backend.GetTargetSP()->GetArchitecture().GetAddressByteSize(); + } + + ~NSIndexPathSyntheticFrontEnd() override = default; + + llvm::Expected<uint32_t> CalculateNumChildren() override { + return m_impl.GetNumIndexes(); + } + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override { + return m_impl.GetIndexAtIndex(idx, m_uint_star_type); + } + + lldb::ChildCacheState Update() override { + m_impl.Clear(); + + auto type_system = m_backend.GetCompilerType().GetTypeSystem(); + if (!type_system) + return lldb::ChildCacheState::eRefetch; + + auto ast = ScratchTypeSystemClang::GetForTarget( + *m_backend.GetExecutionContextRef().GetTargetSP()); + if (!ast) + return lldb::ChildCacheState::eRefetch; + + m_uint_star_type = ast->GetPointerSizedIntType(false); + + static ConstString g__indexes("_indexes"); + static ConstString g__length("_length"); + + ProcessSP process_sp = m_backend.GetProcessSP(); + if (!process_sp) + return lldb::ChildCacheState::eRefetch; + + ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); + + if (!runtime) + return lldb::ChildCacheState::eRefetch; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor( + runtime->GetClassDescriptor(m_backend)); + + if (!descriptor.get() || !descriptor->IsValid()) + return lldb::ChildCacheState::eRefetch; + + uint64_t info_bits(0), value_bits(0), payload(0); + + if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits, &payload)) { + m_impl.m_inlined.SetIndexes(payload, *process_sp); + m_impl.m_mode = Mode::Inlined; + } else { + ObjCLanguageRuntime::ClassDescriptor::iVarDescriptor _indexes_id; + ObjCLanguageRuntime::ClassDescriptor::iVarDescriptor _length_id; + + bool has_indexes(false), has_length(false); + + for (size_t x = 0; x < descriptor->GetNumIVars(); x++) { + const auto &ivar = descriptor->GetIVarAtIndex(x); + if (ivar.m_name == g__indexes) { + _indexes_id = ivar; + has_indexes = true; + } else if (ivar.m_name == g__length) { + _length_id = ivar; + has_length = true; + } + + if (has_length && has_indexes) + break; + } + + if (has_length && has_indexes) { + m_impl.m_outsourced.m_indexes = + m_backend + .GetSyntheticChildAtOffset(_indexes_id.m_offset, + m_uint_star_type.GetPointerType(), + true) + .get(); + ValueObjectSP length_sp(m_backend.GetSyntheticChildAtOffset( + _length_id.m_offset, m_uint_star_type, true)); + if (length_sp) { + m_impl.m_outsourced.m_count = length_sp->GetValueAsUnsigned(0); + if (m_impl.m_outsourced.m_indexes) + m_impl.m_mode = Mode::Outsourced; + } + } + } + return lldb::ChildCacheState::eRefetch; + } + + bool MightHaveChildren() override { return m_impl.m_mode != Mode::Invalid; } + + size_t GetIndexOfChildWithName(ConstString name) override { + const char *item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors()) + return UINT32_MAX; + return idx; + } + + lldb::ValueObjectSP GetSyntheticValue() override { return nullptr; } + +protected: + ObjCLanguageRuntime::ClassDescriptorSP m_descriptor_sp; + + enum class Mode { Inlined, Outsourced, Invalid }; + + struct Impl { + size_t GetNumIndexes() { + switch (m_mode) { + case Mode::Inlined: + return m_inlined.GetNumIndexes(); + case Mode::Outsourced: + return m_outsourced.m_count; + default: + return 0; + } + } + + lldb::ValueObjectSP GetIndexAtIndex(size_t idx, + const CompilerType &desired_type) { + if (idx >= GetNumIndexes()) + return nullptr; + switch (m_mode) { + default: + return nullptr; + case Mode::Inlined: + return m_inlined.GetIndexAtIndex(idx, desired_type); + case Mode::Outsourced: + return m_outsourced.GetIndexAtIndex(idx); + } + } + + struct InlinedIndexes { + public: + void SetIndexes(uint64_t value, Process &p) { + m_indexes = value; + _lengthForInlinePayload(p.GetAddressByteSize()); + m_process = &p; + } + + size_t GetNumIndexes() { return m_count; } + + lldb::ValueObjectSP GetIndexAtIndex(size_t idx, + const CompilerType &desired_type) { + if (!m_process) + return nullptr; + + std::pair<uint64_t, bool> value(_indexAtPositionForInlinePayload(idx)); + if (!value.second) + return nullptr; + + Value v; + if (m_ptr_size == 8) { + Scalar scalar((unsigned long long)value.first); + v = Value(scalar); + } else { + Scalar scalar((unsigned int)value.first); + v = Value(scalar); + } + + v.SetCompilerType(desired_type); + + StreamString idx_name; + idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); + + return ValueObjectConstResult::Create( + m_process, v, ConstString(idx_name.GetString())); + } + + void Clear() { + m_indexes = 0; + m_count = 0; + m_ptr_size = 0; + m_process = nullptr; + } + + InlinedIndexes() {} + + private: + uint64_t m_indexes = 0; + size_t m_count = 0; + uint32_t m_ptr_size = 0; + Process *m_process = nullptr; + + // cfr. Foundation for the details of this code + size_t _lengthForInlinePayload(uint32_t ptr_size) { + m_ptr_size = ptr_size; + if (m_ptr_size == 8) + m_count = ((m_indexes >> 3) & 0x7); + else + m_count = ((m_indexes >> 3) & 0x3); + return m_count; + } + + std::pair<uint64_t, bool> _indexAtPositionForInlinePayload(size_t pos) { + static const uint64_t PACKED_INDEX_MASK = ((1 << 13) - 1); + if (m_ptr_size == 8) { + switch (pos) { + case 3: + case 2: + case 1: + case 0: + return {(m_indexes >> PACKED_INDEX_SHIFT_64(pos)) & + PACKED_INDEX_MASK, + true}; + default: + return {0, false}; + } + } else { + switch (pos) { + case 0: + case 1: + return {(m_indexes >> PACKED_INDEX_SHIFT_32(pos)) & + PACKED_INDEX_MASK, + true}; + default: + return {0, false}; + } + } + return {0, false}; + } + }; + + struct OutsourcedIndexes { + lldb::ValueObjectSP GetIndexAtIndex(size_t idx) { + if (m_indexes) { + ValueObjectSP index_sp(m_indexes->GetSyntheticArrayMember(idx, true)); + return index_sp; + } + return nullptr; + } + + void Clear() { + m_indexes = nullptr; + m_count = 0; + } + + OutsourcedIndexes() {} + + ValueObject *m_indexes = nullptr; + size_t m_count = 0; + }; + + union { + struct InlinedIndexes m_inlined; + struct OutsourcedIndexes m_outsourced; + }; + + void Clear() { + switch (m_mode) { + case Mode::Inlined: + m_inlined.Clear(); + break; + case Mode::Outsourced: + m_outsourced.Clear(); + break; + case Mode::Invalid: + break; + } + m_mode = Mode::Invalid; + } + + Impl() {} + + Mode m_mode = Mode::Invalid; + } m_impl; + + uint32_t m_ptr_size = 0; + CompilerType m_uint_star_type; +}; + +namespace lldb_private { +namespace formatters { + +SyntheticChildrenFrontEnd * +NSIndexPathSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new NSIndexPathSyntheticFrontEnd(valobj_sp); + return nullptr; +} + +} // namespace formatters +} // namespace lldb_private diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSSet.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSSet.cpp new file mode 100644 index 000000000000..7d0a6a507211 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSSet.cpp @@ -0,0 +1,831 @@ +//===-- NSSet.cpp ---------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "NSSet.h" +#include "CFBasicHash.h" + +#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Target/Language.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +std::map<ConstString, CXXFunctionSummaryFormat::Callback> & +NSSet_Additionals::GetAdditionalSummaries() { + static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map; + return g_map; +} + +std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback> & +NSSet_Additionals::GetAdditionalSynthetics() { + static std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback> + g_map; + return g_map; +} + +namespace lldb_private { +namespace formatters { +class NSSetISyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + NSSetISyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~NSSetISyntheticFrontEnd() override; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + struct DataDescriptor_32 { + uint32_t _used : 26; + uint32_t _szidx : 6; + }; + + struct DataDescriptor_64 { + uint64_t _used : 58; + uint32_t _szidx : 6; + }; + + struct SetItemDescriptor { + lldb::addr_t item_ptr; + lldb::ValueObjectSP valobj_sp; + }; + + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size = 8; + DataDescriptor_32 *m_data_32 = nullptr; + DataDescriptor_64 *m_data_64 = nullptr; + lldb::addr_t m_data_ptr = LLDB_INVALID_ADDRESS; + std::vector<SetItemDescriptor> m_children; +}; + +class NSCFSetSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + NSCFSetSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + struct SetItemDescriptor { + lldb::addr_t item_ptr; + lldb::ValueObjectSP valobj_sp; + }; + + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size = 8; + lldb::ByteOrder m_order = lldb::eByteOrderInvalid; + + CFBasicHash m_hashtable; + + CompilerType m_pair_type; + std::vector<SetItemDescriptor> m_children; +}; + +template <typename D32, typename D64> +class GenericNSSetMSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + GenericNSSetMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~GenericNSSetMSyntheticFrontEnd() override; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + + struct SetItemDescriptor { + lldb::addr_t item_ptr; + lldb::ValueObjectSP valobj_sp; + }; + + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size = 8; + D32 *m_data_32; + D64 *m_data_64; + std::vector<SetItemDescriptor> m_children; +}; + +namespace Foundation1300 { + struct DataDescriptor_32 { + uint32_t _used : 26; + uint32_t _size; + uint32_t _mutations; + uint32_t _objs_addr; + }; + + struct DataDescriptor_64 { + uint64_t _used : 58; + uint64_t _size; + uint64_t _mutations; + uint64_t _objs_addr; + }; + + using NSSetMSyntheticFrontEnd = + GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>; +} + +namespace Foundation1428 { + struct DataDescriptor_32 { + uint32_t _used : 26; + uint32_t _size; + uint32_t _objs_addr; + uint32_t _mutations; + }; + + struct DataDescriptor_64 { + uint64_t _used : 58; + uint64_t _size; + uint64_t _objs_addr; + uint64_t _mutations; + }; + + using NSSetMSyntheticFrontEnd = + GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>; +} + +namespace Foundation1437 { + struct DataDescriptor_32 { + uint32_t _cow; + // __table storage + uint32_t _objs_addr; + uint32_t _muts; + uint32_t _used : 26; + uint32_t _szidx : 6; + }; + + struct DataDescriptor_64 { + uint64_t _cow; + // __Table storage + uint64_t _objs_addr; + uint32_t _muts; + uint32_t _used : 26; + uint32_t _szidx : 6; + }; + + using NSSetMSyntheticFrontEnd = + GenericNSSetMSyntheticFrontEnd<DataDescriptor_32, DataDescriptor_64>; + + template <typename DD> + uint64_t + __NSSetMSize_Impl(lldb_private::Process &process, lldb::addr_t valobj_addr, + Status &error) { + const lldb::addr_t start_of_descriptor = + valobj_addr + process.GetAddressByteSize(); + DD descriptor = DD(); + process.ReadMemory(start_of_descriptor, &descriptor, sizeof(descriptor), + error); + if (error.Fail()) { + return 0; + } + return descriptor._used; + } + + uint64_t + __NSSetMSize(lldb_private::Process &process, lldb::addr_t valobj_addr, + Status &error) { + if (process.GetAddressByteSize() == 4) { + return __NSSetMSize_Impl<DataDescriptor_32>(process, valobj_addr, error); + } else { + return __NSSetMSize_Impl<DataDescriptor_64>(process, valobj_addr, error); + } + } +} + +class NSSetCodeRunningSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + NSSetCodeRunningSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~NSSetCodeRunningSyntheticFrontEnd() override; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; +}; +} // namespace formatters +} // namespace lldb_private + +template <bool cf_style> +bool lldb_private::formatters::NSSetSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + static constexpr llvm::StringLiteral g_TypeHint("NSSet"); + + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor( + runtime->GetClassDescriptor(valobj)); + + if (!descriptor || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + bool is_64bit = (ptr_size == 8); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + uint64_t value = 0; + + ConstString class_name(descriptor->GetClassName()); + + static const ConstString g_SetI("__NSSetI"); + static const ConstString g_OrderedSetI("__NSOrderedSetI"); + static const ConstString g_SetM("__NSSetM"); + static const ConstString g_SetCF("__NSCFSet"); + static const ConstString g_SetCFRef("CFSetRef"); + + if (class_name.IsEmpty()) + return false; + + if (class_name == g_SetI || class_name == g_OrderedSetI) { + Status error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, + ptr_size, 0, error); + if (error.Fail()) + return false; + value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U); + } else if (class_name == g_SetM) { + AppleObjCRuntime *apple_runtime = + llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime); + Status error; + if (apple_runtime && apple_runtime->GetFoundationVersion() >= 1437) { + value = Foundation1437::__NSSetMSize(*process_sp, valobj_addr, error); + } else { + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, + ptr_size, 0, error); + value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U); + } + if (error.Fail()) + return false; + } else if (class_name == g_SetCF || class_name == g_SetCFRef) { + ExecutionContext exe_ctx(process_sp); + CFBasicHash cfbh; + if (!cfbh.Update(valobj_addr, exe_ctx)) + return false; + value = cfbh.GetCount(); + } else { + auto &map(NSSet_Additionals::GetAdditionalSummaries()); + auto iter = map.find(class_name), end = map.end(); + if (iter != end) + return iter->second(valobj, stream, options); + else + return false; + } + + llvm::StringRef prefix, suffix; + if (Language *language = Language::FindPlugin(options.GetLanguage())) + std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint); + + stream << prefix; + stream.Printf("%" PRIu64 " %s%s", value, "element", value == 1 ? "" : "s"); + stream << suffix; + return true; +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::NSSetSyntheticFrontEndCreator( + CXXSyntheticChildren *synth, lldb::ValueObjectSP valobj_sp) { + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return nullptr; + ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); + if (!runtime) + return nullptr; + + CompilerType valobj_type(valobj_sp->GetCompilerType()); + Flags flags(valobj_type.GetTypeInfo()); + + if (flags.IsClear(eTypeIsPointer)) { + Status error; + valobj_sp = valobj_sp->AddressOf(error); + if (error.Fail() || !valobj_sp) + return nullptr; + } + + ObjCLanguageRuntime::ClassDescriptorSP descriptor( + runtime->GetClassDescriptor(*valobj_sp)); + + if (!descriptor || !descriptor->IsValid()) + return nullptr; + + ConstString class_name = descriptor->GetClassName(); + + static const ConstString g_SetI("__NSSetI"); + static const ConstString g_OrderedSetI("__NSOrderedSetI"); + static const ConstString g_SetM("__NSSetM"); + static const ConstString g_SetCF("__NSCFSet"); + static const ConstString g_SetCFRef("CFSetRef"); + + if (class_name.IsEmpty()) + return nullptr; + + if (class_name == g_SetI || class_name == g_OrderedSetI) { + return (new NSSetISyntheticFrontEnd(valobj_sp)); + } else if (class_name == g_SetM) { + AppleObjCRuntime *apple_runtime = + llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime); + if (apple_runtime) { + if (apple_runtime->GetFoundationVersion() >= 1437) + return (new Foundation1437::NSSetMSyntheticFrontEnd(valobj_sp)); + else if (apple_runtime->GetFoundationVersion() >= 1428) + return (new Foundation1428::NSSetMSyntheticFrontEnd(valobj_sp)); + else + return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp)); + } else { + return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp)); + } + } else if (class_name == g_SetCF || class_name == g_SetCFRef) { + return (new NSCFSetSyntheticFrontEnd(valobj_sp)); + } else { + auto &map(NSSet_Additionals::GetAdditionalSynthetics()); + auto iter = map.find(class_name), end = map.end(); + if (iter != end) + return iter->second(synth, valobj_sp); + return nullptr; + } +} + +lldb_private::formatters::NSSetISyntheticFrontEnd::NSSetISyntheticFrontEnd( + lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref() { + if (valobj_sp) + Update(); +} + +lldb_private::formatters::NSSetISyntheticFrontEnd::~NSSetISyntheticFrontEnd() { + delete m_data_32; + m_data_32 = nullptr; + delete m_data_64; + m_data_64 = nullptr; +} + +size_t +lldb_private::formatters::NSSetISyntheticFrontEnd::GetIndexOfChildWithName( + ConstString name) { + const char *item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors()) + return UINT32_MAX; + return idx; +} + +llvm::Expected<uint32_t> +lldb_private::formatters::NSSetISyntheticFrontEnd::CalculateNumChildren() { + if (!m_data_32 && !m_data_64) + return 0; + return (m_data_32 ? m_data_32->_used : m_data_64->_used); +} + +lldb::ChildCacheState +lldb_private::formatters::NSSetISyntheticFrontEnd::Update() { + m_children.clear(); + delete m_data_32; + m_data_32 = nullptr; + delete m_data_64; + m_data_64 = nullptr; + m_ptr_size = 0; + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return lldb::ChildCacheState::eRefetch; + m_ptr_size = process_sp->GetAddressByteSize(); + uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size; + Status error; + if (m_ptr_size == 4) { + m_data_32 = new DataDescriptor_32(); + process_sp->ReadMemory(data_location, m_data_32, sizeof(DataDescriptor_32), + error); + } else { + m_data_64 = new DataDescriptor_64(); + process_sp->ReadMemory(data_location, m_data_64, sizeof(DataDescriptor_64), + error); + } + if (error.Fail()) + return lldb::ChildCacheState::eRefetch; + m_data_ptr = data_location + m_ptr_size; + return lldb::ChildCacheState::eReuse; +} + +bool lldb_private::formatters::NSSetISyntheticFrontEnd::MightHaveChildren() { + return true; +} + +lldb::ValueObjectSP +lldb_private::formatters::NSSetISyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + uint32_t num_children = CalculateNumChildrenIgnoringErrors(); + + if (idx >= num_children) + return lldb::ValueObjectSP(); + + ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); + if (!process_sp) + return lldb::ValueObjectSP(); + + if (m_children.empty()) { + // do the scan phase + lldb::addr_t obj_at_idx = 0; + + uint32_t tries = 0; + uint32_t test_idx = 0; + + while (tries < num_children) { + obj_at_idx = m_data_ptr + (test_idx * m_ptr_size); + if (!process_sp) + return lldb::ValueObjectSP(); + Status error; + obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + + test_idx++; + + if (!obj_at_idx) + continue; + tries++; + + SetItemDescriptor descriptor = {obj_at_idx, lldb::ValueObjectSP()}; + + m_children.push_back(descriptor); + } + } + + if (idx >= m_children.size()) // should never happen + return lldb::ValueObjectSP(); + + SetItemDescriptor &set_item = m_children[idx]; + if (!set_item.valobj_sp) { + auto ptr_size = process_sp->GetAddressByteSize(); + DataBufferHeap buffer(ptr_size, 0); + switch (ptr_size) { + case 0: // architecture has no clue - fail + return lldb::ValueObjectSP(); + case 4: + *reinterpret_cast<uint32_t *>(buffer.GetBytes()) = + static_cast<uint32_t>(set_item.item_ptr); + break; + case 8: + *reinterpret_cast<uint64_t *>(buffer.GetBytes()) = + static_cast<uint64_t>(set_item.item_ptr); + break; + default: + lldbassert(false && "pointer size is not 4 nor 8"); + } + StreamString idx_name; + idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); + + DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(), + process_sp->GetByteOrder(), + process_sp->GetAddressByteSize()); + + set_item.valobj_sp = CreateValueObjectFromData( + idx_name.GetString(), data, m_exe_ctx_ref, + m_backend.GetCompilerType().GetBasicTypeFromAST( + lldb::eBasicTypeObjCID)); + } + return set_item.valobj_sp; +} + +lldb_private::formatters::NSCFSetSyntheticFrontEnd::NSCFSetSyntheticFrontEnd( + lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_hashtable(), + m_pair_type() {} + +size_t +lldb_private::formatters::NSCFSetSyntheticFrontEnd::GetIndexOfChildWithName( + ConstString name) { + const char *item_name = name.GetCString(); + const uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors()) + return UINT32_MAX; + return idx; +} + +llvm::Expected<uint32_t> +lldb_private::formatters::NSCFSetSyntheticFrontEnd::CalculateNumChildren() { + if (!m_hashtable.IsValid()) + return 0; + return m_hashtable.GetCount(); +} + +lldb::ChildCacheState +lldb_private::formatters::NSCFSetSyntheticFrontEnd::Update() { + m_children.clear(); + ValueObjectSP valobj_sp = m_backend.GetSP(); + m_ptr_size = 0; + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return lldb::ChildCacheState::eRefetch; + m_ptr_size = process_sp->GetAddressByteSize(); + m_order = process_sp->GetByteOrder(); + return m_hashtable.Update(valobj_sp->GetValueAsUnsigned(0), m_exe_ctx_ref) + ? lldb::ChildCacheState::eReuse + : lldb::ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::NSCFSetSyntheticFrontEnd::MightHaveChildren() { + return true; +} + +lldb::ValueObjectSP +lldb_private::formatters::NSCFSetSyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + lldb::addr_t m_values_ptr = m_hashtable.GetValuePointer(); + + const uint32_t num_children = CalculateNumChildrenIgnoringErrors(); + + if (idx >= num_children) + return lldb::ValueObjectSP(); + + if (m_children.empty()) { + ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); + if (!process_sp) + return lldb::ValueObjectSP(); + + Status error; + lldb::addr_t val_at_idx = 0; + + uint32_t tries = 0; + uint32_t test_idx = 0; + + // Iterate over inferior memory, reading value pointers by shifting the + // cursor by test_index * m_ptr_size. Returns an empty ValueObject if a read + // fails, otherwise, continue until the number of tries matches the number + // of childen. + while (tries < num_children) { + val_at_idx = m_values_ptr + (test_idx * m_ptr_size); + + val_at_idx = process_sp->ReadPointerFromMemory(val_at_idx, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + + test_idx++; + + if (!val_at_idx) + continue; + tries++; + + SetItemDescriptor descriptor = {val_at_idx, lldb::ValueObjectSP()}; + + m_children.push_back(descriptor); + } + } + + if (idx >= m_children.size()) // should never happen + return lldb::ValueObjectSP(); + + SetItemDescriptor &set_item = m_children[idx]; + if (!set_item.valobj_sp) { + + WritableDataBufferSP buffer_sp(new DataBufferHeap(m_ptr_size, 0)); + + switch (m_ptr_size) { + case 0: // architecture has no clue - fail + return lldb::ValueObjectSP(); + case 4: + *reinterpret_cast<uint32_t *>(buffer_sp->GetBytes()) = + static_cast<uint32_t>(set_item.item_ptr); + break; + case 8: + *reinterpret_cast<uint64_t *>(buffer_sp->GetBytes()) = + static_cast<uint64_t>(set_item.item_ptr); + break; + default: + lldbassert(false && "pointer size is not 4 nor 8"); + } + StreamString idx_name; + idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); + + DataExtractor data(buffer_sp, m_order, m_ptr_size); + + set_item.valobj_sp = CreateValueObjectFromData( + idx_name.GetString(), data, m_exe_ctx_ref, + m_backend.GetCompilerType().GetBasicTypeFromAST( + lldb::eBasicTypeObjCID)); + } + + return set_item.valobj_sp; +} + +template <typename D32, typename D64> +lldb_private::formatters::GenericNSSetMSyntheticFrontEnd< + D32, D64>::GenericNSSetMSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), + m_data_32(nullptr), m_data_64(nullptr) { + if (valobj_sp) + Update(); +} + +template <typename D32, typename D64> +lldb_private::formatters::GenericNSSetMSyntheticFrontEnd<D32, D64>:: + GenericNSSetMSyntheticFrontEnd::~GenericNSSetMSyntheticFrontEnd() { + delete m_data_32; + m_data_32 = nullptr; + delete m_data_64; + m_data_64 = nullptr; +} + +template <typename D32, typename D64> +size_t +lldb_private::formatters:: + GenericNSSetMSyntheticFrontEnd<D32, D64>::GetIndexOfChildWithName( + ConstString name) { + const char *item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors()) + return UINT32_MAX; + return idx; +} + +template <typename D32, typename D64> +llvm::Expected<uint32_t> +lldb_private::formatters::GenericNSSetMSyntheticFrontEnd< + D32, D64>::CalculateNumChildren() { + if (!m_data_32 && !m_data_64) + return 0; + return (m_data_32 ? (uint32_t)m_data_32->_used : (uint32_t)m_data_64->_used); +} + +template <typename D32, typename D64> +lldb::ChildCacheState +lldb_private::formatters::GenericNSSetMSyntheticFrontEnd<D32, D64>::Update() { + m_children.clear(); + ValueObjectSP valobj_sp = m_backend.GetSP(); + m_ptr_size = 0; + delete m_data_32; + m_data_32 = nullptr; + delete m_data_64; + m_data_64 = nullptr; + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); + if (!process_sp) + return lldb::ChildCacheState::eRefetch; + m_ptr_size = process_sp->GetAddressByteSize(); + uint64_t data_location = valobj_sp->GetValueAsUnsigned(0) + m_ptr_size; + Status error; + if (m_ptr_size == 4) { + m_data_32 = new D32(); + process_sp->ReadMemory(data_location, m_data_32, sizeof(D32), + error); + } else { + m_data_64 = new D64(); + process_sp->ReadMemory(data_location, m_data_64, sizeof(D64), + error); + } + return error.Success() ? lldb::ChildCacheState::eReuse + : lldb::ChildCacheState::eRefetch; +} + +template <typename D32, typename D64> +bool +lldb_private::formatters:: + GenericNSSetMSyntheticFrontEnd<D32, D64>::MightHaveChildren() { + return true; +} + +template <typename D32, typename D64> +lldb::ValueObjectSP +lldb_private::formatters:: + GenericNSSetMSyntheticFrontEnd<D32, D64>::GetChildAtIndex(uint32_t idx) { + lldb::addr_t m_objs_addr = + (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr); + + uint32_t num_children = CalculateNumChildrenIgnoringErrors(); + + if (idx >= num_children) + return lldb::ValueObjectSP(); + + ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); + if (!process_sp) + return lldb::ValueObjectSP(); + + if (m_children.empty()) { + // do the scan phase + lldb::addr_t obj_at_idx = 0; + + uint32_t tries = 0; + uint32_t test_idx = 0; + + while (tries < num_children) { + obj_at_idx = m_objs_addr + (test_idx * m_ptr_size); + if (!process_sp) + return lldb::ValueObjectSP(); + Status error; + obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + + test_idx++; + + if (!obj_at_idx) + continue; + tries++; + + SetItemDescriptor descriptor = {obj_at_idx, lldb::ValueObjectSP()}; + + m_children.push_back(descriptor); + } + } + + if (idx >= m_children.size()) // should never happen + return lldb::ValueObjectSP(); + + SetItemDescriptor &set_item = m_children[idx]; + if (!set_item.valobj_sp) { + auto ptr_size = process_sp->GetAddressByteSize(); + DataBufferHeap buffer(ptr_size, 0); + switch (ptr_size) { + case 0: // architecture has no clue?? - fail + return lldb::ValueObjectSP(); + case 4: + *((uint32_t *)buffer.GetBytes()) = (uint32_t)set_item.item_ptr; + break; + case 8: + *((uint64_t *)buffer.GetBytes()) = (uint64_t)set_item.item_ptr; + break; + default: + assert(false && "pointer size is not 4 nor 8 - get out of here ASAP"); + } + StreamString idx_name; + idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); + + DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(), + process_sp->GetByteOrder(), + process_sp->GetAddressByteSize()); + + set_item.valobj_sp = CreateValueObjectFromData( + idx_name.GetString(), data, m_exe_ctx_ref, + m_backend.GetCompilerType().GetBasicTypeFromAST( + lldb::eBasicTypeObjCID)); + } + return set_item.valobj_sp; +} + +template bool lldb_private::formatters::NSSetSummaryProvider<true>( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); + +template bool lldb_private::formatters::NSSetSummaryProvider<false>( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSSet.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSSet.h new file mode 100644 index 000000000000..3ad1f694befe --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSSet.h @@ -0,0 +1,39 @@ +//===-- NSSet.h ---------------------------------------------------*- C++ +//-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_NSSET_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_NSSET_H + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Stream.h" + +namespace lldb_private { +namespace formatters { +template <bool cf_style> +bool NSSetSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +SyntheticChildrenFrontEnd *NSSetSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +class NSSet_Additionals { +public: + static std::map<ConstString, CXXFunctionSummaryFormat::Callback> & + GetAdditionalSummaries(); + + static std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback> & + GetAdditionalSynthetics(); +}; +} // namespace formatters +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_NSSET_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.cpp new file mode 100644 index 000000000000..0a30737d9723 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.cpp @@ -0,0 +1,370 @@ +//===-- NSString.cpp ------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "NSString.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/DataFormatters/StringPrinter.h" +#include "lldb/Target/Language.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +std::map<ConstString, CXXFunctionSummaryFormat::Callback> & +NSString_Additionals::GetAdditionalSummaries() { + static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map; + return g_map; +} + +bool lldb_private::formatters::NSStringSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + static constexpr llvm::StringLiteral g_TypeHint("NSString"); + + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor( + runtime->GetClassDescriptor(valobj)); + + if (!descriptor.get() || !descriptor->IsValid()) + return false; + + uint32_t ptr_size = process_sp->GetAddressByteSize(); + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + ConstString class_name_cs = descriptor->GetClassName(); + llvm::StringRef class_name = class_name_cs.GetStringRef(); + + if (class_name.empty()) + return false; + + bool is_tagged_ptr = class_name == "NSTaggedPointerString" && + descriptor->GetTaggedPointerInfo(); + // for a tagged pointer, the descriptor has everything we need + if (is_tagged_ptr) + return NSTaggedString_SummaryProvider(valobj, descriptor, stream, + summary_options); + + auto &additionals_map(NSString_Additionals::GetAdditionalSummaries()); + auto iter = additionals_map.find(class_name_cs), end = additionals_map.end(); + if (iter != end) + return iter->second(valobj, stream, summary_options); + + // if not a tagged pointer that we know about, try the normal route + uint64_t info_bits_location = valobj_addr + ptr_size; + if (process_sp->GetByteOrder() != lldb::eByteOrderLittle) + info_bits_location += 3; + + Status error; + + uint8_t info_bits = process_sp->ReadUnsignedIntegerFromMemory( + info_bits_location, 1, 0, error); + if (error.Fail()) + return false; + + bool is_mutable = (info_bits & 1) == 1; + bool is_inline = (info_bits & 0x60) == 0; + bool has_explicit_length = (info_bits & (1 | 4)) != 4; + bool is_unicode = (info_bits & 0x10) == 0x10; + bool is_path_store = class_name == "NSPathStore2"; + bool has_null = (info_bits & 8) == 8; + + size_t explicit_length = 0; + if (!has_null && has_explicit_length && !is_path_store) { + lldb::addr_t explicit_length_offset = 2 * ptr_size; + if (is_mutable && !is_inline) + explicit_length_offset = + explicit_length_offset + ptr_size; // notInlineMutable.length; + else if (is_inline) + explicit_length = explicit_length + 0; // inline1.length; + else if (!is_inline && !is_mutable) + explicit_length_offset = + explicit_length_offset + ptr_size; // notInlineImmutable1.length; + else + explicit_length_offset = 0; + + if (explicit_length_offset) { + explicit_length_offset = valobj_addr + explicit_length_offset; + explicit_length = process_sp->ReadUnsignedIntegerFromMemory( + explicit_length_offset, 4, 0, error); + } + } + + const llvm::StringSet<> supported_string_classes = { + "NSString", "CFMutableStringRef", + "CFStringRef", "__NSCFConstantString", + "__NSCFString", "NSCFConstantString", + "NSCFString", "NSPathStore2"}; + if (supported_string_classes.count(class_name) == 0) { + // not one of us - but tell me class name + stream.Printf("class name = %s", class_name_cs.GetCString()); + return true; + } + + llvm::StringRef prefix, suffix; + if (Language *language = Language::FindPlugin(summary_options.GetLanguage())) + std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint); + + StringPrinter::ReadStringAndDumpToStreamOptions options(valobj); + options.SetPrefixToken(prefix.str()); + options.SetSuffixToken(suffix.str()); + + if (is_mutable) { + uint64_t location = 2 * ptr_size + valobj_addr; + location = process_sp->ReadPointerFromMemory(location, error); + if (error.Fail()) + return false; + if (has_explicit_length && is_unicode) { + options.SetLocation(location); + options.SetTargetSP(valobj.GetTargetSP()); + options.SetStream(&stream); + options.SetQuote('"'); + options.SetSourceSize(explicit_length); + options.SetHasSourceSize(has_explicit_length); + options.SetNeedsZeroTermination(false); + options.SetIgnoreMaxLength(summary_options.GetCapping() == + TypeSummaryCapping::eTypeSummaryUncapped); + options.SetBinaryZeroIsTerminator(false); + return StringPrinter::ReadStringAndDumpToStream< + StringPrinter::StringElementType::UTF16>(options); + } else { + options.SetLocation(location + 1); + options.SetTargetSP(valobj.GetTargetSP()); + options.SetStream(&stream); + options.SetSourceSize(explicit_length); + options.SetHasSourceSize(has_explicit_length); + options.SetNeedsZeroTermination(false); + options.SetIgnoreMaxLength(summary_options.GetCapping() == + TypeSummaryCapping::eTypeSummaryUncapped); + options.SetBinaryZeroIsTerminator(false); + return StringPrinter::ReadStringAndDumpToStream< + StringPrinter::StringElementType::ASCII>(options); + } + } else if (is_inline && has_explicit_length && !is_unicode && + !is_path_store && !is_mutable) { + uint64_t location = 3 * ptr_size + valobj_addr; + + options.SetLocation(location); + options.SetTargetSP(valobj.GetTargetSP()); + options.SetStream(&stream); + options.SetQuote('"'); + options.SetSourceSize(explicit_length); + options.SetHasSourceSize(has_explicit_length); + options.SetIgnoreMaxLength(summary_options.GetCapping() == + TypeSummaryCapping::eTypeSummaryUncapped); + return StringPrinter::ReadStringAndDumpToStream< + StringPrinter::StringElementType::ASCII>(options); + } else if (is_unicode) { + uint64_t location = valobj_addr + 2 * ptr_size; + if (is_inline) { + if (!has_explicit_length) { + return false; + } else + location += ptr_size; + } else { + location = process_sp->ReadPointerFromMemory(location, error); + if (error.Fail()) + return false; + } + options.SetLocation(location); + options.SetTargetSP(valobj.GetTargetSP()); + options.SetStream(&stream); + options.SetQuote('"'); + options.SetSourceSize(explicit_length); + options.SetHasSourceSize(has_explicit_length); + options.SetNeedsZeroTermination(!has_explicit_length); + options.SetIgnoreMaxLength(summary_options.GetCapping() == + TypeSummaryCapping::eTypeSummaryUncapped); + options.SetBinaryZeroIsTerminator(!has_explicit_length); + return StringPrinter::ReadStringAndDumpToStream< + StringPrinter::StringElementType::UTF16>(options); + } else if (is_path_store) { + // _lengthAndRefCount is the first ivar of NSPathStore2 (after the isa). + uint64_t length_ivar_offset = 1 * ptr_size; + CompilerType length_type = valobj.GetCompilerType().GetBasicTypeFromAST( + lldb::eBasicTypeUnsignedInt); + ValueObjectSP length_valobj_sp = + valobj.GetSyntheticChildAtOffset(length_ivar_offset, length_type, true, + ConstString("_lengthAndRefCount")); + if (!length_valobj_sp) + return false; + // Get the length out of _lengthAndRefCount. + explicit_length = length_valobj_sp->GetValueAsUnsigned(0) >> 20; + lldb::addr_t location = valobj.GetValueAsUnsigned(0) + ptr_size + 4; + + options.SetLocation(location); + options.SetTargetSP(valobj.GetTargetSP()); + options.SetStream(&stream); + options.SetQuote('"'); + options.SetSourceSize(explicit_length); + options.SetHasSourceSize(has_explicit_length); + options.SetNeedsZeroTermination(!has_explicit_length); + options.SetIgnoreMaxLength(summary_options.GetCapping() == + TypeSummaryCapping::eTypeSummaryUncapped); + options.SetBinaryZeroIsTerminator(!has_explicit_length); + return StringPrinter::ReadStringAndDumpToStream< + StringPrinter::StringElementType::UTF16>(options); + } else if (is_inline) { + uint64_t location = valobj_addr + 2 * ptr_size; + if (!has_explicit_length) { + // in this kind of string, the byte before the string content is a length + // byte so let's try and use it to handle the embedded NUL case + Status error; + explicit_length = + process_sp->ReadUnsignedIntegerFromMemory(location, 1, 0, error); + has_explicit_length = !(error.Fail() || explicit_length == 0); + location++; + } + options.SetLocation(location); + options.SetTargetSP(valobj.GetTargetSP()); + options.SetStream(&stream); + options.SetSourceSize(explicit_length); + options.SetHasSourceSize(has_explicit_length); + options.SetNeedsZeroTermination(!has_explicit_length); + options.SetIgnoreMaxLength(summary_options.GetCapping() == + TypeSummaryCapping::eTypeSummaryUncapped); + options.SetBinaryZeroIsTerminator(!has_explicit_length); + if (has_explicit_length) + return StringPrinter::ReadStringAndDumpToStream< + StringPrinter::StringElementType::UTF8>(options); + else + return StringPrinter::ReadStringAndDumpToStream< + StringPrinter::StringElementType::ASCII>(options); + } else { + uint64_t location = valobj_addr + 2 * ptr_size; + location = process_sp->ReadPointerFromMemory(location, error); + if (error.Fail()) + return false; + if (has_explicit_length && !has_null) + explicit_length++; // account for the fact that there is no NULL and we + // need to have one added + options.SetLocation(location); + options.SetTargetSP(valobj.GetTargetSP()); + options.SetStream(&stream); + options.SetSourceSize(explicit_length); + options.SetHasSourceSize(has_explicit_length); + options.SetIgnoreMaxLength(summary_options.GetCapping() == + TypeSummaryCapping::eTypeSummaryUncapped); + return StringPrinter::ReadStringAndDumpToStream< + StringPrinter::StringElementType::ASCII>(options); + } +} + +bool lldb_private::formatters::NSAttributedStringSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + TargetSP target_sp(valobj.GetTargetSP()); + if (!target_sp) + return false; + uint32_t addr_size = target_sp->GetArchitecture().GetAddressByteSize(); + uint64_t pointer_value = valobj.GetValueAsUnsigned(0); + if (!pointer_value) + return false; + pointer_value += addr_size; + CompilerType type(valobj.GetCompilerType()); + ExecutionContext exe_ctx(target_sp, false); + ValueObjectSP child_ptr_sp(valobj.CreateValueObjectFromAddress( + "string_ptr", pointer_value, exe_ctx, type)); + if (!child_ptr_sp) + return false; + DataExtractor data; + Status error; + child_ptr_sp->GetData(data, error); + if (error.Fail()) + return false; + ValueObjectSP child_sp(child_ptr_sp->CreateValueObjectFromData( + "string_data", data, exe_ctx, type)); + child_sp->GetValueAsUnsigned(0); + if (child_sp) + return NSStringSummaryProvider(*child_sp, stream, options); + return false; +} + +bool lldb_private::formatters::NSMutableAttributedStringSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + return NSAttributedStringSummaryProvider(valobj, stream, options); +} + +bool lldb_private::formatters::NSTaggedString_SummaryProvider( + ValueObject &valobj, ObjCLanguageRuntime::ClassDescriptorSP descriptor, + Stream &stream, const TypeSummaryOptions &summary_options) { + static constexpr llvm::StringLiteral g_TypeHint("NSString"); + + if (!descriptor) + return false; + uint64_t len_bits = 0, data_bits = 0; + if (!descriptor->GetTaggedPointerInfo(&len_bits, &data_bits, nullptr)) + return false; + + static const int g_MaxNonBitmaskedLen = 7; // TAGGED_STRING_UNPACKED_MAXLEN + static const int g_SixbitMaxLen = 9; + static const int g_fiveBitMaxLen = 11; + + static const char *sixBitToCharLookup = "eilotrm.apdnsIc ufkMShjTRxgC4013" + "bDNvwyUL2O856P-B79AFKEWV_zGJ/HYX"; + + if (len_bits > g_fiveBitMaxLen) + return false; + + llvm::StringRef prefix, suffix; + if (Language *language = Language::FindPlugin(summary_options.GetLanguage())) + std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint); + + // this is a fairly ugly trick - pretend that the numeric value is actually a + // char* this works under a few assumptions: little endian architecture + // sizeof(uint64_t) > g_MaxNonBitmaskedLen + if (len_bits <= g_MaxNonBitmaskedLen) { + stream << prefix; + stream.Printf("\"%s\"", (const char *)&data_bits); + stream << suffix; + return true; + } + + // if the data is bitmasked, we need to actually process the bytes + uint8_t bitmask = 0; + uint8_t shift_offset = 0; + + if (len_bits <= g_SixbitMaxLen) { + bitmask = 0x03f; + shift_offset = 6; + } else { + bitmask = 0x01f; + shift_offset = 5; + } + + std::vector<uint8_t> bytes; + bytes.resize(len_bits); + for (; len_bits > 0; data_bits >>= shift_offset, --len_bits) { + uint8_t packed = data_bits & bitmask; + bytes.insert(bytes.begin(), sixBitToCharLookup[packed]); + } + + stream << prefix; + stream.Printf("\"%s\"", &bytes[0]); + stream << suffix; + return true; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.h new file mode 100644 index 000000000000..a68cc6c056b0 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.h @@ -0,0 +1,42 @@ +//===-- NSString.h ---------------------------------------------------*- C++ +//-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_NSSTRING_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_NSSTRING_H + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/Utility/Stream.h" + +#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h" + +namespace lldb_private { +namespace formatters { +bool NSStringSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +bool NSTaggedString_SummaryProvider( + ValueObject &valobj, ObjCLanguageRuntime::ClassDescriptorSP descriptor, + Stream &stream, const TypeSummaryOptions &summary_options); + +bool NSAttributedStringSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +bool NSMutableAttributedStringSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); + +class NSString_Additionals { +public: + static std::map<ConstString, CXXFunctionSummaryFormat::Callback> & + GetAdditionalSummaries(); +}; +} // namespace formatters +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_NSSTRING_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCConstants.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCConstants.h new file mode 100644 index 000000000000..c7c498d4cab3 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCConstants.h @@ -0,0 +1,44 @@ +//===-- ObjCConstants.h------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_OBJCCONSTANTS_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_OBJCCONSTANTS_H + +// Objective-C Type Encoding +#define _C_ID '@' +#define _C_CLASS '#' +#define _C_SEL ':' +#define _C_CHR 'c' +#define _C_UCHR 'C' +#define _C_SHT 's' +#define _C_USHT 'S' +#define _C_INT 'i' +#define _C_UINT 'I' +#define _C_LNG 'l' +#define _C_ULNG 'L' +#define _C_LNG_LNG 'q' +#define _C_ULNG_LNG 'Q' +#define _C_FLT 'f' +#define _C_DBL 'd' +#define _C_BFLD 'b' +#define _C_BOOL 'B' +#define _C_VOID 'v' +#define _C_UNDEF '?' +#define _C_PTR '^' +#define _C_CHARPTR '*' +#define _C_ATOM '%' +#define _C_ARY_B '[' +#define _C_ARY_E ']' +#define _C_UNION_B '(' +#define _C_UNION_E ')' +#define _C_STRUCT_B '{' +#define _C_STRUCT_E '}' +#define _C_VECTOR '!' +#define _C_CONST 'r' + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_OBJCCONSTANTS_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp new file mode 100644 index 000000000000..742ae7b14945 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp @@ -0,0 +1,1042 @@ +//===-- ObjCLanguage.cpp --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <mutex> + +#include "ObjCLanguage.h" + +#include "Plugins/ExpressionParser/Clang/ClangUtil.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/DataVisualization.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Symbol/CompilerType.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/StreamString.h" + +#include "llvm/Support/Threading.h" + +#include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h" +#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h" + +#include "CF.h" +#include "Cocoa.h" +#include "CoreMedia.h" +#include "NSDictionary.h" +#include "NSSet.h" +#include "NSString.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +LLDB_PLUGIN_DEFINE(ObjCLanguage) + +void ObjCLanguage::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), "Objective-C Language", + CreateInstance); +} + +void ObjCLanguage::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +// Static Functions + +Language *ObjCLanguage::CreateInstance(lldb::LanguageType language) { + switch (language) { + case lldb::eLanguageTypeObjC: + return new ObjCLanguage(); + default: + return nullptr; + } +} + +std::optional<const ObjCLanguage::MethodName> +ObjCLanguage::MethodName::Create(llvm::StringRef name, bool strict) { + if (name.empty()) + return std::nullopt; + + // Objective-C method minimum requirements: + // - If `strict` is true, must start with '-' or '+' (1 char) + // - Must be followed by '[' (1 char) + // - Must have at least one character for class name (1 char) + // - Must have a space between class name and method name (1 char) + // - Must have at least one character for method name (1 char) + // - Must be end with ']' (1 char) + // This means that the minimum size is 5 characters (6 if `strict`) + // e.g. [a a] (-[a a] or +[a a] if `strict`) + + // We can check length and ending invariants first + if (name.size() < (5 + (strict ? 1 : 0)) || name.back() != ']') + return std::nullopt; + + // Figure out type + Type type = eTypeUnspecified; + if (name.starts_with("+[")) + type = eTypeClassMethod; + else if (name.starts_with("-[")) + type = eTypeInstanceMethod; + + // If there's no type and it's strict, this is invalid + if (strict && type == eTypeUnspecified) + return std::nullopt; + + // If not strict and type unspecified, make sure we start with '[' + if (type == eTypeUnspecified && name.front() != '[') + return std::nullopt; + + // If we've gotten here, we're confident that this looks enough like an + // Objective-C method to treat it like one. + ObjCLanguage::MethodName method_name(name, type); + return method_name; +} + +llvm::StringRef ObjCLanguage::MethodName::GetClassName() const { + llvm::StringRef full = m_full; + const size_t class_start_pos = (full.front() == '[' ? 1 : 2); + const size_t paren_pos = full.find('(', class_start_pos); + // If there's a category we want to stop there + if (paren_pos != llvm::StringRef::npos) + return full.substr(class_start_pos, paren_pos - class_start_pos); + + // Otherwise we find the space separating the class and method + const size_t space_pos = full.find(' ', class_start_pos); + return full.substr(class_start_pos, space_pos - class_start_pos); +} + +llvm::StringRef ObjCLanguage::MethodName::GetClassNameWithCategory() const { + llvm::StringRef full = m_full; + const size_t class_start_pos = (full.front() == '[' ? 1 : 2); + const size_t space_pos = full.find(' ', class_start_pos); + return full.substr(class_start_pos, space_pos - class_start_pos); +} + +llvm::StringRef ObjCLanguage::MethodName::GetSelector() const { + llvm::StringRef full = m_full; + const size_t space_pos = full.find(' '); + if (space_pos == llvm::StringRef::npos) + return llvm::StringRef(); + const size_t closing_bracket = full.find(']', space_pos); + return full.substr(space_pos + 1, closing_bracket - space_pos - 1); +} + +llvm::StringRef ObjCLanguage::MethodName::GetCategory() const { + llvm::StringRef full = m_full; + const size_t open_paren_pos = full.find('('); + const size_t close_paren_pos = full.find(')'); + + if (open_paren_pos == llvm::StringRef::npos || + close_paren_pos == llvm::StringRef::npos) + return llvm::StringRef(); + + return full.substr(open_paren_pos + 1, + close_paren_pos - (open_paren_pos + 1)); +} + +std::string ObjCLanguage::MethodName::GetFullNameWithoutCategory() const { + llvm::StringRef full = m_full; + const size_t open_paren_pos = full.find('('); + const size_t close_paren_pos = full.find(')'); + if (open_paren_pos == llvm::StringRef::npos || + close_paren_pos == llvm::StringRef::npos) + return std::string(); + + llvm::StringRef class_name = GetClassName(); + llvm::StringRef selector_name = GetSelector(); + + // Compute the total size to avoid reallocations + // class name + selector name + '[' + ' ' + ']' + size_t total_size = class_name.size() + selector_name.size() + 3; + if (m_type != eTypeUnspecified) + total_size++; // For + or - + + std::string name_sans_category; + name_sans_category.reserve(total_size); + + if (m_type == eTypeClassMethod) + name_sans_category += '+'; + else if (m_type == eTypeInstanceMethod) + name_sans_category += '-'; + + name_sans_category += '['; + name_sans_category.append(class_name.data(), class_name.size()); + name_sans_category += ' '; + name_sans_category.append(selector_name.data(), selector_name.size()); + name_sans_category += ']'; + + return name_sans_category; +} + +std::vector<Language::MethodNameVariant> +ObjCLanguage::GetMethodNameVariants(ConstString method_name) const { + std::vector<Language::MethodNameVariant> variant_names; + std::optional<const ObjCLanguage::MethodName> objc_method = + ObjCLanguage::MethodName::Create(method_name.GetStringRef(), false); + if (!objc_method) + return variant_names; + + variant_names.emplace_back(ConstString(objc_method->GetSelector()), + lldb::eFunctionNameTypeSelector); + + const std::string name_sans_category = + objc_method->GetFullNameWithoutCategory(); + + if (objc_method->IsClassMethod() || objc_method->IsInstanceMethod()) { + if (!name_sans_category.empty()) + variant_names.emplace_back(ConstString(name_sans_category.c_str()), + lldb::eFunctionNameTypeFull); + } else { + StreamString strm; + + strm.Printf("+%s", objc_method->GetFullName().c_str()); + variant_names.emplace_back(ConstString(strm.GetString()), + lldb::eFunctionNameTypeFull); + strm.Clear(); + + strm.Printf("-%s", objc_method->GetFullName().c_str()); + variant_names.emplace_back(ConstString(strm.GetString()), + lldb::eFunctionNameTypeFull); + strm.Clear(); + + if (!name_sans_category.empty()) { + strm.Printf("+%s", name_sans_category.c_str()); + variant_names.emplace_back(ConstString(strm.GetString()), + lldb::eFunctionNameTypeFull); + strm.Clear(); + + strm.Printf("-%s", name_sans_category.c_str()); + variant_names.emplace_back(ConstString(strm.GetString()), + lldb::eFunctionNameTypeFull); + } + } + + return variant_names; +} + +bool ObjCLanguage::SymbolNameFitsToLanguage(Mangled mangled) const { + ConstString demangled_name = mangled.GetDemangledName(); + if (!demangled_name) + return false; + return ObjCLanguage::IsPossibleObjCMethodName(demangled_name.GetCString()); +} + +static void LoadObjCFormatters(TypeCategoryImplSP objc_category_sp) { + if (!objc_category_sp) + return; + + TypeSummaryImpl::Flags objc_flags; + objc_flags.SetCascades(false) + .SetSkipPointers(true) + .SetSkipReferences(true) + .SetDontShowChildren(true) + .SetDontShowValue(true) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false); + + lldb::TypeSummaryImplSP ObjC_BOOL_summary(new CXXFunctionSummaryFormat( + objc_flags, lldb_private::formatters::ObjCBOOLSummaryProvider, "")); + objc_category_sp->AddTypeSummary("BOOL", eFormatterMatchExact, + ObjC_BOOL_summary); + objc_category_sp->AddTypeSummary("BOOL &", eFormatterMatchExact, + ObjC_BOOL_summary); + objc_category_sp->AddTypeSummary("BOOL *", eFormatterMatchExact, + ObjC_BOOL_summary); + + // we need to skip pointers here since we are special casing a SEL* when + // retrieving its value + objc_flags.SetSkipPointers(true); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::ObjCSELSummaryProvider<false>, + "SEL summary provider", "SEL", objc_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::ObjCSELSummaryProvider<false>, + "SEL summary provider", "struct objc_selector", objc_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::ObjCSELSummaryProvider<false>, + "SEL summary provider", "objc_selector", objc_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::ObjCSELSummaryProvider<true>, + "SEL summary provider", "objc_selector *", objc_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::ObjCSELSummaryProvider<true>, + "SEL summary provider", "SEL *", objc_flags); + + AddCXXSummary(objc_category_sp, + lldb_private::formatters::ObjCClassSummaryProvider, + "Class summary provider", "Class", objc_flags); + + SyntheticChildren::Flags class_synth_flags; + class_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences( + false); + + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::ObjCClassSyntheticFrontEndCreator, + "Class synthetic children", "Class", class_synth_flags); + + objc_flags.SetSkipPointers(false); + objc_flags.SetCascades(true); + objc_flags.SetSkipReferences(false); + + AddStringSummary(objc_category_sp, "${var.__FuncPtr%A}", + "__block_literal_generic", objc_flags); + + AddStringSummary(objc_category_sp, + "${var.years} years, ${var.months} " + "months, ${var.days} days, ${var.hours} " + "hours, ${var.minutes} minutes " + "${var.seconds} seconds", + "CFGregorianUnits", objc_flags); + AddStringSummary(objc_category_sp, + "location=${var.location} length=${var.length}", "CFRange", + objc_flags); + + AddStringSummary(objc_category_sp, + "location=${var.location}, length=${var.length}", "NSRange", + objc_flags); + AddStringSummary(objc_category_sp, "(${var.origin}, ${var.size}), ...", + "NSRectArray", objc_flags); + + AddOneLineSummary(objc_category_sp, "NSPoint", objc_flags); + AddOneLineSummary(objc_category_sp, "NSSize", objc_flags); + AddOneLineSummary(objc_category_sp, "NSRect", objc_flags); + + AddOneLineSummary(objc_category_sp, "CGSize", objc_flags); + AddOneLineSummary(objc_category_sp, "CGPoint", objc_flags); + AddOneLineSummary(objc_category_sp, "CGRect", objc_flags); + + AddStringSummary(objc_category_sp, + "red=${var.red} green=${var.green} blue=${var.blue}", + "RGBColor", objc_flags); + AddStringSummary( + objc_category_sp, + "(t=${var.top}, l=${var.left}, b=${var.bottom}, r=${var.right})", "Rect", + objc_flags); + AddStringSummary(objc_category_sp, "{(v=${var.v}, h=${var.h})}", "Point", + objc_flags); + AddStringSummary(objc_category_sp, + "${var.month}/${var.day}/${var.year} ${var.hour} " + ":${var.minute} :${var.second} dayOfWeek:${var.dayOfWeek}", + "DateTimeRect *", objc_flags); + AddStringSummary(objc_category_sp, + "${var.ld.month}/${var.ld.day}/" + "${var.ld.year} ${var.ld.hour} " + ":${var.ld.minute} :${var.ld.second} " + "dayOfWeek:${var.ld.dayOfWeek}", + "LongDateRect", objc_flags); + AddStringSummary(objc_category_sp, "(x=${var.x}, y=${var.y})", "HIPoint", + objc_flags); + AddStringSummary(objc_category_sp, "origin=${var.origin} size=${var.size}", + "HIRect", objc_flags); + + TypeSummaryImpl::Flags appkit_flags; + appkit_flags.SetCascades(true) + .SetSkipPointers(false) + .SetSkipReferences(false) + .SetDontShowChildren(true) + .SetDontShowValue(false) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false); + + appkit_flags.SetDontShowChildren(false); + + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSArraySummaryProvider, + "NSArray summary provider", "NSArray", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSArraySummaryProvider, + "NSArray summary provider", "NSConstantArray", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSArraySummaryProvider, + "NSArray summary provider", "NSMutableArray", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSArraySummaryProvider, + "NSArray summary provider", "__NSArrayI", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSArraySummaryProvider, + "NSArray summary provider", "__NSArray0", appkit_flags); + AddCXXSummary( + objc_category_sp, lldb_private::formatters::NSArraySummaryProvider, + "NSArray summary provider", "__NSSingleObjectArrayI", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSArraySummaryProvider, + "NSArray summary provider", "__NSArrayM", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSArraySummaryProvider, + "NSArray summary provider", "__NSCFArray", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSArraySummaryProvider, + "NSArray summary provider", "_NSCallStackArray", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSArraySummaryProvider, + "NSArray summary provider", "CFArrayRef", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSArraySummaryProvider, + "NSArray summary provider", "CFMutableArrayRef", appkit_flags); + + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSDictionarySummaryProvider<false>, + "NSDictionary summary provider", "NSDictionary", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSDictionarySummaryProvider<false>, + "NSDictionary summary provider", "NSConstantDictionary", + appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSDictionarySummaryProvider<false>, + "NSDictionary summary provider", "NSMutableDictionary", + appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSDictionarySummaryProvider<false>, + "NSDictionary summary provider", "__NSCFDictionary", + appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSDictionarySummaryProvider<false>, + "NSDictionary summary provider", "__NSDictionaryI", + appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSDictionarySummaryProvider<false>, + "NSDictionary summary provider", "__NSSingleEntryDictionaryI", + appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSDictionarySummaryProvider<false>, + "NSDictionary summary provider", "__NSDictionaryM", + appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSDictionarySummaryProvider<true>, + "NSDictionary summary provider", "CFDictionaryRef", + appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSDictionarySummaryProvider<true>, + "NSDictionary summary provider", "__CFDictionary", + appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSDictionarySummaryProvider<true>, + "NSDictionary summary provider", "CFMutableDictionaryRef", + appkit_flags); + + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSSetSummaryProvider<false>, + "NSSet summary", "NSSet", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSSetSummaryProvider<false>, + "NSMutableSet summary", "NSMutableSet", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSSetSummaryProvider<true>, + "CFSetRef summary", "CFSetRef", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSSetSummaryProvider<true>, + "CFMutableSetRef summary", "CFMutableSetRef", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSSetSummaryProvider<false>, + "__NSCFSet summary", "__NSCFSet", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSSetSummaryProvider<false>, + "__CFSet summary", "__CFSet", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSSetSummaryProvider<false>, + "__NSSetI summary", "__NSSetI", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSSetSummaryProvider<false>, + "__NSSetM summary", "__NSSetM", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSSetSummaryProvider<false>, + "NSCountedSet summary", "NSCountedSet", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSSetSummaryProvider<false>, + "NSMutableSet summary", "NSMutableSet", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSSetSummaryProvider<false>, + "NSOrderedSet summary", "NSOrderedSet", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSSetSummaryProvider<false>, + "__NSOrderedSetI summary", "__NSOrderedSetI", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSSetSummaryProvider<false>, + "__NSOrderedSetM summary", "__NSOrderedSetM", appkit_flags); + + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSError_SummaryProvider, + "NSError summary provider", "NSError", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSException_SummaryProvider, + "NSException summary provider", "NSException", appkit_flags); + + // AddSummary(appkit_category_sp, "${var.key%@} -> ${var.value%@}", + // ConstString("$_lldb_typegen_nspair"), appkit_flags); + + appkit_flags.SetDontShowChildren(true); + + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::NSArraySyntheticFrontEndCreator, + "NSArray synthetic children", "__NSArrayM", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::NSArraySyntheticFrontEndCreator, + "NSArray synthetic children", "__NSArrayI", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::NSArraySyntheticFrontEndCreator, + "NSArray synthetic children", "__NSArray0", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::NSArraySyntheticFrontEndCreator, + "NSArray synthetic children", "__NSSingleObjectArrayI", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::NSArraySyntheticFrontEndCreator, + "NSArray synthetic children", "NSArray", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::NSArraySyntheticFrontEndCreator, + "NSArray synthetic children", "NSConstantArray", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::NSArraySyntheticFrontEndCreator, + "NSArray synthetic children", "NSMutableArray", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::NSArraySyntheticFrontEndCreator, + "NSArray synthetic children", "__NSCFArray", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::NSArraySyntheticFrontEndCreator, + "NSArray synthetic children", "_NSCallStackArray", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::NSArraySyntheticFrontEndCreator, + "NSArray synthetic children", "CFMutableArrayRef", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::NSArraySyntheticFrontEndCreator, + "NSArray synthetic children", "CFArrayRef", + ScriptedSyntheticChildren::Flags()); + + AddCXXSynthetic( + objc_category_sp, + lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, + "NSDictionary synthetic children", "__NSDictionaryM", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic( + objc_category_sp, + lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, + "NSDictionary synthetic children", "NSConstantDictionary", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic( + objc_category_sp, + lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, + "NSDictionary synthetic children", "__NSDictionaryI", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic( + objc_category_sp, + lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, + "NSDictionary synthetic children", "__NSSingleEntryDictionaryI", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic( + objc_category_sp, + lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, + "NSDictionary synthetic children", "__NSCFDictionary", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic( + objc_category_sp, + lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, + "NSDictionary synthetic children", "NSDictionary", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic( + objc_category_sp, + lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, + "NSDictionary synthetic children", "NSMutableDictionary", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic( + objc_category_sp, + lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, + "NSDictionary synthetic children", "CFDictionaryRef", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic( + objc_category_sp, + lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, + "NSDictionary synthetic children", "CFMutableDictionaryRef", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic( + objc_category_sp, + lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, + "NSDictionary synthetic children", "__CFDictionary", + ScriptedSyntheticChildren::Flags()); + + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::NSErrorSyntheticFrontEndCreator, + "NSError synthetic children", "NSError", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::NSExceptionSyntheticFrontEndCreator, + "NSException synthetic children", "NSException", + ScriptedSyntheticChildren::Flags()); + + AddCXXSynthetic( + objc_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, + "NSSet synthetic children", "NSSet", ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::NSSetSyntheticFrontEndCreator, + "__NSSetI synthetic children", "__NSSetI", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::NSSetSyntheticFrontEndCreator, + "__NSSetM synthetic children", "__NSSetM", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::NSSetSyntheticFrontEndCreator, + "__NSCFSet synthetic children", "__NSCFSet", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::NSSetSyntheticFrontEndCreator, + "CFSetRef synthetic children", "CFSetRef", + ScriptedSyntheticChildren::Flags()); + + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::NSSetSyntheticFrontEndCreator, + "NSMutableSet synthetic children", "NSMutableSet", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::NSSetSyntheticFrontEndCreator, + "NSOrderedSet synthetic children", "NSOrderedSet", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::NSSetSyntheticFrontEndCreator, + "__NSOrderedSetI synthetic children", "__NSOrderedSetI", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::NSSetSyntheticFrontEndCreator, + "__NSOrderedSetM synthetic children", "__NSOrderedSetM", + ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::NSSetSyntheticFrontEndCreator, + "__CFSet synthetic children", "__CFSet", + ScriptedSyntheticChildren::Flags()); + + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::NSIndexPathSyntheticFrontEndCreator, + "NSIndexPath synthetic children", "NSIndexPath", + ScriptedSyntheticChildren::Flags()); + + AddCXXSummary(objc_category_sp, + lldb_private::formatters::CFBagSummaryProvider, + "CFBag summary provider", "CFBagRef", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::CFBagSummaryProvider, + "CFBag summary provider", "__CFBag", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::CFBagSummaryProvider, + "CFBag summary provider", "const struct __CFBag", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::CFBagSummaryProvider, + "CFBag summary provider", "CFMutableBagRef", appkit_flags); + + AddCXXSummary( + objc_category_sp, lldb_private::formatters::CFBinaryHeapSummaryProvider, + "CFBinaryHeap summary provider", "CFBinaryHeapRef", appkit_flags); + AddCXXSummary( + objc_category_sp, lldb_private::formatters::CFBinaryHeapSummaryProvider, + "CFBinaryHeap summary provider", "__CFBinaryHeap", appkit_flags); + + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSStringSummaryProvider, + "NSString summary provider", "NSString", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSStringSummaryProvider, + "NSString summary provider", "CFStringRef", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSStringSummaryProvider, + "NSString summary provider", "__CFString", appkit_flags); + AddCXXSummary( + objc_category_sp, lldb_private::formatters::NSStringSummaryProvider, + "NSString summary provider", "CFMutableStringRef", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSStringSummaryProvider, + "NSString summary provider", "NSMutableString", appkit_flags); + AddCXXSummary( + objc_category_sp, lldb_private::formatters::NSStringSummaryProvider, + "NSString summary provider", "__NSCFConstantString", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSStringSummaryProvider, + "NSString summary provider", "__NSCFString", appkit_flags); + AddCXXSummary( + objc_category_sp, lldb_private::formatters::NSStringSummaryProvider, + "NSString summary provider", "NSCFConstantString", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSStringSummaryProvider, + "NSString summary provider", "NSCFString", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSStringSummaryProvider, + "NSString summary provider", "NSPathStore2", appkit_flags); + AddCXXSummary( + objc_category_sp, lldb_private::formatters::NSStringSummaryProvider, + "NSString summary provider", "NSTaggedPointerString", appkit_flags); + + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSAttributedStringSummaryProvider, + "NSAttributedString summary provider", "NSAttributedString", + appkit_flags); + AddCXXSummary( + objc_category_sp, + lldb_private::formatters::NSMutableAttributedStringSummaryProvider, + "NSMutableAttributedString summary provider", "NSMutableAttributedString", + appkit_flags); + AddCXXSummary( + objc_category_sp, + lldb_private::formatters::NSMutableAttributedStringSummaryProvider, + "NSMutableAttributedString summary provider", + "NSConcreteMutableAttributedString", appkit_flags); + + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSBundleSummaryProvider, + "NSBundle summary provider", "NSBundle", appkit_flags); + + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSDataSummaryProvider<false>, + "NSData summary provider", "NSData", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSDataSummaryProvider<false>, + "NSData summary provider", "_NSInlineData", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSDataSummaryProvider<false>, + "NSData summary provider", "NSConcreteData", appkit_flags); + AddCXXSummary( + objc_category_sp, lldb_private::formatters::NSDataSummaryProvider<false>, + "NSData summary provider", "NSConcreteMutableData", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSDataSummaryProvider<false>, + "NSData summary provider", "NSMutableData", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSDataSummaryProvider<false>, + "NSData summary provider", "__NSCFData", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSDataSummaryProvider<true>, + "NSData summary provider", "CFDataRef", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSDataSummaryProvider<true>, + "NSData summary provider", "CFMutableDataRef", appkit_flags); + + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSMachPortSummaryProvider, + "NSMachPort summary provider", "NSMachPort", appkit_flags); + + AddCXXSummary( + objc_category_sp, lldb_private::formatters::NSNotificationSummaryProvider, + "NSNotification summary provider", "NSNotification", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSNotificationSummaryProvider, + "NSNotification summary provider", "NSConcreteNotification", + appkit_flags); + + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSNumberSummaryProvider, + "NSNumber summary provider", "NSNumber", appkit_flags); + AddCXXSummary( + objc_category_sp, lldb_private::formatters::NSNumberSummaryProvider, + "NSNumber summary provider", "NSConstantIntegerNumber", appkit_flags); + AddCXXSummary( + objc_category_sp, lldb_private::formatters::NSNumberSummaryProvider, + "NSNumber summary provider", "NSConstantDoubleNumber", appkit_flags); + AddCXXSummary( + objc_category_sp, lldb_private::formatters::NSNumberSummaryProvider, + "NSNumber summary provider", "NSConstantFloatNumber", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSNumberSummaryProvider, + "CFNumberRef summary provider", "CFNumberRef", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSNumberSummaryProvider, + "NSNumber summary provider", "__NSCFBoolean", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSNumberSummaryProvider, + "NSNumber summary provider", "__NSCFNumber", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSNumberSummaryProvider, + "NSNumber summary provider", "NSCFBoolean", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSNumberSummaryProvider, + "NSNumber summary provider", "NSCFNumber", appkit_flags); + AddCXXSummary( + objc_category_sp, lldb_private::formatters::NSNumberSummaryProvider, + "NSDecimalNumber summary provider", "NSDecimalNumber", appkit_flags); + + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSURLSummaryProvider, + "NSURL summary provider", "NSURL", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSURLSummaryProvider, + "NSURL summary provider", "CFURLRef", appkit_flags); + + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSDateSummaryProvider, + "NSDate summary provider", "NSDate", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSDateSummaryProvider, + "NSDate summary provider", "__NSDate", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSDateSummaryProvider, + "NSDate summary provider", "__NSTaggedDate", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSDateSummaryProvider, + "NSDate summary provider", "NSCalendarDate", appkit_flags); + + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSTimeZoneSummaryProvider, + "NSTimeZone summary provider", "NSTimeZone", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSTimeZoneSummaryProvider, + "NSTimeZone summary provider", "CFTimeZoneRef", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSTimeZoneSummaryProvider, + "NSTimeZone summary provider", "__NSTimeZone", appkit_flags); + + // CFAbsoluteTime is actually a double rather than a pointer to an object we + // do not care about the numeric value, since it is probably meaningless to + // users + appkit_flags.SetDontShowValue(true); + AddCXXSummary( + objc_category_sp, lldb_private::formatters::CFAbsoluteTimeSummaryProvider, + "CFAbsoluteTime summary provider", "CFAbsoluteTime", appkit_flags); + appkit_flags.SetDontShowValue(false); + + AddCXXSummary(objc_category_sp, + lldb_private::formatters::NSIndexSetSummaryProvider, + "NSIndexSet summary provider", "NSIndexSet", appkit_flags); + AddCXXSummary( + objc_category_sp, lldb_private::formatters::NSIndexSetSummaryProvider, + "NSIndexSet summary provider", "NSMutableIndexSet", appkit_flags); + + AddStringSummary(objc_category_sp, + "@\"${var.month%d}/${var.day%d}/${var.year%d} " + "${var.hour%d}:${var.minute%d}:${var.second}\"", + "CFGregorianDate", appkit_flags); + + AddCXXSummary(objc_category_sp, + lldb_private::formatters::CFBitVectorSummaryProvider, + "CFBitVector summary provider", "CFBitVectorRef", appkit_flags); + AddCXXSummary( + objc_category_sp, lldb_private::formatters::CFBitVectorSummaryProvider, + "CFBitVector summary provider", "CFMutableBitVectorRef", appkit_flags); + AddCXXSummary(objc_category_sp, + lldb_private::formatters::CFBitVectorSummaryProvider, + "CFBitVector summary provider", "__CFBitVector", appkit_flags); + AddCXXSummary( + objc_category_sp, lldb_private::formatters::CFBitVectorSummaryProvider, + "CFBitVector summary provider", "__CFMutableBitVector", appkit_flags); +} + +static void LoadCoreMediaFormatters(TypeCategoryImplSP objc_category_sp) { + if (!objc_category_sp) + return; + + TypeSummaryImpl::Flags cm_flags; + cm_flags.SetCascades(true) + .SetDontShowChildren(false) + .SetDontShowValue(false) + .SetHideItemNames(false) + .SetShowMembersOneLiner(false) + .SetSkipPointers(false) + .SetSkipReferences(false); + + AddCXXSummary(objc_category_sp, + lldb_private::formatters::CMTimeSummaryProvider, + "CMTime summary provider", "CMTime", cm_flags); +} + +lldb::TypeCategoryImplSP ObjCLanguage::GetFormatters() { + static llvm::once_flag g_initialize; + static TypeCategoryImplSP g_category; + + llvm::call_once(g_initialize, [this]() -> void { + DataVisualization::Categories::GetCategory(ConstString(GetPluginName()), + g_category); + if (g_category) { + LoadCoreMediaFormatters(g_category); + LoadObjCFormatters(g_category); + } + }); + return g_category; +} + +std::vector<FormattersMatchCandidate> +ObjCLanguage::GetPossibleFormattersMatches(ValueObject &valobj, + lldb::DynamicValueType use_dynamic) { + std::vector<FormattersMatchCandidate> result; + + if (use_dynamic == lldb::eNoDynamicValues) + return result; + + CompilerType compiler_type(valobj.GetCompilerType()); + + const bool check_cpp = false; + const bool check_objc = true; + bool canBeObjCDynamic = + compiler_type.IsPossibleDynamicType(nullptr, check_cpp, check_objc); + + if (canBeObjCDynamic && ClangUtil::IsClangType(compiler_type)) { + do { + lldb::ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + break; + ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); + if (runtime == nullptr) + break; + ObjCLanguageRuntime::ClassDescriptorSP objc_class_sp( + runtime->GetClassDescriptor(valobj)); + if (!objc_class_sp) + break; + if (ConstString name = objc_class_sp->GetClassName()) + result.push_back( + {name, valobj.GetTargetSP()->GetDebugger().GetScriptInterpreter(), + TypeImpl(objc_class_sp->GetType()), + FormattersMatchCandidate::Flags{}}); + } while (false); + } + + return result; +} + +std::unique_ptr<Language::TypeScavenger> ObjCLanguage::GetTypeScavenger() { + class ObjCScavengerResult : public Language::TypeScavenger::Result { + public: + ObjCScavengerResult(CompilerType type) + : Language::TypeScavenger::Result(), m_compiler_type(type) {} + + bool IsValid() override { return m_compiler_type.IsValid(); } + + bool DumpToStream(Stream &stream, bool print_help_if_available) override { + if (IsValid()) { + m_compiler_type.DumpTypeDescription(&stream); + stream.EOL(); + return true; + } + return false; + } + + private: + CompilerType m_compiler_type; + }; + + class ObjCRuntimeScavenger : public Language::TypeScavenger { + protected: + bool Find_Impl(ExecutionContextScope *exe_scope, const char *key, + ResultSet &results) override { + bool result = false; + + if (auto *process = exe_scope->CalculateProcess().get()) { + if (auto *objc_runtime = ObjCLanguageRuntime::Get(*process)) { + if (auto *decl_vendor = objc_runtime->GetDeclVendor()) { + ConstString name(key); + for (const CompilerType &type : + decl_vendor->FindTypes(name, /*max_matches*/ UINT32_MAX)) { + result = true; + std::unique_ptr<Language::TypeScavenger::Result> result( + new ObjCScavengerResult(type)); + results.insert(std::move(result)); + } + } + } + } + + return result; + } + + friend class lldb_private::ObjCLanguage; + }; + + class ObjCModulesScavenger : public Language::TypeScavenger { + protected: + bool Find_Impl(ExecutionContextScope *exe_scope, const char *key, + ResultSet &results) override { + bool result = false; + + if (auto *target = exe_scope->CalculateTarget().get()) { + auto *persistent_vars = llvm::cast<ClangPersistentVariables>( + target->GetPersistentExpressionStateForLanguage( + lldb::eLanguageTypeC)); + if (std::shared_ptr<ClangModulesDeclVendor> clang_modules_decl_vendor = + persistent_vars->GetClangModulesDeclVendor()) { + ConstString key_cs(key); + auto types = clang_modules_decl_vendor->FindTypes( + key_cs, /*max_matches*/ UINT32_MAX); + if (!types.empty()) { + result = true; + std::unique_ptr<Language::TypeScavenger::Result> result( + new ObjCScavengerResult(types.front())); + results.insert(std::move(result)); + } + } + } + + return result; + } + + friend class lldb_private::ObjCLanguage; + }; + + class ObjCDebugInfoScavenger : public Language::ImageListTypeScavenger { + public: + CompilerType AdjustForInclusion(CompilerType &candidate) override { + LanguageType lang_type(candidate.GetMinimumLanguage()); + if (!Language::LanguageIsObjC(lang_type)) + return CompilerType(); + if (candidate.IsTypedefType()) + return candidate.GetTypedefedType(); + return candidate; + } + }; + + return std::unique_ptr<TypeScavenger>( + new Language::EitherTypeScavenger<ObjCModulesScavenger, + ObjCRuntimeScavenger, + ObjCDebugInfoScavenger>()); +} + +std::pair<llvm::StringRef, llvm::StringRef> +ObjCLanguage::GetFormatterPrefixSuffix(llvm::StringRef type_hint) { + static constexpr llvm::StringRef empty; + static const llvm::StringMap< + std::pair<const llvm::StringRef, const llvm::StringRef>> + g_affix_map = { + {"CFBag", {"@", empty}}, + {"CFBinaryHeap", {"@", empty}}, + {"NSString", {"@", empty}}, + {"NSString*", {"@", empty}}, + {"NSNumber:char", {"(char)", empty}}, + {"NSNumber:short", {"(short)", empty}}, + {"NSNumber:int", {"(int)", empty}}, + {"NSNumber:long", {"(long)", empty}}, + {"NSNumber:int128_t", {"(int128_t)", empty}}, + {"NSNumber:float", {"(float)", empty}}, + {"NSNumber:double", {"(double)", empty}}, + {"NSData", {"@\"", "\""}}, + {"NSArray", {"@\"", "\""}}, + }; + return g_affix_map.lookup(type_hint); +} + +bool ObjCLanguage::IsNilReference(ValueObject &valobj) { + const uint32_t mask = eTypeIsObjC | eTypeIsPointer; + bool isObjCpointer = + (((valobj.GetCompilerType().GetTypeInfo(nullptr)) & mask) == mask); + if (!isObjCpointer) + return false; + bool canReadValue = true; + bool isZero = valobj.GetValueAsUnsigned(0, &canReadValue) == 0; + return canReadValue && isZero; +} + +bool ObjCLanguage::IsSourceFile(llvm::StringRef file_path) const { + const auto suffixes = {".h", ".m", ".M"}; + for (auto suffix : suffixes) { + if (file_path.ends_with_insensitive(suffix)) + return true; + } + return false; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h new file mode 100644 index 000000000000..d9c0cd3c18cf --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h @@ -0,0 +1,205 @@ +//===-- ObjCLanguage.h ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_OBJCLANGUAGE_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_OBJCLANGUAGE_H + +#include <cstring> +#include <vector> + +#include "Plugins/Language/ClangCommon/ClangHighlighter.h" +#include "lldb/Target/Language.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class ObjCLanguage : public Language { + ClangHighlighter m_highlighter; + +public: + class MethodName { + public: + /// The static factory method for creating a MethodName. + /// + /// \param[in] name + /// The name of the method. + /// + /// \param[in] strict + /// Control whether or not the name parser is strict about +/- in the + /// front of the name. + /// + /// \return If the name failed to parse as a valid Objective-C method name, + /// returns std::nullopt. Otherwise returns a const MethodName. + static std::optional<const MethodName> Create(llvm::StringRef name, + bool strict); + + /// Determines if this method is a class method + /// + /// \return Returns true if the method is a class method. False otherwise. + bool IsClassMethod() const { return m_type == eTypeClassMethod; } + + /// Determines if this method is an instance method + /// + /// \return Returns true if the method is an instance method. False + /// otherwise. + bool IsInstanceMethod() const { return m_type == eTypeInstanceMethod; } + + /// Returns the full name of the method. + /// + /// This includes the class name, the category name (if applicable), and the + /// selector name. + /// + /// \return The name of the method in the form of a const std::string + /// reference. + const std::string &GetFullName() const { return m_full; } + + /// Creates a variation of this method without the category. + /// If this method has no category, it returns an empty string. + /// + /// Example: + /// Full name: "+[NSString(my_additions) myStringWithCString:]" + /// becomes "+[NSString myStringWithCString:]" + /// + /// \return The method name without the category or an empty string if there + /// was no category to begin with. + std::string GetFullNameWithoutCategory() const; + + /// Returns a reference to the class name. + /// + /// Example: + /// Full name: "+[NSString(my_additions) myStringWithCString:]" + /// will give you "NSString" + /// + /// \return A StringRef to the class name of this method. + llvm::StringRef GetClassName() const; + + /// Returns a reference to the class name with the category. + /// + /// Example: + /// Full name: "+[NSString(my_additions) myStringWithCString:]" + /// will give you "NSString(my_additions)" + /// + /// Note: If your method has no category, this will give the same output as + /// `GetClassName`. + /// + /// \return A StringRef to the class name (including the category) of this + /// method. If there was no category, returns the same as `GetClassName`. + llvm::StringRef GetClassNameWithCategory() const; + + /// Returns a reference to the category name. + /// + /// Example: + /// Full name: "+[NSString(my_additions) myStringWithCString:]" + /// will give you "my_additions" + /// \return A StringRef to the category name of this method. If no category + /// is present, the StringRef is empty. + llvm::StringRef GetCategory() const; + + /// Returns a reference to the selector name. + /// + /// Example: + /// Full name: "+[NSString(my_additions) myStringWithCString:]" + /// will give you "myStringWithCString:" + /// \return A StringRef to the selector of this method. + llvm::StringRef GetSelector() const; + + protected: + enum Type { eTypeUnspecified, eTypeClassMethod, eTypeInstanceMethod }; + + MethodName(llvm::StringRef name, Type type) + : m_full(name.str()), m_type(type) {} + + const std::string m_full; + Type m_type; + }; + + ObjCLanguage() = default; + + ~ObjCLanguage() override = default; + + lldb::LanguageType GetLanguageType() const override { + return lldb::eLanguageTypeObjC; + } + + llvm::StringRef GetUserEntryPointName() const override { return "main"; } + + // Get all possible names for a method. Examples: + // If method_name is "+[NSString(my_additions) myStringWithCString:]" + // variant_names[0] => "+[NSString myStringWithCString:]" + // If name is specified without the leading '+' or '-' like + // "[NSString(my_additions) myStringWithCString:]" + // variant_names[0] => "+[NSString(my_additions) myStringWithCString:]" + // variant_names[1] => "-[NSString(my_additions) myStringWithCString:]" + // variant_names[2] => "+[NSString myStringWithCString:]" + // variant_names[3] => "-[NSString myStringWithCString:]" + // Also returns the FunctionNameType of each possible name. + std::vector<Language::MethodNameVariant> + GetMethodNameVariants(ConstString method_name) const override; + + bool SymbolNameFitsToLanguage(Mangled mangled) const override; + + lldb::TypeCategoryImplSP GetFormatters() override; + + std::vector<FormattersMatchCandidate> + GetPossibleFormattersMatches(ValueObject &valobj, + lldb::DynamicValueType use_dynamic) override; + + std::unique_ptr<TypeScavenger> GetTypeScavenger() override; + + std::pair<llvm::StringRef, llvm::StringRef> + GetFormatterPrefixSuffix(llvm::StringRef type_hint) override; + + bool IsNilReference(ValueObject &valobj) override; + + llvm::StringRef GetNilReferenceSummaryString() override { return "nil"; } + + bool IsSourceFile(llvm::StringRef file_path) const override; + + const Highlighter *GetHighlighter() const override { return &m_highlighter; } + + // Static Functions + static void Initialize(); + + static void Terminate(); + + static lldb_private::Language *CreateInstance(lldb::LanguageType language); + + static llvm::StringRef GetPluginNameStatic() { return "objc"; } + + static bool IsPossibleObjCMethodName(const char *name) { + if (!name) + return false; + bool starts_right = (name[0] == '+' || name[0] == '-') && name[1] == '['; + bool ends_right = (name[strlen(name) - 1] == ']'); + return (starts_right && ends_right); + } + + static bool IsPossibleObjCSelector(const char *name) { + if (!name) + return false; + + if (strchr(name, ':') == nullptr) + return true; + else if (name[strlen(name) - 1] == ':') + return true; + else + return false; + } + + llvm::StringRef GetInstanceVariableName() override { return "self"; } + + bool SupportsExceptionBreakpointsOnThrow() const override { return true; } + + // PluginInterface protocol + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_OBJC_OBJCLANGUAGE_H |