Extended LLDB to handle blocks capturing 'self'
authorSean Callanan <scallanan@apple.com>
Sat, 19 Jan 2013 01:49:02 +0000 (01:49 +0000)
committerSean Callanan <scallanan@apple.com>
Sat, 19 Jan 2013 01:49:02 +0000 (01:49 +0000)
in an Objective-C class method.  Before, errors
of the form

error: cannot find interface declaration for '$__lldb_objc_class'

would appear when running any expression when
the current frame is a block that captures 'self'
from an Objective-C class method.

<rdar://problem/12905561>

llvm-svn: 172880

lldb/source/Expression/ClangExpressionDeclMap.cpp
lldb/source/Expression/ClangUserExpression.cpp
lldb/test/lang/objc/blocks/TestObjCIvarsInBlocks.py
lldb/test/lang/objc/blocks/ivars-in-blocks.h
lldb/test/lang/objc/blocks/ivars-in-blocks.m

index f11b73e..ce6404d 100644 (file)
@@ -2758,7 +2758,6 @@ ClangExpressionDeclMap::FindExternalVisibleDecls (NameSearchContext &context,
             
             if (method_decl)
             {
-
                 ObjCInterfaceDecl* self_interface = method_decl->getClassInterface();
                 
                 if (!self_interface)
@@ -2822,10 +2821,15 @@ ClangExpressionDeclMap::FindExternalVisibleDecls (NameSearchContext &context,
                         return;
                     
                     QualType self_qual_type = QualType::getFromOpaquePtr(self_type->GetClangFullType());
-                    const ObjCObjectPointerType *class_pointer_type = self_qual_type->getAs<ObjCObjectPointerType>();
                     
-                    if (class_pointer_type)
+                    if (self_qual_type->isObjCClassType())
+                    {
+                        return;
+                    }
+                    else if (self_qual_type->isObjCObjectPointerType())
                     {
+                        const ObjCObjectPointerType *class_pointer_type = self_qual_type->getAs<ObjCObjectPointerType>();
+                    
                         QualType class_type = class_pointer_type->getPointeeType();
                         
                         if (log)
@@ -2835,10 +2839,10 @@ ClangExpressionDeclMap::FindExternalVisibleDecls (NameSearchContext &context,
                         }
                         
                         TypeFromUser class_user_type (class_type.getAsOpaquePtr(),
-                                                        self_type->GetClangAST());
+                                                      self_type->GetClangAST());
+                        
                         AddOneType(context, class_user_type, current_id, false);
                                     
-                                    
                         TypeFromUser self_user_type(self_type->GetClangFullType(),
                                                     self_type->GetClangAST());
                         
index d228569..6678381 100644 (file)
@@ -234,13 +234,94 @@ ClangUserExpression::ScanContext(ExecutionContext &exe_ctx, Error &err)
             lldb::LanguageType language = metadata->GetObjectPtrLanguage();
             if (language == lldb::eLanguageTypeC_plus_plus)
             {
+                if (m_enforce_valid_object)
+                {
+                    lldb::VariableListSP variable_list_sp (function_block->GetBlockVariableList (true));
+                    
+                    const char *thisErrorString = "Stopped in a context claiming to capture a C++ object pointer, but 'this' isn't available; pretending we are in a generic context";
+                    
+                    if (!variable_list_sp)
+                    {
+                        err.SetErrorString(thisErrorString);
+                        return;
+                    }
+                    
+                    lldb::VariableSP this_var_sp (variable_list_sp->FindVariable(ConstString("this")));
+                    
+                    if (!this_var_sp ||
+                        !this_var_sp->IsInScope(frame) ||
+                        !this_var_sp->LocationIsValidForFrame (frame))
+                    {
+                        err.SetErrorString(thisErrorString);
+                        return;
+                    }
+                }
+                
                 m_cplusplus = true;
                 m_needs_object_ptr = true;
             }
             else if (language == lldb::eLanguageTypeObjC)
             {
-                m_objectivec = true;
-                m_needs_object_ptr = true;
+                if (m_enforce_valid_object)
+                {
+                    lldb::VariableListSP variable_list_sp (function_block->GetBlockVariableList (true));
+                    
+                    const char *selfErrorString = "Stopped in a context claiming to capture an Objective-C object pointer, but 'self' isn't available; pretending we are in a generic context";
+                    
+                    if (!variable_list_sp)
+                    {
+                        err.SetErrorString(selfErrorString);
+                        return;
+                    }
+                    
+                    lldb::VariableSP self_variable_sp = variable_list_sp->FindVariable(ConstString("self"));
+                    
+                    if (!self_variable_sp ||
+                        !self_variable_sp->IsInScope(frame) ||
+                        !self_variable_sp->LocationIsValidForFrame (frame))
+                    {
+                        err.SetErrorString(selfErrorString);
+                        return;
+                    }
+                    
+                    Type *self_type = self_variable_sp->GetType();
+                    
+                    if (!self_type)
+                    {
+                        err.SetErrorString(selfErrorString);
+                        return;
+                    }
+                    
+                    lldb::clang_type_t self_opaque_type = self_type->GetClangForwardType();
+                    
+                    if (!self_opaque_type)
+                    {
+                        err.SetErrorString(selfErrorString);
+                        return;
+                    }
+                    
+                    clang::QualType self_qual_type = clang::QualType::getFromOpaquePtr(self_opaque_type);
+                    
+                    if (self_qual_type->isObjCClassType())
+                    {
+                        return;
+                    }
+                    else if (self_qual_type->isObjCObjectPointerType())
+                    {
+                        m_objectivec = true;
+                        m_needs_object_ptr = true;
+                    }
+                    else
+                    {
+                        err.SetErrorString(selfErrorString);
+                        return;
+                    }
+                }
+                else
+                {
+                    m_objectivec = true;
+                    m_needs_object_ptr = true;
+                }
             }
         }
     }
index 55ab980..b91c9e8 100644 (file)
@@ -48,6 +48,9 @@ class TestObjCIvarsInBlocks(TestBase):
         breakpoint = target.BreakpointCreateBySourceRegex ('// Break here inside the block.', self.class_source_file_spec)
         self.assertTrue(breakpoint, VALID_BREAKPOINT)
 
+        breakpoint_two = target.BreakpointCreateBySourceRegex ('// Break here inside the class method 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.")
@@ -94,6 +97,21 @@ class TestObjCIvarsInBlocks(TestBase):
         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.")
+
+        process.Continue()
+        self.assertTrue (process.GetState() == lldb.eStateStopped, "Stopped at the second breakpoint.")
+
+        thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, breakpoint_two)
+        self.assertTrue (len(thread_list) == 1)
+        thread = thread_list[0]
+        
+        frame = thread.GetFrameAtIndex(0)
+        self.assertTrue (frame, "frame 0 is valid")
+        
+        expr = frame.EvaluateExpression("(ret)")
+        self.assertTrue (expr, "Successfully got a local variable in a block in a class method.")
+
+        self.assertTrue (expr.GetValueAsSigned (error) == 5, "The local variable in the block was what we expected.")
         
 if __name__ == '__main__':
     import atexit
index a91793d..1ceac33 100644 (file)
@@ -5,6 +5,7 @@
   @public
   int blocky_ivar;
 }
++ (void) classMethod;
 - (IAmBlocky *) init;
 - (int) callABlock: (int) block_value;
 @end
index 0d2aa73..1098a91 100644 (file)
@@ -11,7 +11,22 @@ typedef int (^my_block_ptr_type) (int);
 @end
 
 @implementation IAmBlocky
+
++ (int) addend
+{
+  return 3;
+}
  
++ (void) classMethod
+{
+  int (^my_block)(int) = ^(int foo)
+  {
+    int ret = foo + [self addend];
+    return ret; // Break here inside the class method block.
+  };
+  printf("%d\n", my_block(2));
+}
+
 - (void) makeBlockPtr;
 {
   _block_ptr = ^(int inval)
@@ -34,7 +49,9 @@ typedef int (^my_block_ptr_type) (int);
 {
   if (_block_ptr == NULL)
     [self makeBlockPtr];
-  return _block_ptr (block_value);
+  int ret = _block_ptr (block_value);
+  [IAmBlocky classMethod];
+  return ret;
 }
 @end