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
16using namespace lldb;
17using namespace lldb_private;
18
19bool lldb_private::formatters::GenericOptionalSummaryProvider(
20    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
21  stream.Printf(" Has Value=%s ",
22                valobj.GetNumChildren() == 0 ? "false" : "true");
23
24  return true;
25}
26
27// Synthetic Children Provider
28namespace {
29
30class GenericOptionalFrontend : public SyntheticChildrenFrontEnd {
31public:
32  enum class StdLib {
33    LibCxx,
34    LibStdcpp,
35  };
36
37  GenericOptionalFrontend(ValueObject &valobj, StdLib stdlib);
38
39  size_t GetIndexOfChildWithName(ConstString name) override {
40    return formatters::ExtractIndexFromString(name.GetCString());
41  }
42
43  bool MightHaveChildren() override { return true; }
44  size_t CalculateNumChildren() override { return m_has_value ? 1U : 0U; }
45
46  ValueObjectSP GetChildAtIndex(size_t idx) override;
47  bool Update() override;
48
49private:
50  bool m_has_value = false;
51  StdLib m_stdlib;
52};
53
54} // namespace
55
56GenericOptionalFrontend::GenericOptionalFrontend(ValueObject &valobj,
57                                                 StdLib stdlib)
58    : SyntheticChildrenFrontEnd(valobj), m_stdlib(stdlib) {
59  if (auto target_sp = m_backend.GetTargetSP()) {
60    Update();
61  }
62}
63
64bool GenericOptionalFrontend::Update() {
65  ValueObjectSP engaged_sp;
66
67  if (m_stdlib == StdLib::LibCxx)
68    engaged_sp = m_backend.GetChildMemberWithName("__engaged_");
69  else if (m_stdlib == StdLib::LibStdcpp)
70    engaged_sp = m_backend.GetChildMemberWithName("_M_payload")
71                     ->GetChildMemberWithName("_M_engaged");
72
73  if (!engaged_sp)
74    return false;
75
76  // _M_engaged/__engaged is a bool flag and is true if the optional contains a
77  // value. Converting it to unsigned gives us a size of 1 if it contains a
78  // value and 0 if not.
79  m_has_value = engaged_sp->GetValueAsUnsigned(0) != 0;
80
81  return false;
82}
83
84ValueObjectSP GenericOptionalFrontend::GetChildAtIndex(size_t _idx) {
85  if (!m_has_value)
86    return ValueObjectSP();
87
88  ValueObjectSP val_sp;
89
90  if (m_stdlib == StdLib::LibCxx)
91    // __val_ contains the underlying value of an optional if it has one.
92    // Currently because it is part of an anonymous union
93    // GetChildMemberWithName() does not peer through and find it unless we are
94    // at the parent itself. We can obtain the parent through __engaged_.
95    val_sp = m_backend.GetChildMemberWithName("__engaged_")
96                 ->GetParent()
97                 ->GetChildAtIndex(0)
98                 ->GetChildMemberWithName("__val_");
99  else if (m_stdlib == StdLib::LibStdcpp) {
100    val_sp = m_backend.GetChildMemberWithName("_M_payload")
101                 ->GetChildMemberWithName("_M_payload");
102
103    // In some implementations, _M_value contains the underlying value of an
104    // optional, and in other versions, it's in the payload member.
105    ValueObjectSP candidate = val_sp->GetChildMemberWithName("_M_value");
106    if (candidate)
107      val_sp = candidate;
108  }
109
110  if (!val_sp)
111    return ValueObjectSP();
112
113  CompilerType holder_type = val_sp->GetCompilerType();
114
115  if (!holder_type)
116    return ValueObjectSP();
117
118  return val_sp->Clone(ConstString("Value"));
119}
120
121SyntheticChildrenFrontEnd *
122formatters::LibStdcppOptionalSyntheticFrontEndCreator(
123    CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
124  if (valobj_sp)
125    return new GenericOptionalFrontend(
126        *valobj_sp, GenericOptionalFrontend::StdLib::LibStdcpp);
127  return nullptr;
128}
129
130SyntheticChildrenFrontEnd *formatters::LibcxxOptionalSyntheticFrontEndCreator(
131    CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
132  if (valobj_sp)
133    return new GenericOptionalFrontend(*valobj_sp,
134                                       GenericOptionalFrontend::StdLib::LibCxx);
135  return nullptr;
136}
137