[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