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/Core/ValueObject.h"
14 #include "lldb/Core/ValueObjectConstResult.h"
15 #include "lldb/DataFormatters/FormattersHelpers.h"
16 #include "lldb/Target/Target.h"
17 #include "lldb/Utility/DataBufferHeap.h"
18 #include "lldb/Utility/Endian.h"
19 #include "lldb/Utility/Status.h"
20 #include "lldb/Utility/Stream.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 size_t CalculateNumChildren() override
{
130 lldb::ValueObjectSP
GetChildAtIndex(size_t idx
) override
{
132 case 0: return m_name_sp
;
133 case 1: return m_reason_sp
;
134 case 2: return m_userinfo_sp
;
135 case 3: return m_reserved_sp
;
137 return lldb::ValueObjectSP();
140 bool Update() override
{
143 m_userinfo_sp
.reset();
144 m_reserved_sp
.reset();
146 return ExtractFields(m_backend
, &m_name_sp
, &m_reason_sp
, &m_userinfo_sp
,
150 bool MightHaveChildren() override
{ return true; }
152 size_t GetIndexOfChildWithName(ConstString name
) override
{
153 // NSException has 4 members:
156 // NSDictionary *userInfo;
158 static ConstString
g_name("name");
159 static ConstString
g_reason("reason");
160 static ConstString
g_userInfo("userInfo");
161 static ConstString
g_reserved("reserved");
162 if (name
== g_name
) return 0;
163 if (name
== g_reason
) return 1;
164 if (name
== g_userInfo
) return 2;
165 if (name
== g_reserved
) return 3;
170 ValueObjectSP m_name_sp
;
171 ValueObjectSP m_reason_sp
;
172 ValueObjectSP m_userinfo_sp
;
173 ValueObjectSP m_reserved_sp
;
176 SyntheticChildrenFrontEnd
*
177 lldb_private::formatters::NSExceptionSyntheticFrontEndCreator(
178 CXXSyntheticChildren
*, lldb::ValueObjectSP valobj_sp
) {
179 lldb::ProcessSP
process_sp(valobj_sp
->GetProcessSP());
182 ObjCLanguageRuntime
*runtime
= ObjCLanguageRuntime::Get(*process_sp
);
186 ObjCLanguageRuntime::ClassDescriptorSP
descriptor(
187 runtime
->GetClassDescriptor(*valobj_sp
.get()));
189 if (!descriptor
.get() || !descriptor
->IsValid())
192 const char *class_name
= descriptor
->GetClassName().GetCString();
194 if (!class_name
|| !*class_name
)
197 if (!strcmp(class_name
, "NSException"))
198 return (new NSExceptionSyntheticFrontEnd(valobj_sp
));
199 else if (!strcmp(class_name
, "NSCFException"))
200 return (new NSExceptionSyntheticFrontEnd(valobj_sp
));
201 else if (!strcmp(class_name
, "__NSCFException"))
202 return (new NSExceptionSyntheticFrontEnd(valobj_sp
));