1 //===-- NSError.cpp -------------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "clang/AST/DeclCXX.h"
13 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
14 #include "lldb/Core/ValueObject.h"
15 #include "lldb/Core/ValueObjectConstResult.h"
16 #include "lldb/DataFormatters/FormattersHelpers.h"
17 #include "lldb/Target/Target.h"
18 #include "lldb/Utility/DataBufferHeap.h"
19 #include "lldb/Utility/Endian.h"
20 #include "lldb/Utility/Status.h"
21 #include "lldb/Utility/Stream.h"
23 #include "Plugins/Language/ObjC/NSString.h"
24 #include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
27 using namespace lldb_private
;
28 using namespace lldb_private::formatters
;
30 static lldb::addr_t
DerefToNSErrorPointer(ValueObject
&valobj
) {
31 CompilerType
valobj_type(valobj
.GetCompilerType());
32 Flags
type_flags(valobj_type
.GetTypeInfo());
33 if (type_flags
.AllClear(eTypeHasValue
)) {
34 if (valobj
.IsBaseClass() && valobj
.GetParent())
35 return valobj
.GetParent()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS
);
37 lldb::addr_t ptr_value
= valobj
.GetValueAsUnsigned(LLDB_INVALID_ADDRESS
);
38 if (type_flags
.AllSet(eTypeIsPointer
)) {
39 CompilerType
pointee_type(valobj_type
.GetPointeeType());
40 Flags
pointee_flags(pointee_type
.GetTypeInfo());
41 if (pointee_flags
.AllSet(eTypeIsPointer
)) {
42 if (ProcessSP process_sp
= valobj
.GetProcessSP()) {
44 ptr_value
= process_sp
->ReadPointerFromMemory(ptr_value
, error
);
51 return LLDB_INVALID_ADDRESS
;
54 bool lldb_private::formatters::NSError_SummaryProvider(
55 ValueObject
&valobj
, Stream
&stream
, const TypeSummaryOptions
&options
) {
56 ProcessSP
process_sp(valobj
.GetProcessSP());
60 lldb::addr_t ptr_value
= DerefToNSErrorPointer(valobj
);
61 if (ptr_value
== LLDB_INVALID_ADDRESS
)
64 size_t ptr_size
= process_sp
->GetAddressByteSize();
65 lldb::addr_t code_location
= ptr_value
+ 2 * ptr_size
;
66 lldb::addr_t domain_location
= ptr_value
+ 3 * ptr_size
;
69 uint64_t code
= process_sp
->ReadUnsignedIntegerFromMemory(code_location
,
74 lldb::addr_t domain_str_value
=
75 process_sp
->ReadPointerFromMemory(domain_location
, error
);
76 if (error
.Fail() || domain_str_value
== LLDB_INVALID_ADDRESS
)
79 if (!domain_str_value
) {
80 stream
.Printf("domain: nil - code: %" PRIu64
, code
);
84 InferiorSizedWord
isw(domain_str_value
, *process_sp
);
85 TypeSystemClangSP scratch_ts_sp
=
86 ScratchTypeSystemClang::GetForTarget(process_sp
->GetTarget());
90 ValueObjectSP domain_str_sp
= ValueObject::CreateValueObjectFromData(
91 "domain_str", isw
.GetAsData(process_sp
->GetByteOrder()),
92 valobj
.GetExecutionContextRef(),
93 scratch_ts_sp
->GetBasicType(lldb::eBasicTypeVoid
).GetPointerType());
98 StreamString domain_str_summary
;
99 if (NSStringSummaryProvider(*domain_str_sp
, domain_str_summary
, options
) &&
100 !domain_str_summary
.Empty()) {
101 stream
.Printf("domain: %s - code: %" PRIu64
, domain_str_summary
.GetData(),
105 stream
.Printf("domain: nil - code: %" PRIu64
, code
);
110 class NSErrorSyntheticFrontEnd
: public SyntheticChildrenFrontEnd
{
112 NSErrorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp
)
113 : SyntheticChildrenFrontEnd(*valobj_sp
) {}
115 ~NSErrorSyntheticFrontEnd() override
= default;
116 // no need to delete m_child_ptr - it's kept alive by the cluster manager on
119 size_t CalculateNumChildren() override
{
127 lldb::ValueObjectSP
GetChildAtIndex(size_t idx
) override
{
129 return lldb::ValueObjectSP();
132 return m_child_ptr
->GetSP();
136 bool Update() override
{
137 m_child_ptr
= nullptr;
140 ProcessSP
process_sp(m_backend
.GetProcessSP());
144 lldb::addr_t userinfo_location
= DerefToNSErrorPointer(m_backend
);
145 if (userinfo_location
== LLDB_INVALID_ADDRESS
)
148 size_t ptr_size
= process_sp
->GetAddressByteSize();
150 userinfo_location
+= 4 * ptr_size
;
152 lldb::addr_t userinfo
=
153 process_sp
->ReadPointerFromMemory(userinfo_location
, error
);
154 if (userinfo
== LLDB_INVALID_ADDRESS
|| error
.Fail())
156 InferiorSizedWord
isw(userinfo
, *process_sp
);
157 TypeSystemClangSP scratch_ts_sp
=
158 ScratchTypeSystemClang::GetForTarget(process_sp
->GetTarget());
161 m_child_sp
= CreateValueObjectFromData(
162 "_userInfo", isw
.GetAsData(process_sp
->GetByteOrder()),
163 m_backend
.GetExecutionContextRef(),
164 scratch_ts_sp
->GetBasicType(lldb::eBasicTypeObjCID
));
168 bool MightHaveChildren() override
{ return true; }
170 size_t GetIndexOfChildWithName(ConstString name
) override
{
171 static ConstString
g_userInfo("_userInfo");
172 if (name
== g_userInfo
)
178 // the child here can be "real" (i.e. an actual child of the root) or
179 // synthetized from raw memory if the former, I need to store a plain pointer
180 // to it - or else a loop of references will cause this entire hierarchy of
181 // values to leak if the latter, then I need to store a SharedPointer to it -
182 // so that it only goes away when everyone else in the cluster goes away oh
184 ValueObject
*m_child_ptr
= nullptr;
185 ValueObjectSP m_child_sp
;
188 SyntheticChildrenFrontEnd
*
189 lldb_private::formatters::NSErrorSyntheticFrontEndCreator(
190 CXXSyntheticChildren
*, lldb::ValueObjectSP valobj_sp
) {
191 lldb::ProcessSP
process_sp(valobj_sp
->GetProcessSP());
194 ObjCLanguageRuntime
*runtime
= ObjCLanguageRuntime::Get(*process_sp
);
198 ObjCLanguageRuntime::ClassDescriptorSP
descriptor(
199 runtime
->GetClassDescriptor(*valobj_sp
.get()));
201 if (!descriptor
.get() || !descriptor
->IsValid())
204 const char *class_name
= descriptor
->GetClassName().GetCString();
206 if (!class_name
|| !*class_name
)
209 if (!strcmp(class_name
, "NSError"))
210 return (new NSErrorSyntheticFrontEnd(valobj_sp
));
211 else if (!strcmp(class_name
, "__NSCFError"))
212 return (new NSErrorSyntheticFrontEnd(valobj_sp
));