xref: /freebsd-src/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSError.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
15ffd83dbSDimitry Andric //===-- NSError.cpp -------------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "clang/AST/DeclCXX.h"
100b57cec5SDimitry Andric 
110b57cec5SDimitry Andric #include "Cocoa.h"
120b57cec5SDimitry Andric 
135ffd83dbSDimitry Andric #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
140b57cec5SDimitry Andric #include "lldb/Core/ValueObject.h"
150b57cec5SDimitry Andric #include "lldb/Core/ValueObjectConstResult.h"
160b57cec5SDimitry Andric #include "lldb/DataFormatters/FormattersHelpers.h"
170b57cec5SDimitry Andric #include "lldb/Target/Target.h"
180b57cec5SDimitry Andric #include "lldb/Utility/DataBufferHeap.h"
190b57cec5SDimitry Andric #include "lldb/Utility/Endian.h"
200b57cec5SDimitry Andric #include "lldb/Utility/Status.h"
210b57cec5SDimitry Andric #include "lldb/Utility/Stream.h"
220b57cec5SDimitry Andric 
230b57cec5SDimitry Andric #include "Plugins/Language/ObjC/NSString.h"
240b57cec5SDimitry Andric #include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
250b57cec5SDimitry Andric 
260b57cec5SDimitry Andric using namespace lldb;
270b57cec5SDimitry Andric using namespace lldb_private;
280b57cec5SDimitry Andric using namespace lldb_private::formatters;
290b57cec5SDimitry Andric 
300b57cec5SDimitry Andric static lldb::addr_t DerefToNSErrorPointer(ValueObject &valobj) {
310b57cec5SDimitry Andric   CompilerType valobj_type(valobj.GetCompilerType());
320b57cec5SDimitry Andric   Flags type_flags(valobj_type.GetTypeInfo());
330b57cec5SDimitry Andric   if (type_flags.AllClear(eTypeHasValue)) {
340b57cec5SDimitry Andric     if (valobj.IsBaseClass() && valobj.GetParent())
350b57cec5SDimitry Andric       return valobj.GetParent()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
360b57cec5SDimitry Andric   } else {
370b57cec5SDimitry Andric     lldb::addr_t ptr_value = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
380b57cec5SDimitry Andric     if (type_flags.AllSet(eTypeIsPointer)) {
390b57cec5SDimitry Andric       CompilerType pointee_type(valobj_type.GetPointeeType());
400b57cec5SDimitry Andric       Flags pointee_flags(pointee_type.GetTypeInfo());
410b57cec5SDimitry Andric       if (pointee_flags.AllSet(eTypeIsPointer)) {
420b57cec5SDimitry Andric         if (ProcessSP process_sp = valobj.GetProcessSP()) {
430b57cec5SDimitry Andric           Status error;
440b57cec5SDimitry Andric           ptr_value = process_sp->ReadPointerFromMemory(ptr_value, error);
450b57cec5SDimitry Andric         }
460b57cec5SDimitry Andric       }
470b57cec5SDimitry Andric     }
480b57cec5SDimitry Andric     return ptr_value;
490b57cec5SDimitry Andric   }
500b57cec5SDimitry Andric 
510b57cec5SDimitry Andric   return LLDB_INVALID_ADDRESS;
520b57cec5SDimitry Andric }
530b57cec5SDimitry Andric 
540b57cec5SDimitry Andric bool lldb_private::formatters::NSError_SummaryProvider(
550b57cec5SDimitry Andric     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
560b57cec5SDimitry Andric   ProcessSP process_sp(valobj.GetProcessSP());
570b57cec5SDimitry Andric   if (!process_sp)
580b57cec5SDimitry Andric     return false;
590b57cec5SDimitry Andric 
600b57cec5SDimitry Andric   lldb::addr_t ptr_value = DerefToNSErrorPointer(valobj);
610b57cec5SDimitry Andric   if (ptr_value == LLDB_INVALID_ADDRESS)
620b57cec5SDimitry Andric     return false;
630b57cec5SDimitry Andric 
640b57cec5SDimitry Andric   size_t ptr_size = process_sp->GetAddressByteSize();
650b57cec5SDimitry Andric   lldb::addr_t code_location = ptr_value + 2 * ptr_size;
660b57cec5SDimitry Andric   lldb::addr_t domain_location = ptr_value + 3 * ptr_size;
670b57cec5SDimitry Andric 
680b57cec5SDimitry Andric   Status error;
690b57cec5SDimitry Andric   uint64_t code = process_sp->ReadUnsignedIntegerFromMemory(code_location,
700b57cec5SDimitry Andric                                                             ptr_size, 0, error);
710b57cec5SDimitry Andric   if (error.Fail())
720b57cec5SDimitry Andric     return false;
730b57cec5SDimitry Andric 
740b57cec5SDimitry Andric   lldb::addr_t domain_str_value =
750b57cec5SDimitry Andric       process_sp->ReadPointerFromMemory(domain_location, error);
760b57cec5SDimitry Andric   if (error.Fail() || domain_str_value == LLDB_INVALID_ADDRESS)
770b57cec5SDimitry Andric     return false;
780b57cec5SDimitry Andric 
790b57cec5SDimitry Andric   if (!domain_str_value) {
800b57cec5SDimitry Andric     stream.Printf("domain: nil - code: %" PRIu64, code);
810b57cec5SDimitry Andric     return true;
820b57cec5SDimitry Andric   }
830b57cec5SDimitry Andric 
840b57cec5SDimitry Andric   InferiorSizedWord isw(domain_str_value, *process_sp);
85bdd1243dSDimitry Andric   TypeSystemClangSP scratch_ts_sp =
86bdd1243dSDimitry Andric       ScratchTypeSystemClang::GetForTarget(process_sp->GetTarget());
870b57cec5SDimitry Andric 
88bdd1243dSDimitry Andric   if (!scratch_ts_sp)
89bdd1243dSDimitry Andric     return false;
900b57cec5SDimitry Andric   ValueObjectSP domain_str_sp = ValueObject::CreateValueObjectFromData(
910b57cec5SDimitry Andric       "domain_str", isw.GetAsData(process_sp->GetByteOrder()),
92480093f4SDimitry Andric       valobj.GetExecutionContextRef(),
93bdd1243dSDimitry Andric       scratch_ts_sp->GetBasicType(lldb::eBasicTypeVoid).GetPointerType());
940b57cec5SDimitry Andric 
950b57cec5SDimitry Andric   if (!domain_str_sp)
960b57cec5SDimitry Andric     return false;
970b57cec5SDimitry Andric 
980b57cec5SDimitry Andric   StreamString domain_str_summary;
990b57cec5SDimitry Andric   if (NSStringSummaryProvider(*domain_str_sp, domain_str_summary, options) &&
1000b57cec5SDimitry Andric       !domain_str_summary.Empty()) {
1010b57cec5SDimitry Andric     stream.Printf("domain: %s - code: %" PRIu64, domain_str_summary.GetData(),
1020b57cec5SDimitry Andric                   code);
1030b57cec5SDimitry Andric     return true;
1040b57cec5SDimitry Andric   } else {
1050b57cec5SDimitry Andric     stream.Printf("domain: nil - code: %" PRIu64, code);
1060b57cec5SDimitry Andric     return true;
1070b57cec5SDimitry Andric   }
1080b57cec5SDimitry Andric }
1090b57cec5SDimitry Andric 
1100b57cec5SDimitry Andric class NSErrorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
1110b57cec5SDimitry Andric public:
1120b57cec5SDimitry Andric   NSErrorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
1130b57cec5SDimitry Andric       : SyntheticChildrenFrontEnd(*valobj_sp) {}
1140b57cec5SDimitry Andric 
1150b57cec5SDimitry Andric   ~NSErrorSyntheticFrontEnd() override = default;
1160b57cec5SDimitry Andric   // no need to delete m_child_ptr - it's kept alive by the cluster manager on
1170b57cec5SDimitry Andric   // our behalf
1180b57cec5SDimitry Andric 
119*0fca6ea1SDimitry Andric   llvm::Expected<uint32_t> CalculateNumChildren() override {
1200b57cec5SDimitry Andric     if (m_child_ptr)
1210b57cec5SDimitry Andric       return 1;
1220b57cec5SDimitry Andric     if (m_child_sp)
1230b57cec5SDimitry Andric       return 1;
1240b57cec5SDimitry Andric     return 0;
1250b57cec5SDimitry Andric   }
1260b57cec5SDimitry Andric 
127*0fca6ea1SDimitry Andric   lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override {
1280b57cec5SDimitry Andric     if (idx != 0)
1290b57cec5SDimitry Andric       return lldb::ValueObjectSP();
1300b57cec5SDimitry Andric 
1310b57cec5SDimitry Andric     if (m_child_ptr)
1320b57cec5SDimitry Andric       return m_child_ptr->GetSP();
1330b57cec5SDimitry Andric     return m_child_sp;
1340b57cec5SDimitry Andric   }
1350b57cec5SDimitry Andric 
136*0fca6ea1SDimitry Andric   lldb::ChildCacheState Update() override {
1370b57cec5SDimitry Andric     m_child_ptr = nullptr;
1380b57cec5SDimitry Andric     m_child_sp.reset();
1390b57cec5SDimitry Andric 
1400b57cec5SDimitry Andric     ProcessSP process_sp(m_backend.GetProcessSP());
1410b57cec5SDimitry Andric     if (!process_sp)
142*0fca6ea1SDimitry Andric       return lldb::ChildCacheState::eRefetch;
1430b57cec5SDimitry Andric 
1440b57cec5SDimitry Andric     lldb::addr_t userinfo_location = DerefToNSErrorPointer(m_backend);
1450b57cec5SDimitry Andric     if (userinfo_location == LLDB_INVALID_ADDRESS)
146*0fca6ea1SDimitry Andric       return lldb::ChildCacheState::eRefetch;
1470b57cec5SDimitry Andric 
1480b57cec5SDimitry Andric     size_t ptr_size = process_sp->GetAddressByteSize();
1490b57cec5SDimitry Andric 
1500b57cec5SDimitry Andric     userinfo_location += 4 * ptr_size;
1510b57cec5SDimitry Andric     Status error;
1520b57cec5SDimitry Andric     lldb::addr_t userinfo =
1530b57cec5SDimitry Andric         process_sp->ReadPointerFromMemory(userinfo_location, error);
1540b57cec5SDimitry Andric     if (userinfo == LLDB_INVALID_ADDRESS || error.Fail())
155*0fca6ea1SDimitry Andric       return lldb::ChildCacheState::eRefetch;
1560b57cec5SDimitry Andric     InferiorSizedWord isw(userinfo, *process_sp);
157bdd1243dSDimitry Andric     TypeSystemClangSP scratch_ts_sp =
158bdd1243dSDimitry Andric         ScratchTypeSystemClang::GetForTarget(process_sp->GetTarget());
159bdd1243dSDimitry Andric     if (!scratch_ts_sp)
160*0fca6ea1SDimitry Andric       return lldb::ChildCacheState::eRefetch;
1610b57cec5SDimitry Andric     m_child_sp = CreateValueObjectFromData(
1620b57cec5SDimitry Andric         "_userInfo", isw.GetAsData(process_sp->GetByteOrder()),
1630b57cec5SDimitry Andric         m_backend.GetExecutionContextRef(),
164bdd1243dSDimitry Andric         scratch_ts_sp->GetBasicType(lldb::eBasicTypeObjCID));
165*0fca6ea1SDimitry Andric     return lldb::ChildCacheState::eRefetch;
1660b57cec5SDimitry Andric   }
1670b57cec5SDimitry Andric 
1680b57cec5SDimitry Andric   bool MightHaveChildren() override { return true; }
1690b57cec5SDimitry Andric 
1700b57cec5SDimitry Andric   size_t GetIndexOfChildWithName(ConstString name) override {
17181ad6265SDimitry Andric     static ConstString g_userInfo("_userInfo");
17281ad6265SDimitry Andric     if (name == g_userInfo)
1730b57cec5SDimitry Andric       return 0;
1740b57cec5SDimitry Andric     return UINT32_MAX;
1750b57cec5SDimitry Andric   }
1760b57cec5SDimitry Andric 
1770b57cec5SDimitry Andric private:
1780b57cec5SDimitry Andric   // the child here can be "real" (i.e. an actual child of the root) or
1790b57cec5SDimitry Andric   // synthetized from raw memory if the former, I need to store a plain pointer
1800b57cec5SDimitry Andric   // to it - or else a loop of references will cause this entire hierarchy of
1810b57cec5SDimitry Andric   // values to leak if the latter, then I need to store a SharedPointer to it -
1820b57cec5SDimitry Andric   // so that it only goes away when everyone else in the cluster goes away oh
1830b57cec5SDimitry Andric   // joy!
184fcaf7f86SDimitry Andric   ValueObject *m_child_ptr = nullptr;
1850b57cec5SDimitry Andric   ValueObjectSP m_child_sp;
1860b57cec5SDimitry Andric };
1870b57cec5SDimitry Andric 
1880b57cec5SDimitry Andric SyntheticChildrenFrontEnd *
1890b57cec5SDimitry Andric lldb_private::formatters::NSErrorSyntheticFrontEndCreator(
1900b57cec5SDimitry Andric     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
1910b57cec5SDimitry Andric   lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
1920b57cec5SDimitry Andric   if (!process_sp)
1930b57cec5SDimitry Andric     return nullptr;
1940b57cec5SDimitry Andric   ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
1950b57cec5SDimitry Andric   if (!runtime)
1960b57cec5SDimitry Andric     return nullptr;
1970b57cec5SDimitry Andric 
1980b57cec5SDimitry Andric   ObjCLanguageRuntime::ClassDescriptorSP descriptor(
1990b57cec5SDimitry Andric       runtime->GetClassDescriptor(*valobj_sp.get()));
2000b57cec5SDimitry Andric 
2010b57cec5SDimitry Andric   if (!descriptor.get() || !descriptor->IsValid())
2020b57cec5SDimitry Andric     return nullptr;
2030b57cec5SDimitry Andric 
2040b57cec5SDimitry Andric   const char *class_name = descriptor->GetClassName().GetCString();
2050b57cec5SDimitry Andric 
2060b57cec5SDimitry Andric   if (!class_name || !*class_name)
2070b57cec5SDimitry Andric     return nullptr;
2080b57cec5SDimitry Andric 
2090b57cec5SDimitry Andric   if (!strcmp(class_name, "NSError"))
2100b57cec5SDimitry Andric     return (new NSErrorSyntheticFrontEnd(valobj_sp));
2110b57cec5SDimitry Andric   else if (!strcmp(class_name, "__NSCFError"))
2120b57cec5SDimitry Andric     return (new NSErrorSyntheticFrontEnd(valobj_sp));
2130b57cec5SDimitry Andric 
2140b57cec5SDimitry Andric   return nullptr;
2150b57cec5SDimitry Andric }
216