diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/Language')
62 files changed, 17346 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.cpp new file mode 100644 index 000000000000..2c9b3c425397 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.cpp @@ -0,0 +1,203 @@ +//===-- BlockPointer.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 "BlockPointer.h" + +#include "Plugins/ExpressionParser/Clang/ClangASTImporter.h" +#include "Plugins/ExpressionParser/Clang/ClangPersistentVariables.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Symbol/CompilerType.h" +#include "lldb/Symbol/TypeSystem.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace lldb_private { +namespace formatters { + +class BlockPointerSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + BlockPointerSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_block_struct_type() { + CompilerType block_pointer_type(m_backend.GetCompilerType()); + CompilerType function_pointer_type; + block_pointer_type.IsBlockPointerType(&function_pointer_type); + + TargetSP target_sp(m_backend.GetTargetSP()); + + if (!target_sp) { + return; + } + + auto type_system_or_err = target_sp->GetScratchTypeSystemForLanguage( + lldb::eLanguageTypeC_plus_plus); + if (auto err = type_system_or_err.takeError()) { + LLDB_LOG_ERROR(GetLog(LLDBLog::DataFormatters), std::move(err), + "Failed to get scratch TypeSystemClang: {0}"); + return; + } + + auto ts = block_pointer_type.GetTypeSystem(); + auto clang_ast_context = ts.dyn_cast_or_null<TypeSystemClang>(); + if (!clang_ast_context) + return; + + const char *const isa_name("__isa"); + const CompilerType isa_type = + clang_ast_context->GetBasicType(lldb::eBasicTypeObjCClass); + const char *const flags_name("__flags"); + const CompilerType flags_type = + clang_ast_context->GetBasicType(lldb::eBasicTypeInt); + const char *const reserved_name("__reserved"); + const CompilerType reserved_type = + clang_ast_context->GetBasicType(lldb::eBasicTypeInt); + const char *const FuncPtr_name("__FuncPtr"); + + m_block_struct_type = clang_ast_context->CreateStructForIdentifier( + llvm::StringRef(), {{isa_name, isa_type}, + {flags_name, flags_type}, + {reserved_name, reserved_type}, + {FuncPtr_name, function_pointer_type}}); + } + + ~BlockPointerSyntheticFrontEnd() override = default; + + llvm::Expected<uint32_t> CalculateNumChildren() override { + const bool omit_empty_base_classes = false; + return m_block_struct_type.GetNumChildren(omit_empty_base_classes, nullptr); + } + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override { + if (!m_block_struct_type.IsValid()) { + return lldb::ValueObjectSP(); + } + + if (idx >= CalculateNumChildrenIgnoringErrors()) { + return lldb::ValueObjectSP(); + } + + const bool thread_and_frame_only_if_stopped = true; + ExecutionContext exe_ctx = m_backend.GetExecutionContextRef().Lock( + thread_and_frame_only_if_stopped); + const bool transparent_pointers = false; + const bool omit_empty_base_classes = false; + const bool ignore_array_bounds = false; + ValueObject *value_object = nullptr; + + std::string child_name; + uint32_t child_byte_size = 0; + int32_t child_byte_offset = 0; + uint32_t child_bitfield_bit_size = 0; + uint32_t child_bitfield_bit_offset = 0; + bool child_is_base_class = false; + bool child_is_deref_of_parent = false; + uint64_t language_flags = 0; + + auto child_type_or_err = m_block_struct_type.GetChildCompilerTypeAtIndex( + &exe_ctx, idx, transparent_pointers, omit_empty_base_classes, + ignore_array_bounds, child_name, child_byte_size, child_byte_offset, + child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, + child_is_deref_of_parent, value_object, language_flags); + if (!child_type_or_err) + return ValueObjectConstResult::Create( + exe_ctx.GetBestExecutionContextScope(), + Status(child_type_or_err.takeError())); + CompilerType child_type = *child_type_or_err; + + ValueObjectSP struct_pointer_sp = + m_backend.Cast(m_block_struct_type.GetPointerType()); + + if (!struct_pointer_sp) { + return lldb::ValueObjectSP(); + } + + Status err; + ValueObjectSP struct_sp = struct_pointer_sp->Dereference(err); + + if (!struct_sp || !err.Success()) { + return lldb::ValueObjectSP(); + } + + ValueObjectSP child_sp(struct_sp->GetSyntheticChildAtOffset( + child_byte_offset, child_type, true, + ConstString(child_name.c_str(), child_name.size()))); + + return child_sp; + } + + // return true if this object is now safe to use forever without ever + // updating again; the typical (and tested) answer here is 'false' + lldb::ChildCacheState Update() override { + return lldb::ChildCacheState::eRefetch; + } + + // maybe return false if the block pointer is, say, null + bool MightHaveChildren() override { return true; } + + size_t GetIndexOfChildWithName(ConstString name) override { + if (!m_block_struct_type.IsValid()) + return UINT32_MAX; + + const bool omit_empty_base_classes = false; + return m_block_struct_type.GetIndexOfChildWithName(name.AsCString(), + omit_empty_base_classes); + } + +private: + CompilerType m_block_struct_type; +}; + +} // namespace formatters +} // namespace lldb_private + +bool lldb_private::formatters::BlockPointerSummaryProvider( + ValueObject &valobj, Stream &s, const TypeSummaryOptions &) { + lldb_private::SyntheticChildrenFrontEnd *synthetic_children = + BlockPointerSyntheticFrontEndCreator(nullptr, valobj.GetSP()); + if (!synthetic_children) { + return false; + } + + synthetic_children->Update(); + + static const ConstString s_FuncPtr_name("__FuncPtr"); + + lldb::ValueObjectSP child_sp = synthetic_children->GetChildAtIndex( + synthetic_children->GetIndexOfChildWithName(s_FuncPtr_name)); + + if (!child_sp) { + return false; + } + + lldb::ValueObjectSP qualified_child_representation_sp = + child_sp->GetQualifiedRepresentationIfAvailable( + lldb::eDynamicDontRunTarget, true); + + const char *child_value = + qualified_child_representation_sp->GetValueAsCString(); + + s.Printf("%s", child_value); + + return true; +} + +lldb_private::SyntheticChildrenFrontEnd * +lldb_private::formatters::BlockPointerSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + return new BlockPointerSyntheticFrontEnd(valobj_sp); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.h new file mode 100644 index 000000000000..23f3f7b34b4f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.h @@ -0,0 +1,25 @@ +//===-- BlockPointer.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_CPLUSPLUS_BLOCKPOINTER_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_BLOCKPOINTER_H + +#include "lldb/lldb-forward.h" + +namespace lldb_private { +namespace formatters { +bool BlockPointerSummaryProvider(ValueObject &, Stream &, + const TypeSummaryOptions &); + +SyntheticChildrenFrontEnd * +BlockPointerSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); +} // namespace formatters +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_BLOCKPOINTER_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp new file mode 100644 index 000000000000..06c827c2543f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -0,0 +1,1761 @@ +//===-- CPlusPlusLanguage.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 "CPlusPlusLanguage.h" + +#include <cctype> +#include <cstring> + +#include <functional> +#include <memory> +#include <mutex> +#include <set> + +#include "llvm/ADT/StringRef.h" +#include "llvm/Demangle/ItaniumDemangle.h" + +#include "lldb/Core/Mangled.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/DataFormatters/CXXFunctionPointer.h" +#include "lldb/DataFormatters/DataVisualization.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/DataFormatters/VectorType.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegularExpression.h" + +#include "BlockPointer.h" +#include "CPlusPlusNameParser.h" +#include "Coroutines.h" +#include "CxxStringTypes.h" +#include "Generic.h" +#include "LibCxx.h" +#include "LibCxxAtomic.h" +#include "LibCxxVariant.h" +#include "LibStdcpp.h" +#include "MSVCUndecoratedNameParser.h" +#include "lldb/lldb-enumerations.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +LLDB_PLUGIN_DEFINE(CPlusPlusLanguage) + +void CPlusPlusLanguage::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), "C++ Language", + CreateInstance); +} + +void CPlusPlusLanguage::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +bool CPlusPlusLanguage::SymbolNameFitsToLanguage(Mangled mangled) const { + const char *mangled_name = mangled.GetMangledName().GetCString(); + return mangled_name && CPlusPlusLanguage::IsCPPMangledName(mangled_name); +} + +ConstString CPlusPlusLanguage::GetDemangledFunctionNameWithoutArguments( + Mangled mangled) const { + const char *mangled_name_cstr = mangled.GetMangledName().GetCString(); + ConstString demangled_name = mangled.GetDemangledName(); + if (demangled_name && mangled_name_cstr && mangled_name_cstr[0]) { + if (mangled_name_cstr[0] == '_' && mangled_name_cstr[1] == 'Z' && + (mangled_name_cstr[2] != 'T' && // avoid virtual table, VTT structure, + // typeinfo structure, and typeinfo + // mangled_name + mangled_name_cstr[2] != 'G' && // avoid guard variables + mangled_name_cstr[2] != 'Z')) // named local entities (if we + // eventually handle eSymbolTypeData, + // we will want this back) + { + CPlusPlusLanguage::MethodName cxx_method(demangled_name); + if (!cxx_method.GetBasename().empty()) { + std::string shortname; + if (!cxx_method.GetContext().empty()) + shortname = cxx_method.GetContext().str() + "::"; + shortname += cxx_method.GetBasename().str(); + return ConstString(shortname); + } + } + } + if (demangled_name) + return demangled_name; + return mangled.GetMangledName(); +} + +// Static Functions + +Language *CPlusPlusLanguage::CreateInstance(lldb::LanguageType language) { + // Use plugin for C++ but not for Objective-C++ (which has its own plugin). + if (Language::LanguageIsCPlusPlus(language) && + language != eLanguageTypeObjC_plus_plus) + return new CPlusPlusLanguage(); + return nullptr; +} + +void CPlusPlusLanguage::MethodName::Clear() { + m_full.Clear(); + m_basename = llvm::StringRef(); + m_context = llvm::StringRef(); + m_arguments = llvm::StringRef(); + m_qualifiers = llvm::StringRef(); + m_return_type = llvm::StringRef(); + m_parsed = false; + m_parse_error = false; +} + +static bool ReverseFindMatchingChars(const llvm::StringRef &s, + const llvm::StringRef &left_right_chars, + size_t &left_pos, size_t &right_pos, + size_t pos = llvm::StringRef::npos) { + assert(left_right_chars.size() == 2); + left_pos = llvm::StringRef::npos; + const char left_char = left_right_chars[0]; + const char right_char = left_right_chars[1]; + pos = s.find_last_of(left_right_chars, pos); + if (pos == llvm::StringRef::npos || s[pos] == left_char) + return false; + right_pos = pos; + uint32_t depth = 1; + while (pos > 0 && depth > 0) { + pos = s.find_last_of(left_right_chars, pos); + if (pos == llvm::StringRef::npos) + return false; + if (s[pos] == left_char) { + if (--depth == 0) { + left_pos = pos; + return left_pos < right_pos; + } + } else if (s[pos] == right_char) { + ++depth; + } + } + return false; +} + +static bool IsTrivialBasename(const llvm::StringRef &basename) { + // Check that the basename matches with the following regular expression + // "^~?([A-Za-z_][A-Za-z_0-9]*)$" We are using a hand written implementation + // because it is significantly more efficient then using the general purpose + // regular expression library. + size_t idx = 0; + if (basename.starts_with('~')) + idx = 1; + + if (basename.size() <= idx) + return false; // Empty string or "~" + + if (!std::isalpha(basename[idx]) && basename[idx] != '_') + return false; // First character (after removing the possible '~'') isn't in + // [A-Za-z_] + + // Read all characters matching [A-Za-z_0-9] + ++idx; + while (idx < basename.size()) { + if (!std::isalnum(basename[idx]) && basename[idx] != '_') + break; + ++idx; + } + + // We processed all characters. It is a vaild basename. + return idx == basename.size(); +} + +/// Writes out the function name in 'full_name' to 'out_stream' +/// but replaces each argument type with the variable name +/// and the corresponding pretty-printed value +static bool PrettyPrintFunctionNameWithArgs(Stream &out_stream, + char const *full_name, + ExecutionContextScope *exe_scope, + VariableList const &args) { + CPlusPlusLanguage::MethodName cpp_method{ConstString(full_name)}; + + if (!cpp_method.IsValid()) + return false; + + llvm::StringRef return_type = cpp_method.GetReturnType(); + if (!return_type.empty()) { + out_stream.PutCString(return_type); + out_stream.PutChar(' '); + } + + out_stream.PutCString(cpp_method.GetScopeQualifiedName()); + out_stream.PutChar('('); + + FormatEntity::PrettyPrintFunctionArguments(out_stream, args, exe_scope); + + out_stream.PutChar(')'); + + llvm::StringRef qualifiers = cpp_method.GetQualifiers(); + if (!qualifiers.empty()) { + out_stream.PutChar(' '); + out_stream.PutCString(qualifiers); + } + + return true; +} + +bool CPlusPlusLanguage::MethodName::TrySimplifiedParse() { + // This method tries to parse simple method definitions which are presumably + // most comman in user programs. Definitions that can be parsed by this + // function don't have return types and templates in the name. + // A::B::C::fun(std::vector<T> &) const + size_t arg_start, arg_end; + llvm::StringRef full(m_full.GetCString()); + llvm::StringRef parens("()", 2); + if (ReverseFindMatchingChars(full, parens, arg_start, arg_end)) { + m_arguments = full.substr(arg_start, arg_end - arg_start + 1); + if (arg_end + 1 < full.size()) + m_qualifiers = full.substr(arg_end + 1).ltrim(); + + if (arg_start == 0) + return false; + size_t basename_end = arg_start; + size_t context_start = 0; + size_t context_end = full.rfind(':', basename_end); + if (context_end == llvm::StringRef::npos) + m_basename = full.substr(0, basename_end); + else { + if (context_start < context_end) + m_context = full.substr(context_start, context_end - 1 - context_start); + const size_t basename_begin = context_end + 1; + m_basename = full.substr(basename_begin, basename_end - basename_begin); + } + + if (IsTrivialBasename(m_basename)) { + return true; + } else { + // The C++ basename doesn't match our regular expressions so this can't + // be a valid C++ method, clear everything out and indicate an error + m_context = llvm::StringRef(); + m_basename = llvm::StringRef(); + m_arguments = llvm::StringRef(); + m_qualifiers = llvm::StringRef(); + m_return_type = llvm::StringRef(); + return false; + } + } + return false; +} + +void CPlusPlusLanguage::MethodName::Parse() { + if (!m_parsed && m_full) { + if (TrySimplifiedParse()) { + m_parse_error = false; + } else { + CPlusPlusNameParser parser(m_full.GetStringRef()); + if (auto function = parser.ParseAsFunctionDefinition()) { + m_basename = function->name.basename; + m_context = function->name.context; + m_arguments = function->arguments; + m_qualifiers = function->qualifiers; + m_return_type = function->return_type; + m_parse_error = false; + } else { + m_parse_error = true; + } + } + m_parsed = true; + } +} + +llvm::StringRef CPlusPlusLanguage::MethodName::GetBasename() { + if (!m_parsed) + Parse(); + return m_basename; +} + +llvm::StringRef CPlusPlusLanguage::MethodName::GetContext() { + if (!m_parsed) + Parse(); + return m_context; +} + +llvm::StringRef CPlusPlusLanguage::MethodName::GetArguments() { + if (!m_parsed) + Parse(); + return m_arguments; +} + +llvm::StringRef CPlusPlusLanguage::MethodName::GetQualifiers() { + if (!m_parsed) + Parse(); + return m_qualifiers; +} + +llvm::StringRef CPlusPlusLanguage::MethodName::GetReturnType() { + if (!m_parsed) + Parse(); + return m_return_type; +} + +std::string CPlusPlusLanguage::MethodName::GetScopeQualifiedName() { + if (!m_parsed) + Parse(); + if (m_context.empty()) + return std::string(m_basename); + + std::string res; + res += m_context; + res += "::"; + res += m_basename; + return res; +} + +llvm::StringRef +CPlusPlusLanguage::MethodName::GetBasenameNoTemplateParameters() { + llvm::StringRef basename = GetBasename(); + size_t arg_start, arg_end; + llvm::StringRef parens("<>", 2); + if (ReverseFindMatchingChars(basename, parens, arg_start, arg_end)) + return basename.substr(0, arg_start); + + return basename; +} + +bool CPlusPlusLanguage::MethodName::ContainsPath(llvm::StringRef path) { + if (!m_parsed) + Parse(); + + // If we can't parse the incoming name, then just check that it contains path. + if (m_parse_error) + return m_full.GetStringRef().contains(path); + + llvm::StringRef identifier; + llvm::StringRef context; + std::string path_str = path.str(); + bool success = CPlusPlusLanguage::ExtractContextAndIdentifier( + path_str.c_str(), context, identifier); + if (!success) + return m_full.GetStringRef().contains(path); + + // Basename may include template arguments. + // E.g., + // GetBaseName(): func<int> + // identifier : func + // + // ...but we still want to account for identifiers with template parameter + // lists, e.g., when users set breakpoints on template specializations. + // + // E.g., + // GetBaseName(): func<uint32_t> + // identifier : func<int32_t*> + // + // Try to match the basename with or without template parameters. + if (GetBasename() != identifier && + GetBasenameNoTemplateParameters() != identifier) + return false; + + // Incoming path only had an identifier, so we match. + if (context.empty()) + return true; + // Incoming path has context but this method does not, no match. + if (m_context.empty()) + return false; + + llvm::StringRef haystack = m_context; + if (!haystack.consume_back(context)) + return false; + if (haystack.empty() || !isalnum(haystack.back())) + return true; + + return false; +} + +bool CPlusPlusLanguage::IsCPPMangledName(llvm::StringRef name) { + // FIXME!! we should really run through all the known C++ Language plugins + // and ask each one if this is a C++ mangled name + + Mangled::ManglingScheme scheme = Mangled::GetManglingScheme(name); + + if (scheme == Mangled::eManglingSchemeNone) + return false; + + return true; +} + +bool CPlusPlusLanguage::DemangledNameContainsPath(llvm::StringRef path, + ConstString demangled) const { + MethodName demangled_name(demangled); + return demangled_name.ContainsPath(path); +} + +bool CPlusPlusLanguage::ExtractContextAndIdentifier( + const char *name, llvm::StringRef &context, llvm::StringRef &identifier) { + if (MSVCUndecoratedNameParser::IsMSVCUndecoratedName(name)) + return MSVCUndecoratedNameParser::ExtractContextAndIdentifier(name, context, + identifier); + + CPlusPlusNameParser parser(name); + if (auto full_name = parser.ParseAsFullName()) { + identifier = full_name->basename; + context = full_name->context; + return true; + } + return false; +} + +namespace { +class NodeAllocator { + llvm::BumpPtrAllocator Alloc; + +public: + void reset() { Alloc.Reset(); } + + template <typename T, typename... Args> T *makeNode(Args &&... args) { + return new (Alloc.Allocate(sizeof(T), alignof(T))) + T(std::forward<Args>(args)...); + } + + void *allocateNodeArray(size_t sz) { + return Alloc.Allocate(sizeof(llvm::itanium_demangle::Node *) * sz, + alignof(llvm::itanium_demangle::Node *)); + } +}; + +template <typename Derived> +class ManglingSubstitutor + : public llvm::itanium_demangle::AbstractManglingParser<Derived, + NodeAllocator> { + using Base = + llvm::itanium_demangle::AbstractManglingParser<Derived, NodeAllocator>; + +public: + ManglingSubstitutor() : Base(nullptr, nullptr) {} + + template <typename... Ts> + ConstString substitute(llvm::StringRef Mangled, Ts &&... Vals) { + this->getDerived().reset(Mangled, std::forward<Ts>(Vals)...); + return substituteImpl(Mangled); + } + +protected: + void reset(llvm::StringRef Mangled) { + Base::reset(Mangled.begin(), Mangled.end()); + Written = Mangled.begin(); + Result.clear(); + Substituted = false; + } + + ConstString substituteImpl(llvm::StringRef Mangled) { + Log *log = GetLog(LLDBLog::Language); + if (this->parse() == nullptr) { + LLDB_LOG(log, "Failed to substitute mangling in {0}", Mangled); + return ConstString(); + } + if (!Substituted) + return ConstString(); + + // Append any trailing unmodified input. + appendUnchangedInput(); + LLDB_LOG(log, "Substituted mangling {0} -> {1}", Mangled, Result); + return ConstString(Result); + } + + void trySubstitute(llvm::StringRef From, llvm::StringRef To) { + if (!llvm::StringRef(currentParserPos(), this->numLeft()).starts_with(From)) + return; + + // We found a match. Append unmodified input up to this point. + appendUnchangedInput(); + + // And then perform the replacement. + Result += To; + Written += From.size(); + Substituted = true; + } + +private: + /// Input character until which we have constructed the respective output + /// already. + const char *Written = ""; + + llvm::SmallString<128> Result; + + /// Whether we have performed any substitutions. + bool Substituted = false; + + const char *currentParserPos() const { return this->First; } + + void appendUnchangedInput() { + Result += + llvm::StringRef(Written, std::distance(Written, currentParserPos())); + Written = currentParserPos(); + } +}; + +/// Given a mangled function `Mangled`, replace all the primitive function type +/// arguments of `Search` with type `Replace`. +class TypeSubstitutor : public ManglingSubstitutor<TypeSubstitutor> { + llvm::StringRef Search; + llvm::StringRef Replace; + +public: + void reset(llvm::StringRef Mangled, llvm::StringRef Search, + llvm::StringRef Replace) { + ManglingSubstitutor::reset(Mangled); + this->Search = Search; + this->Replace = Replace; + } + + llvm::itanium_demangle::Node *parseType() { + trySubstitute(Search, Replace); + return ManglingSubstitutor::parseType(); + } +}; + +class CtorDtorSubstitutor : public ManglingSubstitutor<CtorDtorSubstitutor> { +public: + llvm::itanium_demangle::Node * + parseCtorDtorName(llvm::itanium_demangle::Node *&SoFar, NameState *State) { + trySubstitute("C1", "C2"); + trySubstitute("D1", "D2"); + return ManglingSubstitutor::parseCtorDtorName(SoFar, State); + } +}; +} // namespace + +std::vector<ConstString> CPlusPlusLanguage::GenerateAlternateFunctionManglings( + const ConstString mangled_name) const { + std::vector<ConstString> alternates; + + /// Get a basic set of alternative manglings for the given symbol `name`, by + /// making a few basic possible substitutions on basic types, storage duration + /// and `const`ness for the given symbol. The output parameter `alternates` + /// is filled with a best-guess, non-exhaustive set of different manglings + /// for the given name. + + // Maybe we're looking for a const symbol but the debug info told us it was + // non-const... + if (!strncmp(mangled_name.GetCString(), "_ZN", 3) && + strncmp(mangled_name.GetCString(), "_ZNK", 4)) { + std::string fixed_scratch("_ZNK"); + fixed_scratch.append(mangled_name.GetCString() + 3); + alternates.push_back(ConstString(fixed_scratch)); + } + + // Maybe we're looking for a static symbol but we thought it was global... + if (!strncmp(mangled_name.GetCString(), "_Z", 2) && + strncmp(mangled_name.GetCString(), "_ZL", 3)) { + std::string fixed_scratch("_ZL"); + fixed_scratch.append(mangled_name.GetCString() + 2); + alternates.push_back(ConstString(fixed_scratch)); + } + + TypeSubstitutor TS; + // `char` is implementation defined as either `signed` or `unsigned`. As a + // result a char parameter has 3 possible manglings: 'c'-char, 'a'-signed + // char, 'h'-unsigned char. If we're looking for symbols with a signed char + // parameter, try finding matches which have the general case 'c'. + if (ConstString char_fixup = + TS.substitute(mangled_name.GetStringRef(), "a", "c")) + alternates.push_back(char_fixup); + + // long long parameter mangling 'x', may actually just be a long 'l' argument + if (ConstString long_fixup = + TS.substitute(mangled_name.GetStringRef(), "x", "l")) + alternates.push_back(long_fixup); + + // unsigned long long parameter mangling 'y', may actually just be unsigned + // long 'm' argument + if (ConstString ulong_fixup = + TS.substitute(mangled_name.GetStringRef(), "y", "m")) + alternates.push_back(ulong_fixup); + + if (ConstString ctor_fixup = + CtorDtorSubstitutor().substitute(mangled_name.GetStringRef())) + alternates.push_back(ctor_fixup); + + return alternates; +} + +ConstString CPlusPlusLanguage::FindBestAlternateFunctionMangledName( + const Mangled mangled, const SymbolContext &sym_ctx) const { + ConstString demangled = mangled.GetDemangledName(); + if (!demangled) + return ConstString(); + + CPlusPlusLanguage::MethodName cpp_name(demangled); + std::string scope_qualified_name = cpp_name.GetScopeQualifiedName(); + + if (!scope_qualified_name.size()) + return ConstString(); + + if (!sym_ctx.module_sp) + return ConstString(); + + lldb_private::SymbolFile *sym_file = sym_ctx.module_sp->GetSymbolFile(); + if (!sym_file) + return ConstString(); + + std::vector<ConstString> alternates; + sym_file->GetMangledNamesForFunction(scope_qualified_name, alternates); + + std::vector<ConstString> param_and_qual_matches; + std::vector<ConstString> param_matches; + for (size_t i = 0; i < alternates.size(); i++) { + ConstString alternate_mangled_name = alternates[i]; + Mangled mangled(alternate_mangled_name); + ConstString demangled = mangled.GetDemangledName(); + + CPlusPlusLanguage::MethodName alternate_cpp_name(demangled); + if (!cpp_name.IsValid()) + continue; + + if (alternate_cpp_name.GetArguments() == cpp_name.GetArguments()) { + if (alternate_cpp_name.GetQualifiers() == cpp_name.GetQualifiers()) + param_and_qual_matches.push_back(alternate_mangled_name); + else + param_matches.push_back(alternate_mangled_name); + } + } + + if (param_and_qual_matches.size()) + return param_and_qual_matches[0]; // It is assumed that there will be only + // one! + else if (param_matches.size()) + return param_matches[0]; // Return one of them as a best match + else + return ConstString(); +} + +static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { + if (!cpp_category_sp) + return; + + TypeSummaryImpl::Flags stl_summary_flags; + stl_summary_flags.SetCascades(true) + .SetSkipPointers(false) + .SetSkipReferences(false) + .SetDontShowChildren(true) + .SetDontShowValue(true) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStringSummaryProviderASCII, + "std::string summary provider", "^std::__[[:alnum:]]+::string$", + stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStringSummaryProviderASCII, + "std::string summary provider", + "^std::__[[:alnum:]]+::basic_string<char, " + "std::__[[:alnum:]]+::char_traits<char>, " + "std::__[[:alnum:]]+::allocator<char> >$", + stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStringSummaryProviderASCII, + "std::string summary provider", + "^std::__[[:alnum:]]+::basic_string<unsigned char, " + "std::__[[:alnum:]]+::char_traits<unsigned char>, " + "std::__[[:alnum:]]+::allocator<unsigned char> >$", + stl_summary_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStringSummaryProviderUTF16, + "std::u16string summary provider", + "^std::__[[:alnum:]]+::basic_string<char16_t, " + "std::__[[:alnum:]]+::char_traits<char16_t>, " + "std::__[[:alnum:]]+::allocator<char16_t> >$", + stl_summary_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStringSummaryProviderUTF32, + "std::u32string summary provider", + "^std::__[[:alnum:]]+::basic_string<char32_t, " + "std::__[[:alnum:]]+::char_traits<char32_t>, " + "std::__[[:alnum:]]+::allocator<char32_t> >$", + stl_summary_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxWStringSummaryProvider, + "std::wstring summary provider", + "^std::__[[:alnum:]]+::wstring$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxWStringSummaryProvider, + "std::wstring summary provider", + "^std::__[[:alnum:]]+::basic_string<wchar_t, " + "std::__[[:alnum:]]+::char_traits<wchar_t>, " + "std::__[[:alnum:]]+::allocator<wchar_t> >$", + stl_summary_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStringViewSummaryProviderASCII, + "std::string_view summary provider", + "^std::__[[:alnum:]]+::string_view$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStringViewSummaryProviderASCII, + "std::string_view summary provider", + "^std::__[[:alnum:]]+::basic_string_view<char, " + "std::__[[:alnum:]]+::char_traits<char> >$", + stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStringViewSummaryProviderASCII, + "std::string_view summary provider", + "^std::__[[:alnum:]]+::basic_string_view<unsigned char, " + "std::__[[:alnum:]]+::char_traits<unsigned char> >$", + stl_summary_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStringViewSummaryProviderUTF16, + "std::u16string_view summary provider", + "^std::__[[:alnum:]]+::basic_string_view<char16_t, " + "std::__[[:alnum:]]+::char_traits<char16_t> >$", + stl_summary_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStringViewSummaryProviderUTF32, + "std::u32string_view summary provider", + "^std::__[[:alnum:]]+::basic_string_view<char32_t, " + "std::__[[:alnum:]]+::char_traits<char32_t> >$", + stl_summary_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxWStringViewSummaryProvider, + "std::wstring_view summary provider", + "^std::__[[:alnum:]]+::wstring_view$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxWStringViewSummaryProvider, + "std::wstring_view summary provider", + "^std::__[[:alnum:]]+::basic_string_view<wchar_t, " + "std::__[[:alnum:]]+::char_traits<wchar_t> >$", + stl_summary_flags, true); + + SyntheticChildren::Flags stl_synth_flags; + stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences( + false); + SyntheticChildren::Flags stl_deref_flags = stl_synth_flags; + stl_deref_flags.SetFrontEndWantsDereference(); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxBitsetSyntheticFrontEndCreator, + "libc++ std::bitset synthetic children", + "^std::__[[:alnum:]]+::bitset<.+>$", stl_deref_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdVectorSyntheticFrontEndCreator, + "libc++ std::vector synthetic children", + "^std::__[[:alnum:]]+::vector<.+>$", stl_deref_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdValarraySyntheticFrontEndCreator, + "libc++ std::valarray synthetic children", + "^std::__[[:alnum:]]+::valarray<.+>$", stl_deref_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdSliceArraySyntheticFrontEndCreator, + "libc++ std::slice_array synthetic children", + "^std::__[[:alnum:]]+::slice_array<.+>$", stl_deref_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEndCreator, + "libc++ synthetic children for the valarray proxy arrays", + "^std::__[[:alnum:]]+::(gslice|mask|indirect)_array<.+>$", + stl_deref_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdForwardListSyntheticFrontEndCreator, + "libc++ std::forward_list synthetic children", + "^std::__[[:alnum:]]+::forward_list<.+>$", stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdListSyntheticFrontEndCreator, + "libc++ std::list synthetic children", + // A POSIX variant of: "^std::__(?!cxx11:)[[:alnum:]]+::list<.+>$" + // so that it does not clash with: "^std::(__cxx11::)?list<.+>$" + "^std::__([A-Zabd-z0-9]|cx?[A-Za-wyz0-9]|cxx1?[A-Za-z02-9]|" + "cxx11[[:alnum:]])[[:alnum:]]*::list<.+>$", + stl_deref_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, + "libc++ std::map synthetic children", "^std::__[[:alnum:]]+::map<.+> >$", + stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, + "libc++ std::set synthetic children", "^std::__[[:alnum:]]+::set<.+> >$", + stl_deref_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, + "libc++ std::multiset synthetic children", + "^std::__[[:alnum:]]+::multiset<.+> >$", stl_deref_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, + "libc++ std::multimap synthetic children", + "^std::__[[:alnum:]]+::multimap<.+> >$", stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator, + "libc++ std::unordered containers synthetic children", + "^std::__[[:alnum:]]+::unordered_(multi)?(map|set)<.+> >$", + stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxInitializerListSyntheticFrontEndCreator, + "libc++ std::initializer_list synthetic children", + "^std::initializer_list<.+>$", stl_synth_flags, true); + AddCXXSynthetic(cpp_category_sp, LibcxxQueueFrontEndCreator, + "libc++ std::queue synthetic children", + "^std::__[[:alnum:]]+::queue<.+>$", stl_synth_flags, true); + AddCXXSynthetic(cpp_category_sp, LibcxxTupleFrontEndCreator, + "libc++ std::tuple synthetic children", + "^std::__[[:alnum:]]+::tuple<.*>$", stl_synth_flags, true); + AddCXXSynthetic(cpp_category_sp, LibcxxOptionalSyntheticFrontEndCreator, + "libc++ std::optional synthetic children", + "^std::__[[:alnum:]]+::optional<.+>$", stl_synth_flags, true); + AddCXXSynthetic(cpp_category_sp, LibcxxVariantFrontEndCreator, + "libc++ std::variant synthetic children", + "^std::__[[:alnum:]]+::variant<.+>$", stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxAtomicSyntheticFrontEndCreator, + "libc++ std::atomic synthetic children", + "^std::__[[:alnum:]]+::atomic<.+>$", stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdSpanSyntheticFrontEndCreator, + "libc++ std::span synthetic children", "^std::__[[:alnum:]]+::span<.+>$", + stl_deref_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdRangesRefViewSyntheticFrontEndCreator, + "libc++ std::ranges::ref_view synthetic children", + "^std::__[[:alnum:]]+::ranges::ref_view<.+>$", stl_deref_flags, true); + + cpp_category_sp->AddTypeSynthetic( + "^std::__[[:alnum:]]+::deque<.+>$", eFormatterMatchRegex, + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_synth_flags, + "lldb.formatters.cpp.libcxx.stddeque_SynthProvider"))); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator, + "shared_ptr synthetic children", "^std::__[[:alnum:]]+::shared_ptr<.+>$", + stl_synth_flags, true); + + static constexpr const char *const libcxx_std_unique_ptr_regex = + "^std::__[[:alnum:]]+::unique_ptr<.+>$"; + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEndCreator, + "unique_ptr synthetic children", libcxx_std_unique_ptr_regex, + stl_synth_flags, true); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator, + "weak_ptr synthetic children", "^std::__[[:alnum:]]+::weak_ptr<.+>$", + stl_synth_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxFunctionSummaryProvider, + "libc++ std::function summary provider", + "^std::__[[:alnum:]]+::function<.+>$", stl_summary_flags, true); + + static constexpr const char *const libcxx_std_coroutine_handle_regex = + "^std::__[[:alnum:]]+::coroutine_handle<.+>$"; + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEndCreator, + "coroutine_handle synthetic children", libcxx_std_coroutine_handle_regex, + stl_deref_flags, true); + + stl_summary_flags.SetDontShowChildren(false); + stl_summary_flags.SetSkipPointers(false); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::bitset summary provider", + "^std::__[[:alnum:]]+::bitset<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::vector summary provider", + "^std::__[[:alnum:]]+::vector<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::valarray summary provider", + "^std::__[[:alnum:]]+::valarray<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxStdSliceArraySummaryProvider, + "libc++ std::slice_array summary provider", + "^std::__[[:alnum:]]+::slice_array<.+>$", stl_summary_flags, + true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ summary provider for the valarray proxy arrays", + "^std::__[[:alnum:]]+::(gslice|mask|indirect)_array<.+>$", + stl_summary_flags, true); + AddCXXSummary( + cpp_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::list summary provider", + "^std::__[[:alnum:]]+::forward_list<.+>$", stl_summary_flags, true); + AddCXXSummary( + cpp_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::list summary provider", + // A POSIX variant of: "^std::__(?!cxx11:)[[:alnum:]]+::list<.+>$" + // so that it does not clash with: "^std::(__cxx11::)?list<.+>$" + "^std::__([A-Zabd-z0-9]|cx?[A-Za-wyz0-9]|cxx1?[A-Za-z02-9]|" + "cxx11[[:alnum:]])[[:alnum:]]*::list<.+>$", + stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::map summary provider", + "^std::__[[:alnum:]]+::map<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::deque summary provider", + "^std::__[[:alnum:]]+::deque<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::queue summary provider", + "^std::__[[:alnum:]]+::queue<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::set summary provider", + "^std::__[[:alnum:]]+::set<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::multiset summary provider", + "^std::__[[:alnum:]]+::multiset<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::multimap summary provider", + "^std::__[[:alnum:]]+::multimap<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::unordered containers summary provider", + "^std::__[[:alnum:]]+::unordered_(multi)?(map|set)<.+> >$", + stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, LibcxxContainerSummaryProvider, + "libc++ std::tuple summary provider", + "^std::__[[:alnum:]]+::tuple<.*>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibCxxAtomicSummaryProvider, + "libc++ std::atomic summary provider", + "^std::__[[:alnum:]]+::atomic<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::GenericOptionalSummaryProvider, + "libc++ std::optional summary provider", + "^std::__[[:alnum:]]+::optional<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxVariantSummaryProvider, + "libc++ std::variant summary provider", + "^std::__[[:alnum:]]+::variant<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxContainerSummaryProvider, + "libc++ std::span summary provider", + "^std::__[[:alnum:]]+::span<.+>$", stl_summary_flags, true); + + stl_summary_flags.SetSkipPointers(true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxSmartPointerSummaryProvider, + "libc++ std::shared_ptr summary provider", + "^std::__[[:alnum:]]+::shared_ptr<.+>$", stl_summary_flags, + true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxSmartPointerSummaryProvider, + "libc++ std::weak_ptr summary provider", + "^std::__[[:alnum:]]+::weak_ptr<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxUniquePointerSummaryProvider, + "libc++ std::unique_ptr summary provider", + libcxx_std_unique_ptr_regex, stl_summary_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::StdlibCoroutineHandleSummaryProvider, + "libc++ std::coroutine_handle summary provider", + libcxx_std_coroutine_handle_regex, stl_summary_flags, true); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator, + "std::vector iterator synthetic children", + "^std::__[[:alnum:]]+::__wrap_iter<.+>$", stl_synth_flags, true); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator, + "std::map iterator synthetic children", + "^std::__[[:alnum:]]+::__map_(const_)?iterator<.+>$", stl_synth_flags, + true); + + AddCXXSynthetic(cpp_category_sp, + lldb_private::formatters:: + LibCxxUnorderedMapIteratorSyntheticFrontEndCreator, + "std::unordered_map iterator synthetic children", + "^std::__[[:alnum:]]+::__hash_map_(const_)?iterator<.+>$", + stl_synth_flags, true); + // Chrono duration typedefs + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::nanoseconds", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + eTypeOptionHideChildren | eTypeOptionHideValue, "${var.__rep_} ns"))); + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::microseconds", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + eTypeOptionHideChildren | eTypeOptionHideValue, "${var.__rep_} µs"))); + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::milliseconds", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + eTypeOptionHideChildren | eTypeOptionHideValue, "${var.__rep_} ms"))); + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::seconds", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + eTypeOptionHideChildren | eTypeOptionHideValue, "${var.__rep_} s"))); + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::minutes", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "${var.__rep_} min"))); + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::hours", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + eTypeOptionHideChildren | eTypeOptionHideValue, "${var.__rep_} h"))); + + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::days", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "${var.__rep_} days"))); + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::weeks", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "${var.__rep_} weeks"))); + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::months", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "${var.__rep_} months"))); + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::years", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "${var.__rep_} years"))); + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::seconds", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + eTypeOptionHideChildren | eTypeOptionHideValue, "${var.__rep_} s"))); + + // Chrono time point types + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxChronoSysSecondsSummaryProvider, + "libc++ std::chrono::sys_seconds summary provider", + "^std::__[[:alnum:]]+::chrono::time_point<" + "std::__[[:alnum:]]+::chrono::system_clock, " + "std::__[[:alnum:]]+::chrono::duration<.*, " + "std::__[[:alnum:]]+::ratio<1, 1> " + "> >$", + eTypeOptionHideChildren | eTypeOptionHideValue | + eTypeOptionCascade, + true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxChronoSysDaysSummaryProvider, + "libc++ std::chrono::sys_seconds summary provider", + "^std::__[[:alnum:]]+::chrono::time_point<" + "std::__[[:alnum:]]+::chrono::system_clock, " + "std::__[[:alnum:]]+::chrono::duration<int, " + "std::__[[:alnum:]]+::ratio<86400, 1> " + "> >$", + eTypeOptionHideChildren | eTypeOptionHideValue | + eTypeOptionCascade, + true); + + AddCXXSummary( + cpp_category_sp, + lldb_private::formatters::LibcxxChronoLocalSecondsSummaryProvider, + "libc++ std::chrono::local_seconds summary provider", + "^std::__[[:alnum:]]+::chrono::time_point<" + "std::__[[:alnum:]]+::chrono::local_t, " + "std::__[[:alnum:]]+::chrono::duration<.*, " + "std::__[[:alnum:]]+::ratio<1, 1> " + "> >$", + eTypeOptionHideChildren | eTypeOptionHideValue | eTypeOptionCascade, + true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxChronoLocalDaysSummaryProvider, + "libc++ std::chrono::local_seconds summary provider", + "^std::__[[:alnum:]]+::chrono::time_point<" + "std::__[[:alnum:]]+::chrono::local_t, " + "std::__[[:alnum:]]+::chrono::duration<int, " + "std::__[[:alnum:]]+::ratio<86400, 1> " + "> >$", + eTypeOptionHideChildren | eTypeOptionHideValue | + eTypeOptionCascade, + true); + + // Chrono calendar types + + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::day$", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "day=${var.__d_%u}"))); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxChronoMonthSummaryProvider, + "libc++ std::chrono::month summary provider", + "^std::__[[:alnum:]]+::chrono::month$", + eTypeOptionHideChildren | eTypeOptionHideValue, true); + + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::year$", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + eTypeOptionHideChildren | eTypeOptionHideValue, "year=${var.__y_}"))); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxChronoWeekdaySummaryProvider, + "libc++ std::chrono::weekday summary provider", + "^std::__[[:alnum:]]+::chrono::weekday$", + eTypeOptionHideChildren | eTypeOptionHideValue, true); + + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::weekday_indexed$", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + eTypeOptionHideChildren | eTypeOptionHideValue, + "${var.__wd_} index=${var.__idx_%u}"))); + + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::weekday_last$", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "${var.__wd_} index=last"))); + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::month_day$", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "${var.__m_} ${var.__d_}"))); + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::month_day_last$", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "${var.__m_} day=last"))); + + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::month_weekday$", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "${var.__m_} ${var.__wdi_}"))); + + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::month_weekday_last$", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "${var.__m_} ${var.__wdl_}"))); + + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::year_month$", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "${var.__y_} ${var.__m_}"))); + + AddCXXSummary( + cpp_category_sp, + lldb_private::formatters::LibcxxChronoYearMonthDaySummaryProvider, + "libc++ std::chrono::year_month_day summary provider", + "^std::__[[:alnum:]]+::chrono::year_month_day$", + eTypeOptionHideChildren | eTypeOptionHideValue, true); + + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::year_month_day_last$", + eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat(eTypeOptionHideChildren | + eTypeOptionHideValue, + "${var.__y_} ${var.__mdl_}"))); + + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::year_month_weekday$", eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + eTypeOptionHideChildren | eTypeOptionHideValue, + "${var.__y_} ${var.__m_} ${var.__wdi_}"))); + + cpp_category_sp->AddTypeSummary( + "^std::__[[:alnum:]]+::chrono::year_month_weekday_last$", + eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + eTypeOptionHideChildren | eTypeOptionHideValue, + "${var.__y_} ${var.__m_} ${var.__wdl_}"))); +} + +static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { + if (!cpp_category_sp) + return; + + TypeSummaryImpl::Flags stl_summary_flags; + stl_summary_flags.SetCascades(true) + .SetSkipPointers(false) + .SetSkipReferences(false) + .SetDontShowChildren(true) + .SetDontShowValue(true) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false); + + lldb::TypeSummaryImplSP std_string_summary_sp( + new StringSummaryFormat(stl_summary_flags, "${var._M_dataplus._M_p}")); + + lldb::TypeSummaryImplSP cxx11_string_summary_sp(new CXXFunctionSummaryFormat( + stl_summary_flags, LibStdcppStringSummaryProvider, + "libstdc++ c++11 std::string summary provider")); + lldb::TypeSummaryImplSP cxx11_wstring_summary_sp(new CXXFunctionSummaryFormat( + stl_summary_flags, LibStdcppWStringSummaryProvider, + "libstdc++ c++11 std::wstring summary provider")); + + cpp_category_sp->AddTypeSummary("std::string", eFormatterMatchExact, + std_string_summary_sp); + cpp_category_sp->AddTypeSummary("std::basic_string<char>", + eFormatterMatchExact, std_string_summary_sp); + cpp_category_sp->AddTypeSummary( + "std::basic_string<char,std::char_traits<char>,std::allocator<char> >", + eFormatterMatchExact, std_string_summary_sp); + cpp_category_sp->AddTypeSummary( + "std::basic_string<char, std::char_traits<char>, std::allocator<char> >", + eFormatterMatchExact, std_string_summary_sp); + + cpp_category_sp->AddTypeSummary("std::__cxx11::string", eFormatterMatchExact, + cxx11_string_summary_sp); + cpp_category_sp->AddTypeSummary( + "std::__cxx11::basic_string<char, std::char_traits<char>, " + "std::allocator<char> >", + eFormatterMatchExact, cxx11_string_summary_sp); + cpp_category_sp->AddTypeSummary("std::__cxx11::basic_string<unsigned char, " + "std::char_traits<unsigned char>, " + "std::allocator<unsigned char> >", + eFormatterMatchExact, + cxx11_string_summary_sp); + + // making sure we force-pick the summary for printing wstring (_M_p is a + // wchar_t*) + lldb::TypeSummaryImplSP std_wstring_summary_sp( + new StringSummaryFormat(stl_summary_flags, "${var._M_dataplus._M_p%S}")); + + cpp_category_sp->AddTypeSummary("std::wstring", eFormatterMatchExact, + std_wstring_summary_sp); + cpp_category_sp->AddTypeSummary("std::basic_string<wchar_t>", + eFormatterMatchExact, std_wstring_summary_sp); + cpp_category_sp->AddTypeSummary("std::basic_string<wchar_t,std::char_traits<" + "wchar_t>,std::allocator<wchar_t> >", + eFormatterMatchExact, std_wstring_summary_sp); + cpp_category_sp->AddTypeSummary( + "std::basic_string<wchar_t, std::char_traits<wchar_t>, " + "std::allocator<wchar_t> >", + eFormatterMatchExact, std_wstring_summary_sp); + + cpp_category_sp->AddTypeSummary("std::__cxx11::wstring", eFormatterMatchExact, + cxx11_wstring_summary_sp); + cpp_category_sp->AddTypeSummary( + "std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, " + "std::allocator<wchar_t> >", + eFormatterMatchExact, cxx11_wstring_summary_sp); + + SyntheticChildren::Flags stl_synth_flags; + stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences( + false); + SyntheticChildren::Flags stl_deref_flags = stl_synth_flags; + stl_deref_flags.SetFrontEndWantsDereference(); + + cpp_category_sp->AddTypeSynthetic( + "^std::vector<.+>(( )?&)?$", eFormatterMatchRegex, + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_synth_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdVectorSynthProvider"))); + cpp_category_sp->AddTypeSynthetic( + "^std::map<.+> >(( )?&)?$", eFormatterMatchRegex, + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_synth_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider"))); + cpp_category_sp->AddTypeSynthetic( + "^std::deque<.+>(( )?&)?$", eFormatterMatchRegex, + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_deref_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdDequeSynthProvider"))); + cpp_category_sp->AddTypeSynthetic( + "^std::set<.+> >(( )?&)?$", eFormatterMatchRegex, + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_deref_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider"))); + cpp_category_sp->AddTypeSynthetic( + "^std::multimap<.+> >(( )?&)?$", eFormatterMatchRegex, + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_deref_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider"))); + cpp_category_sp->AddTypeSynthetic( + "^std::multiset<.+> >(( )?&)?$", eFormatterMatchRegex, + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_deref_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider"))); + cpp_category_sp->AddTypeSynthetic( + "^std::unordered_(multi)?(map|set)<.+> >$", eFormatterMatchRegex, + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_deref_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdUnorderedMapSynthProvider"))); + cpp_category_sp->AddTypeSynthetic( + "^std::(__cxx11::)?list<.+>(( )?&)?$", eFormatterMatchRegex, + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_deref_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdListSynthProvider"))); + cpp_category_sp->AddTypeSynthetic( + "^std::(__cxx11::)?forward_list<.+>(( )?&)?$", eFormatterMatchRegex, + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_synth_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdForwardListSynthProvider"))); + cpp_category_sp->AddTypeSynthetic( + "^std::variant<.+>$", eFormatterMatchRegex, + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_synth_flags, + "lldb.formatters.cpp.gnu_libstdcpp.VariantSynthProvider"))); + + stl_summary_flags.SetDontShowChildren(false); + stl_summary_flags.SetSkipPointers(false); + cpp_category_sp->AddTypeSummary("^std::bitset<.+>(( )?&)?$", + eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + stl_summary_flags, "size=${svar%#}"))); + cpp_category_sp->AddTypeSummary("^std::vector<.+>(( )?&)?$", + eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + stl_summary_flags, "size=${svar%#}"))); + cpp_category_sp->AddTypeSummary("^std::map<.+> >(( )?&)?$", + eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + stl_summary_flags, "size=${svar%#}"))); + cpp_category_sp->AddTypeSummary("^std::set<.+> >(( )?&)?$", + eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + stl_summary_flags, "size=${svar%#}"))); + cpp_category_sp->AddTypeSummary("^std::deque<.+>(( )?&)?$", + eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + stl_summary_flags, "size=${svar%#}"))); + cpp_category_sp->AddTypeSummary("^std::multimap<.+> >(( )?&)?$", + eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + stl_summary_flags, "size=${svar%#}"))); + cpp_category_sp->AddTypeSummary("^std::multiset<.+> >(( )?&)?$", + eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + stl_summary_flags, "size=${svar%#}"))); + cpp_category_sp->AddTypeSummary("^std::unordered_(multi)?(map|set)<.+> >$", + eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + stl_summary_flags, "size=${svar%#}"))); + cpp_category_sp->AddTypeSummary("^std::(__cxx11::)?list<.+>(( )?&)?$", + eFormatterMatchRegex, + TypeSummaryImplSP(new StringSummaryFormat( + stl_summary_flags, "size=${svar%#}"))); + cpp_category_sp->AddTypeSummary( + "^std::(__cxx11::)?forward_list<.+>(( )?&)?$", eFormatterMatchRegex, + TypeSummaryImplSP(new ScriptSummaryFormat( + stl_summary_flags, + "lldb.formatters.cpp.gnu_libstdcpp.ForwardListSummaryProvider"))); + cpp_category_sp->AddTypeSummary( + "^std::variant<.+>$", eFormatterMatchRegex, + TypeSummaryImplSP(new ScriptSummaryFormat( + stl_summary_flags, + "lldb.formatters.cpp.gnu_libstdcpp.VariantSummaryProvider"))); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibStdcppVectorIteratorSyntheticFrontEndCreator, + "std::vector iterator synthetic children", + "^__gnu_cxx::__normal_iterator<.+>$", stl_synth_flags, true); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEndCreator, + "std::map iterator synthetic children", "^std::_Rb_tree_iterator<.+>$", + stl_synth_flags, true); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibStdcppUniquePtrSyntheticFrontEndCreator, + "std::unique_ptr synthetic children", "^std::unique_ptr<.+>(( )?&)?$", + stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator, + "std::shared_ptr synthetic children", "^std::shared_ptr<.+>(( )?&)?$", + stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator, + "std::weak_ptr synthetic children", "^std::weak_ptr<.+>(( )?&)?$", + stl_synth_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibStdcppTupleSyntheticFrontEndCreator, + "std::tuple synthetic children", "^std::tuple<.+>(( )?&)?$", + stl_synth_flags, true); + + static constexpr const char *const libstdcpp_std_coroutine_handle_regex = + "^std::coroutine_handle<.+>(( )?&)?$"; + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEndCreator, + "std::coroutine_handle synthetic children", + libstdcpp_std_coroutine_handle_regex, stl_deref_flags, true); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibStdcppBitsetSyntheticFrontEndCreator, + "std::bitset synthetic child", "^std::bitset<.+>(( )?&)?$", + stl_deref_flags, true); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibStdcppOptionalSyntheticFrontEndCreator, + "std::optional synthetic child", "^std::optional<.+>(( )?&)?$", + stl_deref_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibStdcppUniquePointerSummaryProvider, + "libstdc++ std::unique_ptr summary provider", + "^std::unique_ptr<.+>(( )?&)?$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibStdcppSmartPointerSummaryProvider, + "libstdc++ std::shared_ptr summary provider", + "^std::shared_ptr<.+>(( )?&)?$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibStdcppSmartPointerSummaryProvider, + "libstdc++ std::weak_ptr summary provider", + "^std::weak_ptr<.+>(( )?&)?$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::StdlibCoroutineHandleSummaryProvider, + "libstdc++ std::coroutine_handle summary provider", + libstdcpp_std_coroutine_handle_regex, stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::GenericOptionalSummaryProvider, + "libstd++ std::optional summary provider", + "^std::optional<.+>(( )?&)?$", stl_summary_flags, true); +} + +static void LoadSystemFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { + if (!cpp_category_sp) + return; + + TypeSummaryImpl::Flags string_flags; + string_flags.SetCascades(true) + .SetSkipPointers(true) + .SetSkipReferences(false) + .SetDontShowChildren(true) + .SetDontShowValue(false) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false); + + TypeSummaryImpl::Flags string_array_flags; + string_array_flags.SetCascades(true) + .SetSkipPointers(true) + .SetSkipReferences(false) + .SetDontShowChildren(true) + .SetDontShowValue(true) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::Char8StringSummaryProvider, + "char8_t * summary provider", "char8_t *", string_flags); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::Char8StringSummaryProvider, + "char8_t [] summary provider", "char8_t ?\\[[0-9]+\\]", + string_array_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::Char16StringSummaryProvider, + "char16_t * summary provider", "char16_t *", string_flags); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::Char16StringSummaryProvider, + "char16_t [] summary provider", "char16_t ?\\[[0-9]+\\]", + string_array_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::Char32StringSummaryProvider, + "char32_t * summary provider", "char32_t *", string_flags); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::Char32StringSummaryProvider, + "char32_t [] summary provider", "char32_t ?\\[[0-9]+\\]", + string_array_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::WCharStringSummaryProvider, + "wchar_t * summary provider", "wchar_t *", string_flags); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::WCharStringSummaryProvider, + "wchar_t * summary provider", "wchar_t ?\\[[0-9]+\\]", + string_array_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::Char16StringSummaryProvider, + "unichar * summary provider", "unichar *", string_flags); + + TypeSummaryImpl::Flags widechar_flags; + widechar_flags.SetDontShowValue(true) + .SetSkipPointers(true) + .SetSkipReferences(false) + .SetCascades(true) + .SetDontShowChildren(true) + .SetHideItemNames(true) + .SetShowMembersOneLiner(false); + + AddCXXSummary(cpp_category_sp, lldb_private::formatters::Char8SummaryProvider, + "char8_t summary provider", "char8_t", widechar_flags); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::Char16SummaryProvider, + "char16_t summary provider", "char16_t", widechar_flags); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::Char32SummaryProvider, + "char32_t summary provider", "char32_t", widechar_flags); + AddCXXSummary(cpp_category_sp, lldb_private::formatters::WCharSummaryProvider, + "wchar_t summary provider", "wchar_t", widechar_flags); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::Char16SummaryProvider, + "unichar summary provider", "unichar", widechar_flags); +} + +std::unique_ptr<Language::TypeScavenger> CPlusPlusLanguage::GetTypeScavenger() { + class CPlusPlusTypeScavenger : public Language::ImageListTypeScavenger { + public: + CompilerType AdjustForInclusion(CompilerType &candidate) override { + LanguageType lang_type(candidate.GetMinimumLanguage()); + if (!Language::LanguageIsC(lang_type) && + !Language::LanguageIsCPlusPlus(lang_type)) + return CompilerType(); + if (candidate.IsTypedefType()) + return candidate.GetTypedefedType(); + return candidate; + } + }; + + return std::unique_ptr<TypeScavenger>(new CPlusPlusTypeScavenger()); +} + +lldb::TypeCategoryImplSP CPlusPlusLanguage::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) { + LoadLibStdcppFormatters(g_category); + LoadLibCxxFormatters(g_category); + LoadSystemFormatters(g_category); + } + }); + return g_category; +} + +HardcodedFormatters::HardcodedSummaryFinder +CPlusPlusLanguage::GetHardcodedSummaries() { + static llvm::once_flag g_initialize; + static ConstString g_vectortypes("VectorTypes"); + static HardcodedFormatters::HardcodedSummaryFinder g_formatters; + + llvm::call_once(g_initialize, []() -> void { + g_formatters.push_back( + [](lldb_private::ValueObject &valobj, lldb::DynamicValueType, + FormatManager &) -> TypeSummaryImpl::SharedPointer { + static CXXFunctionSummaryFormat::SharedPointer formatter_sp( + new CXXFunctionSummaryFormat( + TypeSummaryImpl::Flags(), + lldb_private::formatters::CXXFunctionPointerSummaryProvider, + "Function pointer summary provider")); + if (CompilerType CT = valobj.GetCompilerType(); + CT.IsFunctionPointerType() || CT.IsMemberFunctionPointerType() || + valobj.GetValueType() == lldb::eValueTypeVTableEntry) { + return formatter_sp; + } + return nullptr; + }); + g_formatters.push_back( + [](lldb_private::ValueObject &valobj, lldb::DynamicValueType, + FormatManager &fmt_mgr) -> TypeSummaryImpl::SharedPointer { + static CXXFunctionSummaryFormat::SharedPointer formatter_sp( + new CXXFunctionSummaryFormat( + TypeSummaryImpl::Flags() + .SetCascades(true) + .SetDontShowChildren(true) + .SetHideItemNames(true) + .SetShowMembersOneLiner(true) + .SetSkipPointers(true) + .SetSkipReferences(false), + lldb_private::formatters::VectorTypeSummaryProvider, + "vector_type pointer summary provider")); + if (valobj.GetCompilerType().IsVectorType()) { + if (fmt_mgr.GetCategory(g_vectortypes)->IsEnabled()) + return formatter_sp; + } + return nullptr; + }); + g_formatters.push_back( + [](lldb_private::ValueObject &valobj, lldb::DynamicValueType, + FormatManager &fmt_mgr) -> TypeSummaryImpl::SharedPointer { + static CXXFunctionSummaryFormat::SharedPointer formatter_sp( + new CXXFunctionSummaryFormat( + TypeSummaryImpl::Flags() + .SetCascades(true) + .SetDontShowChildren(true) + .SetHideItemNames(true) + .SetShowMembersOneLiner(true) + .SetSkipPointers(true) + .SetSkipReferences(false), + lldb_private::formatters::BlockPointerSummaryProvider, + "block pointer summary provider")); + if (valobj.GetCompilerType().IsBlockPointerType()) { + return formatter_sp; + } + return nullptr; + }); + }); + + return g_formatters; +} + +HardcodedFormatters::HardcodedSyntheticFinder +CPlusPlusLanguage::GetHardcodedSynthetics() { + static llvm::once_flag g_initialize; + static ConstString g_vectortypes("VectorTypes"); + static HardcodedFormatters::HardcodedSyntheticFinder g_formatters; + + llvm::call_once(g_initialize, []() -> void { + g_formatters.push_back([](lldb_private::ValueObject &valobj, + lldb::DynamicValueType, FormatManager &fmt_mgr) + -> SyntheticChildren::SharedPointer { + static CXXSyntheticChildren::SharedPointer formatter_sp( + new CXXSyntheticChildren( + SyntheticChildren::Flags() + .SetCascades(true) + .SetSkipPointers(true) + .SetSkipReferences(true) + .SetNonCacheable(true), + "vector_type synthetic children", + lldb_private::formatters::VectorTypeSyntheticFrontEndCreator)); + if (valobj.GetCompilerType().IsVectorType()) { + if (fmt_mgr.GetCategory(g_vectortypes)->IsEnabled()) + return formatter_sp; + } + return nullptr; + }); + g_formatters.push_back([](lldb_private::ValueObject &valobj, + lldb::DynamicValueType, FormatManager &fmt_mgr) + -> SyntheticChildren::SharedPointer { + static CXXSyntheticChildren::SharedPointer formatter_sp( + new CXXSyntheticChildren( + SyntheticChildren::Flags() + .SetCascades(true) + .SetSkipPointers(true) + .SetSkipReferences(true) + .SetNonCacheable(true), + "block pointer synthetic children", + lldb_private::formatters::BlockPointerSyntheticFrontEndCreator)); + if (valobj.GetCompilerType().IsBlockPointerType()) { + return formatter_sp; + } + return nullptr; + }); + }); + + return g_formatters; +} + +bool CPlusPlusLanguage::IsNilReference(ValueObject &valobj) { + if (!Language::LanguageIsCPlusPlus(valobj.GetObjectRuntimeLanguage()) || + !valobj.IsPointerType()) + return false; + bool canReadValue = true; + bool isZero = valobj.GetValueAsUnsigned(0, &canReadValue) == 0; + return canReadValue && isZero; +} + +bool CPlusPlusLanguage::IsSourceFile(llvm::StringRef file_path) const { + const auto suffixes = {".cpp", ".cxx", ".c++", ".cc", ".c", + ".h", ".hh", ".hpp", ".hxx", ".h++"}; + for (auto suffix : suffixes) { + if (file_path.ends_with_insensitive(suffix)) + return true; + } + + // Check if we're in a STL path (where the files usually have no extension + // that we could check for. + return file_path.contains("/usr/include/c++/"); +} + +bool CPlusPlusLanguage::GetFunctionDisplayName( + const SymbolContext *sc, const ExecutionContext *exe_ctx, + FunctionNameRepresentation representation, Stream &s) { + switch (representation) { + case FunctionNameRepresentation::eNameWithArgs: { + // Print the function name with arguments in it + if (sc->function) { + ExecutionContextScope *exe_scope = + exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr; + const char *cstr = sc->function->GetName().AsCString(nullptr); + if (cstr) { + const InlineFunctionInfo *inline_info = nullptr; + VariableListSP variable_list_sp; + bool get_function_vars = true; + if (sc->block) { + Block *inline_block = sc->block->GetContainingInlinedBlock(); + + if (inline_block) { + get_function_vars = false; + inline_info = inline_block->GetInlinedFunctionInfo(); + if (inline_info) + variable_list_sp = inline_block->GetBlockVariableList(true); + } + } + + if (get_function_vars) { + variable_list_sp = + sc->function->GetBlock(true).GetBlockVariableList(true); + } + + if (inline_info) { + s.PutCString(cstr); + s.PutCString(" [inlined] "); + cstr = inline_info->GetName().GetCString(); + } + + VariableList args; + if (variable_list_sp) + variable_list_sp->AppendVariablesWithScope(eValueTypeVariableArgument, + args); + if (args.GetSize() > 0) { + if (!PrettyPrintFunctionNameWithArgs(s, cstr, exe_scope, args)) + return false; + } else { + s.PutCString(cstr); + } + return true; + } + } else if (sc->symbol) { + const char *cstr = sc->symbol->GetName().AsCString(nullptr); + if (cstr) { + s.PutCString(cstr); + return true; + } + } + } break; + default: + return false; + } + + return false; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h new file mode 100644 index 000000000000..623d481bf117 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h @@ -0,0 +1,178 @@ +//===-- CPlusPlusLanguage.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_CPLUSPLUS_CPLUSPLUSLANGUAGE_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_CPLUSPLUSLANGUAGE_H + +#include <set> +#include <vector> + +#include "llvm/ADT/StringRef.h" + +#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 CPlusPlusLanguage : public Language { + ClangHighlighter m_highlighter; + +public: + class MethodName { + public: + MethodName() + : m_full(), m_basename(), m_context(), m_arguments(), m_qualifiers() {} + + MethodName(ConstString s) + : m_full(s), m_basename(), m_context(), m_arguments(), m_qualifiers(), + m_parsed(false), m_parse_error(false) {} + + void Clear(); + + bool IsValid() { + if (!m_parsed) + Parse(); + if (m_parse_error) + return false; + return (bool)m_full; + } + + ConstString GetFullName() const { return m_full; } + + std::string GetScopeQualifiedName(); + + llvm::StringRef GetBasename(); + + llvm::StringRef GetContext(); + + llvm::StringRef GetArguments(); + + llvm::StringRef GetQualifiers(); + + /// Returns the methods return-type. + /// + /// Currently returns an empty llvm::StringRef + /// if the return-type is a function pointer. + llvm::StringRef GetReturnType(); + + bool ContainsPath(llvm::StringRef path); + + private: + /// Returns the Basename of this method without a template parameter + /// list, if any. + /// + // Examples: + // + // +--------------------------------+---------+ + // | MethodName | Returns | + // +--------------------------------+---------+ + // | void func() | func | + // | void func<int>() | func | + // | void func<std::vector<int>>() | func | + // +--------------------------------+---------+ + llvm::StringRef GetBasenameNoTemplateParameters(); + + protected: + void Parse(); + bool TrySimplifiedParse(); + + ConstString m_full; // Full name: + // "size_t lldb::SBTarget::GetBreakpointAtIndex(unsigned + // int) const" + llvm::StringRef m_basename; // Basename: "GetBreakpointAtIndex" + llvm::StringRef m_context; // Decl context: "lldb::SBTarget" + llvm::StringRef m_arguments; // Arguments: "(unsigned int)" + llvm::StringRef m_qualifiers; // Qualifiers: "const" + llvm::StringRef m_return_type; // Return type: "size_t" + bool m_parsed = false; + bool m_parse_error = false; + }; + + CPlusPlusLanguage() = default; + + ~CPlusPlusLanguage() override = default; + + lldb::LanguageType GetLanguageType() const override { + return lldb::eLanguageTypeC_plus_plus; + } + + llvm::StringRef GetUserEntryPointName() const override { return "main"; } + + std::unique_ptr<TypeScavenger> GetTypeScavenger() override; + lldb::TypeCategoryImplSP GetFormatters() override; + + HardcodedFormatters::HardcodedSummaryFinder GetHardcodedSummaries() override; + + HardcodedFormatters::HardcodedSyntheticFinder + GetHardcodedSynthetics() override; + + bool IsNilReference(ValueObject &valobj) override; + + llvm::StringRef GetNilReferenceSummaryString() override { return "nullptr"; } + + 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 "cplusplus"; } + + bool SymbolNameFitsToLanguage(Mangled mangled) const override; + + bool DemangledNameContainsPath(llvm::StringRef path, + ConstString demangled) const override; + + ConstString + GetDemangledFunctionNameWithoutArguments(Mangled mangled) const override; + + bool GetFunctionDisplayName(const SymbolContext *sc, + const ExecutionContext *exe_ctx, + FunctionNameRepresentation representation, + Stream &s) override; + + static bool IsCPPMangledName(llvm::StringRef name); + + // Extract C++ context and identifier from a string using heuristic matching + // (as opposed to + // CPlusPlusLanguage::MethodName which has to have a fully qualified C++ name + // with parens and arguments. + // If the name is a lone C identifier (e.g. C) or a qualified C identifier + // (e.g. A::B::C) it will return true, + // and identifier will be the identifier (C and C respectively) and the + // context will be "" and "A::B" respectively. + // If the name fails the heuristic matching for a qualified or unqualified + // C/C++ identifier, then it will return false + // and identifier and context will be unchanged. + + static bool ExtractContextAndIdentifier(const char *name, + llvm::StringRef &context, + llvm::StringRef &identifier); + + std::vector<ConstString> + GenerateAlternateFunctionManglings(const ConstString mangled) const override; + + ConstString FindBestAlternateFunctionMangledName( + const Mangled mangled, const SymbolContext &sym_ctx) const override; + + llvm::StringRef GetInstanceVariableName() override { return "this"; } + + // PluginInterface protocol + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_CPLUSPLUSLANGUAGE_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp new file mode 100644 index 000000000000..d8c095d6edeb --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp @@ -0,0 +1,785 @@ +//===-- CPlusPlusNameParser.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 "CPlusPlusNameParser.h" + +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/TokenKinds.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/Threading.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using ParsedFunction = lldb_private::CPlusPlusNameParser::ParsedFunction; +using ParsedName = lldb_private::CPlusPlusNameParser::ParsedName; +namespace tok = clang::tok; + +std::optional<ParsedFunction> CPlusPlusNameParser::ParseAsFunctionDefinition() { + m_next_token_index = 0; + std::optional<ParsedFunction> result(std::nullopt); + + // Try to parse the name as function without a return type specified e.g. + // main(int, char*[]) + { + Bookmark start_position = SetBookmark(); + result = ParseFunctionImpl(false); + if (result && !HasMoreTokens()) + return result; + } + + // Try to parse the name as function with function pointer return type e.g. + // void (*get_func(const char*))() + result = ParseFuncPtr(true); + if (result) + return result; + + // Finally try to parse the name as a function with non-function return type + // e.g. int main(int, char*[]) + result = ParseFunctionImpl(true); + if (HasMoreTokens()) + return std::nullopt; + return result; +} + +std::optional<ParsedName> CPlusPlusNameParser::ParseAsFullName() { + m_next_token_index = 0; + std::optional<ParsedNameRanges> name_ranges = ParseFullNameImpl(); + if (!name_ranges) + return std::nullopt; + if (HasMoreTokens()) + return std::nullopt; + ParsedName result; + result.basename = GetTextForRange(name_ranges->basename_range); + result.context = GetTextForRange(name_ranges->context_range); + return result; +} + +bool CPlusPlusNameParser::HasMoreTokens() { + return m_next_token_index < m_tokens.size(); +} + +void CPlusPlusNameParser::Advance() { ++m_next_token_index; } + +void CPlusPlusNameParser::TakeBack() { --m_next_token_index; } + +bool CPlusPlusNameParser::ConsumeToken(tok::TokenKind kind) { + if (!HasMoreTokens()) + return false; + + if (!Peek().is(kind)) + return false; + + Advance(); + return true; +} + +template <typename... Ts> bool CPlusPlusNameParser::ConsumeToken(Ts... kinds) { + if (!HasMoreTokens()) + return false; + + if (!Peek().isOneOf(kinds...)) + return false; + + Advance(); + return true; +} + +CPlusPlusNameParser::Bookmark CPlusPlusNameParser::SetBookmark() { + return Bookmark(m_next_token_index); +} + +size_t CPlusPlusNameParser::GetCurrentPosition() { return m_next_token_index; } + +clang::Token &CPlusPlusNameParser::Peek() { + assert(HasMoreTokens()); + return m_tokens[m_next_token_index]; +} + +std::optional<ParsedFunction> +CPlusPlusNameParser::ParseFunctionImpl(bool expect_return_type) { + Bookmark start_position = SetBookmark(); + + ParsedFunction result; + if (expect_return_type) { + size_t return_start = GetCurrentPosition(); + // Consume return type if it's expected. + if (!ConsumeToken(tok::kw_auto) && !ConsumeTypename()) + return std::nullopt; + + size_t return_end = GetCurrentPosition(); + result.return_type = GetTextForRange(Range(return_start, return_end)); + } + + auto maybe_name = ParseFullNameImpl(); + if (!maybe_name) { + return std::nullopt; + } + + size_t argument_start = GetCurrentPosition(); + if (!ConsumeArguments()) { + return std::nullopt; + } + + size_t qualifiers_start = GetCurrentPosition(); + SkipFunctionQualifiers(); + size_t end_position = GetCurrentPosition(); + + result.name.basename = GetTextForRange(maybe_name->basename_range); + result.name.context = GetTextForRange(maybe_name->context_range); + result.arguments = GetTextForRange(Range(argument_start, qualifiers_start)); + result.qualifiers = GetTextForRange(Range(qualifiers_start, end_position)); + start_position.Remove(); + return result; +} + +std::optional<ParsedFunction> +CPlusPlusNameParser::ParseFuncPtr(bool expect_return_type) { + // This function parses a function definition + // that returns a pointer type. + // E.g., double (*(*func(long))(int))(float) + + // Step 1: + // Remove the return type of the innermost + // function pointer type. + // + // Leaves us with: + // (*(*func(long))(int))(float) + Bookmark start_position = SetBookmark(); + if (expect_return_type) { + // Consume return type. + if (!ConsumeTypename()) + return std::nullopt; + } + + // Step 2: + // + // Skip a pointer and parenthesis pair. + // + // Leaves us with: + // (*func(long))(int))(float) + if (!ConsumeToken(tok::l_paren)) + return std::nullopt; + if (!ConsumePtrsAndRefs()) + return std::nullopt; + + // Step 3: + // + // Consume inner function name. This will fail unless + // we stripped all the pointers on the left hand side + // of the function name. + { + Bookmark before_inner_function_pos = SetBookmark(); + auto maybe_inner_function_name = ParseFunctionImpl(false); + if (maybe_inner_function_name) + if (ConsumeToken(tok::r_paren)) + if (ConsumeArguments()) { + SkipFunctionQualifiers(); + start_position.Remove(); + before_inner_function_pos.Remove(); + return maybe_inner_function_name; + } + } + + // Step 4: + // + // Parse the remaining string as a function pointer again. + // This time don't consume the inner-most typename since + // we're left with pointers only. This will strip another + // layer of pointers until we're left with the innermost + // function name/argument. I.e., func(long))(int))(float) + // + // Once we successfully stripped all pointers and gotten + // the innermost function name from ParseFunctionImpl above, + // we consume a single ')' and the arguments '(...)' that follows. + // + // Leaves us with: + // )(float) + // + // This is the remnant of the outer function pointers' arguments. + // Unwinding the recursive calls will remove the remaining + // arguments. + auto maybe_inner_function_ptr_name = ParseFuncPtr(false); + if (maybe_inner_function_ptr_name) + if (ConsumeToken(tok::r_paren)) + if (ConsumeArguments()) { + SkipFunctionQualifiers(); + start_position.Remove(); + return maybe_inner_function_ptr_name; + } + + return std::nullopt; +} + +bool CPlusPlusNameParser::ConsumeArguments() { + return ConsumeBrackets(tok::l_paren, tok::r_paren); +} + +bool CPlusPlusNameParser::ConsumeTemplateArgs() { + Bookmark start_position = SetBookmark(); + if (!HasMoreTokens() || Peek().getKind() != tok::less) + return false; + Advance(); + + // Consuming template arguments is a bit trickier than consuming function + // arguments, because '<' '>' brackets are not always trivially balanced. In + // some rare cases tokens '<' and '>' can appear inside template arguments as + // arithmetic or shift operators not as template brackets. Examples: + // std::enable_if<(10u)<(64), bool> + // f<A<operator<(X,Y)::Subclass>> + // Good thing that compiler makes sure that really ambiguous cases of '>' + // usage should be enclosed within '()' brackets. + int template_counter = 1; + bool can_open_template = false; + while (HasMoreTokens() && template_counter > 0) { + tok::TokenKind kind = Peek().getKind(); + switch (kind) { + case tok::greatergreater: + template_counter -= 2; + can_open_template = false; + Advance(); + break; + case tok::greater: + --template_counter; + can_open_template = false; + Advance(); + break; + case tok::less: + // '<' is an attempt to open a subteamplte + // check if parser is at the point where it's actually possible, + // otherwise it's just a part of an expression like 'sizeof(T)<(10)'. No + // need to do the same for '>' because compiler actually makes sure that + // '>' always surrounded by brackets to avoid ambiguity. + if (can_open_template) + ++template_counter; + can_open_template = false; + Advance(); + break; + case tok::kw_operator: // C++ operator overloading. + if (!ConsumeOperator()) + return false; + can_open_template = true; + break; + case tok::raw_identifier: + can_open_template = true; + Advance(); + break; + case tok::l_square: + // Handle templates tagged with an ABI tag. + // An example demangled/prettified version is: + // func[abi:tag1][abi:tag2]<type[abi:tag3]>(int) + if (ConsumeAbiTag()) + can_open_template = true; + else if (ConsumeBrackets(tok::l_square, tok::r_square)) + can_open_template = false; + else + return false; + break; + case tok::l_paren: + if (!ConsumeArguments()) + return false; + can_open_template = false; + break; + default: + can_open_template = false; + Advance(); + break; + } + } + + if (template_counter != 0) { + return false; + } + start_position.Remove(); + return true; +} + +bool CPlusPlusNameParser::ConsumeAbiTag() { + Bookmark start_position = SetBookmark(); + if (!ConsumeToken(tok::l_square)) + return false; + + if (HasMoreTokens() && Peek().is(tok::raw_identifier) && + Peek().getRawIdentifier() == "abi") + Advance(); + else + return false; + + if (!ConsumeToken(tok::colon)) + return false; + + // Consume the actual tag string (and allow some special characters) + while (ConsumeToken(tok::raw_identifier, tok::comma, tok::period, + tok::numeric_constant)) + ; + + if (!ConsumeToken(tok::r_square)) + return false; + + start_position.Remove(); + return true; +} + +bool CPlusPlusNameParser::ConsumeAnonymousNamespace() { + Bookmark start_position = SetBookmark(); + if (!ConsumeToken(tok::l_paren)) { + return false; + } + constexpr llvm::StringLiteral g_anonymous("anonymous"); + if (HasMoreTokens() && Peek().is(tok::raw_identifier) && + Peek().getRawIdentifier() == g_anonymous) { + Advance(); + } else { + return false; + } + + if (!ConsumeToken(tok::kw_namespace)) { + return false; + } + + if (!ConsumeToken(tok::r_paren)) { + return false; + } + start_position.Remove(); + return true; +} + +bool CPlusPlusNameParser::ConsumeLambda() { + Bookmark start_position = SetBookmark(); + if (!ConsumeToken(tok::l_brace)) { + return false; + } + constexpr llvm::StringLiteral g_lambda("lambda"); + if (HasMoreTokens() && Peek().is(tok::raw_identifier) && + Peek().getRawIdentifier() == g_lambda) { + // Put the matched brace back so we can use ConsumeBrackets + TakeBack(); + } else { + return false; + } + + if (!ConsumeBrackets(tok::l_brace, tok::r_brace)) { + return false; + } + + start_position.Remove(); + return true; +} + +bool CPlusPlusNameParser::ConsumeBrackets(tok::TokenKind left, + tok::TokenKind right) { + Bookmark start_position = SetBookmark(); + if (!HasMoreTokens() || Peek().getKind() != left) + return false; + Advance(); + + int counter = 1; + while (HasMoreTokens() && counter > 0) { + tok::TokenKind kind = Peek().getKind(); + if (kind == right) + --counter; + else if (kind == left) + ++counter; + Advance(); + } + + assert(counter >= 0); + if (counter > 0) { + return false; + } + start_position.Remove(); + return true; +} + +bool CPlusPlusNameParser::ConsumeOperator() { + Bookmark start_position = SetBookmark(); + if (!ConsumeToken(tok::kw_operator)) + return false; + + if (!HasMoreTokens()) { + return false; + } + + const auto &token = Peek(); + + // When clang generates debug info it adds template parameters to names. + // Since clang doesn't add a space between the name and the template parameter + // in some cases we are not generating valid C++ names e.g.: + // + // operator<<A::B> + // + // In some of these cases we will not parse them correctly. This fixes the + // issue by detecting this case and inserting tok::less in place of + // tok::lessless and returning successfully that we consumed the operator. + if (token.getKind() == tok::lessless) { + // Make sure we have more tokens before attempting to look ahead one more. + if (m_next_token_index + 1 < m_tokens.size()) { + // Look ahead two tokens. + clang::Token n_token = m_tokens[m_next_token_index + 1]; + // If we find ( or < then this is indeed operator<< no need for fix. + if (n_token.getKind() != tok::l_paren && n_token.getKind() != tok::less) { + clang::Token tmp_tok; + tmp_tok.startToken(); + tmp_tok.setLength(1); + tmp_tok.setLocation(token.getLocation().getLocWithOffset(1)); + tmp_tok.setKind(tok::less); + + m_tokens[m_next_token_index] = tmp_tok; + + start_position.Remove(); + return true; + } + } + } + + switch (token.getKind()) { + case tok::kw_new: + case tok::kw_delete: + // This is 'new' or 'delete' operators. + Advance(); + // Check for array new/delete. + if (HasMoreTokens() && Peek().is(tok::l_square)) { + // Consume the '[' and ']'. + if (!ConsumeBrackets(tok::l_square, tok::r_square)) + return false; + } + break; + +#define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly) \ + case tok::Token: \ + Advance(); \ + break; +#define OVERLOADED_OPERATOR_MULTI(Name, Spelling, Unary, Binary, MemberOnly) +#include "clang/Basic/OperatorKinds.def" +#undef OVERLOADED_OPERATOR +#undef OVERLOADED_OPERATOR_MULTI + + case tok::l_paren: + // Call operator consume '(' ... ')'. + if (ConsumeBrackets(tok::l_paren, tok::r_paren)) + break; + return false; + + case tok::l_square: + // This is a [] operator. + // Consume the '[' and ']'. + if (ConsumeBrackets(tok::l_square, tok::r_square)) + break; + return false; + + default: + // This might be a cast operator. + if (ConsumeTypename()) + break; + return false; + } + start_position.Remove(); + return true; +} + +void CPlusPlusNameParser::SkipTypeQualifiers() { + while (ConsumeToken(tok::kw_const, tok::kw_volatile)) + ; +} + +void CPlusPlusNameParser::SkipFunctionQualifiers() { + while (ConsumeToken(tok::kw_const, tok::kw_volatile, tok::amp, tok::ampamp)) + ; +} + +bool CPlusPlusNameParser::ConsumeBuiltinType() { + bool result = false; + bool continue_parsing = true; + // Built-in types can be made of a few keywords like 'unsigned long long + // int'. This function consumes all built-in type keywords without checking + // if they make sense like 'unsigned char void'. + while (continue_parsing && HasMoreTokens()) { + switch (Peek().getKind()) { + case tok::kw_short: + case tok::kw_long: + case tok::kw___int64: + case tok::kw___int128: + case tok::kw_signed: + case tok::kw_unsigned: + case tok::kw_void: + case tok::kw_char: + case tok::kw_int: + case tok::kw_half: + case tok::kw_float: + case tok::kw_double: + case tok::kw___float128: + case tok::kw_wchar_t: + case tok::kw_bool: + case tok::kw_char16_t: + case tok::kw_char32_t: + result = true; + Advance(); + break; + default: + continue_parsing = false; + break; + } + } + return result; +} + +void CPlusPlusNameParser::SkipPtrsAndRefs() { + // Ignoring result. + ConsumePtrsAndRefs(); +} + +bool CPlusPlusNameParser::ConsumePtrsAndRefs() { + bool found = false; + SkipTypeQualifiers(); + while (ConsumeToken(tok::star, tok::amp, tok::ampamp, tok::kw_const, + tok::kw_volatile)) { + found = true; + SkipTypeQualifiers(); + } + return found; +} + +bool CPlusPlusNameParser::ConsumeDecltype() { + Bookmark start_position = SetBookmark(); + if (!ConsumeToken(tok::kw_decltype)) + return false; + + if (!ConsumeArguments()) + return false; + + start_position.Remove(); + return true; +} + +bool CPlusPlusNameParser::ConsumeTypename() { + Bookmark start_position = SetBookmark(); + SkipTypeQualifiers(); + if (!ConsumeBuiltinType() && !ConsumeDecltype()) { + if (!ParseFullNameImpl()) + return false; + } + SkipPtrsAndRefs(); + start_position.Remove(); + return true; +} + +std::optional<CPlusPlusNameParser::ParsedNameRanges> +CPlusPlusNameParser::ParseFullNameImpl() { + // Name parsing state machine. + enum class State { + Beginning, // start of the name + AfterTwoColons, // right after :: + AfterIdentifier, // right after alphanumerical identifier ([a-z0-9_]+) + AfterTemplate, // right after template brackets (<something>) + AfterOperator, // right after name of C++ operator + }; + + Bookmark start_position = SetBookmark(); + State state = State::Beginning; + bool continue_parsing = true; + std::optional<size_t> last_coloncolon_position; + + while (continue_parsing && HasMoreTokens()) { + const auto &token = Peek(); + switch (token.getKind()) { + case tok::raw_identifier: // Just a name. + if (state != State::Beginning && state != State::AfterTwoColons) { + continue_parsing = false; + break; + } + Advance(); + state = State::AfterIdentifier; + break; + case tok::l_square: { + // Handles types or functions that were tagged + // with, e.g., + // [[gnu::abi_tag("tag1","tag2")]] func() + // and demangled/prettified into: + // func[abi:tag1][abi:tag2]() + + // ABI tags only appear after a method or type name + const bool valid_state = + state == State::AfterIdentifier || state == State::AfterOperator; + if (!valid_state || !ConsumeAbiTag()) { + continue_parsing = false; + } + + break; + } + case tok::l_paren: { + if (state == State::Beginning || state == State::AfterTwoColons) { + // (anonymous namespace) + if (ConsumeAnonymousNamespace()) { + state = State::AfterIdentifier; + break; + } + } + + // Type declared inside a function 'func()::Type' + if (state != State::AfterIdentifier && state != State::AfterTemplate && + state != State::AfterOperator) { + continue_parsing = false; + break; + } + Bookmark l_paren_position = SetBookmark(); + // Consume the '(' ... ') [const]'. + if (!ConsumeArguments()) { + continue_parsing = false; + break; + } + SkipFunctionQualifiers(); + + // Consume '::' + size_t coloncolon_position = GetCurrentPosition(); + if (!ConsumeToken(tok::coloncolon)) { + continue_parsing = false; + break; + } + l_paren_position.Remove(); + last_coloncolon_position = coloncolon_position; + state = State::AfterTwoColons; + break; + } + case tok::l_brace: + if (state == State::Beginning || state == State::AfterTwoColons) { + if (ConsumeLambda()) { + state = State::AfterIdentifier; + break; + } + } + continue_parsing = false; + break; + case tok::coloncolon: // Type nesting delimiter. + if (state != State::Beginning && state != State::AfterIdentifier && + state != State::AfterTemplate) { + continue_parsing = false; + break; + } + last_coloncolon_position = GetCurrentPosition(); + Advance(); + state = State::AfterTwoColons; + break; + case tok::less: // Template brackets. + if (state != State::AfterIdentifier && state != State::AfterOperator) { + continue_parsing = false; + break; + } + if (!ConsumeTemplateArgs()) { + continue_parsing = false; + break; + } + state = State::AfterTemplate; + break; + case tok::kw_operator: // C++ operator overloading. + if (state != State::Beginning && state != State::AfterTwoColons) { + continue_parsing = false; + break; + } + if (!ConsumeOperator()) { + continue_parsing = false; + break; + } + state = State::AfterOperator; + break; + case tok::tilde: // Destructor. + if (state != State::Beginning && state != State::AfterTwoColons) { + continue_parsing = false; + break; + } + Advance(); + if (ConsumeToken(tok::raw_identifier)) { + state = State::AfterIdentifier; + } else { + TakeBack(); + continue_parsing = false; + } + break; + default: + continue_parsing = false; + break; + } + } + + if (state == State::AfterIdentifier || state == State::AfterOperator || + state == State::AfterTemplate) { + ParsedNameRanges result; + if (last_coloncolon_position) { + result.context_range = + Range(start_position.GetSavedPosition(), *last_coloncolon_position); + result.basename_range = + Range(*last_coloncolon_position + 1, GetCurrentPosition()); + } else { + result.basename_range = + Range(start_position.GetSavedPosition(), GetCurrentPosition()); + } + start_position.Remove(); + return result; + } else { + return std::nullopt; + } +} + +llvm::StringRef CPlusPlusNameParser::GetTextForRange(const Range &range) { + if (range.empty()) + return llvm::StringRef(); + assert(range.begin_index < range.end_index); + assert(range.begin_index < m_tokens.size()); + assert(range.end_index <= m_tokens.size()); + clang::Token &first_token = m_tokens[range.begin_index]; + clang::Token &last_token = m_tokens[range.end_index - 1]; + clang::SourceLocation start_loc = first_token.getLocation(); + clang::SourceLocation end_loc = last_token.getLocation(); + unsigned start_pos = start_loc.getRawEncoding(); + unsigned end_pos = end_loc.getRawEncoding() + last_token.getLength(); + return m_text.take_front(end_pos).drop_front(start_pos); +} + +static const clang::LangOptions &GetLangOptions() { + static clang::LangOptions g_options; + static llvm::once_flag g_once_flag; + llvm::call_once(g_once_flag, []() { + g_options.LineComment = true; + g_options.C99 = true; + g_options.C11 = true; + g_options.CPlusPlus = true; + g_options.CPlusPlus11 = true; + g_options.CPlusPlus14 = true; + g_options.CPlusPlus17 = true; + g_options.CPlusPlus20 = true; + }); + return g_options; +} + +static const llvm::StringMap<tok::TokenKind> &GetKeywordsMap() { + static llvm::StringMap<tok::TokenKind> g_map{ +#define KEYWORD(Name, Flags) {llvm::StringRef(#Name), tok::kw_##Name}, +#include "clang/Basic/TokenKinds.def" +#undef KEYWORD + }; + return g_map; +} + +void CPlusPlusNameParser::ExtractTokens() { + if (m_text.empty()) + return; + clang::Lexer lexer(clang::SourceLocation(), GetLangOptions(), m_text.data(), + m_text.data(), m_text.data() + m_text.size()); + const auto &kw_map = GetKeywordsMap(); + clang::Token token; + for (lexer.LexFromRawLexer(token); !token.is(clang::tok::eof); + lexer.LexFromRawLexer(token)) { + if (token.is(clang::tok::raw_identifier)) { + auto it = kw_map.find(token.getRawIdentifier()); + if (it != kw_map.end()) { + token.setKind(it->getValue()); + } + } + + m_tokens.push_back(token); + } +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h new file mode 100644 index 000000000000..326efb7a8be0 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h @@ -0,0 +1,188 @@ +//===-- CPlusPlusNameParser.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_CPLUSPLUS_CPLUSPLUSNAMEPARSER_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_CPLUSPLUSNAMEPARSER_H + +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" + +#include "lldb/Utility/ConstString.h" +#include "lldb/lldb-private.h" +#include <optional> + +namespace lldb_private { + +// Helps to validate and obtain various parts of C++ definitions. +class CPlusPlusNameParser { +public: + CPlusPlusNameParser(llvm::StringRef text) : m_text(text) { ExtractTokens(); } + + struct ParsedName { + llvm::StringRef basename; + llvm::StringRef context; + }; + + struct ParsedFunction { + ParsedName name; + llvm::StringRef arguments; + llvm::StringRef qualifiers; + llvm::StringRef return_type; + }; + + // Treats given text as a function definition and parses it. + // Function definition might or might not have a return type and this should + // change parsing result. + // Examples: + // main(int, chat const*) + // T fun(int, bool) + // std::vector<int>::push_back(int) + // int& map<int, pair<short, int>>::operator[](short) const + // int (*get_function(const chat *))() + std::optional<ParsedFunction> ParseAsFunctionDefinition(); + + // Treats given text as a potentially nested name of C++ entity (function, + // class, field) and parses it. + // Examples: + // main + // fun + // std::vector<int>::push_back + // map<int, pair<short, int>>::operator[] + // func<C>(int, C&)::nested_class::method + std::optional<ParsedName> ParseAsFullName(); + +private: + // A C++ definition to parse. + llvm::StringRef m_text; + // Tokens extracted from m_text. + llvm::SmallVector<clang::Token, 30> m_tokens; + // Index of the next token to look at from m_tokens. + size_t m_next_token_index = 0; + + // Range of tokens saved in m_next_token_index. + struct Range { + size_t begin_index = 0; + size_t end_index = 0; + + Range() = default; + Range(size_t begin, size_t end) : begin_index(begin), end_index(end) { + assert(end >= begin); + } + + size_t size() const { return end_index - begin_index; } + + bool empty() const { return size() == 0; } + }; + + struct ParsedNameRanges { + Range basename_range; + Range context_range; + }; + + // Bookmark automatically restores parsing position (m_next_token_index) + // when destructed unless it's manually removed with Remove(). + class Bookmark { + public: + Bookmark(size_t &position) + : m_position(position), m_position_value(position) {} + Bookmark(const Bookmark &) = delete; + Bookmark(Bookmark &&b) + : m_position(b.m_position), m_position_value(b.m_position_value), + m_restore(b.m_restore) { + b.Remove(); + } + Bookmark &operator=(Bookmark &&) = delete; + Bookmark &operator=(const Bookmark &) = delete; + + void Remove() { m_restore = false; } + size_t GetSavedPosition() { return m_position_value; } + ~Bookmark() { + if (m_restore) { + m_position = m_position_value; + } + } + + private: + size_t &m_position; + size_t m_position_value; + bool m_restore = true; + }; + + bool HasMoreTokens(); + void Advance(); + void TakeBack(); + bool ConsumeToken(clang::tok::TokenKind kind); + + template <typename... Ts> bool ConsumeToken(Ts... kinds); + Bookmark SetBookmark(); + size_t GetCurrentPosition(); + clang::Token &Peek(); + bool ConsumeBrackets(clang::tok::TokenKind left, clang::tok::TokenKind right); + + std::optional<ParsedFunction> ParseFunctionImpl(bool expect_return_type); + + // Parses functions returning function pointers 'string (*f(int x))(float y)' + std::optional<ParsedFunction> ParseFuncPtr(bool expect_return_type); + + // Consumes function arguments enclosed within '(' ... ')' + bool ConsumeArguments(); + + // Consumes template arguments enclosed within '<' ... '>' + bool ConsumeTemplateArgs(); + + // Consumes '(anonymous namespace)' + bool ConsumeAnonymousNamespace(); + + // Consumes '{lambda ...}' + bool ConsumeLambda(); + + // Consumes operator declaration like 'operator *' or 'operator delete []' + bool ConsumeOperator(); + + // Skips 'const' and 'volatile' + void SkipTypeQualifiers(); + + // Skips 'const', 'volatile', '&', '&&' in the end of the function. + void SkipFunctionQualifiers(); + + // Consumes built-in types like 'int' or 'unsigned long long int' + bool ConsumeBuiltinType(); + + // Consumes types defined via decltype keyword. + bool ConsumeDecltype(); + + // Skips 'const' and 'volatile' + void SkipPtrsAndRefs(); + + // Consumes things like 'const * const &' + bool ConsumePtrsAndRefs(); + + // Consumes full type name like 'Namespace::Class<int>::Method()::InnerClass' + bool ConsumeTypename(); + + /// Consumes ABI tags enclosed within '[abi:' ... ']' + /// + /// Since there is no restriction on what the ABI tag + /// string may contain, this API supports parsing a small + /// set of special characters. + /// + /// The following regex describes the set of supported characters: + /// [A-Za-z,.\s\d]+ + bool ConsumeAbiTag(); + + std::optional<ParsedNameRanges> ParseFullNameImpl(); + llvm::StringRef GetTextForRange(const Range &range); + + // Populate m_tokens by calling clang lexer on m_text. + void ExtractTokens(); +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_CPLUSPLUSNAMEPARSER_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/Coroutines.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/Coroutines.cpp new file mode 100644 index 000000000000..5e63d1d7b214 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/Coroutines.cpp @@ -0,0 +1,227 @@ +//===-- Coroutines.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 "Coroutines.h" + +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +static lldb::addr_t GetCoroFramePtrFromHandle(ValueObjectSP valobj_sp) { + if (!valobj_sp) + return LLDB_INVALID_ADDRESS; + + // We expect a single pointer in the `coroutine_handle` class. + // We don't care about its name. + if (valobj_sp->GetNumChildrenIgnoringErrors() != 1) + return LLDB_INVALID_ADDRESS; + ValueObjectSP ptr_sp(valobj_sp->GetChildAtIndex(0)); + if (!ptr_sp) + return LLDB_INVALID_ADDRESS; + if (!ptr_sp->GetCompilerType().IsPointerType()) + return LLDB_INVALID_ADDRESS; + + AddressType addr_type; + lldb::addr_t frame_ptr_addr = ptr_sp->GetPointerValue(&addr_type); + if (!frame_ptr_addr || frame_ptr_addr == LLDB_INVALID_ADDRESS) + return LLDB_INVALID_ADDRESS; + lldbassert(addr_type == AddressType::eAddressTypeLoad); + if (addr_type != AddressType::eAddressTypeLoad) + return LLDB_INVALID_ADDRESS; + + return frame_ptr_addr; +} + +static Function *ExtractDestroyFunction(lldb::TargetSP target_sp, + lldb::addr_t frame_ptr_addr) { + lldb::ProcessSP process_sp = target_sp->GetProcessSP(); + auto ptr_size = process_sp->GetAddressByteSize(); + + Status error; + auto destroy_func_ptr_addr = frame_ptr_addr + ptr_size; + lldb::addr_t destroy_func_addr = + process_sp->ReadPointerFromMemory(destroy_func_ptr_addr, error); + if (error.Fail()) + return nullptr; + + Address destroy_func_address; + if (!target_sp->ResolveLoadAddress(destroy_func_addr, destroy_func_address)) + return nullptr; + + return destroy_func_address.CalculateSymbolContextFunction(); +} + +static CompilerType InferPromiseType(Function &destroy_func) { + Block &block = destroy_func.GetBlock(true); + auto variable_list = block.GetBlockVariableList(true); + + // clang generates an artificial `__promise` variable inside the + // `destroy` function. Look for it. + auto promise_var = variable_list->FindVariable(ConstString("__promise")); + if (!promise_var) + return {}; + if (!promise_var->IsArtificial()) + return {}; + + Type *promise_type = promise_var->GetType(); + if (!promise_type) + return {}; + return promise_type->GetForwardCompilerType(); +} + +bool lldb_private::formatters::StdlibCoroutineHandleSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + lldb::addr_t frame_ptr_addr = + GetCoroFramePtrFromHandle(valobj.GetNonSyntheticValue()); + if (frame_ptr_addr == LLDB_INVALID_ADDRESS) + return false; + + if (frame_ptr_addr == 0) { + stream << "nullptr"; + } else { + stream.Printf("coro frame = 0x%" PRIx64, frame_ptr_addr); + } + + return true; +} + +lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd:: + StdlibCoroutineHandleSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd:: + ~StdlibCoroutineHandleSyntheticFrontEnd() = default; + +llvm::Expected<uint32_t> lldb_private::formatters:: + StdlibCoroutineHandleSyntheticFrontEnd::CalculateNumChildren() { + if (!m_resume_ptr_sp || !m_destroy_ptr_sp) + return 0; + + return m_promise_ptr_sp ? 3 : 2; +} + +lldb::ValueObjectSP lldb_private::formatters:: + StdlibCoroutineHandleSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { + switch (idx) { + case 0: + return m_resume_ptr_sp; + case 1: + return m_destroy_ptr_sp; + case 2: + return m_promise_ptr_sp; + } + return lldb::ValueObjectSP(); +} + +lldb::ChildCacheState +lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::Update() { + m_resume_ptr_sp.reset(); + m_destroy_ptr_sp.reset(); + m_promise_ptr_sp.reset(); + + ValueObjectSP valobj_sp = m_backend.GetNonSyntheticValue(); + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + + lldb::addr_t frame_ptr_addr = GetCoroFramePtrFromHandle(valobj_sp); + if (frame_ptr_addr == 0 || frame_ptr_addr == LLDB_INVALID_ADDRESS) + return lldb::ChildCacheState::eRefetch; + + auto ts = valobj_sp->GetCompilerType().GetTypeSystem(); + auto ast_ctx = ts.dyn_cast_or_null<TypeSystemClang>(); + if (!ast_ctx) + return lldb::ChildCacheState::eRefetch; + + // Create the `resume` and `destroy` children. + lldb::TargetSP target_sp = m_backend.GetTargetSP(); + auto &exe_ctx = m_backend.GetExecutionContextRef(); + lldb::ProcessSP process_sp = target_sp->GetProcessSP(); + auto ptr_size = process_sp->GetAddressByteSize(); + CompilerType void_type = ast_ctx->GetBasicType(lldb::eBasicTypeVoid); + CompilerType coro_func_type = ast_ctx->CreateFunctionType( + /*result_type=*/void_type, /*args=*/&void_type, /*num_args=*/1, + /*is_variadic=*/false, /*qualifiers=*/0); + CompilerType coro_func_ptr_type = coro_func_type.GetPointerType(); + m_resume_ptr_sp = CreateValueObjectFromAddress( + "resume", frame_ptr_addr + 0 * ptr_size, exe_ctx, coro_func_ptr_type); + lldbassert(m_resume_ptr_sp); + m_destroy_ptr_sp = CreateValueObjectFromAddress( + "destroy", frame_ptr_addr + 1 * ptr_size, exe_ctx, coro_func_ptr_type); + lldbassert(m_destroy_ptr_sp); + + // Get the `promise_type` from the template argument + CompilerType promise_type( + valobj_sp->GetCompilerType().GetTypeTemplateArgument(0)); + if (!promise_type) + return lldb::ChildCacheState::eRefetch; + + // Try to infer the promise_type if it was type-erased + if (promise_type.IsVoidType()) { + if (Function *destroy_func = + ExtractDestroyFunction(target_sp, frame_ptr_addr)) { + if (CompilerType inferred_type = InferPromiseType(*destroy_func)) { + promise_type = inferred_type; + } + } + } + + // If we don't know the promise type, we don't display the `promise` member. + // `CreateValueObjectFromAddress` below would fail for `void` types. + if (promise_type.IsVoidType()) { + return lldb::ChildCacheState::eRefetch; + } + + // Add the `promise` member. We intentionally add `promise` as a pointer type + // instead of a value type, and don't automatically dereference this pointer. + // We do so to avoid potential very deep recursion in case there is a cycle + // formed between `std::coroutine_handle`s and their promises. + lldb::ValueObjectSP promise = CreateValueObjectFromAddress( + "promise", frame_ptr_addr + 2 * ptr_size, exe_ctx, promise_type); + Status error; + lldb::ValueObjectSP promisePtr = promise->AddressOf(error); + if (error.Success()) + m_promise_ptr_sp = promisePtr->Clone(ConstString("promise")); + + return lldb::ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t StdlibCoroutineHandleSyntheticFrontEnd::GetIndexOfChildWithName( + ConstString name) { + if (!m_resume_ptr_sp || !m_destroy_ptr_sp) + return UINT32_MAX; + + if (name == ConstString("resume")) + return 0; + if (name == ConstString("destroy")) + return 1; + if (name == ConstString("promise_ptr") && m_promise_ptr_sp) + return 2; + + return UINT32_MAX; +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new StdlibCoroutineHandleSyntheticFrontEnd(valobj_sp) + : nullptr); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/Coroutines.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/Coroutines.h new file mode 100644 index 000000000000..1d4bc65e2637 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/Coroutines.h @@ -0,0 +1,60 @@ +//===-- Coroutines.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_CPLUSPLUS_COROUTINES_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_COROUTINES_H + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/Utility/Stream.h" + +namespace lldb_private { + +namespace formatters { + +/// Summary provider for `std::coroutine_handle<T>` from libc++, libstdc++ and +/// MSVC STL. +bool StdlibCoroutineHandleSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +/// Synthetic children frontend for `std::coroutine_handle<promise_type>` from +/// libc++, libstdc++ and MSVC STL. Shows the compiler-generated `resume` and +/// `destroy` function pointers as well as the `promise`, if the promise type +/// is `promise_type != void`. +class StdlibCoroutineHandleSyntheticFrontEnd + : public SyntheticChildrenFrontEnd { +public: + StdlibCoroutineHandleSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~StdlibCoroutineHandleSyntheticFrontEnd() 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: + lldb::ValueObjectSP m_resume_ptr_sp; + lldb::ValueObjectSP m_destroy_ptr_sp; + lldb::ValueObjectSP m_promise_ptr_sp; +}; + +SyntheticChildrenFrontEnd * +StdlibCoroutineHandleSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +} // namespace formatters +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_COROUTINES_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp new file mode 100644 index 000000000000..6781c96210a4 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp @@ -0,0 +1,214 @@ +//===-- CxxStringTypes.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 "CxxStringTypes.h" + +#include "llvm/Support/ConvertUTF.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/StringPrinter.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/Host/Time.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" + +#include <algorithm> +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +using StringElementType = StringPrinter::StringElementType; + +static constexpr std::pair<const char *, Format> +getElementTraits(StringElementType ElemType) { + switch (ElemType) { + case StringElementType::UTF8: + return std::make_pair("u8", lldb::eFormatUnicode8); + case StringElementType::UTF16: + return std::make_pair("u", lldb::eFormatUnicode16); + case StringElementType::UTF32: + return std::make_pair("U", lldb::eFormatUnicode32); + default: + return std::make_pair(nullptr, lldb::eFormatInvalid); + } +} + +template <StringElementType ElemType> +static bool CharStringSummaryProvider(ValueObject &valobj, Stream &stream) { + Address valobj_addr = GetArrayAddressOrPointerValue(valobj); + if (!valobj_addr.IsValid()) + return false; + + StringPrinter::ReadStringAndDumpToStreamOptions options(valobj); + options.SetLocation(valobj_addr); + options.SetTargetSP(valobj.GetTargetSP()); + options.SetStream(&stream); + options.SetPrefixToken(getElementTraits(ElemType).first); + + if (!StringPrinter::ReadStringAndDumpToStream<ElemType>(options)) + stream.Printf("Summary Unavailable"); + + return true; +} + +template <StringElementType ElemType> +static bool CharSummaryProvider(ValueObject &valobj, Stream &stream) { + DataExtractor data; + Status error; + valobj.GetData(data, error); + + if (error.Fail()) + return false; + + std::string value; + StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); + + constexpr auto ElemTraits = getElementTraits(ElemType); + valobj.GetValueAsCString(ElemTraits.second, value); + + if (!value.empty()) + stream.Printf("%s ", value.c_str()); + + options.SetData(std::move(data)); + options.SetStream(&stream); + options.SetPrefixToken(ElemTraits.first); + options.SetQuote('\''); + options.SetSourceSize(1); + options.SetBinaryZeroIsTerminator(false); + + return StringPrinter::ReadBufferAndDumpToStream<ElemType>(options); +} + +bool lldb_private::formatters::Char8StringSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + return CharStringSummaryProvider<StringElementType::UTF8>(valobj, stream); +} + +bool lldb_private::formatters::Char16StringSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + return CharStringSummaryProvider<StringElementType::UTF16>(valobj, stream); +} + +bool lldb_private::formatters::Char32StringSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + return CharStringSummaryProvider<StringElementType::UTF32>(valobj, stream); +} + +bool lldb_private::formatters::WCharStringSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + Address valobj_addr = GetArrayAddressOrPointerValue(valobj); + if (!valobj_addr.IsValid()) + return false; + + // Get a wchar_t basic type from the current type system + CompilerType wchar_compiler_type = + valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeWChar); + + if (!wchar_compiler_type) + return false; + + // Safe to pass nullptr for exe_scope here. + std::optional<uint64_t> size = wchar_compiler_type.GetBitSize(nullptr); + if (!size) + return false; + const uint32_t wchar_size = *size; + + StringPrinter::ReadStringAndDumpToStreamOptions options(valobj); + options.SetLocation(valobj_addr); + options.SetTargetSP(valobj.GetTargetSP()); + options.SetStream(&stream); + options.SetPrefixToken("L"); + + switch (wchar_size) { + case 8: + return StringPrinter::ReadStringAndDumpToStream<StringElementType::UTF8>( + options); + case 16: + return StringPrinter::ReadStringAndDumpToStream<StringElementType::UTF16>( + options); + case 32: + return StringPrinter::ReadStringAndDumpToStream<StringElementType::UTF32>( + options); + default: + stream.Printf("size for wchar_t is not valid"); + return true; + } + return true; +} + +bool lldb_private::formatters::Char8SummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + return CharSummaryProvider<StringElementType::UTF8>(valobj, stream); +} + +bool lldb_private::formatters::Char16SummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + return CharSummaryProvider<StringElementType::UTF16>(valobj, stream); +} + +bool lldb_private::formatters::Char32SummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + return CharSummaryProvider<StringElementType::UTF32>(valobj, stream); +} + +bool lldb_private::formatters::WCharSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + DataExtractor data; + Status error; + valobj.GetData(data, error); + + if (error.Fail()) + return false; + + // Get a wchar_t basic type from the current type system + CompilerType wchar_compiler_type = + valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeWChar); + + if (!wchar_compiler_type) + return false; + + // Safe to pass nullptr for exe_scope here. + std::optional<uint64_t> size = wchar_compiler_type.GetBitSize(nullptr); + if (!size) + return false; + const uint32_t wchar_size = *size; + + StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); + options.SetData(std::move(data)); + options.SetStream(&stream); + options.SetPrefixToken("L"); + options.SetQuote('\''); + options.SetSourceSize(1); + options.SetBinaryZeroIsTerminator(false); + + switch (wchar_size) { + case 8: + return StringPrinter::ReadBufferAndDumpToStream<StringElementType::UTF8>( + options); + case 16: + return StringPrinter::ReadBufferAndDumpToStream<StringElementType::UTF16>( + options); + case 32: + return StringPrinter::ReadBufferAndDumpToStream<StringElementType::UTF32>( + options); + default: + stream.Printf("size for wchar_t is not valid"); + return true; + } + return true; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.h new file mode 100644 index 000000000000..2713ded45929 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.h @@ -0,0 +1,49 @@ +//===-- CxxStringTypes.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_CPLUSPLUS_CXXSTRINGTYPES_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_CXXSTRINGTYPES_H + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/Utility/Stream.h" + +namespace lldb_private { +namespace formatters { +bool Char8StringSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // char8_t* + +bool Char16StringSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // char16_t* and unichar* + +bool Char32StringSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // char32_t* + +bool WCharStringSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // wchar_t* + +bool Char8SummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // char8_t + +bool Char16SummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // char16_t and unichar + +bool Char32SummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // char32_t + +bool WCharSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // wchar_t + +} // namespace formatters +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_CXXSTRINGTYPES_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/Generic.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/Generic.h new file mode 100644 index 000000000000..bfb28bebf90b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/Generic.h @@ -0,0 +1,25 @@ +//===-- LibCxx.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_CPLUSPLUS_GENERIC_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_GENERIC_H + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/Utility/Stream.h" + +namespace lldb_private { +namespace formatters { + +bool GenericOptionalSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +} // namespace formatters +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_GENERIC_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp new file mode 100644 index 000000000000..33955dccb6cc --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp @@ -0,0 +1,154 @@ +//===-- GenericBitset.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 "LibCxx.h" +#include "LibStdcpp.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Target/Target.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; + +namespace { + +/// This class can be used for handling bitsets from both libcxx and libstdcpp. +class GenericBitsetFrontEnd : public SyntheticChildrenFrontEnd { +public: + enum class StdLib { + LibCxx, + LibStdcpp, + }; + + GenericBitsetFrontEnd(ValueObject &valobj, StdLib stdlib); + + size_t GetIndexOfChildWithName(ConstString name) override { + return formatters::ExtractIndexFromString(name.GetCString()); + } + + bool MightHaveChildren() override { return true; } + lldb::ChildCacheState Update() override; + llvm::Expected<uint32_t> CalculateNumChildren() override { + return m_elements.size(); + } + ValueObjectSP GetChildAtIndex(uint32_t idx) override; + +private: + llvm::StringRef GetDataContainerMemberName(); + + // The lifetime of a ValueObject and all its derivative ValueObjects + // (children, clones, etc.) is managed by a ClusterManager. These + // objects are only destroyed when every shared pointer to any of them + // is destroyed, so we must not store a shared pointer to any ValueObject + // derived from our backend ValueObject (since we're in the same cluster). + // Value objects created from raw data (i.e. in a different cluster) must + // be referenced via shared pointer to keep them alive, however. + std::vector<ValueObjectSP> m_elements; + ValueObject *m_first = nullptr; + CompilerType m_bool_type; + ByteOrder m_byte_order = eByteOrderInvalid; + uint8_t m_byte_size = 0; + StdLib m_stdlib; +}; +} // namespace + +GenericBitsetFrontEnd::GenericBitsetFrontEnd(ValueObject &valobj, StdLib stdlib) + : SyntheticChildrenFrontEnd(valobj), m_stdlib(stdlib) { + m_bool_type = valobj.GetCompilerType().GetBasicTypeFromAST(eBasicTypeBool); + if (auto target_sp = m_backend.GetTargetSP()) { + m_byte_order = target_sp->GetArchitecture().GetByteOrder(); + m_byte_size = target_sp->GetArchitecture().GetAddressByteSize(); + Update(); + } +} + +llvm::StringRef GenericBitsetFrontEnd::GetDataContainerMemberName() { + static constexpr llvm::StringLiteral s_libcxx_case("__first_"); + static constexpr llvm::StringLiteral s_libstdcpp_case("_M_w"); + switch (m_stdlib) { + case StdLib::LibCxx: + return s_libcxx_case; + case StdLib::LibStdcpp: + return s_libstdcpp_case; + } + llvm_unreachable("Unknown StdLib enum"); +} + +lldb::ChildCacheState GenericBitsetFrontEnd::Update() { + m_elements.clear(); + m_first = nullptr; + + TargetSP target_sp = m_backend.GetTargetSP(); + if (!target_sp) + return lldb::ChildCacheState::eRefetch; + + size_t size = 0; + + if (auto arg = m_backend.GetCompilerType().GetIntegralTemplateArgument(0)) + size = arg->value.getLimitedValue(); + + m_elements.assign(size, ValueObjectSP()); + m_first = + m_backend.GetChildMemberWithName(GetDataContainerMemberName()).get(); + return lldb::ChildCacheState::eRefetch; +} + +ValueObjectSP GenericBitsetFrontEnd::GetChildAtIndex(uint32_t idx) { + if (idx >= m_elements.size() || !m_first) + return ValueObjectSP(); + + if (m_elements[idx]) + return m_elements[idx]; + + ExecutionContext ctx = m_backend.GetExecutionContextRef().Lock(false); + CompilerType type; + ValueObjectSP chunk; + // For small bitsets __first_ is not an array, but a plain size_t. + if (m_first->GetCompilerType().IsArrayType(&type)) { + std::optional<uint64_t> bit_size = + type.GetBitSize(ctx.GetBestExecutionContextScope()); + if (!bit_size || *bit_size == 0) + return {}; + chunk = m_first->GetChildAtIndex(idx / *bit_size); + } else { + type = m_first->GetCompilerType(); + chunk = m_first->GetSP(); + } + if (!type || !chunk) + return {}; + + std::optional<uint64_t> bit_size = + type.GetBitSize(ctx.GetBestExecutionContextScope()); + if (!bit_size || *bit_size == 0) + return {}; + size_t chunk_idx = idx % *bit_size; + uint8_t value = !!(chunk->GetValueAsUnsigned(0) & (uint64_t(1) << chunk_idx)); + DataExtractor data(&value, sizeof(value), m_byte_order, m_byte_size); + + m_elements[idx] = CreateValueObjectFromData(llvm::formatv("[{0}]", idx).str(), + data, ctx, m_bool_type); + + return m_elements[idx]; +} + +SyntheticChildrenFrontEnd *formatters::LibStdcppBitsetSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new GenericBitsetFrontEnd(*valobj_sp, + GenericBitsetFrontEnd::StdLib::LibStdcpp); + return nullptr; +} + +SyntheticChildrenFrontEnd *formatters::LibcxxBitsetSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new GenericBitsetFrontEnd(*valobj_sp, + GenericBitsetFrontEnd::StdLib::LibCxx); + return nullptr; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp new file mode 100644 index 000000000000..23756de7f1e6 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp @@ -0,0 +1,138 @@ +//===-- GenericOptional.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 "Generic.h" +#include "LibCxx.h" +#include "LibStdcpp.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +bool lldb_private::formatters::GenericOptionalSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + stream.Printf(" Has Value=%s ", + valobj.GetNumChildrenIgnoringErrors() == 0 ? "false" : "true"); + + return true; +} + +// Synthetic Children Provider +namespace { + +class GenericOptionalFrontend : public SyntheticChildrenFrontEnd { +public: + enum class StdLib { + LibCxx, + LibStdcpp, + }; + + GenericOptionalFrontend(ValueObject &valobj, StdLib stdlib); + + size_t GetIndexOfChildWithName(ConstString name) override { + return formatters::ExtractIndexFromString(name.GetCString()); + } + + bool MightHaveChildren() override { return true; } + llvm::Expected<uint32_t> CalculateNumChildren() override { + return m_has_value ? 1U : 0U; + } + + ValueObjectSP GetChildAtIndex(uint32_t idx) override; + lldb::ChildCacheState Update() override; + +private: + bool m_has_value = false; + StdLib m_stdlib; +}; + +} // namespace + +GenericOptionalFrontend::GenericOptionalFrontend(ValueObject &valobj, + StdLib stdlib) + : SyntheticChildrenFrontEnd(valobj), m_stdlib(stdlib) { + if (auto target_sp = m_backend.GetTargetSP()) { + Update(); + } +} + +lldb::ChildCacheState GenericOptionalFrontend::Update() { + ValueObjectSP engaged_sp; + + if (m_stdlib == StdLib::LibCxx) + engaged_sp = m_backend.GetChildMemberWithName("__engaged_"); + else if (m_stdlib == StdLib::LibStdcpp) + engaged_sp = m_backend.GetChildMemberWithName("_M_payload") + ->GetChildMemberWithName("_M_engaged"); + + if (!engaged_sp) + return lldb::ChildCacheState::eRefetch; + + // _M_engaged/__engaged is a bool flag and is true if the optional contains a + // value. Converting it to unsigned gives us a size of 1 if it contains a + // value and 0 if not. + m_has_value = engaged_sp->GetValueAsUnsigned(0) != 0; + + return lldb::ChildCacheState::eRefetch; +} + +ValueObjectSP GenericOptionalFrontend::GetChildAtIndex(uint32_t _idx) { + if (!m_has_value) + return ValueObjectSP(); + + ValueObjectSP val_sp; + + if (m_stdlib == StdLib::LibCxx) + // __val_ contains the underlying value of an optional if it has one. + // Currently because it is part of an anonymous union + // GetChildMemberWithName() does not peer through and find it unless we are + // at the parent itself. We can obtain the parent through __engaged_. + val_sp = m_backend.GetChildMemberWithName("__engaged_") + ->GetParent() + ->GetChildAtIndex(0) + ->GetChildMemberWithName("__val_"); + else if (m_stdlib == StdLib::LibStdcpp) { + val_sp = m_backend.GetChildMemberWithName("_M_payload") + ->GetChildMemberWithName("_M_payload"); + + // In some implementations, _M_value contains the underlying value of an + // optional, and in other versions, it's in the payload member. + ValueObjectSP candidate = val_sp->GetChildMemberWithName("_M_value"); + if (candidate) + val_sp = candidate; + } + + if (!val_sp) + return ValueObjectSP(); + + CompilerType holder_type = val_sp->GetCompilerType(); + + if (!holder_type) + return ValueObjectSP(); + + return val_sp->Clone(ConstString("Value")); +} + +SyntheticChildrenFrontEnd * +formatters::LibStdcppOptionalSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new GenericOptionalFrontend( + *valobj_sp, GenericOptionalFrontend::StdLib::LibStdcpp); + return nullptr; +} + +SyntheticChildrenFrontEnd *formatters::LibcxxOptionalSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new GenericOptionalFrontend(*valobj_sp, + GenericOptionalFrontend::StdLib::LibCxx); + return nullptr; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp new file mode 100644 index 000000000000..feaa51a96843 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp @@ -0,0 +1,939 @@ +//===-- LibCxx.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 "LibCxx.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/FormatEntity.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/DataFormatters/VectorIterator.h" +#include "lldb/Target/SectionLoadList.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" + +#include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/lldb-enumerations.h" +#include <optional> +#include <tuple> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +lldb::ValueObjectSP lldb_private::formatters::GetChildMemberWithName( + ValueObject &obj, llvm::ArrayRef<ConstString> alternative_names) { + for (ConstString name : alternative_names) { + lldb::ValueObjectSP child_sp = obj.GetChildMemberWithName(name); + + if (child_sp) + return child_sp; + } + return {}; +} + +lldb::ValueObjectSP +lldb_private::formatters::GetFirstValueOfLibCXXCompressedPair( + ValueObject &pair) { + ValueObjectSP value; + ValueObjectSP first_child = pair.GetChildAtIndex(0); + if (first_child) + value = first_child->GetChildMemberWithName("__value_"); + if (!value) { + // pre-r300140 member name + value = pair.GetChildMemberWithName("__first_"); + } + return value; +} + +lldb::ValueObjectSP +lldb_private::formatters::GetSecondValueOfLibCXXCompressedPair( + ValueObject &pair) { + ValueObjectSP value; + if (pair.GetNumChildrenIgnoringErrors() > 1) { + ValueObjectSP second_child = pair.GetChildAtIndex(1); + if (second_child) { + value = second_child->GetChildMemberWithName("__value_"); + } + } + if (!value) { + // pre-r300140 member name + value = pair.GetChildMemberWithName("__second_"); + } + return value; +} + +bool lldb_private::formatters::LibcxxFunctionSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + + ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); + + if (!valobj_sp) + return false; + + ExecutionContext exe_ctx(valobj_sp->GetExecutionContextRef()); + Process *process = exe_ctx.GetProcessPtr(); + + if (process == nullptr) + return false; + + CPPLanguageRuntime *cpp_runtime = CPPLanguageRuntime::Get(*process); + + if (!cpp_runtime) + return false; + + CPPLanguageRuntime::LibCppStdFunctionCallableInfo callable_info = + cpp_runtime->FindLibCppStdFunctionCallableInfo(valobj_sp); + + switch (callable_info.callable_case) { + case CPPLanguageRuntime::LibCppStdFunctionCallableCase::Invalid: + stream.Printf(" __f_ = %" PRIu64, callable_info.member_f_pointer_value); + return false; + break; + case CPPLanguageRuntime::LibCppStdFunctionCallableCase::Lambda: + stream.Printf( + " Lambda in File %s at Line %u", + callable_info.callable_line_entry.GetFile().GetFilename().GetCString(), + callable_info.callable_line_entry.line); + break; + case CPPLanguageRuntime::LibCppStdFunctionCallableCase::CallableObject: + stream.Printf( + " Function in File %s at Line %u", + callable_info.callable_line_entry.GetFile().GetFilename().GetCString(), + callable_info.callable_line_entry.line); + break; + case CPPLanguageRuntime::LibCppStdFunctionCallableCase::FreeOrMemberFunction: + stream.Printf(" Function = %s ", + callable_info.callable_symbol.GetName().GetCString()); + break; + } + + return true; +} + +bool lldb_private::formatters::LibcxxSmartPointerSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); + if (!valobj_sp) + return false; + ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName("__ptr_")); + ValueObjectSP count_sp( + valobj_sp->GetChildAtNamePath({"__cntrl_", "__shared_owners_"})); + ValueObjectSP weakcount_sp( + valobj_sp->GetChildAtNamePath({"__cntrl_", "__shared_weak_owners_"})); + + if (!ptr_sp) + return false; + + if (ptr_sp->GetValueAsUnsigned(0) == 0) { + stream.Printf("nullptr"); + return true; + } else { + bool print_pointee = false; + Status error; + ValueObjectSP pointee_sp = ptr_sp->Dereference(error); + if (pointee_sp && error.Success()) { + if (pointee_sp->DumpPrintableRepresentation( + stream, ValueObject::eValueObjectRepresentationStyleSummary, + lldb::eFormatInvalid, + ValueObject::PrintableRepresentationSpecialCases::eDisable, + false)) + print_pointee = true; + } + if (!print_pointee) + stream.Printf("ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(0)); + } + + if (count_sp) + stream.Printf(" strong=%" PRIu64, 1 + count_sp->GetValueAsUnsigned(0)); + + if (weakcount_sp) + stream.Printf(" weak=%" PRIu64, 1 + weakcount_sp->GetValueAsUnsigned(0)); + + return true; +} + +bool lldb_private::formatters::LibcxxUniquePointerSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); + if (!valobj_sp) + return false; + + ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName("__ptr_")); + if (!ptr_sp) + return false; + + ptr_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp); + if (!ptr_sp) + return false; + + if (ptr_sp->GetValueAsUnsigned(0) == 0) { + stream.Printf("nullptr"); + return true; + } else { + bool print_pointee = false; + Status error; + ValueObjectSP pointee_sp = ptr_sp->Dereference(error); + if (pointee_sp && error.Success()) { + if (pointee_sp->DumpPrintableRepresentation( + stream, ValueObject::eValueObjectRepresentationStyleSummary, + lldb::eFormatInvalid, + ValueObject::PrintableRepresentationSpecialCases::eDisable, + false)) + print_pointee = true; + } + if (!print_pointee) + stream.Printf("ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(0)); + } + + return true; +} + +/* + (lldb) fr var ibeg --raw --ptr-depth 1 -T + (std::__1::__wrap_iter<int *>) ibeg = { + (std::__1::__wrap_iter<int *>::iterator_type) __i = 0x00000001001037a0 { + (int) *__i = 1 + } + } +*/ + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new VectorIteratorSyntheticFrontEnd( + valobj_sp, {ConstString("__i_"), ConstString("__i")}) + : nullptr); +} + +lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: + LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_cntrl(nullptr) { + if (valobj_sp) + Update(); +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibcxxSharedPtrSyntheticFrontEnd::CalculateNumChildren() { + return (m_cntrl ? 1 : 0); +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + if (!m_cntrl) + return lldb::ValueObjectSP(); + + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return lldb::ValueObjectSP(); + + if (idx == 0) + return valobj_sp->GetChildMemberWithName("__ptr_"); + + if (idx == 1) { + if (auto ptr_sp = valobj_sp->GetChildMemberWithName("__ptr_")) { + Status status; + auto value_type_sp = + valobj_sp->GetCompilerType() + .GetTypeTemplateArgument(0).GetPointerType(); + ValueObjectSP cast_ptr_sp = ptr_sp->Cast(value_type_sp); + ValueObjectSP value_sp = cast_ptr_sp->Dereference(status); + if (status.Success()) { + return value_sp; + } + } + } + + return lldb::ValueObjectSP(); +} + +lldb::ChildCacheState +lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::Update() { + m_cntrl = nullptr; + + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + + TargetSP target_sp(valobj_sp->GetTargetSP()); + if (!target_sp) + return lldb::ChildCacheState::eRefetch; + + lldb::ValueObjectSP cntrl_sp(valobj_sp->GetChildMemberWithName("__cntrl_")); + + m_cntrl = cntrl_sp.get(); // need to store the raw pointer to avoid a circular + // dependency + return lldb::ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (name == "__ptr_") + return 0; + if (name == "$$dereference$$") + return 1; + return UINT32_MAX; +} + +lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: + ~LibcxxSharedPtrSyntheticFrontEnd() = default; + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibcxxSharedPtrSyntheticFrontEnd(valobj_sp) + : nullptr); +} + +lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: + LibcxxUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: + ~LibcxxUniquePtrSyntheticFrontEnd() = default; + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibcxxUniquePtrSyntheticFrontEnd(valobj_sp) + : nullptr); +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibcxxUniquePtrSyntheticFrontEnd::CalculateNumChildren() { + if (m_value_ptr_sp) + return m_deleter_sp ? 2 : 1; + return 0; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + if (!m_value_ptr_sp) + return lldb::ValueObjectSP(); + + if (idx == 0) + return m_value_ptr_sp; + + if (idx == 1) + return m_deleter_sp; + + if (idx == 2) { + Status status; + auto value_sp = m_value_ptr_sp->Dereference(status); + if (status.Success()) { + return value_sp; + } + } + + return lldb::ValueObjectSP(); +} + +lldb::ChildCacheState +lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::Update() { + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + + ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName("__ptr_")); + if (!ptr_sp) + return lldb::ChildCacheState::eRefetch; + + // Retrieve the actual pointer and the deleter, and clone them to give them + // user-friendly names. + ValueObjectSP value_pointer_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp); + if (value_pointer_sp) + m_value_ptr_sp = value_pointer_sp->Clone(ConstString("pointer")); + + ValueObjectSP deleter_sp = GetSecondValueOfLibCXXCompressedPair(*ptr_sp); + if (deleter_sp) + m_deleter_sp = deleter_sp->Clone(ConstString("deleter")); + + return lldb::ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (name == "pointer") + return 0; + if (name == "deleter") + return 1; + if (name == "$$dereference$$") + return 2; + return UINT32_MAX; +} + +bool lldb_private::formatters::LibcxxContainerSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + if (valobj.IsPointerType()) { + uint64_t value = valobj.GetValueAsUnsigned(0); + if (!value) + return false; + stream.Printf("0x%016" PRIx64 " ", value); + } + return FormatEntity::FormatStringRef("size=${svar%#}", stream, nullptr, + nullptr, nullptr, &valobj, false, false); +} + +/// The field layout in a libc++ string (cap, side, data or data, size, cap). +namespace { +enum class StringLayout { CSD, DSC }; +} + +/// Determine the size in bytes of \p valobj (a libc++ std::string object) and +/// extract its data payload. Return the size + payload pair. +// TODO: Support big-endian architectures. +static std::optional<std::pair<uint64_t, ValueObjectSP>> +ExtractLibcxxStringInfo(ValueObject &valobj) { + ValueObjectSP valobj_r_sp = valobj.GetChildMemberWithName("__r_"); + if (!valobj_r_sp || !valobj_r_sp->GetError().Success()) + return {}; + + // __r_ is a compressed_pair of the actual data and the allocator. The data we + // want is in the first base class. + ValueObjectSP valobj_r_base_sp = valobj_r_sp->GetChildAtIndex(0); + if (!valobj_r_base_sp) + return {}; + + ValueObjectSP valobj_rep_sp = + valobj_r_base_sp->GetChildMemberWithName("__value_"); + if (!valobj_rep_sp) + return {}; + + ValueObjectSP l = valobj_rep_sp->GetChildMemberWithName("__l"); + if (!l) + return {}; + + StringLayout layout = l->GetIndexOfChildWithName("__data_") == 0 + ? StringLayout::DSC + : StringLayout::CSD; + + bool short_mode = false; // this means the string is in short-mode and the + // data is stored inline + bool using_bitmasks = true; // Whether the class uses bitmasks for the mode + // flag (pre-D123580). + uint64_t size; + uint64_t size_mode_value = 0; + + ValueObjectSP short_sp = valobj_rep_sp->GetChildMemberWithName("__s"); + if (!short_sp) + return {}; + + ValueObjectSP is_long = short_sp->GetChildMemberWithName("__is_long_"); + ValueObjectSP size_sp = short_sp->GetChildMemberWithName("__size_"); + if (!size_sp) + return {}; + + if (is_long) { + using_bitmasks = false; + short_mode = !is_long->GetValueAsUnsigned(/*fail_value=*/0); + size = size_sp->GetValueAsUnsigned(/*fail_value=*/0); + } else { + // The string mode is encoded in the size field. + size_mode_value = size_sp->GetValueAsUnsigned(0); + uint8_t mode_mask = layout == StringLayout::DSC ? 0x80 : 1; + short_mode = (size_mode_value & mode_mask) == 0; + } + + if (short_mode) { + ValueObjectSP location_sp = short_sp->GetChildMemberWithName("__data_"); + if (using_bitmasks) + size = (layout == StringLayout::DSC) ? size_mode_value + : ((size_mode_value >> 1) % 256); + + if (!location_sp) + return {}; + + // When the small-string optimization takes place, the data must fit in the + // inline string buffer (23 bytes on x86_64/Darwin). If it doesn't, it's + // likely that the string isn't initialized and we're reading garbage. + ExecutionContext exe_ctx(location_sp->GetExecutionContextRef()); + const std::optional<uint64_t> max_bytes = + location_sp->GetCompilerType().GetByteSize( + exe_ctx.GetBestExecutionContextScope()); + if (!max_bytes || size > *max_bytes) + return {}; + + return std::make_pair(size, location_sp); + } + + // we can use the layout_decider object as the data pointer + ValueObjectSP location_sp = l->GetChildMemberWithName("__data_"); + ValueObjectSP size_vo = l->GetChildMemberWithName("__size_"); + ValueObjectSP capacity_vo = l->GetChildMemberWithName("__cap_"); + if (!size_vo || !location_sp || !capacity_vo) + return {}; + size = size_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET); + uint64_t capacity = capacity_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET); + if (!using_bitmasks && layout == StringLayout::CSD) + capacity *= 2; + if (size == LLDB_INVALID_OFFSET || capacity == LLDB_INVALID_OFFSET || + capacity < size) + return {}; + return std::make_pair(size, location_sp); +} + +static bool +LibcxxWStringSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, + ValueObjectSP location_sp, size_t size) { + if (size == 0) { + stream.Printf("L\"\""); + return true; + } + if (!location_sp) + return false; + + StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); + if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) { + const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary(); + if (size > max_size) { + size = max_size; + options.SetIsTruncated(true); + } + } + + DataExtractor extractor; + const size_t bytes_read = location_sp->GetPointeeData(extractor, 0, size); + if (bytes_read < size) + return false; + + // std::wstring::size() is measured in 'characters', not bytes + TypeSystemClangSP scratch_ts_sp = + ScratchTypeSystemClang::GetForTarget(*valobj.GetTargetSP()); + if (!scratch_ts_sp) + return false; + + auto wchar_t_size = + scratch_ts_sp->GetBasicType(lldb::eBasicTypeWChar).GetByteSize(nullptr); + if (!wchar_t_size) + return false; + + options.SetData(std::move(extractor)); + options.SetStream(&stream); + options.SetPrefixToken("L"); + options.SetQuote('"'); + options.SetSourceSize(size); + options.SetBinaryZeroIsTerminator(false); + + switch (*wchar_t_size) { + case 1: + return StringPrinter::ReadBufferAndDumpToStream< + lldb_private::formatters::StringPrinter::StringElementType::UTF8>( + options); + break; + + case 2: + return StringPrinter::ReadBufferAndDumpToStream< + lldb_private::formatters::StringPrinter::StringElementType::UTF16>( + options); + break; + + case 4: + return StringPrinter::ReadBufferAndDumpToStream< + lldb_private::formatters::StringPrinter::StringElementType::UTF32>( + options); + } + return false; +} + +bool lldb_private::formatters::LibcxxWStringSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + auto string_info = ExtractLibcxxStringInfo(valobj); + if (!string_info) + return false; + uint64_t size; + ValueObjectSP location_sp; + std::tie(size, location_sp) = *string_info; + + return ::LibcxxWStringSummaryProvider(valobj, stream, summary_options, + location_sp, size); +} + +template <StringPrinter::StringElementType element_type> +static bool +LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, + std::string prefix_token, ValueObjectSP location_sp, + uint64_t size) { + + if (size == 0) { + stream.Printf("\"\""); + return true; + } + + if (!location_sp) + return false; + + StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); + + if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) { + const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary(); + if (size > max_size) { + size = max_size; + options.SetIsTruncated(true); + } + } + + { + DataExtractor extractor; + const size_t bytes_read = location_sp->GetPointeeData(extractor, 0, size); + if (bytes_read < size) + return false; + + options.SetData(std::move(extractor)); + } + options.SetStream(&stream); + if (prefix_token.empty()) + options.SetPrefixToken(nullptr); + else + options.SetPrefixToken(prefix_token); + options.SetQuote('"'); + options.SetSourceSize(size); + options.SetBinaryZeroIsTerminator(false); + return StringPrinter::ReadBufferAndDumpToStream<element_type>(options); +} + +template <StringPrinter::StringElementType element_type> +static bool +LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, + std::string prefix_token) { + auto string_info = ExtractLibcxxStringInfo(valobj); + if (!string_info) + return false; + uint64_t size; + ValueObjectSP location_sp; + std::tie(size, location_sp) = *string_info; + + return LibcxxStringSummaryProvider<element_type>( + valobj, stream, summary_options, prefix_token, location_sp, size); +} +template <StringPrinter::StringElementType element_type> +static bool formatStringImpl(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, + std::string prefix_token) { + StreamString scratch_stream; + const bool success = LibcxxStringSummaryProvider<element_type>( + valobj, scratch_stream, summary_options, prefix_token); + if (success) + stream << scratch_stream.GetData(); + else + stream << "Summary Unavailable"; + return true; +} + +bool lldb_private::formatters::LibcxxStringSummaryProviderASCII( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + return formatStringImpl<StringPrinter::StringElementType::ASCII>( + valobj, stream, summary_options, ""); +} + +bool lldb_private::formatters::LibcxxStringSummaryProviderUTF16( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + return formatStringImpl<StringPrinter::StringElementType::UTF16>( + valobj, stream, summary_options, "u"); +} + +bool lldb_private::formatters::LibcxxStringSummaryProviderUTF32( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + return formatStringImpl<StringPrinter::StringElementType::UTF32>( + valobj, stream, summary_options, "U"); +} + +static std::tuple<bool, ValueObjectSP, size_t> +LibcxxExtractStringViewData(ValueObject& valobj) { + auto dataobj = GetChildMemberWithName( + valobj, {ConstString("__data_"), ConstString("__data")}); + auto sizeobj = GetChildMemberWithName( + valobj, {ConstString("__size_"), ConstString("__size")}); + if (!dataobj || !sizeobj) + return std::make_tuple<bool,ValueObjectSP,size_t>(false, {}, {}); + + if (!dataobj->GetError().Success() || !sizeobj->GetError().Success()) + return std::make_tuple<bool,ValueObjectSP,size_t>(false, {}, {}); + + bool success{false}; + uint64_t size = sizeobj->GetValueAsUnsigned(0, &success); + if (!success) + return std::make_tuple<bool,ValueObjectSP,size_t>(false, {}, {}); + + return std::make_tuple(true,dataobj,size); +} + +template <StringPrinter::StringElementType element_type> +static bool formatStringViewImpl(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options, + std::string prefix_token) { + + bool success; + ValueObjectSP dataobj; + size_t size; + std::tie(success, dataobj, size) = LibcxxExtractStringViewData(valobj); + + if (!success) { + stream << "Summary Unavailable"; + return true; + } + + return LibcxxStringSummaryProvider<element_type>( + valobj, stream, summary_options, prefix_token, dataobj, size); +} + +bool lldb_private::formatters::LibcxxStringViewSummaryProviderASCII( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + return formatStringViewImpl<StringPrinter::StringElementType::ASCII>( + valobj, stream, summary_options, ""); +} + +bool lldb_private::formatters::LibcxxStringViewSummaryProviderUTF16( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + return formatStringViewImpl<StringPrinter::StringElementType::UTF16>( + valobj, stream, summary_options, "u"); +} + +bool lldb_private::formatters::LibcxxStringViewSummaryProviderUTF32( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + return formatStringViewImpl<StringPrinter::StringElementType::UTF32>( + valobj, stream, summary_options, "U"); +} + +bool lldb_private::formatters::LibcxxWStringViewSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options) { + + bool success; + ValueObjectSP dataobj; + size_t size; + std::tie(success, dataobj, size) = LibcxxExtractStringViewData(valobj); + + if (!success) { + stream << "Summary Unavailable"; + return true; + } + + return ::LibcxxWStringSummaryProvider(valobj, stream, summary_options, + dataobj, size); +} + +static bool +LibcxxChronoTimePointSecondsSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options, + const char *fmt) { + ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__d_"); + if (!ptr_sp) + return false; + ptr_sp = ptr_sp->GetChildMemberWithName("__rep_"); + if (!ptr_sp) + return false; + +#ifndef _WIN32 + // The date time in the chrono library is valid in the range + // [-32767-01-01T00:00:00Z, 32767-12-31T23:59:59Z]. A 64-bit time_t has a + // larger range, the function strftime is not able to format the entire range + // of time_t. The exact point has not been investigated; it's limited to + // chrono's range. + const std::time_t chrono_timestamp_min = + -1'096'193'779'200; // -32767-01-01T00:00:00Z + const std::time_t chrono_timestamp_max = + 971'890'963'199; // 32767-12-31T23:59:59Z +#else + const std::time_t chrono_timestamp_min = -43'200; // 1969-12-31T12:00:00Z + const std::time_t chrono_timestamp_max = + 32'536'850'399; // 3001-01-19T21:59:59 +#endif + + const std::time_t seconds = ptr_sp->GetValueAsSigned(0); + if (seconds < chrono_timestamp_min || seconds > chrono_timestamp_max) + stream.Printf("timestamp=%" PRId64 " s", static_cast<int64_t>(seconds)); + else { + std::array<char, 128> str; + std::size_t size = + std::strftime(str.data(), str.size(), fmt, gmtime(&seconds)); + if (size == 0) + return false; + + stream.Printf("date/time=%s timestamp=%" PRId64 " s", str.data(), + static_cast<int64_t>(seconds)); + } + + return true; +} + +bool lldb_private::formatters::LibcxxChronoSysSecondsSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + return LibcxxChronoTimePointSecondsSummaryProvider(valobj, stream, options, + "%FT%H:%M:%SZ"); +} + +bool lldb_private::formatters::LibcxxChronoLocalSecondsSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + return LibcxxChronoTimePointSecondsSummaryProvider(valobj, stream, options, + "%FT%H:%M:%S"); +} + +static bool +LibcxxChronoTimepointDaysSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options, + const char *fmt) { + ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__d_"); + if (!ptr_sp) + return false; + ptr_sp = ptr_sp->GetChildMemberWithName("__rep_"); + if (!ptr_sp) + return false; + +#ifndef _WIN32 + // The date time in the chrono library is valid in the range + // [-32767-01-01Z, 32767-12-31Z]. A 32-bit time_t has a larger range, the + // function strftime is not able to format the entire range of time_t. The + // exact point has not been investigated; it's limited to chrono's range. + const int chrono_timestamp_min = -12'687'428; // -32767-01-01Z + const int chrono_timestamp_max = 11'248'737; // 32767-12-31Z +#else + const int chrono_timestamp_min = 0; // 1970-01-01Z + const int chrono_timestamp_max = 376'583; // 3001-01-19Z +#endif + + const int days = ptr_sp->GetValueAsSigned(0); + if (days < chrono_timestamp_min || days > chrono_timestamp_max) + stream.Printf("timestamp=%d days", days); + + else { + const std::time_t seconds = std::time_t(86400) * days; + + std::array<char, 128> str; + std::size_t size = + std::strftime(str.data(), str.size(), fmt, gmtime(&seconds)); + if (size == 0) + return false; + + stream.Printf("date=%s timestamp=%d days", str.data(), days); + } + + return true; +} + +bool lldb_private::formatters::LibcxxChronoSysDaysSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + return LibcxxChronoTimepointDaysSummaryProvider(valobj, stream, options, + "%FZ"); +} + +bool lldb_private::formatters::LibcxxChronoLocalDaysSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + return LibcxxChronoTimepointDaysSummaryProvider(valobj, stream, options, + "%F"); +} + +bool lldb_private::formatters::LibcxxChronoMonthSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + // FIXME: These are the names used in the C++20 ostream operator. Since LLVM + // uses C++17 it's not possible to use the ostream operator directly. + static const std::array<std::string_view, 12> months = { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December"}; + + ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__m_"); + if (!ptr_sp) + return false; + + const unsigned month = ptr_sp->GetValueAsUnsigned(0); + if (month >= 1 && month <= 12) + stream << "month=" << months[month - 1]; + else + stream.Printf("month=%u", month); + + return true; +} + +bool lldb_private::formatters::LibcxxChronoWeekdaySummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + // FIXME: These are the names used in the C++20 ostream operator. Since LLVM + // uses C++17 it's not possible to use the ostream operator directly. + static const std::array<std::string_view, 7> weekdays = { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday"}; + + ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__wd_"); + if (!ptr_sp) + return false; + + const unsigned weekday = ptr_sp->GetValueAsUnsigned(0); + if (weekday < 7) + stream << "weekday=" << weekdays[weekday]; + else + stream.Printf("weekday=%u", weekday); + + return true; +} + +bool lldb_private::formatters::LibcxxChronoYearMonthDaySummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + ValueObjectSP ptr_sp = valobj.GetChildMemberWithName("__y_"); + if (!ptr_sp) + return false; + ptr_sp = ptr_sp->GetChildMemberWithName("__y_"); + if (!ptr_sp) + return false; + int year = ptr_sp->GetValueAsSigned(0); + + ptr_sp = valobj.GetChildMemberWithName("__m_"); + if (!ptr_sp) + return false; + ptr_sp = ptr_sp->GetChildMemberWithName("__m_"); + if (!ptr_sp) + return false; + const unsigned month = ptr_sp->GetValueAsUnsigned(0); + + ptr_sp = valobj.GetChildMemberWithName("__d_"); + if (!ptr_sp) + return false; + ptr_sp = ptr_sp->GetChildMemberWithName("__d_"); + if (!ptr_sp) + return false; + const unsigned day = ptr_sp->GetValueAsUnsigned(0); + + stream << "date="; + if (year < 0) { + stream << '-'; + year = -year; + } + stream.Printf("%04d-%02u-%02u", year, month, day); + + return true; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h new file mode 100644 index 000000000000..5307b5251ca8 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h @@ -0,0 +1,244 @@ +//===-- LibCxx.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_CPLUSPLUS_LIBCXX_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBCXX_H + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/Utility/Stream.h" + +namespace lldb_private { +namespace formatters { + +/// Find a child member of \c obj_sp, trying all alternative names in order. +lldb::ValueObjectSP +GetChildMemberWithName(ValueObject &obj, + llvm::ArrayRef<ConstString> alternative_names); + +lldb::ValueObjectSP GetFirstValueOfLibCXXCompressedPair(ValueObject &pair); +lldb::ValueObjectSP GetSecondValueOfLibCXXCompressedPair(ValueObject &pair); + + +bool LibcxxStringSummaryProviderASCII( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options); // libc++ std::string + +bool LibcxxStringSummaryProviderUTF16( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options); // libc++ std::u16string + +bool LibcxxStringSummaryProviderUTF32( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options); // libc++ std::u32string + +bool LibcxxWStringSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::wstring + +bool LibcxxStringViewSummaryProviderASCII( + ValueObject &valueObj, Stream &stream, + const TypeSummaryOptions &summary_options); // libc++ std::string_view + +bool LibcxxStringViewSummaryProviderUTF16( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options); // libc++ std::u16string_view + +bool LibcxxStringViewSummaryProviderUTF32( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &summary_options); // libc++ std::u32string_view + +bool LibcxxWStringViewSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::wstring_view + +bool LibcxxStdSliceArraySummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::slice_array + +bool LibcxxSmartPointerSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions + &options); // libc++ std::shared_ptr<> and std::weak_ptr<> + +// libc++ std::unique_ptr<> +bool LibcxxUniquePointerSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +bool LibcxxFunctionSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::function<> + +SyntheticChildrenFrontEnd * +LibcxxVectorBoolSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +bool LibcxxContainerSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +/// Formatter for libc++ std::span<>. +bool LibcxxSpanSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +SyntheticChildrenFrontEnd * +LibCxxVectorIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +class LibcxxSharedPtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibcxxSharedPtrSyntheticFrontEnd(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; + + ~LibcxxSharedPtrSyntheticFrontEnd() override; + +private: + ValueObject *m_cntrl; +}; + +class LibcxxUniquePtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibcxxUniquePtrSyntheticFrontEnd(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; + + ~LibcxxUniquePtrSyntheticFrontEnd() override; + +private: + lldb::ValueObjectSP m_value_ptr_sp; + lldb::ValueObjectSP m_deleter_sp; +}; + +SyntheticChildrenFrontEnd * +LibcxxBitsetSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxSharedPtrSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxUniquePtrSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxStdVectorSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxStdValarraySyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxStdSliceArraySyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxStdProxyArraySyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxStdListSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxStdForwardListSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxStdMapSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibCxxMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxStdUnorderedMapSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibCxxUnorderedMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxInitializerListSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd *LibcxxQueueFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd *LibcxxTupleFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxOptionalSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp); + +SyntheticChildrenFrontEnd * +LibcxxVariantFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp); + +SyntheticChildrenFrontEnd * +LibcxxStdSpanSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibcxxStdRangesRefViewSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +bool LibcxxChronoSysSecondsSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::chrono::sys_seconds + +bool LibcxxChronoSysDaysSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::chrono::sys_days + +bool LibcxxChronoLocalSecondsSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::chrono::local_seconds + +bool LibcxxChronoLocalDaysSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::chrono::local_days + +bool LibcxxChronoMonthSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::chrono::month + +bool LibcxxChronoWeekdaySummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::chrono::weekday + +bool LibcxxChronoYearMonthDaySummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::chrono::year_month_day + +} // namespace formatters +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBCXX_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.cpp new file mode 100644 index 000000000000..7f30dc186291 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.cpp @@ -0,0 +1,151 @@ +//===-- LibCxxAtomic.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 "LibCxxAtomic.h" +#include "lldb/DataFormatters/FormattersHelpers.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +// +// We are supporting two versions of libc++ std::atomic +// +// Given std::atomic<int> i; +// +// The previous version of std::atomic was laid out like this +// +// (lldb) frame var -L -R i +// 0x00007ffeefbff9a0: (std::__1::atomic<int>) i = { +// 0x00007ffeefbff9a0: std::__1::__atomic_base<int, true> = { +// 0x00007ffeefbff9a0: std::__1::__atomic_base<int, false> = { +// 0x00007ffeefbff9a0: __a_ = 5 +// } +// } +// } +// +// In this case we need to obtain __a_ and the current version is laid out as so +// +// (lldb) frame var -L -R i +// 0x00007ffeefbff9b0: (std::__1::atomic<int>) i = { +// 0x00007ffeefbff9b0: std::__1::__atomic_base<int, true> = { +// 0x00007ffeefbff9b0: std::__1::__atomic_base<int, false> = { +// 0x00007ffeefbff9b0: __a_ = { +// 0x00007ffeefbff9b0: std::__1::__cxx_atomic_base_impl<int> = { +// 0x00007ffeefbff9b0: __a_value = 5 +// } +// } +// } +// } +//} +// +// In this case we need to obtain __a_value +// +// The below method covers both cases and returns the relevant member as a +// ValueObjectSP +// +ValueObjectSP +lldb_private::formatters::GetLibCxxAtomicValue(ValueObject &valobj) { + ValueObjectSP non_sythetic = valobj.GetNonSyntheticValue(); + if (!non_sythetic) + return {}; + + ValueObjectSP member__a_ = non_sythetic->GetChildMemberWithName("__a_"); + if (!member__a_) + return {}; + + ValueObjectSP member__a_value = + member__a_->GetChildMemberWithName("__a_value"); + if (!member__a_value) + return member__a_; + + return member__a_value; +} + +bool lldb_private::formatters::LibCxxAtomicSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + + if (ValueObjectSP atomic_value = GetLibCxxAtomicValue(valobj)) { + std::string summary; + if (atomic_value->GetSummaryAsCString(summary, options) && + summary.size() > 0) { + stream.Printf("%s", summary.c_str()); + return true; + } + } + + return false; +} + +namespace lldb_private { +namespace formatters { +class LibcxxStdAtomicSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibcxxStdAtomicSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibcxxStdAtomicSyntheticFrontEnd() 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: + ValueObject *m_real_child = nullptr; +}; +} // namespace formatters +} // namespace lldb_private + +lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd:: + LibcxxStdAtomicSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) {} + +lldb::ChildCacheState +lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd::Update() { + ValueObjectSP atomic_value = GetLibCxxAtomicValue(m_backend); + if (atomic_value) + m_real_child = GetLibCxxAtomicValue(m_backend).get(); + + return lldb::ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibcxxStdAtomicSyntheticFrontEnd::CalculateNumChildren() { + return m_real_child ? 1 : 0; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + if (idx == 0) + return m_real_child->GetSP()->Clone(ConstString("Value")); + return nullptr; +} + +size_t lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + return name == "Value" ? 0 : UINT32_MAX; +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibcxxAtomicSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new LibcxxStdAtomicSyntheticFrontEnd(valobj_sp); + return nullptr; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.h new file mode 100644 index 000000000000..6fcceb645c7b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.h @@ -0,0 +1,33 @@ +//===-- LibCxxAtomic.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_CPLUSPLUS_LIBCXXATOMIC_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBCXXATOMIC_H + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/Utility/Stream.h" + +namespace lldb_private { +namespace formatters { + +lldb::ValueObjectSP GetLibCxxAtomicValue(ValueObject &valobj); + +bool LibCxxAtomicSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +SyntheticChildrenFrontEnd * +LibcxxAtomicSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +} // namespace formatters +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBCXXATOMIC_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp new file mode 100644 index 000000000000..bd9c72497664 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp @@ -0,0 +1,119 @@ +//===-- LibCxxInitializerList.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 "LibCxx.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Utility/ConstString.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace lldb_private { +namespace formatters { +class LibcxxInitializerListSyntheticFrontEnd + : public SyntheticChildrenFrontEnd { +public: + LibcxxInitializerListSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibcxxInitializerListSyntheticFrontEnd() 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: + ValueObject *m_start = nullptr; + CompilerType m_element_type; + uint32_t m_element_size = 0; + size_t m_num_elements = 0; +}; +} // namespace formatters +} // namespace lldb_private + +lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd:: + LibcxxInitializerListSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type() { + if (valobj_sp) + Update(); +} + +lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd:: + ~LibcxxInitializerListSyntheticFrontEnd() { + // this needs to stay around because it's a child object who will follow its + // parent's life cycle + // delete m_start; +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibcxxInitializerListSyntheticFrontEnd::CalculateNumChildren() { + m_num_elements = 0; + ValueObjectSP size_sp(m_backend.GetChildMemberWithName("__size_")); + if (size_sp) + m_num_elements = size_sp->GetValueAsUnsigned(0); + return m_num_elements; +} + +lldb::ValueObjectSP lldb_private::formatters:: + LibcxxInitializerListSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { + if (!m_start) + return lldb::ValueObjectSP(); + + uint64_t offset = idx * m_element_size; + offset = offset + m_start->GetValueAsUnsigned(0); + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + return CreateValueObjectFromAddress(name.GetString(), offset, + m_backend.GetExecutionContextRef(), + m_element_type); +} + +lldb::ChildCacheState +lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd::Update() { + m_start = nullptr; + m_num_elements = 0; + m_element_type = m_backend.GetCompilerType().GetTypeTemplateArgument(0); + if (!m_element_type.IsValid()) + return lldb::ChildCacheState::eRefetch; + + if (std::optional<uint64_t> size = m_element_type.GetByteSize(nullptr)) { + m_element_size = *size; + // Store raw pointers or end up with a circular dependency. + m_start = m_backend.GetChildMemberWithName("__begin_").get(); + } + + return lldb::ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (!m_start) + return UINT32_MAX; + return ExtractIndexFromString(name.GetCString()); +} + +lldb_private::SyntheticChildrenFrontEnd * +lldb_private::formatters::LibcxxInitializerListSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibcxxInitializerListSyntheticFrontEnd(valobj_sp) + : nullptr); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp new file mode 100644 index 000000000000..d7cfeb30557c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp @@ -0,0 +1,426 @@ +//===-- LibCxxList.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 "LibCxx.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" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace { + +class ListEntry { +public: + ListEntry() = default; + ListEntry(ValueObjectSP entry_sp) : m_entry_sp(std::move(entry_sp)) {} + ListEntry(ValueObject *entry) + : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {} + + ListEntry next() { + if (!m_entry_sp) + return ListEntry(); + return ListEntry(m_entry_sp->GetChildMemberWithName("__next_")); + } + + ListEntry prev() { + if (!m_entry_sp) + return ListEntry(); + return ListEntry(m_entry_sp->GetChildMemberWithName("__prev_")); + } + + uint64_t value() const { + if (!m_entry_sp) + return 0; + return m_entry_sp->GetValueAsUnsigned(0); + } + + bool null() { return (value() == 0); } + + explicit operator bool() { return GetEntry() && !null(); } + + ValueObjectSP GetEntry() { return m_entry_sp; } + + void SetEntry(ValueObjectSP entry) { m_entry_sp = entry; } + + bool operator==(const ListEntry &rhs) const { return value() == rhs.value(); } + + bool operator!=(const ListEntry &rhs) const { return !(*this == rhs); } + +private: + ValueObjectSP m_entry_sp; +}; + +class ListIterator { +public: + ListIterator() = default; + ListIterator(ListEntry entry) : m_entry(std::move(entry)) {} + ListIterator(ValueObjectSP entry) : m_entry(std::move(entry)) {} + ListIterator(ValueObject *entry) : m_entry(entry) {} + + ValueObjectSP value() { return m_entry.GetEntry(); } + + ValueObjectSP advance(size_t count) { + if (count == 0) + return m_entry.GetEntry(); + if (count == 1) { + next(); + return m_entry.GetEntry(); + } + while (count > 0) { + next(); + count--; + if (m_entry.null()) + return lldb::ValueObjectSP(); + } + return m_entry.GetEntry(); + } + + bool operator==(const ListIterator &rhs) const { + return (rhs.m_entry == m_entry); + } + +protected: + void next() { m_entry = m_entry.next(); } + + void prev() { m_entry = m_entry.prev(); } + +private: + ListEntry m_entry; +}; + +class AbstractListFrontEnd : public SyntheticChildrenFrontEnd { +public: + size_t GetIndexOfChildWithName(ConstString name) override { + return ExtractIndexFromString(name.GetCString()); + } + bool MightHaveChildren() override { return true; } + lldb::ChildCacheState Update() override; + +protected: + AbstractListFrontEnd(ValueObject &valobj) + : SyntheticChildrenFrontEnd(valobj) {} + + size_t m_count = 0; + ValueObject *m_head = nullptr; + + static constexpr bool g_use_loop_detect = true; + size_t m_loop_detected = 0; // The number of elements that have had loop + // detection run over them. + ListEntry m_slow_runner; // Used for loop detection + ListEntry m_fast_runner; // Used for loop detection + + size_t m_list_capping_size = 0; + CompilerType m_element_type; + std::map<size_t, ListIterator> m_iterators; + + bool HasLoop(size_t count); + ValueObjectSP GetItem(size_t idx); +}; + +class ForwardListFrontEnd : public AbstractListFrontEnd { +public: + ForwardListFrontEnd(ValueObject &valobj); + + llvm::Expected<uint32_t> CalculateNumChildren() override; + ValueObjectSP GetChildAtIndex(uint32_t idx) override; + lldb::ChildCacheState Update() override; +}; + +class ListFrontEnd : public AbstractListFrontEnd { +public: + ListFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~ListFrontEnd() override = default; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + +private: + lldb::addr_t m_node_address = 0; + ValueObject *m_tail = nullptr; +}; + +} // end anonymous namespace + +lldb::ChildCacheState AbstractListFrontEnd::Update() { + m_loop_detected = 0; + m_count = UINT32_MAX; + m_head = nullptr; + m_list_capping_size = 0; + m_slow_runner.SetEntry(nullptr); + m_fast_runner.SetEntry(nullptr); + m_iterators.clear(); + + if (m_backend.GetTargetSP()) + m_list_capping_size = + m_backend.GetTargetSP()->GetMaximumNumberOfChildrenToDisplay(); + if (m_list_capping_size == 0) + m_list_capping_size = 255; + + CompilerType list_type = m_backend.GetCompilerType(); + if (list_type.IsReferenceType()) + list_type = list_type.GetNonReferenceType(); + + if (list_type.GetNumTemplateArguments() == 0) + return lldb::ChildCacheState::eRefetch; + m_element_type = list_type.GetTypeTemplateArgument(0); + + return lldb::ChildCacheState::eRefetch; +} + +bool AbstractListFrontEnd::HasLoop(size_t count) { + if (!g_use_loop_detect) + return false; + // don't bother checking for a loop if we won't actually need to jump nodes + if (m_count < 2) + return false; + + if (m_loop_detected == 0) { + // This is the first time we are being run (after the last update). Set up + // the loop invariant for the first element. + m_slow_runner = ListEntry(m_head).next(); + m_fast_runner = m_slow_runner.next(); + m_loop_detected = 1; + } + + // Loop invariant: + // Loop detection has been run over the first m_loop_detected elements. If + // m_slow_runner == m_fast_runner then the loop has been detected after + // m_loop_detected elements. + const size_t steps_to_run = std::min(count, m_count); + while (m_loop_detected < steps_to_run && m_slow_runner && m_fast_runner && + m_slow_runner != m_fast_runner) { + + m_slow_runner = m_slow_runner.next(); + m_fast_runner = m_fast_runner.next().next(); + m_loop_detected++; + } + if (count <= m_loop_detected) + return false; // No loop in the first m_loop_detected elements. + if (!m_slow_runner || !m_fast_runner) + return false; // Reached the end of the list. Definitely no loops. + return m_slow_runner == m_fast_runner; +} + +ValueObjectSP AbstractListFrontEnd::GetItem(size_t idx) { + size_t advance = idx; + ListIterator current(m_head); + if (idx > 0) { + auto cached_iterator = m_iterators.find(idx - 1); + if (cached_iterator != m_iterators.end()) { + current = cached_iterator->second; + advance = 1; + } + } + ValueObjectSP value_sp = current.advance(advance); + m_iterators[idx] = current; + return value_sp; +} + +ForwardListFrontEnd::ForwardListFrontEnd(ValueObject &valobj) + : AbstractListFrontEnd(valobj) { + Update(); +} + +llvm::Expected<uint32_t> ForwardListFrontEnd::CalculateNumChildren() { + if (m_count != UINT32_MAX) + return m_count; + + ListEntry current(m_head); + m_count = 0; + while (current && m_count < m_list_capping_size) { + ++m_count; + current = current.next(); + } + return m_count; +} + +ValueObjectSP ForwardListFrontEnd::GetChildAtIndex(uint32_t idx) { + if (idx >= CalculateNumChildrenIgnoringErrors()) + return nullptr; + + if (!m_head) + return nullptr; + + if (HasLoop(idx + 1)) + return nullptr; + + ValueObjectSP current_sp = GetItem(idx); + if (!current_sp) + return nullptr; + + current_sp = current_sp->GetChildAtIndex(1); // get the __value_ child + if (!current_sp) + return nullptr; + + // we need to copy current_sp into a new object otherwise we will end up with + // all items named __value_ + DataExtractor data; + Status error; + current_sp->GetData(data, error); + if (error.Fail()) + return nullptr; + + return CreateValueObjectFromData(llvm::formatv("[{0}]", idx).str(), data, + m_backend.GetExecutionContextRef(), + m_element_type); +} + +lldb::ChildCacheState ForwardListFrontEnd::Update() { + AbstractListFrontEnd::Update(); + + Status err; + ValueObjectSP backend_addr(m_backend.AddressOf(err)); + if (err.Fail() || !backend_addr) + return lldb::ChildCacheState::eRefetch; + + ValueObjectSP impl_sp(m_backend.GetChildMemberWithName("__before_begin_")); + if (!impl_sp) + return lldb::ChildCacheState::eRefetch; + impl_sp = GetFirstValueOfLibCXXCompressedPair(*impl_sp); + if (!impl_sp) + return lldb::ChildCacheState::eRefetch; + m_head = impl_sp->GetChildMemberWithName("__next_").get(); + return lldb::ChildCacheState::eRefetch; +} + +ListFrontEnd::ListFrontEnd(lldb::ValueObjectSP valobj_sp) + : AbstractListFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +llvm::Expected<uint32_t> ListFrontEnd::CalculateNumChildren() { + if (m_count != UINT32_MAX) + return m_count; + if (!m_head || !m_tail || m_node_address == 0) + return 0; + ValueObjectSP size_alloc(m_backend.GetChildMemberWithName("__size_alloc_")); + if (size_alloc) { + ValueObjectSP value = GetFirstValueOfLibCXXCompressedPair(*size_alloc); + if (value) { + m_count = value->GetValueAsUnsigned(UINT32_MAX); + } + } + if (m_count != UINT32_MAX) { + return m_count; + } else { + uint64_t next_val = m_head->GetValueAsUnsigned(0); + uint64_t prev_val = m_tail->GetValueAsUnsigned(0); + if (next_val == 0 || prev_val == 0) + return 0; + if (next_val == m_node_address) + return 0; + if (next_val == prev_val) + return 1; + uint64_t size = 2; + ListEntry current(m_head); + while (current.next() && current.next().value() != m_node_address) { + size++; + current = current.next(); + if (size > m_list_capping_size) + break; + } + return m_count = (size - 1); + } +} + +lldb::ValueObjectSP ListFrontEnd::GetChildAtIndex(uint32_t idx) { + static ConstString g_value("__value_"); + static ConstString g_next("__next_"); + + if (idx >= CalculateNumChildrenIgnoringErrors()) + return lldb::ValueObjectSP(); + + if (!m_head || !m_tail || m_node_address == 0) + return lldb::ValueObjectSP(); + + if (HasLoop(idx + 1)) + return lldb::ValueObjectSP(); + + ValueObjectSP current_sp = GetItem(idx); + if (!current_sp) + return lldb::ValueObjectSP(); + + current_sp = current_sp->GetChildAtIndex(1); // get the __value_ child + if (!current_sp) + return lldb::ValueObjectSP(); + + if (current_sp->GetName() == g_next) { + ProcessSP process_sp(current_sp->GetProcessSP()); + if (!process_sp) + return lldb::ValueObjectSP(); + + // if we grabbed the __next_ pointer, then the child is one pointer deep-er + lldb::addr_t addr = current_sp->GetParent()->GetPointerValue(); + addr = addr + 2 * process_sp->GetAddressByteSize(); + ExecutionContext exe_ctx(process_sp); + current_sp = + CreateValueObjectFromAddress("__value_", addr, exe_ctx, m_element_type); + if (!current_sp) + return lldb::ValueObjectSP(); + } + + // we need to copy current_sp into a new object otherwise we will end up with + // all items named __value_ + DataExtractor data; + Status error; + current_sp->GetData(data, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + return CreateValueObjectFromData(name.GetString(), data, + m_backend.GetExecutionContextRef(), + m_element_type); +} + +lldb::ChildCacheState ListFrontEnd::Update() { + AbstractListFrontEnd::Update(); + m_tail = nullptr; + m_node_address = 0; + + Status err; + ValueObjectSP backend_addr(m_backend.AddressOf(err)); + if (err.Fail() || !backend_addr) + return lldb::ChildCacheState::eRefetch; + m_node_address = backend_addr->GetValueAsUnsigned(0); + if (!m_node_address || m_node_address == LLDB_INVALID_ADDRESS) + return lldb::ChildCacheState::eRefetch; + ValueObjectSP impl_sp(m_backend.GetChildMemberWithName("__end_")); + if (!impl_sp) + return lldb::ChildCacheState::eRefetch; + m_head = impl_sp->GetChildMemberWithName("__next_").get(); + m_tail = impl_sp->GetChildMemberWithName("__prev_").get(); + return lldb::ChildCacheState::eRefetch; +} + +SyntheticChildrenFrontEnd *formatters::LibcxxStdListSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new ListFrontEnd(valobj_sp) : nullptr); +} + +SyntheticChildrenFrontEnd * +formatters::LibcxxStdForwardListSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return valobj_sp ? new ForwardListFrontEnd(*valobj_sp) : nullptr; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp new file mode 100644 index 000000000000..5106a63d531f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp @@ -0,0 +1,499 @@ +//===-- LibCxxMap.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 "LibCxx.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 "lldb/lldb-enumerations.h" +#include "lldb/lldb-forward.h" +#include <cstdint> +#include <locale> +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +// The flattened layout of the std::__tree_iterator::__ptr_ looks +// as follows: +// +// The following shows the contiguous block of memory: +// +// +-----------------------------+ class __tree_end_node +// __ptr_ | pointer __left_; | +// +-----------------------------+ class __tree_node_base +// | pointer __right_; | +// | __parent_pointer __parent_; | +// | bool __is_black_; | +// +-----------------------------+ class __tree_node +// | __node_value_type __value_; | <<< our key/value pair +// +-----------------------------+ +// +// where __ptr_ has type __iter_pointer. + +class MapEntry { +public: + MapEntry() = default; + explicit MapEntry(ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {} + explicit MapEntry(ValueObject *entry) + : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {} + + ValueObjectSP left() const { + if (!m_entry_sp) + return m_entry_sp; + return m_entry_sp->GetSyntheticChildAtOffset( + 0, m_entry_sp->GetCompilerType(), true); + } + + ValueObjectSP right() const { + if (!m_entry_sp) + return m_entry_sp; + return m_entry_sp->GetSyntheticChildAtOffset( + m_entry_sp->GetProcessSP()->GetAddressByteSize(), + m_entry_sp->GetCompilerType(), true); + } + + ValueObjectSP parent() const { + if (!m_entry_sp) + return m_entry_sp; + return m_entry_sp->GetSyntheticChildAtOffset( + 2 * m_entry_sp->GetProcessSP()->GetAddressByteSize(), + m_entry_sp->GetCompilerType(), true); + } + + uint64_t value() const { + if (!m_entry_sp) + return 0; + return m_entry_sp->GetValueAsUnsigned(0); + } + + bool error() const { + if (!m_entry_sp) + return true; + return m_entry_sp->GetError().Fail(); + } + + bool null() const { return (value() == 0); } + + ValueObjectSP GetEntry() const { return m_entry_sp; } + + void SetEntry(ValueObjectSP entry) { m_entry_sp = entry; } + + bool operator==(const MapEntry &rhs) const { + return (rhs.m_entry_sp.get() == m_entry_sp.get()); + } + +private: + ValueObjectSP m_entry_sp; +}; + +class MapIterator { +public: + MapIterator(ValueObject *entry, size_t depth = 0) + : m_entry(entry), m_max_depth(depth), m_error(false) {} + + MapIterator() = default; + + ValueObjectSP value() { return m_entry.GetEntry(); } + + ValueObjectSP advance(size_t count) { + ValueObjectSP fail; + if (m_error) + return fail; + size_t steps = 0; + while (count > 0) { + next(); + count--, steps++; + if (m_error || m_entry.null() || (steps > m_max_depth)) + return fail; + } + return m_entry.GetEntry(); + } + +private: + /// Mimicks libc++'s __tree_next algorithm, which libc++ uses + /// in its __tree_iteartor::operator++. + void next() { + if (m_entry.null()) + return; + MapEntry right(m_entry.right()); + if (!right.null()) { + m_entry = tree_min(std::move(right)); + return; + } + size_t steps = 0; + while (!is_left_child(m_entry)) { + if (m_entry.error()) { + m_error = true; + return; + } + m_entry.SetEntry(m_entry.parent()); + steps++; + if (steps > m_max_depth) { + m_entry = MapEntry(); + return; + } + } + m_entry = MapEntry(m_entry.parent()); + } + + /// Mimicks libc++'s __tree_min algorithm. + MapEntry tree_min(MapEntry x) { + if (x.null()) + return MapEntry(); + MapEntry left(x.left()); + size_t steps = 0; + while (!left.null()) { + if (left.error()) { + m_error = true; + return MapEntry(); + } + x = left; + left.SetEntry(x.left()); + steps++; + if (steps > m_max_depth) + return MapEntry(); + } + return x; + } + + bool is_left_child(const MapEntry &x) { + if (x.null()) + return false; + MapEntry rhs(x.parent()); + rhs.SetEntry(rhs.left()); + return x.value() == rhs.value(); + } + + MapEntry m_entry; + size_t m_max_depth = 0; + bool m_error = false; +}; + +namespace lldb_private { +namespace formatters { +class LibcxxStdMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibcxxStdMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibcxxStdMapSyntheticFrontEnd() 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: + /// Returns the ValueObject for the __tree_node type that + /// holds the key/value pair of the node at index \ref idx. + /// + /// \param[in] idx The child index that we're looking to get + /// the key/value pair for. + /// + /// \param[in] max_depth The maximum search depth after which + /// we stop trying to find the key/value + /// pair for. + /// + /// \returns On success, returns the ValueObjectSP corresponding + /// to the __tree_node's __value_ member (which holds + /// the key/value pair the formatter wants to display). + /// On failure, will return nullptr. + ValueObjectSP GetKeyValuePair(size_t idx, size_t max_depth); + + ValueObject *m_tree = nullptr; + ValueObject *m_root_node = nullptr; + CompilerType m_node_ptr_type; + size_t m_count = UINT32_MAX; + std::map<size_t, MapIterator> m_iterators; +}; + +class LibCxxMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibCxxMapIteratorSyntheticFrontEnd(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; + + ~LibCxxMapIteratorSyntheticFrontEnd() override = default; + +private: + ValueObjectSP m_pair_sp = nullptr; +}; +} // namespace formatters +} // namespace lldb_private + +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: + LibcxxStdMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibcxxStdMapSyntheticFrontEnd::CalculateNumChildren() { + if (m_count != UINT32_MAX) + return m_count; + + if (m_tree == nullptr) + return 0; + + ValueObjectSP size_node(m_tree->GetChildMemberWithName("__pair3_")); + if (!size_node) + return 0; + + size_node = GetFirstValueOfLibCXXCompressedPair(*size_node); + + if (!size_node) + return 0; + + m_count = size_node->GetValueAsUnsigned(0); + return m_count; +} + +ValueObjectSP +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetKeyValuePair( + size_t idx, size_t max_depth) { + MapIterator iterator(m_root_node, max_depth); + + size_t advance_by = idx; + if (idx > 0) { + // If we have already created the iterator for the previous + // index, we can start from there and advance by 1. + auto cached_iterator = m_iterators.find(idx - 1); + if (cached_iterator != m_iterators.end()) { + iterator = cached_iterator->second; + advance_by = 1; + } + } + + ValueObjectSP iterated_sp(iterator.advance(advance_by)); + if (!iterated_sp) + // this tree is garbage - stop + return nullptr; + + if (!m_node_ptr_type.IsValid()) + return nullptr; + + // iterated_sp is a __iter_pointer at this point. + // We can cast it to a __node_pointer (which is what libc++ does). + auto value_type_sp = iterated_sp->Cast(m_node_ptr_type); + if (!value_type_sp) + return nullptr; + + // Finally, get the key/value pair. + value_type_sp = value_type_sp->GetChildMemberWithName("__value_"); + if (!value_type_sp) + return nullptr; + + m_iterators[idx] = iterator; + + return value_type_sp; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + static ConstString g_cc_("__cc_"), g_cc("__cc"); + static ConstString g_nc("__nc"); + uint32_t num_children = CalculateNumChildrenIgnoringErrors(); + if (idx >= num_children) + return nullptr; + + if (m_tree == nullptr || m_root_node == nullptr) + return nullptr; + + ValueObjectSP key_val_sp = GetKeyValuePair(idx, /*max_depth=*/num_children); + if (!key_val_sp) { + // this will stop all future searches until an Update() happens + m_tree = nullptr; + return nullptr; + } + + // at this point we have a valid + // we need to copy current_sp into a new object otherwise we will end up with + // all items named __value_ + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + auto potential_child_sp = key_val_sp->Clone(ConstString(name.GetString())); + if (potential_child_sp) { + switch (potential_child_sp->GetNumChildrenIgnoringErrors()) { + case 1: { + auto child0_sp = potential_child_sp->GetChildAtIndex(0); + if (child0_sp && + (child0_sp->GetName() == g_cc_ || child0_sp->GetName() == g_cc)) + potential_child_sp = child0_sp->Clone(ConstString(name.GetString())); + break; + } + case 2: { + auto child0_sp = potential_child_sp->GetChildAtIndex(0); + auto child1_sp = potential_child_sp->GetChildAtIndex(1); + if (child0_sp && + (child0_sp->GetName() == g_cc_ || child0_sp->GetName() == g_cc) && + child1_sp && child1_sp->GetName() == g_nc) + potential_child_sp = child0_sp->Clone(ConstString(name.GetString())); + break; + } + } + } + return potential_child_sp; +} + +lldb::ChildCacheState +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::Update() { + m_count = UINT32_MAX; + m_tree = m_root_node = nullptr; + m_iterators.clear(); + m_tree = m_backend.GetChildMemberWithName("__tree_").get(); + if (!m_tree) + return lldb::ChildCacheState::eRefetch; + m_root_node = m_tree->GetChildMemberWithName("__begin_node_").get(); + m_node_ptr_type = + m_tree->GetCompilerType().GetDirectNestedTypeWithName("__node_pointer"); + + return lldb::ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + return ExtractIndexFromString(name.GetCString()); +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibcxxStdMapSyntheticFrontEnd(valobj_sp) : nullptr); +} + +lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: + LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +lldb::ChildCacheState +lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::Update() { + m_pair_sp.reset(); + + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + + TargetSP target_sp(valobj_sp->GetTargetSP()); + if (!target_sp) + return lldb::ChildCacheState::eRefetch; + + // m_backend is a std::map::iterator + // ...which is a __map_iterator<__tree_iterator<..., __node_pointer, ...>> + // + // Then, __map_iterator::__i_ is a __tree_iterator + auto tree_iter_sp = valobj_sp->GetChildMemberWithName("__i_"); + if (!tree_iter_sp) + return lldb::ChildCacheState::eRefetch; + + // Type is __tree_iterator::__node_pointer + // (We could alternatively also get this from the template argument) + auto node_pointer_type = + tree_iter_sp->GetCompilerType().GetDirectNestedTypeWithName( + "__node_pointer"); + if (!node_pointer_type.IsValid()) + return lldb::ChildCacheState::eRefetch; + + // __ptr_ is a __tree_iterator::__iter_pointer + auto iter_pointer_sp = tree_iter_sp->GetChildMemberWithName("__ptr_"); + if (!iter_pointer_sp) + return lldb::ChildCacheState::eRefetch; + + // Cast the __iter_pointer to a __node_pointer (which stores our key/value + // pair) + auto node_pointer_sp = iter_pointer_sp->Cast(node_pointer_type); + if (!node_pointer_sp) + return lldb::ChildCacheState::eRefetch; + + auto key_value_sp = node_pointer_sp->GetChildMemberWithName("__value_"); + if (!key_value_sp) + return lldb::ChildCacheState::eRefetch; + + // Create the synthetic child, which is a pair where the key and value can be + // retrieved by querying the synthetic frontend for + // GetIndexOfChildWithName("first") and GetIndexOfChildWithName("second") + // respectively. + // + // std::map stores the actual key/value pair in value_type::__cc_ (or + // previously __cc). + key_value_sp = key_value_sp->Clone(ConstString("pair")); + if (key_value_sp->GetNumChildrenIgnoringErrors() == 1) { + auto child0_sp = key_value_sp->GetChildAtIndex(0); + if (child0_sp && + (child0_sp->GetName() == "__cc_" || child0_sp->GetName() == "__cc")) + key_value_sp = child0_sp->Clone(ConstString("pair")); + } + + m_pair_sp = key_value_sp; + + return lldb::ChildCacheState::eRefetch; +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibCxxMapIteratorSyntheticFrontEnd::CalculateNumChildren() { + return 2; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + if (!m_pair_sp) + return nullptr; + + return m_pair_sp->GetChildAtIndex(idx); +} + +bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (!m_pair_sp) + return UINT32_MAX; + + return m_pair_sp->GetIndexOfChildWithName(name); +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibCxxMapIteratorSyntheticFrontEnd(valobj_sp) + : nullptr); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxProxyArray.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxProxyArray.cpp new file mode 100644 index 000000000000..726f06523b97 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxProxyArray.cpp @@ -0,0 +1,194 @@ +//===-- LibCxxProxyArray.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 "LibCxx.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace lldb_private { +namespace formatters { + +/// Data formatter for libc++'s std::"proxy_array". +/// +/// A proxy_array's are created by using: +/// std::gslice_array operator[](const std::gslice& gslicearr); +/// std::mask_array operator[](const std::valarray<bool>& boolarr); +/// std::indirect_array operator[](const std::valarray<std::size_t>& indarr); +/// +/// These arrays have the following members: +/// - __vp_ points to std::valarray::__begin_ +/// - __1d_ an array of offsets of the elements from @a __vp_ +class LibcxxStdProxyArraySyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibcxxStdProxyArraySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibcxxStdProxyArraySyntheticFrontEnd() 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: + /// A non-owning pointer to the array's __vp_. + ValueObject *m_base = nullptr; + /// The type of the array's template argument T. + CompilerType m_element_type; + /// The sizeof the array's template argument T. + uint32_t m_element_size = 0; + + /// A non-owning pointer to the array's __1d_.__begin_. + ValueObject *m_start = nullptr; + /// A non-owning pointer to the array's __1d_.__end_. + ValueObject *m_finish = nullptr; + /// The type of the __1d_ array's template argument T (size_t). + CompilerType m_element_type_size_t; + /// The sizeof the __1d_ array's template argument T (size_t) + uint32_t m_element_size_size_t = 0; +}; + +} // namespace formatters +} // namespace lldb_private + +lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEnd:: + LibcxxStdProxyArraySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type() { + if (valobj_sp) + Update(); +} + +lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEnd:: + ~LibcxxStdProxyArraySyntheticFrontEnd() { + // these need to stay around because they are child objects who will follow + // their parent's life cycle + // delete m_base; +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibcxxStdProxyArraySyntheticFrontEnd::CalculateNumChildren() { + + if (!m_start || !m_finish) + return 0; + uint64_t start_val = m_start->GetValueAsUnsigned(0); + uint64_t finish_val = m_finish->GetValueAsUnsigned(0); + + if (start_val == 0 || finish_val == 0) + return 0; + + if (start_val >= finish_val) + return 0; + + size_t num_children = (finish_val - start_val); + if (num_children % m_element_size_size_t) + return 0; + return num_children / m_element_size_size_t; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + if (!m_base) + return lldb::ValueObjectSP(); + + uint64_t offset = idx * m_element_size_size_t; + offset = offset + m_start->GetValueAsUnsigned(0); + + lldb::ValueObjectSP indirect = CreateValueObjectFromAddress( + "", offset, m_backend.GetExecutionContextRef(), m_element_type_size_t); + if (!indirect) + return lldb::ValueObjectSP(); + + const size_t value = indirect->GetValueAsUnsigned(0); + if (!value) + return lldb::ValueObjectSP(); + + offset = value * m_element_size; + offset = offset + m_base->GetValueAsUnsigned(0); + + StreamString name; + name.Printf("[%" PRIu64 "] -> [%zu]", (uint64_t)idx, value); + return CreateValueObjectFromAddress(name.GetString(), offset, + m_backend.GetExecutionContextRef(), + m_element_type); +} + +lldb::ChildCacheState +lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEnd::Update() { + m_base = nullptr; + m_start = nullptr; + m_finish = nullptr; + + CompilerType type = m_backend.GetCompilerType(); + if (type.GetNumTemplateArguments() == 0) + return ChildCacheState::eRefetch; + + m_element_type = type.GetTypeTemplateArgument(0); + if (std::optional<uint64_t> size = m_element_type.GetByteSize(nullptr)) + m_element_size = *size; + + if (m_element_size == 0) + return ChildCacheState::eRefetch; + + ValueObjectSP vector = m_backend.GetChildMemberWithName("__1d_"); + if (!vector) + return ChildCacheState::eRefetch; + + type = vector->GetCompilerType(); + if (type.GetNumTemplateArguments() == 0) + return ChildCacheState::eRefetch; + + m_element_type_size_t = type.GetTypeTemplateArgument(0); + if (std::optional<uint64_t> size = m_element_type_size_t.GetByteSize(nullptr)) + m_element_size_size_t = *size; + + if (m_element_size_size_t == 0) + return ChildCacheState::eRefetch; + + ValueObjectSP base = m_backend.GetChildMemberWithName("__vp_"); + ValueObjectSP start = vector->GetChildMemberWithName("__begin_"); + ValueObjectSP finish = vector->GetChildMemberWithName("__end_"); + if (!base || !start || !finish) + return ChildCacheState::eRefetch; + + m_base = base.get(); + m_start = start.get(); + m_finish = finish.get(); + + return ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (!m_base) + return std::numeric_limits<size_t>::max(); + return ExtractIndexFromString(name.GetCString()); +} + +lldb_private::SyntheticChildrenFrontEnd * +lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + return new LibcxxStdProxyArraySyntheticFrontEnd(valobj_sp); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxQueue.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxQueue.cpp new file mode 100644 index 000000000000..5b459a17fe29 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxQueue.cpp @@ -0,0 +1,65 @@ +//===-- LibCxxQueue.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 "LibCxx.h" +#include "lldb/DataFormatters/FormattersHelpers.h" + +using namespace lldb; +using namespace lldb_private; + +namespace { + +class QueueFrontEnd : public SyntheticChildrenFrontEnd { +public: + QueueFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) { + Update(); + } + + size_t GetIndexOfChildWithName(ConstString name) override { + return m_container_sp ? m_container_sp->GetIndexOfChildWithName(name) + : UINT32_MAX; + } + + bool MightHaveChildren() override { return true; } + lldb::ChildCacheState Update() override; + + llvm::Expected<uint32_t> CalculateNumChildren() override { + return m_container_sp ? m_container_sp->GetNumChildren() : 0; + } + + ValueObjectSP GetChildAtIndex(uint32_t idx) override { + return m_container_sp ? m_container_sp->GetChildAtIndex(idx) + : nullptr; + } + +private: + // The lifetime of a ValueObject and all its derivative ValueObjects + // (children, clones, etc.) is managed by a ClusterManager. These + // objects are only destroyed when every shared pointer to any of them + // is destroyed, so we must not store a shared pointer to any ValueObject + // derived from our backend ValueObject (since we're in the same cluster). + ValueObject* m_container_sp = nullptr; +}; +} // namespace + +lldb::ChildCacheState QueueFrontEnd::Update() { + m_container_sp = nullptr; + ValueObjectSP c_sp = m_backend.GetChildMemberWithName("c"); + if (!c_sp) + return lldb::ChildCacheState::eRefetch; + m_container_sp = c_sp->GetSyntheticValue().get(); + return lldb::ChildCacheState::eRefetch; +} + +SyntheticChildrenFrontEnd * +formatters::LibcxxQueueFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new QueueFrontEnd(*valobj_sp); + return nullptr; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxRangesRefView.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxRangesRefView.cpp new file mode 100644 index 000000000000..01a7b8f142ec --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxRangesRefView.cpp @@ -0,0 +1,88 @@ +//===-- LibCxxRangesRefView.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 "LibCxx.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Utility/ConstString.h" +#include "llvm/ADT/APSInt.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace lldb_private { +namespace formatters { + +class LibcxxStdRangesRefViewSyntheticFrontEnd + : public SyntheticChildrenFrontEnd { +public: + LibcxxStdRangesRefViewSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibcxxStdRangesRefViewSyntheticFrontEnd() override = default; + + llvm::Expected<uint32_t> CalculateNumChildren() override { + // __range_ will be the sole child of this type + return 1; + } + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override { + // Since we only have a single child, return it + assert(idx == 0); + return m_range_sp; + } + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override { return true; } + + size_t GetIndexOfChildWithName(ConstString name) override { + // We only have a single child + return 0; + } + +private: + /// Pointer to the dereferenced __range_ member + lldb::ValueObjectSP m_range_sp = nullptr; +}; + +lldb_private::formatters::LibcxxStdRangesRefViewSyntheticFrontEnd:: + LibcxxStdRangesRefViewSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +lldb::ChildCacheState +lldb_private::formatters::LibcxxStdRangesRefViewSyntheticFrontEnd::Update() { + ValueObjectSP range_ptr = + GetChildMemberWithName(m_backend, {ConstString("__range_")}); + if (!range_ptr) + return lldb::ChildCacheState::eRefetch; + + lldb_private::Status error; + m_range_sp = range_ptr->Dereference(error); + + return error.Success() ? lldb::ChildCacheState::eReuse + : lldb::ChildCacheState::eRefetch; +} + +lldb_private::SyntheticChildrenFrontEnd * +LibcxxStdRangesRefViewSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + CompilerType type = valobj_sp->GetCompilerType(); + if (!type.IsValid()) + return nullptr; + return new LibcxxStdRangesRefViewSyntheticFrontEnd(valobj_sp); +} + +} // namespace formatters +} // namespace lldb_private diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxSliceArray.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxSliceArray.cpp new file mode 100644 index 000000000000..c33d8e91af70 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxSliceArray.cpp @@ -0,0 +1,166 @@ +//===-- LibCxxSliceArray.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 "LibCxx.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace lldb_private { +namespace formatters { + +bool LibcxxStdSliceArraySummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options) { + ValueObjectSP obj = valobj.GetNonSyntheticValue(); + if (!obj) + return false; + + ValueObjectSP ptr_sp = obj->GetChildMemberWithName("__size_"); + if (!ptr_sp) + return false; + const size_t size = ptr_sp->GetValueAsUnsigned(0); + + ptr_sp = obj->GetChildMemberWithName("__stride_"); + if (!ptr_sp) + return false; + const size_t stride = ptr_sp->GetValueAsUnsigned(0); + + stream.Printf("stride=%zu size=%zu", stride, size); + + return true; +} + +/// Data formatter for libc++'s std::slice_array. +/// +/// A slice_array is created by using: +/// operator[](std::slice slicearr); +/// and std::slice is created by: +/// slice(std::size_t start, std::size_t size, std::size_t stride); +/// The std::slice_array has the following members: +/// - __vp_ points to std::valarray::__begin_ + @a start +/// - __size_ is @a size +/// - __stride_is @a stride +class LibcxxStdSliceArraySyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibcxxStdSliceArraySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibcxxStdSliceArraySyntheticFrontEnd() 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: + /// A non-owning pointer to slice_array.__vp_. + ValueObject *m_start = nullptr; + /// slice_array.__size_. + size_t m_size = 0; + /// slice_array.__stride_. + size_t m_stride = 0; + /// The type of slice_array's template argument T. + CompilerType m_element_type; + /// The sizeof slice_array's template argument T. + uint32_t m_element_size = 0; +}; + +} // namespace formatters +} // namespace lldb_private + +lldb_private::formatters::LibcxxStdSliceArraySyntheticFrontEnd:: + LibcxxStdSliceArraySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type() { + if (valobj_sp) + Update(); +} + +lldb_private::formatters::LibcxxStdSliceArraySyntheticFrontEnd:: + ~LibcxxStdSliceArraySyntheticFrontEnd() { + // these need to stay around because they are child objects who will follow + // their parent's life cycle + // delete m_start; +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibcxxStdSliceArraySyntheticFrontEnd::CalculateNumChildren() { + return m_size; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxStdSliceArraySyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + if (!m_start) + return lldb::ValueObjectSP(); + + uint64_t offset = idx * m_stride * m_element_size; + offset = offset + m_start->GetValueAsUnsigned(0); + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + return CreateValueObjectFromAddress(name.GetString(), offset, + m_backend.GetExecutionContextRef(), + m_element_type); +} + +lldb::ChildCacheState +lldb_private::formatters::LibcxxStdSliceArraySyntheticFrontEnd::Update() { + m_start = nullptr; + + CompilerType type = m_backend.GetCompilerType(); + if (type.GetNumTemplateArguments() == 0) + return ChildCacheState::eRefetch; + + m_element_type = type.GetTypeTemplateArgument(0); + if (std::optional<uint64_t> size = m_element_type.GetByteSize(nullptr)) + m_element_size = *size; + + if (m_element_size == 0) + return ChildCacheState::eRefetch; + + ValueObjectSP start = m_backend.GetChildMemberWithName("__vp_"); + ValueObjectSP size = m_backend.GetChildMemberWithName("__size_"); + ValueObjectSP stride = m_backend.GetChildMemberWithName("__stride_"); + + if (!start || !size || !stride) + return ChildCacheState::eRefetch; + + m_start = start.get(); + m_size = size->GetValueAsUnsigned(0); + m_stride = stride->GetValueAsUnsigned(0); + + return ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::LibcxxStdSliceArraySyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibcxxStdSliceArraySyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (!m_start) + return std::numeric_limits<size_t>::max(); + return ExtractIndexFromString(name.GetCString()); +} + +lldb_private::SyntheticChildrenFrontEnd * +lldb_private::formatters::LibcxxStdSliceArraySyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + return new LibcxxStdSliceArraySyntheticFrontEnd(valobj_sp); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp new file mode 100644 index 000000000000..9895f336bfd0 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp @@ -0,0 +1,153 @@ +//===-- LibCxxSpan.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 "LibCxx.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Utility/ConstString.h" +#include "llvm/ADT/APSInt.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace lldb_private { +namespace formatters { + +class LibcxxStdSpanSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibcxxStdSpanSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibcxxStdSpanSyntheticFrontEnd() override = default; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + /// Determines properties of the std::span<> associated with this object + // + // std::span can either be instantiated with a compile-time known + // extent or a std::dynamic_extent (this is the default if only the + // type template argument is provided). The layout of std::span + // depends on whether the extent is dynamic or not. For static + // extents (e.g., std::span<int, 9>): + // + // (std::__1::span<const int, 9>) s = { + // __data = 0x000000016fdff494 + // } + // + // For dynamic extents, e.g., std::span<int>, the layout is: + // + // (std::__1::span<const int, 18446744073709551615>) s = { + // __data = 0x000000016fdff494 + // __size = 6 + // } + // + // This function checks for a '__size' member to determine the number + // of elements in the span. If no such member exists, we get the size + // from the only other place it can be: the template argument. + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + ValueObject *m_start = nullptr; ///< First element of span. Held, not owned. + CompilerType m_element_type{}; ///< Type of span elements. + size_t m_num_elements = 0; ///< Number of elements in span. + uint32_t m_element_size = 0; ///< Size in bytes of each span element. +}; + +lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd:: + LibcxxStdSpanSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibcxxStdSpanSyntheticFrontEnd::CalculateNumChildren() { + return m_num_elements; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + if (!m_start) + return {}; + + uint64_t offset = idx * m_element_size; + offset = offset + m_start->GetValueAsUnsigned(0); + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + return CreateValueObjectFromAddress(name.GetString(), offset, + m_backend.GetExecutionContextRef(), + m_element_type); +} + +lldb::ChildCacheState +lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd::Update() { + // Get element type. + ValueObjectSP data_type_finder_sp = GetChildMemberWithName( + m_backend, {ConstString("__data_"), ConstString("__data")}); + if (!data_type_finder_sp) + return lldb::ChildCacheState::eRefetch; + + m_element_type = data_type_finder_sp->GetCompilerType().GetPointeeType(); + + // Get element size. + if (std::optional<uint64_t> size = m_element_type.GetByteSize(nullptr)) { + m_element_size = *size; + + // Get data. + if (m_element_size > 0) { + m_start = data_type_finder_sp.get(); + } + + // Get number of elements. + if (auto size_sp = GetChildMemberWithName( + m_backend, {ConstString("__size_"), ConstString("__size")})) { + m_num_elements = size_sp->GetValueAsUnsigned(0); + } else if (auto arg = + m_backend.GetCompilerType().GetIntegralTemplateArgument(1)) { + + m_num_elements = arg->value.getLimitedValue(); + } + } + + return lldb::ChildCacheState::eReuse; +} + +bool lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (!m_start) + return UINT32_MAX; + return ExtractIndexFromString(name.GetCString()); +} + +lldb_private::SyntheticChildrenFrontEnd * +LibcxxStdSpanSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + CompilerType type = valobj_sp->GetCompilerType(); + if (!type.IsValid() || type.GetNumTemplateArguments() != 2) + return nullptr; + return new LibcxxStdSpanSyntheticFrontEnd(valobj_sp); +} + +} // namespace formatters +} // namespace lldb_private diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp new file mode 100644 index 000000000000..3e3259ab428d --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp @@ -0,0 +1,95 @@ +//===-- LibCxxTuple.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 "LibCxx.h" +#include "lldb/DataFormatters/FormattersHelpers.h" + +using namespace lldb; +using namespace lldb_private; + +namespace { + +class TupleFrontEnd: public SyntheticChildrenFrontEnd { +public: + TupleFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) { + Update(); + } + + size_t GetIndexOfChildWithName(ConstString name) override { + return formatters::ExtractIndexFromString(name.GetCString()); + } + + bool MightHaveChildren() override { return true; } + lldb::ChildCacheState Update() override; + llvm::Expected<uint32_t> CalculateNumChildren() override { + return m_elements.size(); + } + ValueObjectSP GetChildAtIndex(uint32_t idx) override; + +private: + // The lifetime of a ValueObject and all its derivative ValueObjects + // (children, clones, etc.) is managed by a ClusterManager. These + // objects are only destroyed when every shared pointer to any of them + // is destroyed, so we must not store a shared pointer to any ValueObject + // derived from our backend ValueObject (since we're in the same cluster). + std::vector<ValueObject*> m_elements; + ValueObject* m_base = nullptr; +}; +} + +lldb::ChildCacheState TupleFrontEnd::Update() { + m_elements.clear(); + m_base = nullptr; + + ValueObjectSP base_sp; + base_sp = m_backend.GetChildMemberWithName("__base_"); + if (!base_sp) { + // Pre r304382 name of the base element. + base_sp = m_backend.GetChildMemberWithName("base_"); + } + if (!base_sp) + return lldb::ChildCacheState::eRefetch; + m_base = base_sp.get(); + m_elements.assign(base_sp->GetCompilerType().GetNumDirectBaseClasses(), + nullptr); + return lldb::ChildCacheState::eRefetch; +} + +ValueObjectSP TupleFrontEnd::GetChildAtIndex(uint32_t idx) { + if (idx >= m_elements.size()) + return ValueObjectSP(); + if (!m_base) + return ValueObjectSP(); + if (m_elements[idx]) + return m_elements[idx]->GetSP(); + + CompilerType holder_type = + m_base->GetCompilerType().GetDirectBaseClassAtIndex(idx, nullptr); + if (!holder_type) + return ValueObjectSP(); + ValueObjectSP holder_sp = m_base->GetChildAtIndex(idx); + if (!holder_sp) + return ValueObjectSP(); + + ValueObjectSP elem_sp = holder_sp->GetChildAtIndex(0); + if (elem_sp) + m_elements[idx] = + elem_sp->Clone(ConstString(llvm::formatv("[{0}]", idx).str())).get(); + + if (m_elements[idx]) + return m_elements[idx]->GetSP(); + return ValueObjectSP(); +} + +SyntheticChildrenFrontEnd * +formatters::LibcxxTupleFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new TupleFrontEnd(*valobj_sp); + return nullptr; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp new file mode 100644 index 000000000000..93e7f4f4fd86 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp @@ -0,0 +1,388 @@ +//===-- LibCxxUnorderedMap.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 "LibCxx.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/ConstString.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" +#include "llvm/ADT/StringRef.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace lldb_private { +namespace formatters { +class LibcxxStdUnorderedMapSyntheticFrontEnd + : public SyntheticChildrenFrontEnd { +public: + LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibcxxStdUnorderedMapSyntheticFrontEnd() 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: + CompilerType m_element_type; + CompilerType m_node_type; + ValueObject *m_tree = nullptr; + size_t m_num_elements = 0; + ValueObject *m_next_element = nullptr; + std::vector<std::pair<ValueObject *, uint64_t>> m_elements_cache; +}; + +class LibCxxUnorderedMapIteratorSyntheticFrontEnd + : public SyntheticChildrenFrontEnd { +public: + LibCxxUnorderedMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibCxxUnorderedMapIteratorSyntheticFrontEnd() 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: + lldb::ValueObjectSP m_pair_sp; ///< ValueObject for the key/value pair + ///< that the iterator currently points + ///< to. +}; + +} // namespace formatters +} // namespace lldb_private + +lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: + LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type(), + m_elements_cache() { + if (valobj_sp) + Update(); +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibcxxStdUnorderedMapSyntheticFrontEnd::CalculateNumChildren() { + return m_num_elements; +} + +static void consumeInlineNamespace(llvm::StringRef &name) { + // Delete past an inline namespace, if any: __[a-zA-Z0-9_]+:: + auto scratch = name; + if (scratch.consume_front("__") && std::isalnum(scratch[0])) { + scratch = scratch.drop_while([](char c) { return std::isalnum(c); }); + if (scratch.consume_front("::")) { + // Successfully consumed a namespace. + name = scratch; + } + } +} + +static bool isStdTemplate(ConstString type_name, llvm::StringRef type) { + llvm::StringRef name = type_name.GetStringRef(); + // The type name may be prefixed with `std::__<inline-namespace>::`. + if (name.consume_front("std::")) + consumeInlineNamespace(name); + return name.consume_front(type) && name.starts_with("<"); +} + +static bool isUnorderedMap(ConstString type_name) { + return isStdTemplate(type_name, "unordered_map") || + isStdTemplate(type_name, "unordered_multimap"); +} + +lldb::ValueObjectSP lldb_private::formatters:: + LibcxxStdUnorderedMapSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { + if (idx >= CalculateNumChildrenIgnoringErrors()) + return lldb::ValueObjectSP(); + if (m_tree == nullptr) + return lldb::ValueObjectSP(); + + while (idx >= m_elements_cache.size()) { + if (m_next_element == nullptr) + return lldb::ValueObjectSP(); + + Status error; + ValueObjectSP node_sp = m_next_element->Dereference(error); + if (!node_sp || error.Fail()) + return lldb::ValueObjectSP(); + + ValueObjectSP value_sp = node_sp->GetChildMemberWithName("__value_"); + ValueObjectSP hash_sp = node_sp->GetChildMemberWithName("__hash_"); + if (!hash_sp || !value_sp) { + if (!m_element_type) { + auto p1_sp = m_backend.GetChildAtNamePath({"__table_", "__p1_"}); + if (!p1_sp) + return nullptr; + + ValueObjectSP first_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp); + if (!first_sp) + return nullptr; + + m_element_type = first_sp->GetCompilerType(); + m_element_type = m_element_type.GetTypeTemplateArgument(0); + m_element_type = m_element_type.GetPointeeType(); + m_node_type = m_element_type; + m_element_type = m_element_type.GetTypeTemplateArgument(0); + // This synthetic provider is used for both unordered_(multi)map and + // unordered_(multi)set. For unordered_map, the element type has an + // additional type layer, an internal struct (`__hash_value_type`) + // that wraps a std::pair. Peel away the internal wrapper type - whose + // structure is of no value to users, to expose the std::pair. This + // matches the structure returned by the std::map synthetic provider. + if (isUnorderedMap(m_backend.GetTypeName())) { + std::string name; + CompilerType field_type = m_element_type.GetFieldAtIndex( + 0, name, nullptr, nullptr, nullptr); + CompilerType actual_type = field_type.GetTypedefedType(); + if (isStdTemplate(actual_type.GetTypeName(), "pair")) + m_element_type = actual_type; + } + } + if (!m_node_type) + return nullptr; + node_sp = m_next_element->Cast(m_node_type.GetPointerType()) + ->Dereference(error); + if (!node_sp || error.Fail()) + return nullptr; + + hash_sp = node_sp->GetChildMemberWithName("__hash_"); + if (!hash_sp) + return nullptr; + + value_sp = node_sp->GetChildMemberWithName("__value_"); + if (!value_sp) { + // clang-format off + // Since D101206 (ba79fb2e1f), libc++ wraps the `__value_` in an + // anonymous union. + // Child 0: __hash_node_base base class + // Child 1: __hash_ + // Child 2: anonymous union + // clang-format on + auto anon_union_sp = node_sp->GetChildAtIndex(2); + if (!anon_union_sp) + return nullptr; + + value_sp = anon_union_sp->GetChildMemberWithName("__value_"); + if (!value_sp) + return nullptr; + } + } + m_elements_cache.push_back( + {value_sp.get(), hash_sp->GetValueAsUnsigned(0)}); + m_next_element = node_sp->GetChildMemberWithName("__next_").get(); + if (!m_next_element || m_next_element->GetValueAsUnsigned(0) == 0) + m_next_element = nullptr; + } + + std::pair<ValueObject *, uint64_t> val_hash = m_elements_cache[idx]; + if (!val_hash.first) + return lldb::ValueObjectSP(); + StreamString stream; + stream.Printf("[%" PRIu64 "]", (uint64_t)idx); + DataExtractor data; + Status error; + val_hash.first->GetData(data, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + const bool thread_and_frame_only_if_stopped = true; + ExecutionContext exe_ctx = val_hash.first->GetExecutionContextRef().Lock( + thread_and_frame_only_if_stopped); + return CreateValueObjectFromData(stream.GetString(), data, exe_ctx, + m_element_type); +} + +lldb::ChildCacheState +lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::Update() { + m_num_elements = 0; + m_next_element = nullptr; + m_elements_cache.clear(); + ValueObjectSP table_sp = m_backend.GetChildMemberWithName("__table_"); + if (!table_sp) + return lldb::ChildCacheState::eRefetch; + + ValueObjectSP p2_sp = table_sp->GetChildMemberWithName("__p2_"); + if (!p2_sp) + return lldb::ChildCacheState::eRefetch; + + ValueObjectSP num_elements_sp = GetFirstValueOfLibCXXCompressedPair(*p2_sp); + if (!num_elements_sp) + return lldb::ChildCacheState::eRefetch; + + ValueObjectSP p1_sp = table_sp->GetChildMemberWithName("__p1_"); + if (!p1_sp) + return lldb::ChildCacheState::eRefetch; + + ValueObjectSP value_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp); + if (!value_sp) + return lldb::ChildCacheState::eRefetch; + + m_tree = value_sp->GetChildMemberWithName("__next_").get(); + if (m_tree == nullptr) + return lldb::ChildCacheState::eRefetch; + + m_num_elements = num_elements_sp->GetValueAsUnsigned(0); + + if (m_num_elements > 0) + m_next_element = m_tree; + + return lldb::ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + return ExtractIndexFromString(name.GetCString()); +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibcxxStdUnorderedMapSyntheticFrontEnd(valobj_sp) + : nullptr); +} + +lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd:: + LibCxxUnorderedMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +lldb::ChildCacheState lldb_private::formatters:: + LibCxxUnorderedMapIteratorSyntheticFrontEnd::Update() { + m_pair_sp.reset(); + + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + + TargetSP target_sp(valobj_sp->GetTargetSP()); + + if (!target_sp) + return lldb::ChildCacheState::eRefetch; + + // Get the unordered_map::iterator + // m_backend is an 'unordered_map::iterator', aka a + // '__hash_map_iterator<__hash_table::iterator>' + // + // __hash_map_iterator::__i_ is a __hash_table::iterator (aka + // __hash_iterator<__node_pointer>) + auto hash_iter_sp = valobj_sp->GetChildMemberWithName("__i_"); + if (!hash_iter_sp) + return lldb::ChildCacheState::eRefetch; + + // Type is '__hash_iterator<__node_pointer>' + auto hash_iter_type = hash_iter_sp->GetCompilerType(); + if (!hash_iter_type.IsValid()) + return lldb::ChildCacheState::eRefetch; + + // Type is '__node_pointer' + auto node_pointer_type = hash_iter_type.GetTypeTemplateArgument(0); + if (!node_pointer_type.IsValid()) + return lldb::ChildCacheState::eRefetch; + + // Cast the __hash_iterator to a __node_pointer (which stores our key/value + // pair) + auto hash_node_sp = hash_iter_sp->Cast(node_pointer_type); + if (!hash_node_sp) + return lldb::ChildCacheState::eRefetch; + + auto key_value_sp = hash_node_sp->GetChildMemberWithName("__value_"); + if (!key_value_sp) { + // clang-format off + // Since D101206 (ba79fb2e1f), libc++ wraps the `__value_` in an + // anonymous union. + // Child 0: __hash_node_base base class + // Child 1: __hash_ + // Child 2: anonymous union + // clang-format on + auto anon_union_sp = hash_node_sp->GetChildAtIndex(2); + if (!anon_union_sp) + return lldb::ChildCacheState::eRefetch; + + key_value_sp = anon_union_sp->GetChildMemberWithName("__value_"); + if (!key_value_sp) + return lldb::ChildCacheState::eRefetch; + } + + // Create the synthetic child, which is a pair where the key and value can be + // retrieved by querying the synthetic frontend for + // GetIndexOfChildWithName("first") and GetIndexOfChildWithName("second") + // respectively. + // + // std::unordered_map stores the actual key/value pair in + // __hash_value_type::__cc_ (or previously __cc). + auto potential_child_sp = key_value_sp->Clone(ConstString("pair")); + if (potential_child_sp) + if (potential_child_sp->GetNumChildrenIgnoringErrors() == 1) + if (auto child0_sp = potential_child_sp->GetChildAtIndex(0); + child0_sp->GetName() == "__cc_" || child0_sp->GetName() == "__cc") + potential_child_sp = child0_sp->Clone(ConstString("pair")); + + m_pair_sp = potential_child_sp; + + return lldb::ChildCacheState::eRefetch; +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibCxxUnorderedMapIteratorSyntheticFrontEnd::CalculateNumChildren() { + return 2; +} + +lldb::ValueObjectSP lldb_private::formatters:: + LibCxxUnorderedMapIteratorSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { + if (m_pair_sp) + return m_pair_sp->GetChildAtIndex(idx); + return lldb::ValueObjectSP(); +} + +bool lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (name == "first") + return 0; + if (name == "second") + return 1; + return UINT32_MAX; +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibCxxUnorderedMapIteratorSyntheticFrontEnd(valobj_sp) + : nullptr); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxValarray.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxValarray.cpp new file mode 100644 index 000000000000..99f94406e99a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxValarray.cpp @@ -0,0 +1,145 @@ +//===-- LibCxxValarray.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 "LibCxx.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace lldb_private { +namespace formatters { +class LibcxxStdValarraySyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibcxxStdValarraySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibcxxStdValarraySyntheticFrontEnd() 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: + /// A non-owning pointer to valarray's __begin_ member. + ValueObject *m_start = nullptr; + /// A non-owning pointer to valarray's __end_ member. + ValueObject *m_finish = nullptr; + /// The type of valarray's template argument T. + CompilerType m_element_type; + /// The sizeof valarray's template argument T. + uint32_t m_element_size = 0; +}; + +} // namespace formatters +} // namespace lldb_private + +lldb_private::formatters::LibcxxStdValarraySyntheticFrontEnd:: + LibcxxStdValarraySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type() { + if (valobj_sp) + Update(); +} + +lldb_private::formatters::LibcxxStdValarraySyntheticFrontEnd:: + ~LibcxxStdValarraySyntheticFrontEnd() { + // these need to stay around because they are child objects who will follow + // their parent's life cycle + // delete m_start; + // delete m_finish; +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibcxxStdValarraySyntheticFrontEnd::CalculateNumChildren() { + if (!m_start || !m_finish) + return 0; + uint64_t start_val = m_start->GetValueAsUnsigned(0); + uint64_t finish_val = m_finish->GetValueAsUnsigned(0); + + if (start_val == 0 || finish_val == 0) + return 0; + + if (start_val >= finish_val) + return 0; + + size_t num_children = (finish_val - start_val); + if (num_children % m_element_size) + return 0; + return num_children / m_element_size; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxStdValarraySyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + if (!m_start || !m_finish) + return lldb::ValueObjectSP(); + + uint64_t offset = idx * m_element_size; + offset = offset + m_start->GetValueAsUnsigned(0); + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + return CreateValueObjectFromAddress(name.GetString(), offset, + m_backend.GetExecutionContextRef(), + m_element_type); +} + +lldb::ChildCacheState +lldb_private::formatters::LibcxxStdValarraySyntheticFrontEnd::Update() { + m_start = m_finish = nullptr; + + CompilerType type = m_backend.GetCompilerType(); + if (type.GetNumTemplateArguments() == 0) + return ChildCacheState::eRefetch; + + m_element_type = type.GetTypeTemplateArgument(0); + if (std::optional<uint64_t> size = m_element_type.GetByteSize(nullptr)) + m_element_size = *size; + + if (m_element_size == 0) + return ChildCacheState::eRefetch; + + ValueObjectSP start = m_backend.GetChildMemberWithName("__begin_"); + ValueObjectSP finish = m_backend.GetChildMemberWithName("__end_"); + + if (!start || !finish) + return ChildCacheState::eRefetch; + + m_start = start.get(); + m_finish = finish.get(); + + return ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::LibcxxStdValarraySyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibcxxStdValarraySyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (!m_start || !m_finish) + return std::numeric_limits<size_t>::max(); + return ExtractIndexFromString(name.GetCString()); +} + +lldb_private::SyntheticChildrenFrontEnd * +lldb_private::formatters::LibcxxStdValarraySyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + return new LibcxxStdValarraySyntheticFrontEnd(valobj_sp); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp new file mode 100644 index 000000000000..62794318e077 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp @@ -0,0 +1,281 @@ +//===-- LibCxxVariant.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 "LibCxxVariant.h" +#include "LibCxx.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Symbol/CompilerType.h" +#include "lldb/Utility/LLDBAssert.h" + +#include "llvm/ADT/ScopeExit.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; + +// libc++ variant implementation contains two members that we care about both +// are contained in the __impl member. +// - __index which tells us which of the variadic template types is the active +// type for the variant +// - __data is a variadic union which recursively contains itself as member +// which refers to the tailing variadic types. +// - __head which refers to the leading non pack type +// - __value refers to the actual value contained +// - __tail which refers to the remaining pack types +// +// e.g. given std::variant<int,double,char> v1 +// +// (lldb) frame var -R v1.__impl.__data +//(... __union<... 0, int, double, char>) v1.__impl.__data = { +// ... +// __head = { +// __value = ... +// } +// __tail = { +// ... +// __head = { +// __value = ... +// } +// __tail = { +// ... +// __head = { +// __value = ... +// ... +// +// So given +// - __index equal to 0 the active value is contained in +// +// __data.__head.__value +// +// - __index equal to 1 the active value is contained in +// +// __data.__tail.__head.__value +// +// - __index equal to 2 the active value is contained in +// +// __data.__tail.__tail.__head.__value +// + +namespace { +// libc++ std::variant index could have one of three states +// 1) Valid, we can obtain it and its not variant_npos +// 2) Invalid, we can't obtain it or it is not a type we expect +// 3) NPos, its value is variant_npos which means the variant has no value +enum class LibcxxVariantIndexValidity { Valid, Invalid, NPos }; + +uint64_t VariantNposValue(uint64_t index_byte_size) { + switch (index_byte_size) { + case 1: + return static_cast<uint8_t>(-1); + case 2: + return static_cast<uint16_t>(-1); + case 4: + return static_cast<uint32_t>(-1); + } + lldbassert(false && "Unknown index type size"); + return static_cast<uint32_t>(-1); // Fallback to stable ABI type. +} + +LibcxxVariantIndexValidity +LibcxxVariantGetIndexValidity(ValueObjectSP &impl_sp) { + ValueObjectSP index_sp(impl_sp->GetChildMemberWithName("__index")); + + if (!index_sp) + return LibcxxVariantIndexValidity::Invalid; + + // In the stable ABI, the type of __index is just int. + // In the unstable ABI, where _LIBCPP_ABI_VARIANT_INDEX_TYPE_OPTIMIZATION is + // enabled, the type can either be unsigned char/short/int depending on + // how many variant types there are. + // We only need to do this here when comparing against npos, because npos is + // just `-1`, but that translates to different unsigned values depending on + // the byte size. + CompilerType index_type = index_sp->GetCompilerType(); + + std::optional<uint64_t> index_type_bytes = index_type.GetByteSize(nullptr); + if (!index_type_bytes) + return LibcxxVariantIndexValidity::Invalid; + + uint64_t npos_value = VariantNposValue(*index_type_bytes); + uint64_t index_value = index_sp->GetValueAsUnsigned(0); + + if (index_value == npos_value) + return LibcxxVariantIndexValidity::NPos; + + return LibcxxVariantIndexValidity::Valid; +} + +std::optional<uint64_t> LibcxxVariantIndexValue(ValueObjectSP &impl_sp) { + ValueObjectSP index_sp(impl_sp->GetChildMemberWithName("__index")); + + if (!index_sp) + return {}; + + return {index_sp->GetValueAsUnsigned(0)}; +} + +ValueObjectSP LibcxxVariantGetNthHead(ValueObjectSP &impl_sp, uint64_t index) { + ValueObjectSP data_sp(impl_sp->GetChildMemberWithName("__data")); + + if (!data_sp) + return ValueObjectSP{}; + + ValueObjectSP current_level = data_sp; + for (uint64_t n = index; n != 0; --n) { + ValueObjectSP tail_sp(current_level->GetChildMemberWithName("__tail")); + + if (!tail_sp) + return ValueObjectSP{}; + + current_level = tail_sp; + } + + return current_level->GetChildMemberWithName("__head"); +} +} // namespace + +namespace lldb_private { +namespace formatters { +bool LibcxxVariantSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options) { + ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); + if (!valobj_sp) + return false; + + ValueObjectSP impl_sp = GetChildMemberWithName( + *valobj_sp, {ConstString("__impl_"), ConstString("__impl")}); + + if (!impl_sp) + return false; + + LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp); + + if (validity == LibcxxVariantIndexValidity::Invalid) + return false; + + if (validity == LibcxxVariantIndexValidity::NPos) { + stream.Printf(" No Value"); + return true; + } + + auto optional_index_value = LibcxxVariantIndexValue(impl_sp); + + if (!optional_index_value) + return false; + + uint64_t index_value = *optional_index_value; + + ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value); + + if (!nth_head) + return false; + + CompilerType head_type = nth_head->GetCompilerType(); + + if (!head_type) + return false; + + CompilerType template_type = head_type.GetTypeTemplateArgument(1); + + if (!template_type) + return false; + + stream << " Active Type = " << template_type.GetDisplayTypeName() << " "; + + return true; +} +} // namespace formatters +} // namespace lldb_private + +namespace { +class VariantFrontEnd : public SyntheticChildrenFrontEnd { +public: + VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) { + Update(); + } + + size_t GetIndexOfChildWithName(ConstString name) override { + return formatters::ExtractIndexFromString(name.GetCString()); + } + + bool MightHaveChildren() override { return true; } + lldb::ChildCacheState Update() override; + llvm::Expected<uint32_t> CalculateNumChildren() override { return m_size; } + ValueObjectSP GetChildAtIndex(uint32_t idx) override; + +private: + size_t m_size = 0; +}; +} // namespace + +lldb::ChildCacheState VariantFrontEnd::Update() { + m_size = 0; + ValueObjectSP impl_sp = formatters::GetChildMemberWithName( + m_backend, {ConstString("__impl_"), ConstString("__impl")}); + if (!impl_sp) + return lldb::ChildCacheState::eRefetch; + + LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp); + + if (validity == LibcxxVariantIndexValidity::Invalid) + return lldb::ChildCacheState::eRefetch; + + if (validity == LibcxxVariantIndexValidity::NPos) + return lldb::ChildCacheState::eReuse; + + m_size = 1; + + return lldb::ChildCacheState::eRefetch; +} + +ValueObjectSP VariantFrontEnd::GetChildAtIndex(uint32_t idx) { + if (idx >= m_size) + return {}; + + ValueObjectSP impl_sp = formatters::GetChildMemberWithName( + m_backend, {ConstString("__impl_"), ConstString("__impl")}); + if (!impl_sp) + return {}; + + auto optional_index_value = LibcxxVariantIndexValue(impl_sp); + + if (!optional_index_value) + return {}; + + uint64_t index_value = *optional_index_value; + + ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value); + + if (!nth_head) + return {}; + + CompilerType head_type = nth_head->GetCompilerType(); + + if (!head_type) + return {}; + + CompilerType template_type = head_type.GetTypeTemplateArgument(1); + + if (!template_type) + return {}; + + ValueObjectSP head_value(nth_head->GetChildMemberWithName("__value")); + + if (!head_value) + return {}; + + return head_value->Clone(ConstString("Value")); +} + +SyntheticChildrenFrontEnd * +formatters::LibcxxVariantFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new VariantFrontEnd(*valobj_sp); + return nullptr; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.h new file mode 100644 index 000000000000..9488d94efda7 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.h @@ -0,0 +1,29 @@ +//===-- LibCxxVariant.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_CPLUSPLUS_LIBCXXVARIANT_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBCXXVARIANT_H + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/Utility/Stream.h" + +namespace lldb_private { +namespace formatters { +bool LibcxxVariantSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libc++ std::variant<> + +SyntheticChildrenFrontEnd *LibcxxVariantFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +} // namespace formatters +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBCXXVARIANT_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp new file mode 100644 index 000000000000..461fed35164b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp @@ -0,0 +1,279 @@ +//===-- LibCxxVector.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 "LibCxx.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Utility/ConstString.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace lldb_private { +namespace formatters { +class LibcxxStdVectorSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibcxxStdVectorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibcxxStdVectorSyntheticFrontEnd() 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: + ValueObject *m_start = nullptr; + ValueObject *m_finish = nullptr; + CompilerType m_element_type; + uint32_t m_element_size = 0; +}; + +class LibcxxVectorBoolSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibcxxVectorBoolSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override { return true; } + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + CompilerType m_bool_type; + ExecutionContextRef m_exe_ctx_ref; + uint64_t m_count = 0; + lldb::addr_t m_base_data_address = 0; + std::map<size_t, lldb::ValueObjectSP> m_children; +}; + +} // namespace formatters +} // namespace lldb_private + +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd:: + LibcxxStdVectorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type() { + if (valobj_sp) + Update(); +} + +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd:: + ~LibcxxStdVectorSyntheticFrontEnd() { + // these need to stay around because they are child objects who will follow + // their parent's life cycle + // delete m_start; + // delete m_finish; +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibcxxStdVectorSyntheticFrontEnd::CalculateNumChildren() { + if (!m_start || !m_finish) + return 0; + uint64_t start_val = m_start->GetValueAsUnsigned(0); + uint64_t finish_val = m_finish->GetValueAsUnsigned(0); + + if (start_val == 0 || finish_val == 0) + return 0; + + if (start_val >= finish_val) + return 0; + + size_t num_children = (finish_val - start_val); + if (num_children % m_element_size) + return 0; + return num_children / m_element_size; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + if (!m_start || !m_finish) + return lldb::ValueObjectSP(); + + uint64_t offset = idx * m_element_size; + offset = offset + m_start->GetValueAsUnsigned(0); + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + return CreateValueObjectFromAddress(name.GetString(), offset, + m_backend.GetExecutionContextRef(), + m_element_type); +} + +lldb::ChildCacheState +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::Update() { + m_start = m_finish = nullptr; + ValueObjectSP data_type_finder_sp( + m_backend.GetChildMemberWithName("__end_cap_")); + if (!data_type_finder_sp) + return lldb::ChildCacheState::eRefetch; + + data_type_finder_sp = + GetFirstValueOfLibCXXCompressedPair(*data_type_finder_sp); + if (!data_type_finder_sp) + return lldb::ChildCacheState::eRefetch; + + m_element_type = data_type_finder_sp->GetCompilerType().GetPointeeType(); + if (std::optional<uint64_t> size = m_element_type.GetByteSize(nullptr)) { + m_element_size = *size; + + if (m_element_size > 0) { + // store raw pointers or end up with a circular dependency + m_start = m_backend.GetChildMemberWithName("__begin_").get(); + m_finish = m_backend.GetChildMemberWithName("__end_").get(); + } + } + return lldb::ChildCacheState::eRefetch; +} + +bool lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (!m_start || !m_finish) + return UINT32_MAX; + return ExtractIndexFromString(name.GetCString()); +} + +lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd:: + LibcxxVectorBoolSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_bool_type(), m_exe_ctx_ref(), + m_children() { + if (valobj_sp) { + Update(); + m_bool_type = + valobj_sp->GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeBool); + } +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibcxxVectorBoolSyntheticFrontEnd::CalculateNumChildren() { + return m_count; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + auto iter = m_children.find(idx), end = m_children.end(); + if (iter != end) + return iter->second; + if (idx >= m_count) + return {}; + if (m_base_data_address == 0 || m_count == 0) + return {}; + if (!m_bool_type) + return {}; + size_t byte_idx = (idx >> 3); // divide by 8 to get byte index + size_t bit_index = (idx & 7); // efficient idx % 8 for bit index + lldb::addr_t byte_location = m_base_data_address + byte_idx; + ProcessSP process_sp(m_exe_ctx_ref.GetProcessSP()); + if (!process_sp) + return {}; + uint8_t byte = 0; + uint8_t mask = 0; + Status err; + size_t bytes_read = process_sp->ReadMemory(byte_location, &byte, 1, err); + if (err.Fail() || bytes_read == 0) + return {}; + mask = 1 << bit_index; + bool bit_set = ((byte & mask) != 0); + std::optional<uint64_t> size = m_bool_type.GetByteSize(nullptr); + if (!size) + return {}; + WritableDataBufferSP buffer_sp(new DataBufferHeap(*size, 0)); + if (bit_set && buffer_sp && buffer_sp->GetBytes()) { + // regardless of endianness, anything non-zero is true + *(buffer_sp->GetBytes()) = 1; + } + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + ValueObjectSP retval_sp(CreateValueObjectFromData( + name.GetString(), + DataExtractor(buffer_sp, process_sp->GetByteOrder(), + process_sp->GetAddressByteSize()), + m_exe_ctx_ref, m_bool_type)); + if (retval_sp) + m_children[idx] = retval_sp; + return retval_sp; +} + +/*(std::__1::vector<std::__1::allocator<bool> >) vBool = { + __begin_ = 0x00000001001000e0 + __size_ = 56 + __cap_alloc_ = { + std::__1::__libcpp_compressed_pair_imp<unsigned long, + std::__1::allocator<unsigned long> > = { + __first_ = 1 + } + } + }*/ + +lldb::ChildCacheState +lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::Update() { + m_children.clear(); + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + ValueObjectSP size_sp(valobj_sp->GetChildMemberWithName("__size_")); + if (!size_sp) + return lldb::ChildCacheState::eRefetch; + m_count = size_sp->GetValueAsUnsigned(0); + if (!m_count) + return lldb::ChildCacheState::eReuse; + ValueObjectSP begin_sp(valobj_sp->GetChildMemberWithName("__begin_")); + if (!begin_sp) { + m_count = 0; + return lldb::ChildCacheState::eRefetch; + } + m_base_data_address = begin_sp->GetValueAsUnsigned(0); + if (!m_base_data_address) { + m_count = 0; + return lldb::ChildCacheState::eRefetch; + } + return lldb::ChildCacheState::eRefetch; +} + +size_t lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (!m_count || !m_base_data_address) + return UINT32_MAX; + const char *item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildrenIgnoringErrors()) + return UINT32_MAX; + return idx; +} + +lldb_private::SyntheticChildrenFrontEnd * +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + CompilerType type = valobj_sp->GetCompilerType(); + if (!type.IsValid() || type.GetNumTemplateArguments() == 0) + return nullptr; + CompilerType arg_type = type.GetTypeTemplateArgument(0); + if (arg_type.GetTypeName() == "bool") + return new LibcxxVectorBoolSyntheticFrontEnd(valobj_sp); + return new LibcxxStdVectorSyntheticFrontEnd(valobj_sp); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp new file mode 100644 index 000000000000..86bb575af5ca --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp @@ -0,0 +1,472 @@ +//===-- LibStdcpp.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 "LibStdcpp.h" +#include "LibCxx.h" + +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/StringPrinter.h" +#include "lldb/DataFormatters/VectorIterator.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 <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace { + +class LibstdcppMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd { + /* + (std::_Rb_tree_iterator<std::pair<const int, std::basic_string<char, + std::char_traits<char>, std::allocator<char> > > >) ibeg = { + (_Base_ptr) _M_node = 0x0000000100103910 { + (std::_Rb_tree_color) _M_color = _S_black + (std::_Rb_tree_node_base::_Base_ptr) _M_parent = 0x00000001001038c0 + (std::_Rb_tree_node_base::_Base_ptr) _M_left = 0x0000000000000000 + (std::_Rb_tree_node_base::_Base_ptr) _M_right = 0x0000000000000000 + } + } + */ + +public: + explicit LibstdcppMapIteratorSyntheticFrontEnd(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; + lldb::addr_t m_pair_address = 0; + CompilerType m_pair_type; + lldb::ValueObjectSP m_pair_sp; +}; + +class LibStdcppSharedPtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + explicit LibStdcppSharedPtrSyntheticFrontEnd(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: + + // The lifetime of a ValueObject and all its derivative ValueObjects + // (children, clones, etc.) is managed by a ClusterManager. These + // objects are only destroyed when every shared pointer to any of them + // is destroyed, so we must not store a shared pointer to any ValueObject + // derived from our backend ValueObject (since we're in the same cluster). + ValueObject* m_ptr_obj = nullptr; // Underlying pointer (held, not owned) + ValueObject* m_obj_obj = nullptr; // Underlying object (held, not owned) +}; + +} // end of anonymous namespace + +LibstdcppMapIteratorSyntheticFrontEnd::LibstdcppMapIteratorSyntheticFrontEnd( + lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_pair_type(), + m_pair_sp() { + if (valobj_sp) + Update(); +} + +lldb::ChildCacheState LibstdcppMapIteratorSyntheticFrontEnd::Update() { + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + + TargetSP target_sp(valobj_sp->GetTargetSP()); + + if (!target_sp) + return lldb::ChildCacheState::eRefetch; + + bool is_64bit = (target_sp->GetArchitecture().GetAddressByteSize() == 8); + + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + + ValueObjectSP _M_node_sp(valobj_sp->GetChildMemberWithName("_M_node")); + if (!_M_node_sp) + return lldb::ChildCacheState::eRefetch; + + m_pair_address = _M_node_sp->GetValueAsUnsigned(0); + if (m_pair_address == 0) + return lldb::ChildCacheState::eRefetch; + + m_pair_address += (is_64bit ? 32 : 16); + + CompilerType my_type(valobj_sp->GetCompilerType()); + if (my_type.GetNumTemplateArguments() >= 1) { + CompilerType pair_type = my_type.GetTypeTemplateArgument(0); + if (!pair_type) + return lldb::ChildCacheState::eRefetch; + m_pair_type = pair_type; + } else + return lldb::ChildCacheState::eRefetch; + + return lldb::ChildCacheState::eReuse; +} + +llvm::Expected<uint32_t> +LibstdcppMapIteratorSyntheticFrontEnd::CalculateNumChildren() { + return 2; +} + +lldb::ValueObjectSP +LibstdcppMapIteratorSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { + if (m_pair_address != 0 && m_pair_type) { + if (!m_pair_sp) + m_pair_sp = CreateValueObjectFromAddress("pair", m_pair_address, + m_exe_ctx_ref, m_pair_type); + if (m_pair_sp) + return m_pair_sp->GetChildAtIndex(idx); + } + return lldb::ValueObjectSP(); +} + +bool LibstdcppMapIteratorSyntheticFrontEnd::MightHaveChildren() { return true; } + +size_t LibstdcppMapIteratorSyntheticFrontEnd::GetIndexOfChildWithName( + ConstString name) { + if (name == "first") + return 0; + if (name == "second") + return 1; + return UINT32_MAX; +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibstdcppMapIteratorSyntheticFrontEnd(valobj_sp) + : nullptr); +} + +/* + (lldb) fr var ibeg --ptr-depth 1 + (__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > >) + ibeg = { + _M_current = 0x00000001001037a0 { + *_M_current = 1 + } + } + */ + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibStdcppVectorIteratorSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new VectorIteratorSyntheticFrontEnd( + valobj_sp, {ConstString("_M_current")}) + : nullptr); +} + +lldb_private::formatters::VectorIteratorSyntheticFrontEnd:: + VectorIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp, + llvm::ArrayRef<ConstString> item_names) + : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), + m_item_names(item_names), m_item_sp() { + if (valobj_sp) + Update(); +} + +lldb::ChildCacheState VectorIteratorSyntheticFrontEnd::Update() { + m_item_sp.reset(); + + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + + ValueObjectSP item_ptr = + formatters::GetChildMemberWithName(*valobj_sp, m_item_names); + if (!item_ptr) + return lldb::ChildCacheState::eRefetch; + if (item_ptr->GetValueAsUnsigned(0) == 0) + return lldb::ChildCacheState::eRefetch; + Status err; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + m_item_sp = CreateValueObjectFromAddress( + "item", item_ptr->GetValueAsUnsigned(0), m_exe_ctx_ref, + item_ptr->GetCompilerType().GetPointeeType()); + if (err.Fail()) + m_item_sp.reset(); + return lldb::ChildCacheState::eRefetch; +} + +llvm::Expected<uint32_t> +VectorIteratorSyntheticFrontEnd::CalculateNumChildren() { + return 1; +} + +lldb::ValueObjectSP +VectorIteratorSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { + if (idx == 0) + return m_item_sp; + return lldb::ValueObjectSP(); +} + +bool VectorIteratorSyntheticFrontEnd::MightHaveChildren() { return true; } + +size_t VectorIteratorSyntheticFrontEnd::GetIndexOfChildWithName( + ConstString name) { + if (name == "item") + return 0; + return UINT32_MAX; +} + +bool lldb_private::formatters::LibStdcppStringSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + const bool scalar_is_load_addr = true; + AddressType addr_type; + lldb::addr_t addr_of_string = LLDB_INVALID_ADDRESS; + if (valobj.IsPointerOrReferenceType()) { + Status error; + ValueObjectSP pointee_sp = valobj.Dereference(error); + if (pointee_sp && error.Success()) + addr_of_string = pointee_sp->GetAddressOf(scalar_is_load_addr, &addr_type); + } else + addr_of_string = + valobj.GetAddressOf(scalar_is_load_addr, &addr_type); + if (addr_of_string != LLDB_INVALID_ADDRESS) { + switch (addr_type) { + case eAddressTypeLoad: { + ProcessSP process_sp(valobj.GetProcessSP()); + if (!process_sp) + return false; + + StringPrinter::ReadStringAndDumpToStreamOptions options(valobj); + Status error; + lldb::addr_t addr_of_data = + process_sp->ReadPointerFromMemory(addr_of_string, error); + if (error.Fail() || addr_of_data == 0 || + addr_of_data == LLDB_INVALID_ADDRESS) + return false; + options.SetLocation(addr_of_data); + options.SetTargetSP(valobj.GetTargetSP()); + options.SetStream(&stream); + options.SetNeedsZeroTermination(false); + options.SetBinaryZeroIsTerminator(true); + lldb::addr_t size_of_data = process_sp->ReadPointerFromMemory( + addr_of_string + process_sp->GetAddressByteSize(), error); + if (error.Fail()) + return false; + options.SetSourceSize(size_of_data); + options.SetHasSourceSize(true); + + if (!StringPrinter::ReadStringAndDumpToStream< + StringPrinter::StringElementType::UTF8>(options)) { + stream.Printf("Summary Unavailable"); + return true; + } else + return true; + } break; + case eAddressTypeHost: + break; + case eAddressTypeInvalid: + case eAddressTypeFile: + break; + } + } + return false; +} + +bool lldb_private::formatters::LibStdcppWStringSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + const bool scalar_is_load_addr = true; + AddressType addr_type; + lldb::addr_t addr_of_string = + valobj.GetAddressOf(scalar_is_load_addr, &addr_type); + if (addr_of_string != LLDB_INVALID_ADDRESS) { + switch (addr_type) { + case eAddressTypeLoad: { + ProcessSP process_sp(valobj.GetProcessSP()); + if (!process_sp) + return false; + + CompilerType wchar_compiler_type = + valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeWChar); + + if (!wchar_compiler_type) + return false; + + // Safe to pass nullptr for exe_scope here. + std::optional<uint64_t> size = wchar_compiler_type.GetBitSize(nullptr); + if (!size) + return false; + const uint32_t wchar_size = *size; + + StringPrinter::ReadStringAndDumpToStreamOptions options(valobj); + Status error; + lldb::addr_t addr_of_data = + process_sp->ReadPointerFromMemory(addr_of_string, error); + if (error.Fail() || addr_of_data == 0 || + addr_of_data == LLDB_INVALID_ADDRESS) + return false; + options.SetLocation(addr_of_data); + options.SetTargetSP(valobj.GetTargetSP()); + options.SetStream(&stream); + options.SetNeedsZeroTermination(false); + options.SetBinaryZeroIsTerminator(false); + lldb::addr_t size_of_data = process_sp->ReadPointerFromMemory( + addr_of_string + process_sp->GetAddressByteSize(), error); + if (error.Fail()) + return false; + options.SetSourceSize(size_of_data); + options.SetHasSourceSize(true); + options.SetPrefixToken("L"); + + switch (wchar_size) { + case 8: + return StringPrinter::ReadStringAndDumpToStream< + StringPrinter::StringElementType::UTF8>(options); + case 16: + return StringPrinter::ReadStringAndDumpToStream< + StringPrinter::StringElementType::UTF16>(options); + case 32: + return StringPrinter::ReadStringAndDumpToStream< + StringPrinter::StringElementType::UTF32>(options); + default: + stream.Printf("size for wchar_t is not valid"); + return true; + } + return true; + } break; + case eAddressTypeHost: + break; + case eAddressTypeInvalid: + case eAddressTypeFile: + break; + } + } + return false; +} + +LibStdcppSharedPtrSyntheticFrontEnd::LibStdcppSharedPtrSyntheticFrontEnd( + lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +llvm::Expected<uint32_t> +LibStdcppSharedPtrSyntheticFrontEnd::CalculateNumChildren() { + return 1; +} + +lldb::ValueObjectSP +LibStdcppSharedPtrSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { + if (idx == 0) + return m_ptr_obj->GetSP(); + if (idx == 1) { + if (m_ptr_obj && !m_obj_obj) { + Status error; + ValueObjectSP obj_obj = m_ptr_obj->Dereference(error); + if (error.Success()) + m_obj_obj = obj_obj->Clone(ConstString("object")).get(); + } + if (m_obj_obj) + return m_obj_obj->GetSP(); + } + return lldb::ValueObjectSP(); +} + +lldb::ChildCacheState LibStdcppSharedPtrSyntheticFrontEnd::Update() { + auto backend = m_backend.GetSP(); + if (!backend) + return lldb::ChildCacheState::eRefetch; + + auto valobj_sp = backend->GetNonSyntheticValue(); + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + + auto ptr_obj_sp = valobj_sp->GetChildMemberWithName("_M_ptr"); + if (!ptr_obj_sp) + return lldb::ChildCacheState::eRefetch; + + m_ptr_obj = ptr_obj_sp->Clone(ConstString("pointer")).get(); + m_obj_obj = nullptr; + + return lldb::ChildCacheState::eRefetch; +} + +bool LibStdcppSharedPtrSyntheticFrontEnd::MightHaveChildren() { return true; } + +size_t LibStdcppSharedPtrSyntheticFrontEnd::GetIndexOfChildWithName( + ConstString name) { + if (name == "pointer") + return 0; + if (name == "object" || name == "$$dereference$$") + return 1; + return UINT32_MAX; +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibStdcppSharedPtrSyntheticFrontEnd(valobj_sp) + : nullptr); +} + +bool lldb_private::formatters::LibStdcppSmartPointerSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); + if (!valobj_sp) + return false; + + ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName("_M_ptr")); + if (!ptr_sp) + return false; + + ValueObjectSP usecount_sp( + valobj_sp->GetChildAtNamePath({"_M_refcount", "_M_pi", "_M_use_count"})); + if (!usecount_sp) + return false; + + if (ptr_sp->GetValueAsUnsigned(0) == 0 || + usecount_sp->GetValueAsUnsigned(0) == 0) { + stream.Printf("nullptr"); + return true; + } + + Status error; + ValueObjectSP pointee_sp = ptr_sp->Dereference(error); + if (pointee_sp && error.Success()) { + if (pointee_sp->DumpPrintableRepresentation( + stream, ValueObject::eValueObjectRepresentationStyleSummary, + lldb::eFormatInvalid, + ValueObject::PrintableRepresentationSpecialCases::eDisable, + false)) { + return true; + } + } + + stream.Printf("ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(0)); + return true; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h new file mode 100644 index 000000000000..1c1c8fdb9ea4 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h @@ -0,0 +1,67 @@ +//===-- LibStdcpp.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_CPLUSPLUS_LIBSTDCPP_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBSTDCPP_H + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/Utility/Stream.h" + +namespace lldb_private { +namespace formatters { +bool LibStdcppStringSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libcstdc++ c++11 std::string + +bool LibStdcppWStringSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libcstdc++ c++11 std::wstring + +bool LibStdcppSmartPointerSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions + &options); // libstdc++ std::shared_ptr<> and std::weak_ptr<> + +bool LibStdcppUniquePointerSummaryProvider( + ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); // libstdc++ std::unique_ptr<> + +SyntheticChildrenFrontEnd * +LibstdcppMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibStdcppTupleSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibStdcppBitsetSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibStdcppOptionalSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibStdcppVectorIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibStdcppSharedPtrSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * +LibStdcppUniquePtrSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +} // namespace formatters +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBSTDCPP_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp new file mode 100644 index 000000000000..05199ba35b9a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp @@ -0,0 +1,112 @@ +//===-- LibStdcppTuple.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 "LibStdcpp.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/Utility/ConstString.h" + +#include <memory> +#include <vector> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace { + +class LibStdcppTupleSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + explicit LibStdcppTupleSyntheticFrontEnd(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: + // The lifetime of a ValueObject and all its derivative ValueObjects + // (children, clones, etc.) is managed by a ClusterManager. These + // objects are only destroyed when every shared pointer to any of them + // is destroyed, so we must not store a shared pointer to any ValueObject + // derived from our backend ValueObject (since we're in the same cluster). + std::vector<ValueObject*> m_members; +}; + +} // end of anonymous namespace + +LibStdcppTupleSyntheticFrontEnd::LibStdcppTupleSyntheticFrontEnd( + lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + Update(); +} + +lldb::ChildCacheState LibStdcppTupleSyntheticFrontEnd::Update() { + m_members.clear(); + + ValueObjectSP valobj_backend_sp = m_backend.GetSP(); + if (!valobj_backend_sp) + return lldb::ChildCacheState::eRefetch; + + ValueObjectSP next_child_sp = valobj_backend_sp->GetNonSyntheticValue(); + while (next_child_sp != nullptr) { + ValueObjectSP current_child = next_child_sp; + next_child_sp = nullptr; + + size_t child_count = current_child->GetNumChildrenIgnoringErrors(); + for (size_t i = 0; i < child_count; ++i) { + ValueObjectSP child_sp = current_child->GetChildAtIndex(i); + llvm::StringRef name_str = child_sp->GetName().GetStringRef(); + if (name_str.starts_with("std::_Tuple_impl<")) { + next_child_sp = child_sp; + } else if (name_str.starts_with("std::_Head_base<")) { + ValueObjectSP value_sp = + child_sp->GetChildMemberWithName("_M_head_impl"); + if (value_sp) { + StreamString name; + name.Printf("[%zd]", m_members.size()); + m_members.push_back(value_sp->Clone(ConstString(name.GetString())).get()); + } + } + } + } + + return lldb::ChildCacheState::eRefetch; +} + +bool LibStdcppTupleSyntheticFrontEnd::MightHaveChildren() { return true; } + +lldb::ValueObjectSP +LibStdcppTupleSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { + if (idx < m_members.size() && m_members[idx]) + return m_members[idx]->GetSP(); + return lldb::ValueObjectSP(); +} + +llvm::Expected<uint32_t> +LibStdcppTupleSyntheticFrontEnd::CalculateNumChildren() { + return m_members.size(); +} + +size_t LibStdcppTupleSyntheticFrontEnd::GetIndexOfChildWithName( + ConstString name) { + return ExtractIndexFromString(name.GetCString()); +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibStdcppTupleSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibStdcppTupleSyntheticFrontEnd(valobj_sp) : nullptr); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp new file mode 100644 index 000000000000..92f540d9ca52 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp @@ -0,0 +1,183 @@ +//===-- LibStdcppUniquePointer.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 "LibStdcpp.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/Utility/ConstString.h" + +#include <memory> +#include <vector> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace { + +class LibStdcppUniquePtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + explicit LibStdcppUniquePtrSyntheticFrontEnd(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; + + bool GetSummary(Stream &stream, const TypeSummaryOptions &options); + +private: + // The lifetime of a ValueObject and all its derivative ValueObjects + // (children, clones, etc.) is managed by a ClusterManager. These + // objects are only destroyed when every shared pointer to any of them + // is destroyed, so we must not store a shared pointer to any ValueObject + // derived from our backend ValueObject (since we're in the same cluster). + ValueObject* m_ptr_obj = nullptr; + ValueObject* m_obj_obj = nullptr; + ValueObject* m_del_obj = nullptr; + + ValueObjectSP GetTuple(); +}; + +} // end of anonymous namespace + +LibStdcppUniquePtrSyntheticFrontEnd::LibStdcppUniquePtrSyntheticFrontEnd( + lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + Update(); +} + +ValueObjectSP LibStdcppUniquePtrSyntheticFrontEnd::GetTuple() { + ValueObjectSP valobj_backend_sp = m_backend.GetSP(); + + if (!valobj_backend_sp) + return nullptr; + + ValueObjectSP valobj_sp = valobj_backend_sp->GetNonSyntheticValue(); + if (!valobj_sp) + return nullptr; + + ValueObjectSP obj_child_sp = valobj_sp->GetChildMemberWithName("_M_t"); + if (!obj_child_sp) + return nullptr; + + ValueObjectSP obj_subchild_sp = obj_child_sp->GetChildMemberWithName("_M_t"); + + // if there is a _M_t subchild, the tuple is found in the obj_subchild_sp + // (for libstdc++ 6.0.23). + if (obj_subchild_sp) { + return obj_subchild_sp; + } + + return obj_child_sp; +} + +lldb::ChildCacheState LibStdcppUniquePtrSyntheticFrontEnd::Update() { + ValueObjectSP tuple_sp = GetTuple(); + + if (!tuple_sp) + return lldb::ChildCacheState::eRefetch; + + std::unique_ptr<SyntheticChildrenFrontEnd> tuple_frontend( + LibStdcppTupleSyntheticFrontEndCreator(nullptr, tuple_sp)); + + ValueObjectSP ptr_obj = tuple_frontend->GetChildAtIndex(0); + if (ptr_obj) + m_ptr_obj = ptr_obj->Clone(ConstString("pointer")).get(); + + // Add a 'deleter' child if there was a non-empty deleter type specified. + // + // The object might have size=1 in the TypeSystem but occupies no dedicated + // storage due to no_unique_address, so infer the actual size from the total + // size of the unique_ptr class. If sizeof(unique_ptr) == sizeof(void*) then + // the deleter is empty and should be hidden. + if (tuple_sp->GetByteSize() > ptr_obj->GetByteSize()) { + ValueObjectSP del_obj = tuple_frontend->GetChildAtIndex(1); + if (del_obj) + m_del_obj = del_obj->Clone(ConstString("deleter")).get(); + } + m_obj_obj = nullptr; + + return lldb::ChildCacheState::eRefetch; +} + +bool LibStdcppUniquePtrSyntheticFrontEnd::MightHaveChildren() { return true; } + +lldb::ValueObjectSP +LibStdcppUniquePtrSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { + if (idx == 0 && m_ptr_obj) + return m_ptr_obj->GetSP(); + if (idx == 1 && m_del_obj) + return m_del_obj->GetSP(); + if (idx == 2) { + if (m_ptr_obj && !m_obj_obj) { + Status error; + ValueObjectSP obj_obj = m_ptr_obj->Dereference(error); + if (error.Success()) { + m_obj_obj = obj_obj->Clone(ConstString("object")).get(); + } + } + if (m_obj_obj) + return m_obj_obj->GetSP(); + } + return lldb::ValueObjectSP(); +} + +llvm::Expected<uint32_t> +LibStdcppUniquePtrSyntheticFrontEnd::CalculateNumChildren() { + if (m_del_obj) + return 2; + return 1; +} + +size_t LibStdcppUniquePtrSyntheticFrontEnd::GetIndexOfChildWithName( + ConstString name) { + if (name == "ptr" || name == "pointer") + return 0; + if (name == "del" || name == "deleter") + return 1; + if (name == "obj" || name == "object" || name == "$$dereference$$") + return 2; + return UINT32_MAX; +} + +bool LibStdcppUniquePtrSyntheticFrontEnd::GetSummary( + Stream &stream, const TypeSummaryOptions &options) { + if (!m_ptr_obj) + return false; + + bool success; + uint64_t ptr_value = m_ptr_obj->GetValueAsUnsigned(0, &success); + if (!success) + return false; + if (ptr_value == 0) + stream.Printf("nullptr"); + else + stream.Printf("0x%" PRIx64, ptr_value); + return true; +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibStdcppUniquePtrSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibStdcppUniquePtrSyntheticFrontEnd(valobj_sp) + : nullptr); +} + +bool lldb_private::formatters::LibStdcppUniquePointerSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + LibStdcppUniquePtrSyntheticFrontEnd formatter(valobj.GetSP()); + return formatter.GetSummary(stream, options); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.cpp new file mode 100644 index 000000000000..72afc6fe75ac --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.cpp @@ -0,0 +1,105 @@ +//===-- MSVCUndecoratedNameParser.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 "MSVCUndecoratedNameParser.h" + +#include <stack> + +MSVCUndecoratedNameParser::MSVCUndecoratedNameParser(llvm::StringRef name) { + // Global ctor and dtor are global functions. + if (name.contains("dynamic initializer for") || + name.contains("dynamic atexit destructor for")) { + m_specifiers.emplace_back(name, name); + return; + } + + std::size_t last_base_start = 0; + + std::stack<std::size_t> stack; + unsigned int open_angle_brackets = 0; + for (size_t i = 0; i < name.size(); i++) { + switch (name[i]) { + case '<': + // Do not treat `operator<' and `operator<<' as templates + // (sometimes they represented as `<' and `<<' in the name). + if (i == last_base_start || + (i == last_base_start + 1 && name[last_base_start] == '<')) + break; + + stack.push(i); + open_angle_brackets++; + + break; + case '>': + if (!stack.empty() && name[stack.top()] == '<') { + open_angle_brackets--; + stack.pop(); + } + + break; + case '`': + stack.push(i); + + break; + case '\'': + while (!stack.empty()) { + std::size_t top = stack.top(); + if (name[top] == '<') + open_angle_brackets--; + + stack.pop(); + + if (name[top] == '`') + break; + } + + break; + case ':': + if (open_angle_brackets) + break; + if (i == 0 || name[i - 1] != ':') + break; + + m_specifiers.emplace_back(name.take_front(i - 1), + name.slice(last_base_start, i - 1)); + + last_base_start = i + 1; + break; + default: + break; + } + } + + m_specifiers.emplace_back(name, name.drop_front(last_base_start)); +} + +bool MSVCUndecoratedNameParser::IsMSVCUndecoratedName(llvm::StringRef name) { + return name.contains('`'); +} + +bool MSVCUndecoratedNameParser::ExtractContextAndIdentifier( + llvm::StringRef name, llvm::StringRef &context, + llvm::StringRef &identifier) { + MSVCUndecoratedNameParser parser(name); + llvm::ArrayRef<MSVCUndecoratedNameSpecifier> specs = parser.GetSpecifiers(); + + std::size_t count = specs.size(); + identifier = count > 0 ? specs[count - 1].GetBaseName() : ""; + context = count > 1 ? specs[count - 2].GetFullName() : ""; + + return count; +} + +llvm::StringRef MSVCUndecoratedNameParser::DropScope(llvm::StringRef name) { + MSVCUndecoratedNameParser parser(name); + llvm::ArrayRef<MSVCUndecoratedNameSpecifier> specs = parser.GetSpecifiers(); + if (specs.empty()) + return ""; + + return specs[specs.size() - 1].GetBaseName(); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h new file mode 100644 index 000000000000..e5b60a0a1d5b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h @@ -0,0 +1,50 @@ +//===-- MSVCUndecoratedNameParser.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_CPLUSPLUS_MSVCUNDECORATEDNAMEPARSER_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_MSVCUNDECORATEDNAMEPARSER_H + +#include <vector> + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" + +class MSVCUndecoratedNameSpecifier { +public: + MSVCUndecoratedNameSpecifier(llvm::StringRef full_name, + llvm::StringRef base_name) + : m_full_name(full_name), m_base_name(base_name) {} + + llvm::StringRef GetFullName() const { return m_full_name; } + llvm::StringRef GetBaseName() const { return m_base_name; } + +private: + llvm::StringRef m_full_name; + llvm::StringRef m_base_name; +}; + +class MSVCUndecoratedNameParser { +public: + explicit MSVCUndecoratedNameParser(llvm::StringRef name); + + llvm::ArrayRef<MSVCUndecoratedNameSpecifier> GetSpecifiers() const { + return m_specifiers; + } + + static bool IsMSVCUndecoratedName(llvm::StringRef name); + static bool ExtractContextAndIdentifier(llvm::StringRef name, + llvm::StringRef &context, + llvm::StringRef &identifier); + + static llvm::StringRef DropScope(llvm::StringRef name); + +private: + std::vector<MSVCUndecoratedNameSpecifier> m_specifiers; +}; + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.cpp new file mode 100644 index 000000000000..6e56e29f8c31 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.cpp @@ -0,0 +1,259 @@ +//===-- ClangHighlighter.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 "ClangHighlighter.h" + +#include "lldb/Host/FileSystem.h" +#include "lldb/Target/Language.h" +#include "lldb/Utility/AnsiTerminal.h" +#include "lldb/Utility/StreamString.h" + +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/MemoryBuffer.h" +#include <optional> + +using namespace lldb_private; + +bool ClangHighlighter::isKeyword(llvm::StringRef token) const { + return keywords.contains(token); +} + +ClangHighlighter::ClangHighlighter() { +#define KEYWORD(X, N) keywords.insert(#X); +#include "clang/Basic/TokenKinds.def" +} + +/// Determines which style should be applied to the given token. +/// \param highlighter +/// The current highlighter that should use the style. +/// \param token +/// The current token. +/// \param tok_str +/// The string in the source code the token represents. +/// \param options +/// The style we use for coloring the source code. +/// \param in_pp_directive +/// If we are currently in a preprocessor directive. NOTE: This is +/// passed by reference and will be updated if the current token starts +/// or ends a preprocessor directive. +/// \return +/// The ColorStyle that should be applied to the token. +static HighlightStyle::ColorStyle +determineClangStyle(const ClangHighlighter &highlighter, + const clang::Token &token, llvm::StringRef tok_str, + const HighlightStyle &options, bool &in_pp_directive) { + using namespace clang; + + if (token.is(tok::comment)) { + // If we were in a preprocessor directive before, we now left it. + in_pp_directive = false; + return options.comment; + } else if (in_pp_directive || token.getKind() == tok::hash) { + // Let's assume that the rest of the line is a PP directive. + in_pp_directive = true; + // Preprocessor directives are hard to match, so we have to hack this in. + return options.pp_directive; + } else if (tok::isStringLiteral(token.getKind())) + return options.string_literal; + else if (tok::isLiteral(token.getKind())) + return options.scalar_literal; + else if (highlighter.isKeyword(tok_str)) + return options.keyword; + else + switch (token.getKind()) { + case tok::raw_identifier: + case tok::identifier: + return options.identifier; + case tok::l_brace: + case tok::r_brace: + return options.braces; + case tok::l_square: + case tok::r_square: + return options.square_brackets; + case tok::l_paren: + case tok::r_paren: + return options.parentheses; + case tok::comma: + return options.comma; + case tok::coloncolon: + case tok::colon: + return options.colon; + + case tok::amp: + case tok::ampamp: + case tok::ampequal: + case tok::star: + case tok::starequal: + case tok::plus: + case tok::plusplus: + case tok::plusequal: + case tok::minus: + case tok::arrow: + case tok::minusminus: + case tok::minusequal: + case tok::tilde: + case tok::exclaim: + case tok::exclaimequal: + case tok::slash: + case tok::slashequal: + case tok::percent: + case tok::percentequal: + case tok::less: + case tok::lessless: + case tok::lessequal: + case tok::lesslessequal: + case tok::spaceship: + case tok::greater: + case tok::greatergreater: + case tok::greaterequal: + case tok::greatergreaterequal: + case tok::caret: + case tok::caretequal: + case tok::pipe: + case tok::pipepipe: + case tok::pipeequal: + case tok::question: + case tok::equal: + case tok::equalequal: + return options.operators; + default: + break; + } + return HighlightStyle::ColorStyle(); +} + +void ClangHighlighter::Highlight(const HighlightStyle &options, + llvm::StringRef line, + std::optional<size_t> cursor_pos, + llvm::StringRef previous_lines, + Stream &result) const { + using namespace clang; + + FileSystemOptions file_opts; + FileManager file_mgr(file_opts, + FileSystem::Instance().GetVirtualFileSystem()); + + // The line might end in a backslash which would cause Clang to drop the + // backslash and the terminating new line. This makes sense when parsing C++, + // but when highlighting we care about preserving the backslash/newline. To + // not lose this information we remove the new line here so that Clang knows + // this is just a single line we are highlighting. We add back the newline + // after tokenizing. + llvm::StringRef line_ending = ""; + // There are a few legal line endings Clang recognizes and we need to + // temporarily remove from the string. + if (line.consume_back("\r\n")) + line_ending = "\r\n"; + else if (line.consume_back("\n")) + line_ending = "\n"; + else if (line.consume_back("\r")) + line_ending = "\r"; + + unsigned line_number = previous_lines.count('\n') + 1U; + + // Let's build the actual source code Clang needs and setup some utility + // objects. + std::string full_source = previous_lines.str() + line.str(); + llvm::IntrusiveRefCntPtr<DiagnosticIDs> diag_ids(new DiagnosticIDs()); + llvm::IntrusiveRefCntPtr<DiagnosticOptions> diags_opts( + new DiagnosticOptions()); + DiagnosticsEngine diags(diag_ids, diags_opts); + clang::SourceManager SM(diags, file_mgr); + auto buf = llvm::MemoryBuffer::getMemBuffer(full_source); + + FileID FID = SM.createFileID(buf->getMemBufferRef()); + + // Let's just enable the latest ObjC and C++ which should get most tokens + // right. + LangOptions Opts; + Opts.ObjC = true; + // FIXME: This should probably set CPlusPlus, CPlusPlus11, ... too + Opts.CPlusPlus17 = true; + Opts.LineComment = true; + + Lexer lex(FID, buf->getMemBufferRef(), SM, Opts); + // The lexer should keep whitespace around. + lex.SetKeepWhitespaceMode(true); + + // Keeps track if we have entered a PP directive. + bool in_pp_directive = false; + + // True once we actually lexed the user provided line. + bool found_user_line = false; + + // True if we already highlighted the token under the cursor, false otherwise. + bool highlighted_cursor = false; + Token token; + bool exit = false; + while (!exit) { + // Returns true if this is the last token we get from the lexer. + exit = lex.LexFromRawLexer(token); + + bool invalid = false; + unsigned current_line_number = + SM.getSpellingLineNumber(token.getLocation(), &invalid); + if (current_line_number != line_number) + continue; + found_user_line = true; + + // We don't need to print any tokens without a spelling line number. + if (invalid) + continue; + + // Same as above but with the column number. + invalid = false; + unsigned start = SM.getSpellingColumnNumber(token.getLocation(), &invalid); + if (invalid) + continue; + // Column numbers start at 1, but indexes in our string start at 0. + --start; + + // Annotations don't have a length, so let's skip them. + if (token.isAnnotation()) + continue; + + // Extract the token string from our source code. + llvm::StringRef tok_str = line.substr(start, token.getLength()); + + // If the token is just an empty string, we can skip all the work below. + if (tok_str.empty()) + continue; + + // If the cursor is inside this token, we have to apply the 'selected' + // highlight style before applying the actual token color. + llvm::StringRef to_print = tok_str; + StreamString storage; + auto end = start + token.getLength(); + if (cursor_pos && end > *cursor_pos && !highlighted_cursor) { + highlighted_cursor = true; + options.selected.Apply(storage, tok_str); + to_print = storage.GetString(); + } + + // See how we are supposed to highlight this token. + HighlightStyle::ColorStyle color = + determineClangStyle(*this, token, tok_str, options, in_pp_directive); + + color.Apply(result, to_print); + } + + // Add the line ending we trimmed before tokenizing. + result << line_ending; + + // If we went over the whole file but couldn't find our own file, then + // somehow our setup was wrong. When we're in release mode we just give the + // user the normal line and pretend we don't know how to highlight it. In + // debug mode we bail out with an assert as this should never happen. + if (!found_user_line) { + result << line; + assert(false && "We couldn't find the user line in the input file?"); + } +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.h b/contrib/llvm-project/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.h new file mode 100644 index 000000000000..20bb09502d42 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.h @@ -0,0 +1,38 @@ +//===-- ClangHighlighter.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_CLANGCOMMON_CLANGHIGHLIGHTER_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CLANGCOMMON_CLANGHIGHLIGHTER_H + +#include "lldb/Utility/Stream.h" +#include "llvm/ADT/StringSet.h" + +#include "lldb/Core/Highlighter.h" +#include <optional> + +namespace lldb_private { + +class ClangHighlighter : public Highlighter { + llvm::StringSet<> keywords; + +public: + ClangHighlighter(); + llvm::StringRef GetName() const override { return "clang"; } + + void Highlight(const HighlightStyle &options, llvm::StringRef line, + std::optional<size_t> cursor_pos, + llvm::StringRef previous_lines, Stream &s) const override; + + /// Returns true if the given string represents a keywords in any Clang + /// supported language. + bool isKeyword(llvm::StringRef token) const; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_CLANGCOMMON_CLANGHIGHLIGHTER_H 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 diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.cpp b/contrib/llvm-project/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.cpp new file mode 100644 index 000000000000..79830e529df2 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.cpp @@ -0,0 +1,45 @@ +//===-- ObjCPlusPlusLanguage.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 "ObjCPlusPlusLanguage.h" + +#include "lldb/Core/PluginManager.h" +#include "lldb/Utility/ConstString.h" + +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(ObjCPlusPlusLanguage) + +bool ObjCPlusPlusLanguage::IsSourceFile(llvm::StringRef file_path) const { + const auto suffixes = {".h", ".mm"}; + for (auto suffix : suffixes) { + if (file_path.ends_with_insensitive(suffix)) + return true; + } + return false; +} + +void ObjCPlusPlusLanguage::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), "Objective-C++ Language", + CreateInstance); +} + +void ObjCPlusPlusLanguage::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +// Static Functions +Language *ObjCPlusPlusLanguage::CreateInstance(lldb::LanguageType language) { + switch (language) { + case lldb::eLanguageTypeObjC_plus_plus: + return new ObjCPlusPlusLanguage(); + default: + return nullptr; + } +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h b/contrib/llvm-project/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h new file mode 100644 index 000000000000..1beab9348eb7 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h @@ -0,0 +1,55 @@ +//===-- ObjCPlusPlusLanguage.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_OBJCPLUSPLUS_OBJCPLUSPLUSLANGUAGE_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_OBJCPLUSPLUS_OBJCPLUSPLUSLANGUAGE_H + +#include "Plugins/Language/ClangCommon/ClangHighlighter.h" +#include "lldb/Target/Language.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class ObjCPlusPlusLanguage : public Language { + ClangHighlighter m_highlighter; + +public: + ObjCPlusPlusLanguage() = default; + + ~ObjCPlusPlusLanguage() override = default; + + lldb::LanguageType GetLanguageType() const override { + return lldb::eLanguageTypeObjC_plus_plus; + } + + llvm::StringRef GetUserEntryPointName() const override { return "main"; } + + 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); + + llvm::StringRef GetInstanceVariableName() override { return "self"; } + + static llvm::StringRef GetPluginNameStatic() { return "objcplusplus"; } + + // PluginInterface protocol + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_OBJCPLUSPLUS_OBJCPLUSPLUSLANGUAGE_H |