[Lldb-commits] [lldb] r167061 - in /lldb/trunk: include/lldb/API/SBFrame.h include/lldb/Symbol/ClangExternalASTSourceCommon.h source/Expression/ClangExpressionDeclMap.cpp source/Expression/ClangUserExpression.cpp test/lang/objc/blocks/ test/lang/objc/blocks/Makefile test/lang/objc/blocks/TestObjCIvarsInBlocks.py test/lang/objc/blocks/ivars-in-blocks.h test/lang/objc/blocks/ivars-in-blocks.m test/lang/objc/blocks/main.m
Jim Ingham
jingham at apple.com
Tue Oct 30 16:35:55 PDT 2012
Author: jingham
Date: Tue Oct 30 18:35:54 2012
New Revision: 167061
URL: http://llvm.org/viewvc/llvm-project?rev=167061&view=rev
Log:
Make blocks that capture their containing method's object pointer look like methods of
the containing class so that direct ivar access will work in the expression parser.
<rdar://problem/9797999>
Added:
lldb/trunk/test/lang/objc/blocks/
lldb/trunk/test/lang/objc/blocks/Makefile
lldb/trunk/test/lang/objc/blocks/TestObjCIvarsInBlocks.py
lldb/trunk/test/lang/objc/blocks/ivars-in-blocks.h
lldb/trunk/test/lang/objc/blocks/ivars-in-blocks.m
lldb/trunk/test/lang/objc/blocks/main.m
Modified:
lldb/trunk/include/lldb/API/SBFrame.h
lldb/trunk/include/lldb/Symbol/ClangExternalASTSourceCommon.h
lldb/trunk/source/Expression/ClangExpressionDeclMap.cpp
lldb/trunk/source/Expression/ClangUserExpression.cpp
Modified: lldb/trunk/include/lldb/API/SBFrame.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/API/SBFrame.h?rev=167061&r1=167060&r2=167061&view=diff
==============================================================================
--- lldb/trunk/include/lldb/API/SBFrame.h (original)
+++ lldb/trunk/include/lldb/API/SBFrame.h Tue Oct 30 18:35:54 2012
@@ -185,6 +185,9 @@
/// 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
Modified: lldb/trunk/include/lldb/Symbol/ClangExternalASTSourceCommon.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Symbol/ClangExternalASTSourceCommon.h?rev=167061&r1=167060&r2=167061&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Symbol/ClangExternalASTSourceCommon.h (original)
+++ lldb/trunk/include/lldb/Symbol/ClangExternalASTSourceCommon.h Tue Oct 30 18:35:54 2012
@@ -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 @@
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)
Modified: lldb/trunk/source/Expression/ClangExpressionDeclMap.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Expression/ClangExpressionDeclMap.cpp?rev=167061&r1=167060&r2=167061&view=diff
==============================================================================
--- lldb/trunk/source/Expression/ClangExpressionDeclMap.cpp (original)
+++ lldb/trunk/source/Expression/ClangExpressionDeclMap.cpp Tue Oct 30 18:35:54 2012
@@ -2470,34 +2470,83 @@
clang::CXXMethodDecl *method_decl = llvm::dyn_cast<clang::CXXMethodDecl>(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());
- }
-
- AddOneType(context, class_user_type, current_id, true);
- if (method_decl->isInstance())
- {
- // self is a pointer to the object
+ clang::CXXRecordDecl *class_decl = method_decl->getParent();
+
+ QualType class_qual_type(class_decl->getTypeForDecl(), 0);
- QualType class_pointer_type = method_decl->getASTContext().getPointerType(class_qual_type);
+ TypeFromUser class_user_type (class_qual_type.getAsOpaquePtr(),
+ &class_decl->getASTContext());
- TypeFromUser self_user_type(class_pointer_type.getAsOpaquePtr(),
- &method_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());
+ }
- m_struct_vars->m_object_pointer_type = self_user_type;
+ 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;
+ }
+ }
+ else
+ {
+ // 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.
+
+ VariableList *vars = frame->GetVariableList(false);
+
+ lldb::VariableSP this_var = vars->FindVariable(ConstString("this"));
+
+ 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<PointerType>();
+
+ 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 @@
clang::ObjCMethodDecl *method_decl = llvm::dyn_cast<clang::ObjCMethodDecl>(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());
- }
-
- 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();
+
+ ObjCInterfaceDecl* self_interface = method_decl->getClassInterface();
- 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();
-
- TypeFromUser self_user_type(class_type.getAsOpaquePtr(),
- &method_decl->getASTContext());
-
- m_struct_vars->m_object_pointer_type = self_user_type;
+ // 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.
+
+ 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)
+ return;
+
+ QualType self_qual_type = QualType::getFromOpaquePtr(self_type->GetClangFullType());
+ const ObjCObjectPointerType *class_pointer_type = self_qual_type->getAs<ObjCObjectPointerType>();
+
+ 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;
Modified: lldb/trunk/source/Expression/ClangUserExpression.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Expression/ClangUserExpression.cpp?rev=167061&r1=167060&r2=167061&view=diff
==============================================================================
--- lldb/trunk/source/Expression/ClangUserExpression.cpp (original)
+++ lldb/trunk/source/Expression/ClangUserExpression.cpp Tue Oct 30 18:35:54 2012
@@ -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 @@
m_static_method = true;
}
}
+ else if (clang::FunctionDecl *function_decl = llvm::dyn_cast<clang::FunctionDecl>(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
Added: lldb/trunk/test/lang/objc/blocks/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/lang/objc/blocks/Makefile?rev=167061&view=auto
==============================================================================
--- lldb/trunk/test/lang/objc/blocks/Makefile (added)
+++ lldb/trunk/test/lang/objc/blocks/Makefile Tue Oct 30 18:35:54 2012
@@ -0,0 +1,6 @@
+LEVEL = ../../../make
+
+OBJC_SOURCES := ivars-in-blocks.m main.m
+LDFLAGS = $(CFLAGS) -lobjc -framework Foundation
+
+include $(LEVEL)/Makefile.rules
Added: lldb/trunk/test/lang/objc/blocks/TestObjCIvarsInBlocks.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/lang/objc/blocks/TestObjCIvarsInBlocks.py?rev=167061&view=auto
==============================================================================
--- lldb/trunk/test/lang/objc/blocks/TestObjCIvarsInBlocks.py (added)
+++ lldb/trunk/test/lang/objc/blocks/TestObjCIvarsInBlocks.py Tue Oct 30 18:35:54 2012
@@ -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()
Added: lldb/trunk/test/lang/objc/blocks/ivars-in-blocks.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/lang/objc/blocks/ivars-in-blocks.h?rev=167061&view=auto
==============================================================================
--- lldb/trunk/test/lang/objc/blocks/ivars-in-blocks.h (added)
+++ lldb/trunk/test/lang/objc/blocks/ivars-in-blocks.h Tue Oct 30 18:35:54 2012
@@ -0,0 +1,10 @@
+#import <Foundation/Foundation.h>
+
+ at interface IAmBlocky : NSObject
+{
+ @public
+ int blocky_ivar;
+}
+- (IAmBlocky *) init;
+- (int) callABlock: (int) block_value;
+ at end
Added: lldb/trunk/test/lang/objc/blocks/ivars-in-blocks.m
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/lang/objc/blocks/ivars-in-blocks.m?rev=167061&view=auto
==============================================================================
--- lldb/trunk/test/lang/objc/blocks/ivars-in-blocks.m (added)
+++ lldb/trunk/test/lang/objc/blocks/ivars-in-blocks.m Tue Oct 30 18:35:54 2012
@@ -0,0 +1,40 @@
+#import "ivars-in-blocks.h"
+
+typedef int (^my_block_ptr_type) (int);
+
+ at interface IAmBlocky()
+{
+ int _hidden_ivar;
+ my_block_ptr_type _block_ptr;
+}
+
+ at end
+
+ at 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);
+}
+ at end
+
Added: lldb/trunk/test/lang/objc/blocks/main.m
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/lang/objc/blocks/main.m?rev=167061&view=auto
==============================================================================
--- lldb/trunk/test/lang/objc/blocks/main.m (added)
+++ lldb/trunk/test/lang/objc/blocks/main.m Tue Oct 30 18:35:54 2012
@@ -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;
+}
More information about the lldb-commits
mailing list