diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h --- a/lldb/include/lldb/Symbol/TypeSystem.h +++ b/lldb/include/lldb/Symbol/TypeSystem.h @@ -253,6 +253,8 @@ virtual CompilerType GetAtomicType(lldb::opaque_compiler_type_t type); + virtual CompilerType GetEmptyStructType(ConstString type_name); + virtual CompilerType AddConstModifier(lldb::opaque_compiler_type_t type); virtual CompilerType AddVolatileModifier(lldb::opaque_compiler_type_t type); diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp --- a/lldb/source/Core/ValueObject.cpp +++ b/lldb/source/Core/ValueObject.cpp @@ -687,6 +687,11 @@ language_flags); } + if (!valobj && synthetic_array_member) + valobj = GetSyntheticValue() + ->GetChildAtIndex(synthetic_index, synthetic_array_member) + .get(); + return valobj; } @@ -2830,6 +2835,35 @@ child_is_base_class, child_is_deref_of_parent, eAddressTypeInvalid, language_flags); } + + // In case of C opaque pointers, create an empty struct type and try to + // recreate a new ValueObjectChild using it. + if (!m_deref_valobj) { + if (Language::LanguageIsCFamily(GetPreferredDisplayLanguage())) { + if (HasSyntheticValue()) { + TypeSystem *type_system = compiler_type.GetTypeSystem(); + if (type_system) { + + child_compiler_type = + type_system->GetEmptyStructType(compiler_type.GetTypeName()); + + if (child_compiler_type) { + ConstString child_name; + if (!child_name_str.empty()) + child_name.SetCString(child_name_str.c_str()); + + m_deref_valobj = new ValueObjectChild( + *this, child_compiler_type, child_name, child_byte_size, + child_byte_offset, child_bitfield_bit_size, + child_bitfield_bit_offset, child_is_base_class, + child_is_deref_of_parent, eAddressTypeInvalid, + language_flags); + } + } + } + } + } + } else if (HasSyntheticValue()) { m_deref_valobj = GetSyntheticValue() diff --git a/lldb/source/Plugins/Language/ObjC/NSSet.cpp b/lldb/source/Plugins/Language/ObjC/NSSet.cpp --- a/lldb/source/Plugins/Language/ObjC/NSSet.cpp +++ b/lldb/source/Plugins/Language/ObjC/NSSet.cpp @@ -282,6 +282,7 @@ static const ConstString g_OrderedSetI("__NSOrderedSetI"); static const ConstString g_SetM("__NSSetM"); static const ConstString g_SetCF("__NSCFSet"); + static const ConstString g_SetCFRef("CFSetRef"); if (class_name.IsEmpty()) return false; @@ -306,7 +307,7 @@ } if (error.Fail()) return false; - } else if (class_name == g_SetCF) { + } else if (class_name == g_SetCF || class_name == g_SetCFRef) { ExecutionContext exe_ctx(process_sp); CFBasicHash cfbh; if (!cfbh.Update(valobj_addr, exe_ctx)) @@ -367,6 +368,7 @@ static const ConstString g_OrderedSetI("__NSOrderedSetI"); static const ConstString g_SetM("__NSSetM"); static const ConstString g_SetCF("__NSCFSet"); + static const ConstString g_SetCFRef("CFSetRef"); if (class_name.IsEmpty()) return nullptr; @@ -386,7 +388,7 @@ } else { return (new Foundation1300::NSSetMSyntheticFrontEnd(valobj_sp)); } - } else if (class_name == g_SetCF) { + } else if (class_name == g_SetCF || class_name == g_SetCFRef) { return (new NSCFSetSyntheticFrontEnd(valobj_sp)); } else { auto &map(NSSet_Additionals::GetAdditionalSynthetics()); diff --git a/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp b/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp --- a/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp +++ b/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp @@ -610,6 +610,10 @@ lldb_private::formatters::NSSetSyntheticFrontEndCreator, "__NSCFSet synthetic children", ConstString("__NSCFSet"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(objc_category_sp, + lldb_private::formatters::NSSetSyntheticFrontEndCreator, + "CFSetRef synthetic children", ConstString("CFSetRef"), + ScriptedSyntheticChildren::Flags()); AddCXXSynthetic( objc_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -729,6 +729,8 @@ CompilerType GetAtomicType(lldb::opaque_compiler_type_t type) override; + CompilerType GetEmptyStructType(ConstString type_name) override; + CompilerType AddConstModifier(lldb::opaque_compiler_type_t type) override; CompilerType AddVolatileModifier(lldb::opaque_compiler_type_t type) override; diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -4445,6 +4445,23 @@ return GetType(getASTContext().getAtomicType(GetQualType(type))); } +CompilerType TypeSystemClang::GetEmptyStructType(ConstString type_name) { + if (type_name.IsEmpty()) + return CompilerType(); + + CompilerType empty_struct_type = CreateRecordType( + nullptr, OptionalClangModuleID(), lldb::eAccessPublic, + type_name.GetCString(), clang::TTK_Struct, lldb::eLanguageTypeC); + + if (!empty_struct_type) + return CompilerType(); + + StartTagDeclarationDefinition(empty_struct_type); + CompleteTagDeclarationDefinition(empty_struct_type); + + return empty_struct_type; +} + CompilerType TypeSystemClang::AddConstModifier(lldb::opaque_compiler_type_t type) { if (type) { diff --git a/lldb/source/Symbol/TypeSystem.cpp b/lldb/source/Symbol/TypeSystem.cpp --- a/lldb/source/Symbol/TypeSystem.cpp +++ b/lldb/source/Symbol/TypeSystem.cpp @@ -97,6 +97,10 @@ return CompilerType(); } +CompilerType TypeSystem::GetEmptyStructType(ConstString type_name) { + return CompilerType(); +} + CompilerType TypeSystem::AddConstModifier(lldb::opaque_compiler_type_t type) { return CompilerType(); } diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjCNSContainer.py b/lldb/test/API/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjCNSContainer.py --- a/lldb/test/API/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjCNSContainer.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjCNSContainer.py @@ -32,7 +32,7 @@ '(NSDictionary *) nscfDictionary = ', ' 4 key/value pairs', '(CFDictionaryRef) cfDictionaryRef = ', - ' 3 key/value pairs', + ' 2 key/value pairs', '(NSDictionary *) newMutableDictionary = ', ' 21 key/value pairs', '(CFArrayRef) cfarray_ref = ', @@ -57,10 +57,23 @@ self.expect( - 'frame var nscfSet', + 'frame variable -d run-target *cfDictionaryRef', + patterns=[ + '\(CFDictionaryRef\) \*cfDictionaryRef =', + 'key = 0x.* @"foo"', + 'value = 0x.* @"foo"', + 'key = 0x.* @"bar"', + 'value = 0x.* @"bar"', + ]) + + + self.expect( + 'frame var nscfSet cfSetRef', substrs=[ '(NSSet *) nscfSet = ', '2 elements', + '(CFSetRef) cfSetRef = ', + '2 elements', ]) self.expect( @@ -71,6 +84,14 @@ '\[1\] = 0x.* @".*"', ]) + self.expect( + 'frame variable -d run-target *cfSetRef', + patterns=[ + '\(CFSetRef\) \*cfSetRef =', + '\[0\] = 0x.* @".*"', + '\[1\] = 0x.* @".*"', + ]) + self.expect( 'frame variable iset1 iset2 imset', substrs=['4 indexes', '512 indexes', '10 indexes']) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-objc/main.m b/lldb/test/API/functionalities/data-formatter/data-formatter-objc/main.m --- a/lldb/test/API/functionalities/data-formatter/data-formatter-objc/main.m +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-objc/main.m @@ -482,8 +482,7 @@ CFDictionaryCreate(nil, (void *)cfKeys, (void *)cfValues, 2, nil, nil)); NSDictionary *nscfDictionary = CFBridgingRelease( CFDictionaryCreate(nil, (void *)cfKeys, (void *)cfValues, 4, nil, nil)); - CFDictionaryRef cfDictionaryRef = - CFDictionaryCreate(nil, (void *)cfKeys, (void *)cfValues, 3, nil, nil); + CFDictionaryRef cfDictionaryRef = (__bridge CFDictionaryRef)nsDictionary; NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:@"hello world from foo" @@ -542,6 +541,7 @@ [nsmutableset addObject:str4]; NSSet *nscfSet = CFBridgingRelease(CFSetCreate(nil, (void *)cfValues, 2, nil)); + CFSetRef cfSetRef = (__bridge CFSetRef)nscfSet; CFDataRef data_ref = CFDataCreate(kCFAllocatorDefault, [immutableData bytes], 5); diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-opaque-ptr/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-opaque-ptr/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-opaque-ptr/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-opaque-ptr/TestDataFormatterOpaquePtr.py b/lldb/test/API/functionalities/data-formatter/data-formatter-opaque-ptr/TestDataFormatterOpaquePtr.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-opaque-ptr/TestDataFormatterOpaquePtr.py @@ -0,0 +1,51 @@ +""" +Test lldb data formatter subsystem. +""" + + + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class OpaquePtrDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set breakpoint here.') + + # FIXME: Still need some adjustements (WIP) + @expectedFailureAll() + def test_with_run_command(self): + """Test that that file and class static variables display correctly.""" + self.build() +# self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) + + self.build() + lldbutil.run_to_line_breakpoint(self, lldb.SBFileSpec(self.source), + self.line) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.runCmd("command script import opaque_ptr.py") + + self.runCmd("type summary add Opaque --python-function opaque_ptr.OpaquePtrSummaryProvider -C no") + self.runCmd("type summary add OpaqueRef --python-function opaque_ptr.OpaquePtrSummaryProvider") + self.runCmd("type synthetic add Opaque* --python-class opaque_ptr.OpaquePtrSyntheticChildProvider") +# self.runCmd("type summary add Opaque --python-function opaque_ptr.OpaquePtrSummaryProvider -C no") + + self.expect("frame variable base", substrs=['contains a value!']) + self.expect("frame variable ptr", substrs=['contains a value!']) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-opaque-ptr/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-opaque-ptr/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-opaque-ptr/main.cpp @@ -0,0 +1,9 @@ +#include "opaque_ptr.h" + +typedef Opaque *OpaquePtr; + +int main() { + Opaque base(7); + OpaquePtr ptr = &base; + return 0; // Set breakpoint here. +} diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-opaque-ptr/opaque_ptr.h b/lldb/test/API/functionalities/data-formatter/data-formatter-opaque-ptr/opaque_ptr.h new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-opaque-ptr/opaque_ptr.h @@ -0,0 +1,8 @@ +class Opaque { + class Impl; + Impl *pImpl; + +public: + Opaque(int); + ~Opaque(); +}; diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-opaque-ptr/opaque_ptr.cc b/lldb/test/API/functionalities/data-formatter/data-formatter-opaque-ptr/opaque_ptr.cc new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-opaque-ptr/opaque_ptr.cc @@ -0,0 +1,9 @@ +#include "opaque_ptr.h" + +class Opaque::Impl { + int value; // private data +public: + Impl(int value) : value(value) {} +}; +Opaque::Opaque(int value) : pImpl{new Impl(value)} {} +Opaque::~Opaque() = default; diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-opaque-ptr/opaque_ptr.py b/lldb/test/API/functionalities/data-formatter/data-formatter-opaque-ptr/opaque_ptr.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-opaque-ptr/opaque_ptr.py @@ -0,0 +1,42 @@ +import lldb +import lldb.formatters +import lldb.formatters.synth as synth + +def ReadOpaquePointerAddress(var): + error = lldb.SBError() + + ptr = var.GetChildMemberWithName('pImpl') + + if not ptr or not ptr.IsValid(): + error.SetErrorString('Invalid pointer.') + return error, None + + return error, ptr.GetValue() + +def ReadOpaquePointerValue(var): + error = lldb.SBError() + + ptr = var.GetChildAtIndex(0) + + if not ptr or not ptr.IsValid(): + error.SetErrorString('Invalid pointer.') + return error, None + + return error, ptr.GetValue; + +def OpaquePtrSummaryProvider(value, _): + error, addr = ReadOpaquePointerAddress(value) + if error.Fail(): + return "{} has no value!".format(addr) + return "{} contains a value!".format(addr) + +class OpaquePtrSyntheticChildProvider(synth.PythonObjectSyntheticChildProvider): + def __init__(self, value, dict): + synth.PythonObjectSyntheticChildProvider.__init__(self, value, dict) + + def make_children(self): + error, value = ReadOpaquePointerValue(self.value) + + if error.Fail(): + return [] + return [('value', value)]