1//===-- LibStdcppUniquePointer.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 "LibStdcpp.h"
10
11#include "lldb/Core/ValueObject.h"
12#include "lldb/DataFormatters/FormattersHelpers.h"
13#include "lldb/DataFormatters/TypeSynthetic.h"
14#include "lldb/Utility/ConstString.h"
15
16#include <memory>
17#include <vector>
18
19using namespace lldb;
20using namespace lldb_private;
21using namespace lldb_private::formatters;
22
23namespace {
24
25class LibStdcppUniquePtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
26public:
27  explicit LibStdcppUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
28
29  size_t CalculateNumChildren() override;
30
31  lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
32
33  bool Update() override;
34
35  bool MightHaveChildren() override;
36
37  size_t GetIndexOfChildWithName(ConstString name) override;
38
39  bool GetSummary(Stream &stream, const TypeSummaryOptions &options);
40
41private:
42  // The lifetime of a ValueObject and all its derivative ValueObjects
43  // (children, clones, etc.) is managed by a ClusterManager. These
44  // objects are only destroyed when every shared pointer to any of them
45  // is destroyed, so we must not store a shared pointer to any ValueObject
46  // derived from our backend ValueObject (since we're in the same cluster).
47  ValueObject* m_ptr_obj = nullptr;
48  ValueObject* m_obj_obj = nullptr;
49  ValueObject* m_del_obj = nullptr;
50
51  ValueObjectSP GetTuple();
52};
53
54} // end of anonymous namespace
55
56LibStdcppUniquePtrSyntheticFrontEnd::LibStdcppUniquePtrSyntheticFrontEnd(
57    lldb::ValueObjectSP valobj_sp)
58    : SyntheticChildrenFrontEnd(*valobj_sp) {
59  Update();
60}
61
62ValueObjectSP LibStdcppUniquePtrSyntheticFrontEnd::GetTuple() {
63  ValueObjectSP valobj_backend_sp = m_backend.GetSP();
64
65  if (!valobj_backend_sp)
66    return nullptr;
67
68  ValueObjectSP valobj_sp = valobj_backend_sp->GetNonSyntheticValue();
69  if (!valobj_sp)
70    return nullptr;
71
72  ValueObjectSP obj_child_sp = valobj_sp->GetChildMemberWithName("_M_t");
73  if (!obj_child_sp)
74      return nullptr;
75
76  ValueObjectSP obj_subchild_sp = obj_child_sp->GetChildMemberWithName("_M_t");
77
78  // if there is a _M_t subchild, the tuple is found in the obj_subchild_sp
79  // (for libstdc++ 6.0.23).
80  if (obj_subchild_sp) {
81    return obj_subchild_sp;
82  }
83
84  return obj_child_sp;
85}
86
87bool LibStdcppUniquePtrSyntheticFrontEnd::Update() {
88  ValueObjectSP tuple_sp = GetTuple();
89
90  if (!tuple_sp)
91    return false;
92
93  std::unique_ptr<SyntheticChildrenFrontEnd> tuple_frontend(
94      LibStdcppTupleSyntheticFrontEndCreator(nullptr, tuple_sp));
95
96  ValueObjectSP ptr_obj = tuple_frontend->GetChildAtIndex(0);
97  if (ptr_obj)
98    m_ptr_obj = ptr_obj->Clone(ConstString("pointer")).get();
99
100  // Add a 'deleter' child if there was a non-empty deleter type specified.
101  //
102  // The object might have size=1 in the TypeSystem but occupies no dedicated
103  // storage due to no_unique_address, so infer the actual size from the total
104  // size of the unique_ptr class. If sizeof(unique_ptr) == sizeof(void*) then
105  // the deleter is empty and should be hidden.
106  if (tuple_sp->GetByteSize() > ptr_obj->GetByteSize()) {
107    ValueObjectSP del_obj = tuple_frontend->GetChildAtIndex(1);
108    if (del_obj)
109      m_del_obj = del_obj->Clone(ConstString("deleter")).get();
110  }
111  m_obj_obj = nullptr;
112
113  return false;
114}
115
116bool LibStdcppUniquePtrSyntheticFrontEnd::MightHaveChildren() { return true; }
117
118lldb::ValueObjectSP
119LibStdcppUniquePtrSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
120  if (idx == 0 && m_ptr_obj)
121    return m_ptr_obj->GetSP();
122  if (idx == 1 && m_del_obj)
123    return m_del_obj->GetSP();
124  if (idx == 2) {
125    if (m_ptr_obj && !m_obj_obj) {
126      Status error;
127      ValueObjectSP obj_obj = m_ptr_obj->Dereference(error);
128      if (error.Success()) {
129        m_obj_obj = obj_obj->Clone(ConstString("object")).get();
130      }
131    }
132    if (m_obj_obj)
133      return m_obj_obj->GetSP();
134  }
135  return lldb::ValueObjectSP();
136}
137
138size_t LibStdcppUniquePtrSyntheticFrontEnd::CalculateNumChildren() {
139  if (m_del_obj)
140    return 2;
141  return 1;
142}
143
144size_t LibStdcppUniquePtrSyntheticFrontEnd::GetIndexOfChildWithName(
145    ConstString name) {
146  if (name == "ptr" || name == "pointer")
147    return 0;
148  if (name == "del" || name == "deleter")
149    return 1;
150  if (name == "obj" || name == "object" || name == "$$dereference$$")
151    return 2;
152  return UINT32_MAX;
153}
154
155bool LibStdcppUniquePtrSyntheticFrontEnd::GetSummary(
156    Stream &stream, const TypeSummaryOptions &options) {
157  if (!m_ptr_obj)
158    return false;
159
160  bool success;
161  uint64_t ptr_value = m_ptr_obj->GetValueAsUnsigned(0, &success);
162  if (!success)
163    return false;
164  if (ptr_value == 0)
165    stream.Printf("nullptr");
166  else
167    stream.Printf("0x%" PRIx64, ptr_value);
168  return true;
169}
170
171SyntheticChildrenFrontEnd *
172lldb_private::formatters::LibStdcppUniquePtrSyntheticFrontEndCreator(
173    CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
174  return (valobj_sp ? new LibStdcppUniquePtrSyntheticFrontEnd(valobj_sp)
175                    : nullptr);
176}
177
178bool lldb_private::formatters::LibStdcppUniquePointerSummaryProvider(
179    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
180  LibStdcppUniquePtrSyntheticFrontEnd formatter(valobj.GetSP());
181  return formatter.GetSummary(stream, options);
182}
183