1 //===-- NSException.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 #include "clang/AST/DeclCXX.h" 11 12 #include "Cocoa.h" 13 14 #include "lldb/Core/ValueObject.h" 15 #include "lldb/Core/ValueObjectConstResult.h" 16 #include "lldb/DataFormatters/FormattersHelpers.h" 17 #include "lldb/Symbol/ClangASTContext.h" 18 #include "lldb/Target/ObjCLanguageRuntime.h" 19 #include "lldb/Target/ProcessStructReader.h" 20 #include "lldb/Target/Target.h" 21 #include "lldb/Utility/DataBufferHeap.h" 22 #include "lldb/Utility/Endian.h" 23 #include "lldb/Utility/Status.h" 24 #include "lldb/Utility/Stream.h" 25 26 #include "Plugins/Language/ObjC/NSString.h" 27 28 using namespace lldb; 29 using namespace lldb_private; 30 using namespace lldb_private::formatters; 31 32 static bool ExtractFields(ValueObject &valobj, ValueObjectSP *name_sp, 33 ValueObjectSP *reason_sp, ValueObjectSP *userinfo_sp, 34 ValueObjectSP *reserved_sp) { 35 ProcessSP process_sp(valobj.GetProcessSP()); 36 if (!process_sp) 37 return false; 38 39 lldb::addr_t ptr = LLDB_INVALID_ADDRESS; 40 41 CompilerType valobj_type(valobj.GetCompilerType()); 42 Flags type_flags(valobj_type.GetTypeInfo()); 43 if (type_flags.AllClear(eTypeHasValue)) { 44 if (valobj.IsBaseClass() && valobj.GetParent()) 45 ptr = valobj.GetParent()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); 46 } else { 47 ptr = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS); 48 } 49 50 if (ptr == LLDB_INVALID_ADDRESS) 51 return false; 52 size_t ptr_size = process_sp->GetAddressByteSize(); 53 54 Status error; 55 auto name = process_sp->ReadPointerFromMemory(ptr + 1 * ptr_size, error); 56 if (error.Fail() || name == LLDB_INVALID_ADDRESS) 57 return false; 58 auto reason = process_sp->ReadPointerFromMemory(ptr + 2 * ptr_size, error); 59 if (error.Fail() || reason == LLDB_INVALID_ADDRESS) 60 return false; 61 auto userinfo = process_sp->ReadPointerFromMemory(ptr + 3 * ptr_size, error); 62 if (error.Fail() || userinfo == LLDB_INVALID_ADDRESS) 63 return false; 64 auto reserved = process_sp->ReadPointerFromMemory(ptr + 4 * ptr_size, error); 65 if (error.Fail() || reserved == LLDB_INVALID_ADDRESS) 66 return false; 67 68 InferiorSizedWord name_isw(name, *process_sp); 69 InferiorSizedWord reason_isw(reason, *process_sp); 70 InferiorSizedWord userinfo_isw(userinfo, *process_sp); 71 InferiorSizedWord reserved_isw(reserved, *process_sp); 72 73 CompilerType voidstar = process_sp->GetTarget() 74 .GetScratchClangASTContext() 75 ->GetBasicType(lldb::eBasicTypeVoid) 76 .GetPointerType(); 77 78 if (name_sp) 79 *name_sp = ValueObject::CreateValueObjectFromData( 80 "name", name_isw.GetAsData(process_sp->GetByteOrder()), 81 valobj.GetExecutionContextRef(), voidstar); 82 if (reason_sp) 83 *reason_sp = ValueObject::CreateValueObjectFromData( 84 "reason", reason_isw.GetAsData(process_sp->GetByteOrder()), 85 valobj.GetExecutionContextRef(), voidstar); 86 if (userinfo_sp) 87 *userinfo_sp = ValueObject::CreateValueObjectFromData( 88 "userInfo", userinfo_isw.GetAsData(process_sp->GetByteOrder()), 89 valobj.GetExecutionContextRef(), voidstar); 90 if (reserved_sp) 91 *reserved_sp = ValueObject::CreateValueObjectFromData( 92 "reserved", reserved_isw.GetAsData(process_sp->GetByteOrder()), 93 valobj.GetExecutionContextRef(), voidstar); 94 95 return true; 96 } 97 98 bool lldb_private::formatters::NSException_SummaryProvider( 99 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 100 lldb::ValueObjectSP name_sp; 101 lldb::ValueObjectSP reason_sp; 102 if (!ExtractFields(valobj, &name_sp, &reason_sp, nullptr, nullptr)) 103 return false; 104 105 if (!name_sp || !reason_sp) 106 return false; 107 108 StreamString name_str_summary; 109 StreamString reason_str_summary; 110 if (NSStringSummaryProvider(*name_sp, name_str_summary, options) && 111 NSStringSummaryProvider(*reason_sp, reason_str_summary, options) && 112 !name_str_summary.Empty() && !reason_str_summary.Empty()) { 113 stream.Printf("name: %s - reason: %s", name_str_summary.GetData(), 114 reason_str_summary.GetData()); 115 return true; 116 } else 117 return false; 118 } 119 120 class NSExceptionSyntheticFrontEnd : public SyntheticChildrenFrontEnd { 121 public: 122 NSExceptionSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) 123 : SyntheticChildrenFrontEnd(*valobj_sp) {} 124 125 ~NSExceptionSyntheticFrontEnd() override = default; 126 127 size_t CalculateNumChildren() override { 128 return 4; 129 } 130 131 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override { 132 switch (idx) { 133 case 0: return m_name_sp; 134 case 1: return m_reason_sp; 135 case 2: return m_userinfo_sp; 136 case 3: return m_reserved_sp; 137 } 138 return lldb::ValueObjectSP(); 139 } 140 141 bool Update() override { 142 m_name_sp.reset(); 143 m_reason_sp.reset(); 144 m_userinfo_sp.reset(); 145 m_reserved_sp.reset(); 146 147 return ExtractFields(m_backend, &m_name_sp, &m_reason_sp, &m_userinfo_sp, 148 &m_reserved_sp); 149 } 150 151 bool MightHaveChildren() override { return true; } 152 153 size_t GetIndexOfChildWithName(const ConstString &name) override { 154 // NSException has 4 members: 155 // NSString *name; 156 // NSString *reason; 157 // NSDictionary *userInfo; 158 // id reserved; 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; 167 return UINT32_MAX; 168 } 169 170 private: 171 ValueObjectSP m_name_sp; 172 ValueObjectSP m_reason_sp; 173 ValueObjectSP m_userinfo_sp; 174 ValueObjectSP m_reserved_sp; 175 }; 176 177 SyntheticChildrenFrontEnd * 178 lldb_private::formatters::NSExceptionSyntheticFrontEndCreator( 179 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { 180 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); 181 if (!process_sp) 182 return nullptr; 183 ObjCLanguageRuntime *runtime = 184 (ObjCLanguageRuntime *)process_sp->GetLanguageRuntime( 185 lldb::eLanguageTypeObjC); 186 if (!runtime) 187 return nullptr; 188 189 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 190 runtime->GetClassDescriptor(*valobj_sp.get())); 191 192 if (!descriptor.get() || !descriptor->IsValid()) 193 return nullptr; 194 195 const char *class_name = descriptor->GetClassName().GetCString(); 196 197 if (!class_name || !*class_name) 198 return nullptr; 199 200 if (!strcmp(class_name, "NSException")) 201 return (new NSExceptionSyntheticFrontEnd(valobj_sp)); 202 else if (!strcmp(class_name, "NSCFException")) 203 return (new NSExceptionSyntheticFrontEnd(valobj_sp)); 204 else if (!strcmp(class_name, "__NSCFException")) 205 return (new NSExceptionSyntheticFrontEnd(valobj_sp)); 206 207 return nullptr; 208 } 209