aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lldb/source/Plugins/Language
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/Language')
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.cpp203
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/BlockPointer.h25
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp1761
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h178
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp785
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.h188
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/Coroutines.cpp227
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/Coroutines.h60
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.cpp214
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/CxxStringTypes.h49
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/Generic.h25
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp154
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/GenericOptional.cpp138
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp939
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h244
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.cpp151
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.h33
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp119
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp426
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp499
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxProxyArray.cpp194
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxQueue.cpp65
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxRangesRefView.cpp88
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxSliceArray.cpp166
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp153
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxTuple.cpp95
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp388
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxValarray.cpp145
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp281
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.h29
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp279
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp472
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h67
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppTuple.cpp112
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp183
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.cpp105
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h50
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.cpp259
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.h38
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CF.cpp286
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CF.h32
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CFBasicHash.cpp114
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CFBasicHash.h76
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/Cocoa.cpp1258
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/Cocoa.h116
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CoreMedia.cpp88
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/CoreMedia.h25
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSArray.cpp868
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp1388
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSDictionary.h93
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSError.cpp215
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSException.cpp206
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSIndexPath.cpp321
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSSet.cpp831
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSSet.h39
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.cpp370
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.h42
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCConstants.h44
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp1042
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h205
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.cpp45
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h55
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