Index: lldb.xcodeproj/project.pbxproj =================================================================== --- lldb.xcodeproj/project.pbxproj +++ lldb.xcodeproj/project.pbxproj @@ -400,6 +400,8 @@ AF9FF1F71FAA79FE00474976 /* LibCxxQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF9FF1F61FAA79FE00474976 /* LibCxxQueue.cpp */; }; AF9FF1F51FAA79A400474976 /* LibCxxTuple.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF9FF1F41FAA79A400474976 /* LibCxxTuple.cpp */; }; 945261C41B9A11FC00BF138D /* LibCxxUnorderedMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 945261BA1B9A11E800BF138D /* LibCxxUnorderedMap.cpp */; }; + E414F6F121388F6C00C50BC6 /* LibCxxVariant.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E414F6F021388F6B00C50BC6 /* LibCxxVariant.cpp */; }; + E414F6EE21388F0300C50BC6 /* LibCxxVariant.h in Headers */ = {isa = PBXBuildFile; fileRef = E414F6ED21388F0200C50BC6 /* LibCxxVariant.h */; }; 945261C51B9A11FC00BF138D /* LibCxxVector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 945261BB1B9A11E800BF138D /* LibCxxVector.cpp */; }; 945261C61B9A11FC00BF138D /* LibStdcpp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 945261BC1B9A11E800BF138D /* LibStdcpp.cpp */; }; 4CDB8D6E1DBA91B6006C5B13 /* LibStdcppTuple.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CDB8D681DBA91A6006C5B13 /* LibStdcppTuple.cpp */; }; @@ -2052,6 +2054,8 @@ AF9FF1F61FAA79FE00474976 /* LibCxxQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LibCxxQueue.cpp; path = Language/CPlusPlus/LibCxxQueue.cpp; sourceTree = ""; }; AF9FF1F41FAA79A400474976 /* LibCxxTuple.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LibCxxTuple.cpp; path = Language/CPlusPlus/LibCxxTuple.cpp; sourceTree = ""; }; 945261BA1B9A11E800BF138D /* LibCxxUnorderedMap.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = LibCxxUnorderedMap.cpp; path = Language/CPlusPlus/LibCxxUnorderedMap.cpp; sourceTree = ""; }; + E414F6F021388F6B00C50BC6 /* LibCxxVariant.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LibCxxVariant.cpp; path = Language/CPlusPlus/LibCxxVariant.cpp; sourceTree = ""; }; + E414F6ED21388F0200C50BC6 /* LibCxxVariant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LibCxxVariant.h; path = Language/CPlusPlus/LibCxxVariant.h; sourceTree = ""; }; 945261BB1B9A11E800BF138D /* LibCxxVector.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = LibCxxVector.cpp; path = Language/CPlusPlus/LibCxxVector.cpp; sourceTree = ""; }; 945261BC1B9A11E800BF138D /* LibStdcpp.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = LibStdcpp.cpp; path = Language/CPlusPlus/LibStdcpp.cpp; sourceTree = ""; }; 945261BD1B9A11E800BF138D /* LibStdcpp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = LibStdcpp.h; path = Language/CPlusPlus/LibStdcpp.h; sourceTree = ""; }; @@ -6467,6 +6471,8 @@ AF9FF1F61FAA79FE00474976 /* LibCxxQueue.cpp */, AF9FF1F41FAA79A400474976 /* LibCxxTuple.cpp */, 945261BA1B9A11E800BF138D /* LibCxxUnorderedMap.cpp */, + E414F6ED21388F0200C50BC6 /* LibCxxVariant.h */, + E414F6F021388F6B00C50BC6 /* LibCxxVariant.cpp */, 945261BB1B9A11E800BF138D /* LibCxxVector.cpp */, 945261BD1B9A11E800BF138D /* LibStdcpp.h */, 945261BC1B9A11E800BF138D /* LibStdcpp.cpp */, @@ -7064,6 +7070,7 @@ 6D0F614F1C80AB0C00A4ECEE /* JavaLanguageRuntime.h in Headers */, AF27AD561D3603EA00CF2833 /* DynamicLoaderDarwin.h in Headers */, AFCB2BBE1BF577F40018B553 /* PythonExceptionState.h in Headers */, + E414F6EE21388F0300C50BC6 /* LibCxxVariant.h in Headers */, 267F684B1CC02DED0086832B /* ABISysV_s390x.h in Headers */, AF8AD6311BEC28A400150209 /* PlatformAppleWatchSimulator.h in Headers */, ); @@ -7850,6 +7857,7 @@ AF0C112818580CD800C4C45B /* QueueItem.cpp in Sources */, AF254E31170CCC33007AE5C9 /* PlatformDarwinKernel.cpp in Sources */, 2689004913353E0400698AC0 /* Scalar.cpp in Sources */, + E414F6F121388F6C00C50BC6 /* LibCxxVariant.cpp in Sources */, 263FDE601A79A01500E68013 /* FormatEntity.cpp in Sources */, AF8AD62E1BEC28A400150209 /* PlatformAppleTVSimulator.cpp in Sources */, 2689004A13353E0400698AC0 /* SearchFilter.cpp in Sources */, Index: packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/variant/Makefile =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/variant/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../../../make + +CXX_SOURCES := main.cpp + +USE_LIBCPP := 1 +include $(LEVEL)/Makefile.rules +CXXFLAGS += -std=c++17 Index: packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/variant/TestDataFormatterLibcxxVariant.py =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/variant/TestDataFormatterLibcxxVariant.py @@ -0,0 +1,81 @@ +""" +Test lldb data formatter subsystem. +""" + +from __future__ import print_function + + +import os +import time +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class LibcxxOptionalDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(["libc++"]) + ## We are skipping clang version less that 5.0 since this test requires -std=c++17 + @skipIf(oslist=no_match(["macosx"]), compiler="clang", compiler_version=['<', '5.0']) + ## We are skipping gcc version less that 5.1 since this test requires -std=c++17 + @skipIf(compiler="gcc", compiler_version=['<', '5.1']) + + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() + + (self.target, self.process, _, bkpt) = lldbutil.run_to_source_breakpoint(self, '// break here', + lldb.SBFileSpec("main.cpp", False)) + + self.runCmd( "frame variable has_variant" ) + + output = self.res.GetOutput() + + ## The variable has_variant tells us if the test program + ## detected we have a sufficient libc++ version to support variant + ## false means we do not and therefore should skip the test + if output.find("(bool) has_variant = false") != -1 : + self.skipTest( "std::variant not supported" ) + + lldbutil.continue_to_breakpoint(self.process, bkpt) + + self.expect("frame variable v1", + substrs=['v1 = Active Type = int {', + 'Value = 12', + '}']) + + self.expect("frame variable v1_ref", + substrs=['v1_ref = Active Type = int : {', + 'Value = 12', + '}']) + + self.expect("frame variable v_v1", + substrs=['v_v1 = Active Type = std::__1::variant {', + 'Value = Active Type = int {', + 'Value = 12', + '}', + '}']) + + lldbutil.continue_to_breakpoint(self.process, bkpt) + + self.expect("frame variable v1", + substrs=['v1 = Active Type = double {', + 'Value = 2', + '}']) + + lldbutil.continue_to_breakpoint(self.process, bkpt) + + self.expect("frame variable v2", + substrs=['v2 = Active Type = double {', + 'Value = 2', + '}']) + + self.expect("frame variable v3", + substrs=['v3 = Active Type = char {', + 'Value = \'A\'', + '}']) + + self.expect("frame variable v_no_value", + substrs=['v_no_value = No Value']) Index: packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/variant/main.cpp =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/variant/main.cpp @@ -0,0 +1,60 @@ +#include +#include +#include + +// If we have libc++ 4.0 or greater we should have +// According to libc++ C++1z status page https://libcxx.llvm.org/cxx1z_status.html +#if _LIBCPP_VERSION >= 4000 +#include +#define HAVE_VARIANT 1 +#else +#define HAVE_VARIANT 0 +#endif + +struct S { + operator int() { throw 42; } +} ; + + +int main() +{ + bool has_variant = HAVE_VARIANT ; + + printf( "%d\n", has_variant ) ; // break here + +#if HAVE_VARIANT == 1 + std::variant v1; + std::variant &v1_ref = v1; + std::variant v2; + std::variant v3; + std::variant> v_v1 ; + std::variant v_no_value; + + v1 = 12; // v contains int + v_v1 = v1 ; + int i = std::get(v1); + printf( "%d\n", i ); // break here + + v2 = 2.0 ; + double d = std::get(v2) ; + printf( "%f\n", d ); + + v3 = 'A' ; + char c = std::get(v3) ; + printf( "%d\n", c ); + + // Checking v1 above and here to make sure we done maintain the incorrect + // state when we change its value. + v1 = 2.0; + d = std::get(v1) ; + printf( "%f\n", d ); // break here + + try { + v_no_value.emplace<0>(S()); + } catch( ... ) {} + + printf( "%zu\n", v_no_value.index() ) ; +#endif + + return 0; // break here +} Index: source/Plugins/Language/CPlusPlus/CMakeLists.txt =================================================================== --- source/Plugins/Language/CPlusPlus/CMakeLists.txt +++ source/Plugins/Language/CPlusPlus/CMakeLists.txt @@ -13,6 +13,7 @@ LibCxxQueue.cpp LibCxxTuple.cpp LibCxxUnorderedMap.cpp + LibCxxVariant.cpp LibCxxVector.cpp LibStdcpp.cpp LibStdcppTuple.cpp Index: source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp =================================================================== --- source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -39,6 +39,7 @@ #include "CxxStringTypes.h" #include "LibCxx.h" #include "LibCxxAtomic.h" +#include "LibCxxVariant.h" #include "LibStdcpp.h" using namespace lldb; @@ -516,6 +517,10 @@ "libc++ std::optional synthetic children", ConstString("^std::__(ndk)?1::optional<.+>(( )?&)?$"), stl_synth_flags, true); + AddCXXSynthetic(cpp_category_sp, LibcxxVariantFrontEndCreator, + "libc++ std::variant synthetic children", + ConstString("^std::__(ndk)?1::variant<.+>(( )?&)?$"), + stl_synth_flags, true); AddCXXSynthetic( cpp_category_sp, lldb_private::formatters::LibcxxAtomicSyntheticFrontEndCreator, @@ -612,6 +617,11 @@ "libc++ std::optional summary provider", ConstString("^std::__(ndk)?1::optional<.+>(( )?&)?$"), stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::LibcxxVariantSummaryProvider, + "libc++ std::variant summary provider", + ConstString("^std::__(ndk)?1::variant<.+>(( )?&)?$"), + stl_summary_flags, true); stl_summary_flags.SetSkipPointers(true); Index: source/Plugins/Language/CPlusPlus/LibCxx.h =================================================================== --- source/Plugins/Language/CPlusPlus/LibCxx.h +++ source/Plugins/Language/CPlusPlus/LibCxx.h @@ -141,6 +141,10 @@ LibcxxOptionalFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp); +SyntheticChildrenFrontEnd * +LibcxxVariantFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp); + } // namespace formatters } // namespace lldb_private Index: source/Plugins/Language/CPlusPlus/LibCxx.cpp =================================================================== --- source/Plugins/Language/CPlusPlus/LibCxx.cpp +++ source/Plugins/Language/CPlusPlus/LibCxx.cpp @@ -55,6 +55,8 @@ return true; } + + bool lldb_private::formatters::LibcxxSmartPointerSummaryProvider( ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); Index: source/Plugins/Language/CPlusPlus/LibCxxVariant.h =================================================================== --- /dev/null +++ source/Plugins/Language/CPlusPlus/LibCxxVariant.h @@ -0,0 +1,32 @@ +//===-- LibCxxVariant.h -------------------------------------------*- C++ +//-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_LibCxxVariant_h_ +#define liblldb_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 // liblldb_LibCxxVariant_h_ Index: source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp =================================================================== --- /dev/null +++ source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp @@ -0,0 +1,274 @@ +//===-- LibCxxVariant.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "LibCxxVariant.h" +#include "lldb/DataFormatters/FormattersHelpers.h" + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/ScopeExit.h" + +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 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 +}; + +LibcxxVariantIndexValidity LibcxxVariantGetIndexValidity( ValueObjectSP &impl_sp ) { + ValueObjectSP index_sp( + impl_sp->GetChildMemberWithName(ConstString("__index"), true)); + + if (!index_sp) + return LibcxxVariantIndexValidity::INVALID; + + int64_t index_value = index_sp->GetValueAsSigned(0); + + CompilerType index_compiler_type = index_sp->GetCompilerType(); + + // We are expecting two layers of typedefs before we obtain the basic type + // The next two checks verify this. + if (!index_compiler_type.IsTypedefType()) + return LibcxxVariantIndexValidity::INVALID; + + if (!index_compiler_type.GetTypedefedType().IsTypedefType()) + return LibcxxVariantIndexValidity::INVALID; + + lldb::BasicType index_basic_type = + index_compiler_type.GetTypedefedType().GetTypedefedType().GetBasicTypeEnumeration(); + + if ( index_basic_type == eBasicTypeInvalid) + return LibcxxVariantIndexValidity::INVALID; + + if ( index_value == -1 ) + return LibcxxVariantIndexValidity::NPOS; + + return LibcxxVariantIndexValidity::VALID; +} + +llvm::Optional LibcxxVariantIndexValue( ValueObjectSP &impl_sp ) { + ValueObjectSP index_sp( + impl_sp->GetChildMemberWithName(ConstString("__index"), true)); + + if (!index_sp) + return {} ; + + return {index_sp->GetValueAsUnsigned(0)} ; +} + +ValueObjectSP LibcxxVariantGetNthHead( ValueObjectSP &impl_sp, uint64_t index ) { + ValueObjectSP data_sp( + impl_sp->GetChildMemberWithName(ConstString("__data"), true)); + + if (!data_sp) + return ValueObjectSP{}; + + ValueObjectSP current_level = data_sp; + for( uint64_t n = index; n != 0; --n) { + ValueObjectSP tail_sp( + current_level->GetChildMemberWithName(ConstString("__tail"), true)); + + if (!tail_sp) + return ValueObjectSP{} ; + + current_level = tail_sp ; + } + + return current_level->GetChildMemberWithName(ConstString("__head"), true) ; + } ; +} + +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( + valobj_sp->GetChildMemberWithName(ConstString("__impl"), true)); + + 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.Printf(" Active Type = %s ", template_type.GetTypeName().GetCString() ); + + return true; +} +} // namespace formatters +} // namespace lldb_private + +namespace { +class VariantFrontEnd : public SyntheticChildrenFrontEnd { +public: + VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) { + Update(); + } + + size_t GetIndexOfChildWithName(const ConstString &name) override { + return formatters::ExtractIndexFromString(name.GetCString()); + } + + bool MightHaveChildren() override { return true; } + bool Update() override; + size_t CalculateNumChildren() override { return m_size; } + ValueObjectSP GetChildAtIndex(size_t idx) override; + +private: + size_t m_size = 0; + ValueObjectSP m_base_sp; +}; +} // namespace + +bool VariantFrontEnd::Update() { + m_size = 0 ; + ValueObjectSP impl_sp( + m_backend.GetChildMemberWithName(ConstString("__impl"), true)); + if (!impl_sp) + return false; + + LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp ) ; + + if (validity == LibcxxVariantIndexValidity::INVALID) + return false ; + + if (validity == LibcxxVariantIndexValidity::NPOS) + return true ; + + m_size = 1 ; + + return false; +} + +ValueObjectSP VariantFrontEnd::GetChildAtIndex(size_t idx) { + if (idx >= m_size) + return ValueObjectSP(); + + ValueObjectSP impl_sp( + m_backend.GetChildMemberWithName(ConstString("__impl"), true)); + + auto optional_index_value = LibcxxVariantIndexValue( impl_sp ); + + if (!optional_index_value) + return ValueObjectSP() ; + + uint64_t index_value = *optional_index_value ; + + ValueObjectSP nth_head = LibcxxVariantGetNthHead( impl_sp, index_value ) ; + + if (!nth_head) + return ValueObjectSP(); + + CompilerType head_type = nth_head->GetCompilerType() ; + + if (!head_type) + return ValueObjectSP(); + + CompilerType template_type = head_type.GetTypeTemplateArgument(1) ; + + if (!template_type) + return ValueObjectSP(); + + ValueObjectSP head_value( nth_head->GetChildMemberWithName(ConstString("__value"), true)); + + if (!head_value) + return ValueObjectSP(); + + return head_value->Clone(ConstString(ConstString("Value").AsCString())); +} + +SyntheticChildrenFrontEnd * +formatters::LibcxxVariantFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new VariantFrontEnd(*valobj_sp); + return nullptr; +}