|  | //===-- 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 "MsvcStl.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, | 
|  | MsvcStl, | 
|  | }; | 
|  |  | 
|  | GenericOptionalFrontend(ValueObject &valobj, StdLib stdlib); | 
|  |  | 
|  | llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override { | 
|  | if (name == "$$dereference$$") | 
|  | return 0; | 
|  | auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); | 
|  | if (!optional_idx) { | 
|  | return llvm::createStringError("Type has no child named '%s'", | 
|  | name.AsCString()); | 
|  | } | 
|  | return *optional_idx; | 
|  | } | 
|  |  | 
|  | 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) { | 
|  | if (ValueObjectSP payload = m_backend.GetChildMemberWithName("_M_payload")) | 
|  | engaged_sp = payload->GetChildMemberWithName("_M_engaged"); | 
|  | } else if (m_stdlib == StdLib::MsvcStl) | 
|  | engaged_sp = m_backend.GetChildMemberWithName("_Has_value"); | 
|  |  | 
|  | 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; | 
|  | } else if (m_stdlib == StdLib::MsvcStl) | 
|  | // Same issue as with LibCxx | 
|  | val_sp = m_backend.GetChildMemberWithName("_Has_value") | 
|  | ->GetParent() | 
|  | ->GetChildAtIndex(0) | 
|  | ->GetChildMemberWithName("_Value"); | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | bool formatters::IsMsvcStlOptional(ValueObject &valobj) { | 
|  | if (auto valobj_sp = valobj.GetNonSyntheticValue()) | 
|  | return valobj_sp->GetChildMemberWithName("_Has_value") != nullptr; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | SyntheticChildrenFrontEnd *formatters::MsvcStlOptionalSyntheticFrontEndCreator( | 
|  | CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { | 
|  | if (valobj_sp) | 
|  | return new GenericOptionalFrontend( | 
|  | *valobj_sp, GenericOptionalFrontend::StdLib::MsvcStl); | 
|  | return nullptr; | 
|  | } |