| 1 | //===-- GenericOptional.cpp ----------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===---------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "Generic.h" |
| 10 | #include "LibCxx.h" |
| 11 | #include "LibStdcpp.h" |
| 12 | #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" |
| 13 | #include "lldb/DataFormatters/FormattersHelpers.h" |
| 14 | #include "lldb/Target/Target.h" |
| 15 | |
| 16 | using namespace lldb; |
| 17 | using namespace lldb_private; |
| 18 | |
| 19 | bool lldb_private::formatters::GenericOptionalSummaryProvider( |
| 20 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
| 21 | stream.Printf(format: " Has Value=%s " , |
| 22 | valobj.GetNumChildrenIgnoringErrors() == 0 ? "false" : "true" ); |
| 23 | |
| 24 | return true; |
| 25 | } |
| 26 | |
| 27 | // Synthetic Children Provider |
| 28 | namespace { |
| 29 | |
| 30 | class GenericOptionalFrontend : public SyntheticChildrenFrontEnd { |
| 31 | public: |
| 32 | enum class StdLib { |
| 33 | LibCxx, |
| 34 | LibStdcpp, |
| 35 | }; |
| 36 | |
| 37 | GenericOptionalFrontend(ValueObject &valobj, StdLib stdlib); |
| 38 | |
| 39 | llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override { |
| 40 | if (name == "$$dereference$$" ) |
| 41 | return 0; |
| 42 | auto optional_idx = formatters::ExtractIndexFromString(item_name: name.GetCString()); |
| 43 | if (!optional_idx) { |
| 44 | return llvm::createStringError(Fmt: "Type has no child named '%s'" , |
| 45 | Vals: name.AsCString()); |
| 46 | } |
| 47 | return *optional_idx; |
| 48 | } |
| 49 | |
| 50 | llvm::Expected<uint32_t> CalculateNumChildren() override { |
| 51 | return m_has_value ? 1U : 0U; |
| 52 | } |
| 53 | |
| 54 | ValueObjectSP GetChildAtIndex(uint32_t idx) override; |
| 55 | lldb::ChildCacheState Update() override; |
| 56 | |
| 57 | private: |
| 58 | bool m_has_value = false; |
| 59 | StdLib m_stdlib; |
| 60 | }; |
| 61 | |
| 62 | } // namespace |
| 63 | |
| 64 | GenericOptionalFrontend::GenericOptionalFrontend(ValueObject &valobj, |
| 65 | StdLib stdlib) |
| 66 | : SyntheticChildrenFrontEnd(valobj), m_stdlib(stdlib) { |
| 67 | if (auto target_sp = m_backend.GetTargetSP()) { |
| 68 | Update(); |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | lldb::ChildCacheState GenericOptionalFrontend::Update() { |
| 73 | ValueObjectSP engaged_sp; |
| 74 | |
| 75 | if (m_stdlib == StdLib::LibCxx) |
| 76 | engaged_sp = m_backend.GetChildMemberWithName(name: "__engaged_" ); |
| 77 | else if (m_stdlib == StdLib::LibStdcpp) { |
| 78 | if (ValueObjectSP payload = m_backend.GetChildMemberWithName(name: "_M_payload" )) |
| 79 | engaged_sp = payload->GetChildMemberWithName(name: "_M_engaged" ); |
| 80 | } |
| 81 | |
| 82 | if (!engaged_sp) |
| 83 | return lldb::ChildCacheState::eRefetch; |
| 84 | |
| 85 | // _M_engaged/__engaged is a bool flag and is true if the optional contains a |
| 86 | // value. Converting it to unsigned gives us a size of 1 if it contains a |
| 87 | // value and 0 if not. |
| 88 | m_has_value = engaged_sp->GetValueAsUnsigned(fail_value: 0) != 0; |
| 89 | |
| 90 | return lldb::ChildCacheState::eRefetch; |
| 91 | } |
| 92 | |
| 93 | ValueObjectSP GenericOptionalFrontend::GetChildAtIndex(uint32_t _idx) { |
| 94 | if (!m_has_value) |
| 95 | return ValueObjectSP(); |
| 96 | |
| 97 | ValueObjectSP val_sp; |
| 98 | |
| 99 | if (m_stdlib == StdLib::LibCxx) |
| 100 | // __val_ contains the underlying value of an optional if it has one. |
| 101 | // Currently because it is part of an anonymous union |
| 102 | // GetChildMemberWithName() does not peer through and find it unless we are |
| 103 | // at the parent itself. We can obtain the parent through __engaged_. |
| 104 | val_sp = m_backend.GetChildMemberWithName(name: "__engaged_" ) |
| 105 | ->GetParent() |
| 106 | ->GetChildAtIndex(idx: 0) |
| 107 | ->GetChildMemberWithName(name: "__val_" ); |
| 108 | else if (m_stdlib == StdLib::LibStdcpp) { |
| 109 | val_sp = m_backend.GetChildMemberWithName(name: "_M_payload" ) |
| 110 | ->GetChildMemberWithName(name: "_M_payload" ); |
| 111 | |
| 112 | // In some implementations, _M_value contains the underlying value of an |
| 113 | // optional, and in other versions, it's in the payload member. |
| 114 | ValueObjectSP candidate = val_sp->GetChildMemberWithName(name: "_M_value" ); |
| 115 | if (candidate) |
| 116 | val_sp = candidate; |
| 117 | } |
| 118 | |
| 119 | if (!val_sp) |
| 120 | return ValueObjectSP(); |
| 121 | |
| 122 | CompilerType holder_type = val_sp->GetCompilerType(); |
| 123 | |
| 124 | if (!holder_type) |
| 125 | return ValueObjectSP(); |
| 126 | |
| 127 | return val_sp->Clone(new_name: ConstString("Value" )); |
| 128 | } |
| 129 | |
| 130 | SyntheticChildrenFrontEnd * |
| 131 | formatters::LibStdcppOptionalSyntheticFrontEndCreator( |
| 132 | CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
| 133 | if (valobj_sp) |
| 134 | return new GenericOptionalFrontend( |
| 135 | *valobj_sp, GenericOptionalFrontend::StdLib::LibStdcpp); |
| 136 | return nullptr; |
| 137 | } |
| 138 | |
| 139 | SyntheticChildrenFrontEnd *formatters::LibcxxOptionalSyntheticFrontEndCreator( |
| 140 | CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
| 141 | if (valobj_sp) |
| 142 | return new GenericOptionalFrontend(*valobj_sp, |
| 143 | GenericOptionalFrontend::StdLib::LibCxx); |
| 144 | return nullptr; |
| 145 | } |
| 146 | |