diff --git a/lldb/include/lldb/API/SBFrame.h b/lldb/include/lldb/API/SBFrame.h index 8af708b7e5e9..ea26ed3c4fc1 100644 --- a/lldb/include/lldb/API/SBFrame.h +++ b/lldb/include/lldb/API/SBFrame.h @@ -185,6 +185,9 @@ public: /// Find variables, register sets, registers, or persistent variables using /// the frame as the scope. /// + /// NB. This function does not look up ivars in the function object pointer. + /// To do that use GetValueForVariablePath. + /// /// The version that doesn't supply a 'use_dynamic' value will use the /// target's default. lldb::SBValue diff --git a/lldb/include/lldb/Symbol/ClangExternalASTSourceCommon.h b/lldb/include/lldb/Symbol/ClangExternalASTSourceCommon.h index 7dc37899497f..de96f9fd30dd 100644 --- a/lldb/include/lldb/Symbol/ClangExternalASTSourceCommon.h +++ b/lldb/include/lldb/Symbol/ClangExternalASTSourceCommon.h @@ -37,6 +37,7 @@ #endif #include "lldb/lldb-defines.h" +#include "lldb/lldb-enumerations.h" #include "lldb/Core/dwarf.h" namespace lldb_private { @@ -95,6 +96,19 @@ public: m_has_object_ptr = false; } + lldb::LanguageType + GetObjectPtrLanguage () const + { + if (m_has_object_ptr) + { + if (m_is_self) + return lldb::eLanguageTypeObjC; + else + return lldb::eLanguageTypeC_plus_plus; + } + return lldb::eLanguageTypeUnknown; + + } const char *GetObjectPtrName() const { if (m_has_object_ptr) diff --git a/lldb/source/Expression/ClangExpressionDeclMap.cpp b/lldb/source/Expression/ClangExpressionDeclMap.cpp index f8304b98b6bf..f54d53e7f7e0 100644 --- a/lldb/source/Expression/ClangExpressionDeclMap.cpp +++ b/lldb/source/Expression/ClangExpressionDeclMap.cpp @@ -2470,34 +2470,83 @@ ClangExpressionDeclMap::FindExternalVisibleDecls (NameSearchContext &context, clang::CXXMethodDecl *method_decl = llvm::dyn_cast(decl_context); - if (!method_decl) - return; - - clang::CXXRecordDecl *class_decl = method_decl->getParent(); - - QualType class_qual_type(class_decl->getTypeForDecl(), 0); - - TypeFromUser class_user_type (class_qual_type.getAsOpaquePtr(), - &class_decl->getASTContext()); - - if (log) + if (method_decl) { - ASTDumper ast_dumper(class_qual_type); - log->Printf(" CEDM::FEVD[%u] Adding type for $__lldb_class: %s", current_id, ast_dumper.GetCString()); + + clang::CXXRecordDecl *class_decl = method_decl->getParent(); + + QualType class_qual_type(class_decl->getTypeForDecl(), 0); + + TypeFromUser class_user_type (class_qual_type.getAsOpaquePtr(), + &class_decl->getASTContext()); + + if (log) + { + ASTDumper ast_dumper(class_qual_type); + log->Printf(" CEDM::FEVD[%u] Adding type for $__lldb_class: %s", current_id, ast_dumper.GetCString()); + } + + AddOneType(context, class_user_type, current_id, true); + + if (method_decl->isInstance()) + { + // self is a pointer to the object + + QualType class_pointer_type = method_decl->getASTContext().getPointerType(class_qual_type); + + TypeFromUser self_user_type(class_pointer_type.getAsOpaquePtr(), + &method_decl->getASTContext()); + + m_struct_vars->m_object_pointer_type = self_user_type; + } } - - AddOneType(context, class_user_type, current_id, true); - - if (method_decl->isInstance()) + else { - // self is a pointer to the object + // This branch will get hit if we are executing code in the context of a function that + // claims to have an object pointer (through DW_AT_object_pointer?) but is not formally a + // method of the class. In that case, just look up the "this" variable in the the current + // scope and use its type. + // FIXME: This code is formally correct, but clang doesn't currently emit DW_AT_object_pointer + // for C++ so it hasn't actually been tested. - QualType class_pointer_type = method_decl->getASTContext().getPointerType(class_qual_type); + VariableList *vars = frame->GetVariableList(false); - TypeFromUser self_user_type(class_pointer_type.getAsOpaquePtr(), - &method_decl->getASTContext()); + lldb::VariableSP this_var = vars->FindVariable(ConstString("this")); - m_struct_vars->m_object_pointer_type = self_user_type; + if (this_var && + this_var->IsInScope(frame) && + this_var->LocationIsValidForFrame (frame)) + { + Type *this_type = this_var->GetType(); + + if (!this_type) + return; + + QualType this_qual_type = QualType::getFromOpaquePtr(this_type->GetClangFullType()); + const PointerType *class_pointer_type = this_qual_type->getAs(); + + if (class_pointer_type) + { + QualType class_type = class_pointer_type->getPointeeType(); + + if (log) + { + ASTDumper ast_dumper(this_type->GetClangFullType()); + log->Printf(" FEVD[%u] Adding type for $__lldb_objc_class: %s", current_id, ast_dumper.GetCString()); + } + + TypeFromUser class_user_type (class_type.getAsOpaquePtr(), + this_type->GetClangAST()); + AddOneType(context, class_user_type, current_id, false); + + + TypeFromUser this_user_type(this_type->GetClangFullType(), + this_type->GetClangAST()); + + m_struct_vars->m_object_pointer_type = this_user_type; + return; + } + } } return; @@ -2529,65 +2578,96 @@ ClangExpressionDeclMap::FindExternalVisibleDecls (NameSearchContext &context, clang::ObjCMethodDecl *method_decl = llvm::dyn_cast(decl_context); - if (!method_decl) - return; - - ObjCInterfaceDecl* self_interface = method_decl->getClassInterface(); - - if (!self_interface) - return; - - const clang::Type *interface_type = self_interface->getTypeForDecl(); - - TypeFromUser class_user_type(QualType(interface_type, 0).getAsOpaquePtr(), - &method_decl->getASTContext()); - - if (log) + if (method_decl) { - ASTDumper ast_dumper(interface_type); - log->Printf(" FEVD[%u] Adding type for $__lldb_objc_class: %s", current_id, ast_dumper.GetCString()); - } + + ObjCInterfaceDecl* self_interface = method_decl->getClassInterface(); - AddOneType(context, class_user_type, current_id, false); - -#if 0 - VariableList *vars = frame->GetVariableList(false); - - lldb::VariableSP self_var = vars->FindVariable(ConstString("self")); - - if (self_var && - self_var->IsInScope(frame) && - self_var->LocationIsValidForFrame (frame)) { - Type *self_type = self_var->GetType(); - - if (!self_type) + if (!self_interface) return; - TypeFromUser self_user_type(self_type->GetClangFullType(), - self_type->GetClangAST()); - } -#endif - - if (method_decl->isInstanceMethod()) - { - // self is a pointer to the object + const clang::Type *interface_type = self_interface->getTypeForDecl(); + + TypeFromUser class_user_type(QualType(interface_type, 0).getAsOpaquePtr(), + &method_decl->getASTContext()); - QualType class_pointer_type = method_decl->getASTContext().getObjCObjectPointerType(QualType(interface_type, 0)); - - TypeFromUser self_user_type(class_pointer_type.getAsOpaquePtr(), - &method_decl->getASTContext()); - - m_struct_vars->m_object_pointer_type = self_user_type; + if (log) + { + ASTDumper ast_dumper(interface_type); + log->Printf(" FEVD[%u] Adding type for $__lldb_objc_class: %s", current_id, ast_dumper.GetCString()); + } + + AddOneType(context, class_user_type, current_id, false); + + if (method_decl->isInstanceMethod()) + { + // self is a pointer to the object + + QualType class_pointer_type = method_decl->getASTContext().getObjCObjectPointerType(QualType(interface_type, 0)); + + TypeFromUser self_user_type(class_pointer_type.getAsOpaquePtr(), + &method_decl->getASTContext()); + + m_struct_vars->m_object_pointer_type = self_user_type; + } + else + { + // self is a Class pointer + QualType class_type = method_decl->getASTContext().getObjCClassType(); + + TypeFromUser self_user_type(class_type.getAsOpaquePtr(), + &method_decl->getASTContext()); + + m_struct_vars->m_object_pointer_type = self_user_type; + } + + return; } else { - // self is a Class pointer - QualType class_type = method_decl->getASTContext().getObjCClassType(); + // This branch will get hit if we are executing code in the context of a function that + // claims to have an object pointer (through DW_AT_object_pointer?) but is not formally a + // method of the class. In that case, just look up the "self" variable in the the current + // scope and use its type. - TypeFromUser self_user_type(class_type.getAsOpaquePtr(), - &method_decl->getASTContext()); + VariableList *vars = frame->GetVariableList(false); - m_struct_vars->m_object_pointer_type = self_user_type; + lldb::VariableSP self_var = vars->FindVariable(ConstString("self")); + + if (self_var && + self_var->IsInScope(frame) && + self_var->LocationIsValidForFrame (frame)) + { + Type *self_type = self_var->GetType(); + + if (!self_type) + return; + + QualType self_qual_type = QualType::getFromOpaquePtr(self_type->GetClangFullType()); + const ObjCObjectPointerType *class_pointer_type = self_qual_type->getAs(); + + if (class_pointer_type) + { + QualType class_type = class_pointer_type->getPointeeType(); + + if (log) + { + ASTDumper ast_dumper(self_type->GetClangFullType()); + log->Printf(" FEVD[%u] Adding type for $__lldb_objc_class: %s", current_id, ast_dumper.GetCString()); + } + + TypeFromUser class_user_type (class_type.getAsOpaquePtr(), + self_type->GetClangAST()); + AddOneType(context, class_user_type, current_id, false); + + + TypeFromUser self_user_type(self_type->GetClangFullType(), + self_type->GetClangAST()); + + m_struct_vars->m_object_pointer_type = self_user_type; + return; + } + } } return; diff --git a/lldb/source/Expression/ClangUserExpression.cpp b/lldb/source/Expression/ClangUserExpression.cpp index 450728272e64..9dfd0998a251 100644 --- a/lldb/source/Expression/ClangUserExpression.cpp +++ b/lldb/source/Expression/ClangUserExpression.cpp @@ -31,6 +31,10 @@ #include "lldb/Expression/ExpressionSourceCode.h" #include "lldb/Host/Host.h" #include "lldb/Symbol/Block.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/ClangExternalASTSourceCommon.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" @@ -192,6 +196,29 @@ ClangUserExpression::ScanContext(ExecutionContext &exe_ctx, Error &err) m_static_method = true; } } + else if (clang::FunctionDecl *function_decl = llvm::dyn_cast(decl_context)) + { + // We might also have a function that said in the debug information that it captured an + // object pointer. The best way to deal with getting to the ivars at present it by pretending + // that this is a method of a class in whatever runtime the debug info says the object pointer + // belongs to. Do that here. + + ClangASTMetadata *metadata = ClangASTContext::GetMetadata (&decl_context->getParentASTContext(), (uintptr_t) function_decl); + if (metadata && metadata->HasObjectPtr()) + { + lldb::LanguageType language = metadata->GetObjectPtrLanguage(); + if (language == lldb::eLanguageTypeC_plus_plus) + { + m_cplusplus = true; + m_needs_object_ptr = true; + } + else if (language == lldb::eLanguageTypeObjC) + { + m_objectivec = true; + m_needs_object_ptr = true; + } + } + } } // This is a really nasty hack, meant to fix Objective-C expressions of the form diff --git a/lldb/test/lang/objc/blocks/Makefile b/lldb/test/lang/objc/blocks/Makefile new file mode 100644 index 000000000000..0af83591826f --- /dev/null +++ b/lldb/test/lang/objc/blocks/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +OBJC_SOURCES := ivars-in-blocks.m main.m +LDFLAGS = $(CFLAGS) -lobjc -framework Foundation + +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/lang/objc/blocks/TestObjCIvarsInBlocks.py b/lldb/test/lang/objc/blocks/TestObjCIvarsInBlocks.py new file mode 100644 index 000000000000..9371b0f1bccd --- /dev/null +++ b/lldb/test/lang/objc/blocks/TestObjCIvarsInBlocks.py @@ -0,0 +1,101 @@ +"""Test printing ivars and ObjC objects captured in blocks that are made in methods of an ObjC class.""" + +import os, time +import unittest2 +import lldb +from lldbtest import * +import lldbutil + +class TestObjCIvarsInBlocks(TestBase): + + mydir = os.path.join("lang", "objc", "blocks") + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + # This test requires the 2.0 runtime, so it will fail on i386. + @expectedFailurei386 + @python_api_test + @dsym_test + def test_with_dsym_and_python_api(self): + """Test printing the ivars of the self when captured in blocks""" + self.buildDsym() + self.ivars_in_blocks() + + @python_api_test + # This test requires the 2.0 runtime, so it will fail on i386. + @expectedFailurei386 + @dwarf_test + def test_with_dwarf_and_python_api(self): + """Test printing the ivars of the self when captured in blocks""" + self.buildDwarf() + self.ivars_in_blocks() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break inside main(). + self.main_source = "main.m" + self.class_source = "ivars-in-blocks.m" + self.class_source_file_spec = lldb.SBFileSpec(self.class_source) + + def ivars_in_blocks (self): + """Test printing the ivars of the self when captured in blocks""" + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateBySourceRegex ('// Break here inside the block.', self.class_source_file_spec) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + process = target.LaunchSimple (None, None, os.getcwd()) + self.assertTrue (process, "Created a process.") + self.assertTrue (process.GetState() == lldb.eStateStopped, "Stopped it too.") + + thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, breakpoint) + self.assertTrue (len(thread_list) == 1) + thread = thread_list[0] + + frame = thread.GetFrameAtIndex(0) + self.assertTrue (frame, "frame 0 is valid") + + # First use the FindVariable API to see if we can find the ivar by undecorated name: + direct_blocky = frame.GetValueForVariablePath ("blocky_ivar") + self.assertTrue(direct_blocky, "Found direct access to blocky_ivar.") + + # Now get it as a member of "self" and make sure the two values are equal: + self_var = frame.GetValueForVariablePath ("self") + self.assertTrue (self_var, "Found self in block.") + indirect_blocky = self_var.GetChildMemberWithName ("blocky_ivar") + self.assertTrue (indirect_blocky, "Found blocky_ivar through self") + + error = lldb.SBError() + direct_value = direct_blocky.GetValueAsSigned(error) + self.assertTrue (error.Success(), "Got direct value for blocky_ivar") + + indirect_value = indirect_blocky.GetValueAsSigned (error) + self.assertTrue (error.Success(), "Got indirect value for blocky_ivar") + + self.assertTrue (direct_value == indirect_value, "Direct and indirect values are equal.") + + # Now make sure that we can get at the captured ivar through the expression parser. + # Doing a little trivial math will force this into the real expression parser: + direct_expr = frame.EvaluateExpression ("blocky_ivar + 10") + self.assertTrue (direct_expr, "Got blocky_ivar through the expression parser") + + # Again, get the value through self directly and make sure they are the same: + indirect_expr = frame.EvaluateExpression ("self->blocky_ivar + 10") + self.assertTrue (indirect_expr, "Got blocky ivar through expression parser using self.") + + direct_value = direct_expr.GetValueAsSigned (error) + self.assertTrue (error.Success(), "Got value from direct use of expression parser") + + indirect_value = indirect_expr.GetValueAsSigned (error) + self.assertTrue (error.Success(), "Got value from indirect access using the expression parser") + + self.assertTrue (direct_value == indirect_value, "Direct ivar access and indirect through expression parser produce same value.") + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/lldb/test/lang/objc/blocks/ivars-in-blocks.h b/lldb/test/lang/objc/blocks/ivars-in-blocks.h new file mode 100644 index 000000000000..a91793d8bde7 --- /dev/null +++ b/lldb/test/lang/objc/blocks/ivars-in-blocks.h @@ -0,0 +1,10 @@ +#import + +@interface IAmBlocky : NSObject +{ + @public + int blocky_ivar; +} +- (IAmBlocky *) init; +- (int) callABlock: (int) block_value; +@end diff --git a/lldb/test/lang/objc/blocks/ivars-in-blocks.m b/lldb/test/lang/objc/blocks/ivars-in-blocks.m new file mode 100644 index 000000000000..0d2aa73475dc --- /dev/null +++ b/lldb/test/lang/objc/blocks/ivars-in-blocks.m @@ -0,0 +1,40 @@ +#import "ivars-in-blocks.h" + +typedef int (^my_block_ptr_type) (int); + +@interface IAmBlocky() +{ + int _hidden_ivar; + my_block_ptr_type _block_ptr; +} + +@end + +@implementation IAmBlocky + +- (void) makeBlockPtr; +{ + _block_ptr = ^(int inval) + { + _hidden_ivar += inval; + return blocky_ivar * inval; // Break here inside the block. + }; +} + +- (IAmBlocky *) init +{ + blocky_ivar = 10; + _hidden_ivar = 20; + // Interesting... Apparently you can't make a block in your init method. This crashes... + // [self makeBlockPtr]; + return self; +} + +- (int) callABlock: (int) block_value +{ + if (_block_ptr == NULL) + [self makeBlockPtr]; + return _block_ptr (block_value); +} +@end + diff --git a/lldb/test/lang/objc/blocks/main.m b/lldb/test/lang/objc/blocks/main.m new file mode 100644 index 000000000000..0c56f45da464 --- /dev/null +++ b/lldb/test/lang/objc/blocks/main.m @@ -0,0 +1,10 @@ +#import "ivars-in-blocks.h" + +int +main (int argc, char **argv) +{ + IAmBlocky *my_blocky = [[IAmBlocky alloc] init]; + int blocky_value; + blocky_value = [my_blocky callABlock: 33]; + return 0; +}