| //===-- NSException.cpp ---------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/AST/DeclCXX.h" |
| |
| #include "Cocoa.h" |
| |
| #include "lldb/Core/ValueObject.h" |
| #include "lldb/Core/ValueObjectConstResult.h" |
| #include "lldb/DataFormatters/FormattersHelpers.h" |
| #include "lldb/Target/ProcessStructReader.h" |
| #include "lldb/Target/Target.h" |
| #include "lldb/Utility/DataBufferHeap.h" |
| #include "lldb/Utility/Endian.h" |
| #include "lldb/Utility/Status.h" |
| #include "lldb/Utility/Stream.h" |
| |
| #include "Plugins/Language/ObjC/NSString.h" |
| #include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h" |
| #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| using namespace lldb_private::formatters; |
| |
| static bool ExtractFields(ValueObject &valobj, ValueObjectSP *name_sp, |
| ValueObjectSP *reason_sp, ValueObjectSP *userinfo_sp, |
| ValueObjectSP *reserved_sp) { |
| ProcessSP process_sp(valobj.GetProcessSP()); |
| if (!process_sp) |
| return false; |
| |
| lldb::addr_t ptr = LLDB_INVALID_ADDRESS; |
| |
| CompilerType valobj_type(valobj.GetCompilerType()); |
| Flags type_flags(valobj_type.GetTypeInfo()); |
| if (type_flags.AllClear(eTypeHasValue)) { |
| if (valobj.IsBaseClass() && valobj.GetParent()) |
| ptr = valobj.GetParent()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); |
| } else { |
| ptr = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS); |
| } |
| |
| if (ptr == LLDB_INVALID_ADDRESS) |
| return false; |
| size_t ptr_size = process_sp->GetAddressByteSize(); |
| |
| Status error; |
| auto name = process_sp->ReadPointerFromMemory(ptr + 1 * ptr_size, error); |
| if (error.Fail() || name == LLDB_INVALID_ADDRESS) |
| return false; |
| auto reason = process_sp->ReadPointerFromMemory(ptr + 2 * ptr_size, error); |
| if (error.Fail() || reason == LLDB_INVALID_ADDRESS) |
| return false; |
| auto userinfo = process_sp->ReadPointerFromMemory(ptr + 3 * ptr_size, error); |
| if (error.Fail() || userinfo == LLDB_INVALID_ADDRESS) |
| return false; |
| auto reserved = process_sp->ReadPointerFromMemory(ptr + 4 * ptr_size, error); |
| if (error.Fail() || reserved == LLDB_INVALID_ADDRESS) |
| return false; |
| |
| InferiorSizedWord name_isw(name, *process_sp); |
| InferiorSizedWord reason_isw(reason, *process_sp); |
| InferiorSizedWord userinfo_isw(userinfo, *process_sp); |
| InferiorSizedWord reserved_isw(reserved, *process_sp); |
| |
| auto *clang_ast_context = TypeSystemClang::GetScratch(process_sp->GetTarget()); |
| if (!clang_ast_context) |
| return false; |
| |
| CompilerType voidstar = |
| clang_ast_context->GetBasicType(lldb::eBasicTypeVoid).GetPointerType(); |
| |
| if (name_sp) |
| *name_sp = ValueObject::CreateValueObjectFromData( |
| "name", name_isw.GetAsData(process_sp->GetByteOrder()), |
| valobj.GetExecutionContextRef(), voidstar); |
| if (reason_sp) |
| *reason_sp = ValueObject::CreateValueObjectFromData( |
| "reason", reason_isw.GetAsData(process_sp->GetByteOrder()), |
| valobj.GetExecutionContextRef(), voidstar); |
| if (userinfo_sp) |
| *userinfo_sp = ValueObject::CreateValueObjectFromData( |
| "userInfo", userinfo_isw.GetAsData(process_sp->GetByteOrder()), |
| valobj.GetExecutionContextRef(), voidstar); |
| if (reserved_sp) |
| *reserved_sp = ValueObject::CreateValueObjectFromData( |
| "reserved", reserved_isw.GetAsData(process_sp->GetByteOrder()), |
| valobj.GetExecutionContextRef(), voidstar); |
| |
| return true; |
| } |
| |
| bool lldb_private::formatters::NSException_SummaryProvider( |
| ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
| lldb::ValueObjectSP reason_sp; |
| if (!ExtractFields(valobj, nullptr, &reason_sp, nullptr, nullptr)) |
| return false; |
| |
| if (!reason_sp) { |
| stream.Printf("No reason"); |
| return false; |
| } |
| |
| StreamString reason_str_summary; |
| if (NSStringSummaryProvider(*reason_sp, reason_str_summary, options) && |
| !reason_str_summary.Empty()) { |
| stream.Printf("%s", reason_str_summary.GetData()); |
| return true; |
| } else |
| return false; |
| } |
| |
| class NSExceptionSyntheticFrontEnd : public SyntheticChildrenFrontEnd { |
| public: |
| NSExceptionSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) |
| : SyntheticChildrenFrontEnd(*valobj_sp) {} |
| |
| ~NSExceptionSyntheticFrontEnd() override = default; |
| |
| size_t CalculateNumChildren() override { |
| return 4; |
| } |
| |
| lldb::ValueObjectSP GetChildAtIndex(size_t idx) override { |
| switch (idx) { |
| case 0: return m_name_sp; |
| case 1: return m_reason_sp; |
| case 2: return m_userinfo_sp; |
| case 3: return m_reserved_sp; |
| } |
| return lldb::ValueObjectSP(); |
| } |
| |
| bool Update() override { |
| m_name_sp.reset(); |
| m_reason_sp.reset(); |
| m_userinfo_sp.reset(); |
| m_reserved_sp.reset(); |
| |
| return ExtractFields(m_backend, &m_name_sp, &m_reason_sp, &m_userinfo_sp, |
| &m_reserved_sp); |
| } |
| |
| bool MightHaveChildren() override { return true; } |
| |
| size_t GetIndexOfChildWithName(ConstString name) override { |
| // NSException has 4 members: |
| // NSString *name; |
| // NSString *reason; |
| // NSDictionary *userInfo; |
| // id reserved; |
| static ConstString g___name("name"); |
| static ConstString g___reason("reason"); |
| static ConstString g___userInfo("userInfo"); |
| static ConstString g___reserved("reserved"); |
| if (name == g___name) return 0; |
| if (name == g___reason) return 1; |
| if (name == g___userInfo) return 2; |
| if (name == g___reserved) return 3; |
| return UINT32_MAX; |
| } |
| |
| private: |
| ValueObjectSP m_name_sp; |
| ValueObjectSP m_reason_sp; |
| ValueObjectSP m_userinfo_sp; |
| ValueObjectSP m_reserved_sp; |
| }; |
| |
| SyntheticChildrenFrontEnd * |
| lldb_private::formatters::NSExceptionSyntheticFrontEndCreator( |
| CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
| lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); |
| if (!process_sp) |
| return nullptr; |
| ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp); |
| if (!runtime) |
| return nullptr; |
| |
| ObjCLanguageRuntime::ClassDescriptorSP descriptor( |
| runtime->GetClassDescriptor(*valobj_sp.get())); |
| |
| if (!descriptor.get() || !descriptor->IsValid()) |
| return nullptr; |
| |
| const char *class_name = descriptor->GetClassName().GetCString(); |
| |
| if (!class_name || !*class_name) |
| return nullptr; |
| |
| if (!strcmp(class_name, "NSException")) |
| return (new NSExceptionSyntheticFrontEnd(valobj_sp)); |
| else if (!strcmp(class_name, "NSCFException")) |
| return (new NSExceptionSyntheticFrontEnd(valobj_sp)); |
| else if (!strcmp(class_name, "__NSCFException")) |
| return (new NSExceptionSyntheticFrontEnd(valobj_sp)); |
| |
| return nullptr; |
| } |