1 //===-- NSException.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 "lldb/DataFormatters/FormattersHelpers.h"
14 #include "lldb/Target/Target.h"
15 #include "lldb/Utility/DataBufferHeap.h"
16 #include "lldb/Utility/Endian.h"
17 #include "lldb/Utility/Status.h"
18 #include "lldb/Utility/Stream.h"
19 #include "lldb/ValueObject/ValueObject.h"
20 #include "lldb/ValueObject/ValueObjectConstResult.h"
22 #include "Plugins/Language/ObjC/NSString.h"
23 #include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
24 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
27 using namespace lldb_private
;
28 using namespace lldb_private::formatters
;
30 static bool ExtractFields(ValueObject
&valobj
, ValueObjectSP
*name_sp
,
31 ValueObjectSP
*reason_sp
, ValueObjectSP
*userinfo_sp
,
32 ValueObjectSP
*reserved_sp
) {
33 ProcessSP
process_sp(valobj
.GetProcessSP());
37 lldb::addr_t ptr
= LLDB_INVALID_ADDRESS
;
39 CompilerType
valobj_type(valobj
.GetCompilerType());
40 Flags
type_flags(valobj_type
.GetTypeInfo());
41 if (type_flags
.AllClear(eTypeHasValue
)) {
42 if (valobj
.IsBaseClass() && valobj
.GetParent())
43 ptr
= valobj
.GetParent()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS
);
45 ptr
= valobj
.GetValueAsUnsigned(LLDB_INVALID_ADDRESS
);
48 if (ptr
== LLDB_INVALID_ADDRESS
)
50 size_t ptr_size
= process_sp
->GetAddressByteSize();
53 auto name
= process_sp
->ReadPointerFromMemory(ptr
+ 1 * ptr_size
, error
);
54 if (error
.Fail() || name
== LLDB_INVALID_ADDRESS
)
56 auto reason
= process_sp
->ReadPointerFromMemory(ptr
+ 2 * ptr_size
, error
);
57 if (error
.Fail() || reason
== LLDB_INVALID_ADDRESS
)
59 auto userinfo
= process_sp
->ReadPointerFromMemory(ptr
+ 3 * ptr_size
, error
);
60 if (error
.Fail() || userinfo
== LLDB_INVALID_ADDRESS
)
62 auto reserved
= process_sp
->ReadPointerFromMemory(ptr
+ 4 * ptr_size
, error
);
63 if (error
.Fail() || reserved
== LLDB_INVALID_ADDRESS
)
66 InferiorSizedWord
name_isw(name
, *process_sp
);
67 InferiorSizedWord
reason_isw(reason
, *process_sp
);
68 InferiorSizedWord
userinfo_isw(userinfo
, *process_sp
);
69 InferiorSizedWord
reserved_isw(reserved
, *process_sp
);
71 TypeSystemClangSP scratch_ts_sp
=
72 ScratchTypeSystemClang::GetForTarget(process_sp
->GetTarget());
76 CompilerType voidstar
=
77 scratch_ts_sp
->GetBasicType(lldb::eBasicTypeVoid
).GetPointerType();
80 *name_sp
= ValueObject::CreateValueObjectFromData(
81 "name", name_isw
.GetAsData(process_sp
->GetByteOrder()),
82 valobj
.GetExecutionContextRef(), voidstar
);
84 *reason_sp
= ValueObject::CreateValueObjectFromData(
85 "reason", reason_isw
.GetAsData(process_sp
->GetByteOrder()),
86 valobj
.GetExecutionContextRef(), voidstar
);
88 *userinfo_sp
= ValueObject::CreateValueObjectFromData(
89 "userInfo", userinfo_isw
.GetAsData(process_sp
->GetByteOrder()),
90 valobj
.GetExecutionContextRef(), voidstar
);
92 *reserved_sp
= ValueObject::CreateValueObjectFromData(
93 "reserved", reserved_isw
.GetAsData(process_sp
->GetByteOrder()),
94 valobj
.GetExecutionContextRef(), voidstar
);
99 bool lldb_private::formatters::NSException_SummaryProvider(
100 ValueObject
&valobj
, Stream
&stream
, const TypeSummaryOptions
&options
) {
101 lldb::ValueObjectSP reason_sp
;
102 if (!ExtractFields(valobj
, nullptr, &reason_sp
, nullptr, nullptr))
106 stream
.Printf("No reason");
110 StreamString reason_str_summary
;
111 if (NSStringSummaryProvider(*reason_sp
, reason_str_summary
, options
) &&
112 !reason_str_summary
.Empty()) {
113 stream
.Printf("%s", reason_str_summary
.GetData());
119 class NSExceptionSyntheticFrontEnd
: public SyntheticChildrenFrontEnd
{
121 NSExceptionSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp
)
122 : SyntheticChildrenFrontEnd(*valobj_sp
) {}
124 ~NSExceptionSyntheticFrontEnd() override
= default;
126 llvm::Expected
<uint32_t> CalculateNumChildren() override
{ return 4; }
128 lldb::ValueObjectSP
GetChildAtIndex(uint32_t idx
) override
{
130 case 0: return m_name_sp
;
131 case 1: return m_reason_sp
;
132 case 2: return m_userinfo_sp
;
133 case 3: return m_reserved_sp
;
135 return lldb::ValueObjectSP();
138 lldb::ChildCacheState
Update() override
{
141 m_userinfo_sp
.reset();
142 m_reserved_sp
.reset();
144 const auto ret
= ExtractFields(m_backend
, &m_name_sp
, &m_reason_sp
,
145 &m_userinfo_sp
, &m_reserved_sp
);
147 return ret
? lldb::ChildCacheState::eReuse
148 : lldb::ChildCacheState::eRefetch
;
151 bool MightHaveChildren() override
{ return true; }
153 size_t GetIndexOfChildWithName(ConstString name
) override
{
154 // NSException has 4 members:
157 // NSDictionary *userInfo;
159 static ConstString
g_name("name");
160 static ConstString
g_reason("reason");
161 static ConstString
g_userInfo("userInfo");
162 static ConstString
g_reserved("reserved");
163 if (name
== g_name
) return 0;
164 if (name
== g_reason
) return 1;
165 if (name
== g_userInfo
) return 2;
166 if (name
== g_reserved
) return 3;
171 ValueObjectSP m_name_sp
;
172 ValueObjectSP m_reason_sp
;
173 ValueObjectSP m_userinfo_sp
;
174 ValueObjectSP m_reserved_sp
;
177 SyntheticChildrenFrontEnd
*
178 lldb_private::formatters::NSExceptionSyntheticFrontEndCreator(
179 CXXSyntheticChildren
*, lldb::ValueObjectSP valobj_sp
) {
180 lldb::ProcessSP
process_sp(valobj_sp
->GetProcessSP());
183 ObjCLanguageRuntime
*runtime
= ObjCLanguageRuntime::Get(*process_sp
);
187 ObjCLanguageRuntime::ClassDescriptorSP
descriptor(
188 runtime
->GetClassDescriptor(*valobj_sp
.get()));
190 if (!descriptor
.get() || !descriptor
->IsValid())
193 const char *class_name
= descriptor
->GetClassName().GetCString();
195 if (!class_name
|| !*class_name
)
198 if (!strcmp(class_name
, "NSException"))
199 return (new NSExceptionSyntheticFrontEnd(valobj_sp
));
200 else if (!strcmp(class_name
, "NSCFException"))
201 return (new NSExceptionSyntheticFrontEnd(valobj_sp
));
202 else if (!strcmp(class_name
, "__NSCFException"))
203 return (new NSExceptionSyntheticFrontEnd(valobj_sp
));