1 //===-- NSError.cpp ---------------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 // C Includes
11 // C++ Includes
12 // Other libraries and framework includes
13 #include "clang/AST/DeclCXX.h"
14 
15 // Project includes
16 #include "Cocoa.h"
17 
18 #include "lldb/Core/DataBufferHeap.h"
19 #include "lldb/Core/Error.h"
20 #include "lldb/Core/Stream.h"
21 #include "lldb/Core/ValueObject.h"
22 #include "lldb/Core/ValueObjectConstResult.h"
23 #include "lldb/DataFormatters/FormattersHelpers.h"
24 #include "lldb/Host/Endian.h"
25 #include "lldb/Symbol/ClangASTContext.h"
26 #include "lldb/Target/ObjCLanguageRuntime.h"
27 #include "lldb/Target/Target.h"
28 
29 #include "lldb/Utility/ProcessStructReader.h"
30 
31 #include "Plugins/Language/ObjC/NSString.h"
32 
33 using namespace lldb;
34 using namespace lldb_private;
35 using namespace lldb_private::formatters;
36 
37 static lldb::addr_t
38 DerefToNSErrorPointer (ValueObject& valobj)
39 {
40     CompilerType valobj_type(valobj.GetCompilerType());
41     Flags type_flags(valobj_type.GetTypeInfo());
42     if (type_flags.AllClear(eTypeHasValue))
43     {
44         if (valobj.IsBaseClass() && valobj.GetParent())
45             return valobj.GetParent()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
46     }
47     else
48     {
49         lldb::addr_t ptr_value = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
50         if (type_flags.AllSet(eTypeIsPointer))
51         {
52             CompilerType pointee_type(valobj_type.GetPointeeType());
53             Flags pointee_flags(pointee_type.GetTypeInfo());
54             if (pointee_flags.AllSet(eTypeIsPointer))
55             {
56                 if (ProcessSP process_sp = valobj.GetProcessSP())
57                 {
58                     Error error;
59                     ptr_value = process_sp->ReadPointerFromMemory(ptr_value, error);
60                 }
61             }
62         }
63         return ptr_value;
64     }
65 
66     return LLDB_INVALID_ADDRESS;
67 }
68 
69 bool
70 lldb_private::formatters::NSError_SummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options)
71 {
72     ProcessSP process_sp(valobj.GetProcessSP());
73     if (!process_sp)
74         return false;
75 
76     lldb::addr_t ptr_value = DerefToNSErrorPointer(valobj);
77     if (ptr_value == LLDB_INVALID_ADDRESS)
78         return false;
79 
80     size_t ptr_size = process_sp->GetAddressByteSize();
81     lldb::addr_t code_location = ptr_value + 2 * ptr_size;
82     lldb::addr_t domain_location = ptr_value + 3 * ptr_size;
83 
84     Error error;
85     uint64_t code = process_sp->ReadUnsignedIntegerFromMemory(code_location, ptr_size, 0, error);
86     if (error.Fail())
87         return false;
88 
89     lldb::addr_t domain_str_value = process_sp->ReadPointerFromMemory(domain_location, error);
90     if (error.Fail() || domain_str_value == LLDB_INVALID_ADDRESS)
91         return false;
92 
93     if (!domain_str_value)
94     {
95         stream.Printf("domain: nil - code: %" PRIu64, code);
96         return true;
97     }
98 
99     InferiorSizedWord isw(domain_str_value, *process_sp);
100 
101     ValueObjectSP domain_str_sp = ValueObject::CreateValueObjectFromData("domain_str", isw.GetAsData(process_sp->GetByteOrder()), valobj.GetExecutionContextRef(), process_sp->GetTarget().GetScratchClangASTContext()->GetBasicType(lldb::eBasicTypeVoid).GetPointerType());
102 
103     if (!domain_str_sp)
104         return false;
105 
106     StreamString domain_str_summary;
107     if (NSStringSummaryProvider(*domain_str_sp, domain_str_summary, options) && !domain_str_summary.Empty())
108     {
109         stream.Printf("domain: %s - code: %" PRIu64, domain_str_summary.GetData(), code);
110         return true;
111     }
112     else
113     {
114         stream.Printf("domain: nil - code: %" PRIu64, code);
115         return true;
116     }
117 }
118 
119 class NSErrorSyntheticFrontEnd : public SyntheticChildrenFrontEnd
120 {
121 public:
122     NSErrorSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) :
123     SyntheticChildrenFrontEnd(*valobj_sp)
124     {}
125 
126     ~NSErrorSyntheticFrontEnd() override = default;
127     // no need to delete m_child_ptr - it's kept alive by the cluster manager on our behalf
128 
129     size_t
130     CalculateNumChildren() override
131     {
132         if (m_child_ptr)
133             return 1;
134         if (m_child_sp)
135             return 1;
136         return 0;
137     }
138 
139     lldb::ValueObjectSP
140     GetChildAtIndex(size_t idx) override
141     {
142         if (idx != 0)
143             return lldb::ValueObjectSP();
144 
145         if (m_child_ptr)
146             return m_child_ptr->GetSP();
147         return m_child_sp;
148     }
149 
150     bool
151     Update() override
152     {
153         m_child_ptr = nullptr;
154         m_child_sp.reset();
155 
156         ProcessSP process_sp(m_backend.GetProcessSP());
157         if (!process_sp)
158             return false;
159 
160         lldb::addr_t userinfo_location = DerefToNSErrorPointer(m_backend);
161         if (userinfo_location == LLDB_INVALID_ADDRESS)
162             return false;
163 
164         size_t ptr_size = process_sp->GetAddressByteSize();
165 
166         userinfo_location += 4 * ptr_size;
167         Error error;
168         lldb::addr_t userinfo = process_sp->ReadPointerFromMemory(userinfo_location, error);
169         if (userinfo == LLDB_INVALID_ADDRESS || error.Fail())
170             return false;
171         InferiorSizedWord isw(userinfo,*process_sp);
172         m_child_sp = ValueObject::CreateValueObjectFromData("_userInfo",
173                                                             isw.GetAsData(process_sp->GetByteOrder()),
174                                                             m_backend.GetExecutionContextRef(),
175                                                             process_sp->GetTarget().GetScratchClangASTContext()->GetBasicType(lldb::eBasicTypeObjCID));
176         return false;
177     }
178 
179     bool
180     MightHaveChildren() override
181     {
182         return true;
183     }
184 
185     size_t
186     GetIndexOfChildWithName(const ConstString &name) override
187     {
188         static ConstString g___userInfo("_userInfo");
189         if (name == g___userInfo)
190             return 0;
191         return UINT32_MAX;
192     }
193 
194 private:
195     // the child here can be "real" (i.e. an actual child of the root) or synthetized from raw memory
196     // if the former, I need to store a plain pointer to it - or else a loop of references will cause this entire hierarchy of values to leak
197     // if the latter, then I need to store a SharedPointer to it - so that it only goes away when everyone else in the cluster goes away
198     // oh joy!
199     ValueObject* m_child_ptr;
200     ValueObjectSP m_child_sp;
201 };
202 
203 SyntheticChildrenFrontEnd*
204 lldb_private::formatters::NSErrorSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp)
205 {
206     lldb::ProcessSP process_sp (valobj_sp->GetProcessSP());
207     if (!process_sp)
208         return nullptr;
209     ObjCLanguageRuntime *runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
210     if (!runtime)
211         return nullptr;
212 
213     ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(*valobj_sp.get()));
214 
215     if (!descriptor.get() || !descriptor->IsValid())
216         return nullptr;
217 
218     const char* class_name = descriptor->GetClassName().GetCString();
219 
220     if (!class_name || !*class_name)
221         return nullptr;
222 
223     if (!strcmp(class_name,"NSError"))
224         return (new NSErrorSyntheticFrontEnd(valobj_sp));
225     else if (!strcmp(class_name,"__NSCFError"))
226         return (new NSErrorSyntheticFrontEnd(valobj_sp));
227 
228     return nullptr;
229 }
230