From a2868d4c2e612aad10bfff144bf80441132e0857 Mon Sep 17 00:00:00 2001 From: Sean Callanan Date: Sat, 19 Jan 2013 01:49:02 +0000 Subject: [PATCH] Extended LLDB to handle blocks capturing 'self' 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. llvm-svn: 172880 --- lldb/source/Expression/ClangExpressionDeclMap.cpp | 14 ++-- lldb/source/Expression/ClangUserExpression.cpp | 85 +++++++++++++++++++++- .../test/lang/objc/blocks/TestObjCIvarsInBlocks.py | 18 +++++ lldb/test/lang/objc/blocks/ivars-in-blocks.h | 1 + lldb/test/lang/objc/blocks/ivars-in-blocks.m | 19 ++++- 5 files changed, 129 insertions(+), 8 deletions(-) diff --git a/lldb/source/Expression/ClangExpressionDeclMap.cpp b/lldb/source/Expression/ClangExpressionDeclMap.cpp index f11b73e..ce6404d 100644 --- a/lldb/source/Expression/ClangExpressionDeclMap.cpp +++ b/lldb/source/Expression/ClangExpressionDeclMap.cpp @@ -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(); - 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(); + 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()); diff --git a/lldb/source/Expression/ClangUserExpression.cpp b/lldb/source/Expression/ClangUserExpression.cpp index d228569..6678381 100644 --- a/lldb/source/Expression/ClangUserExpression.cpp +++ b/lldb/source/Expression/ClangUserExpression.cpp @@ -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; + } } } } diff --git a/lldb/test/lang/objc/blocks/TestObjCIvarsInBlocks.py b/lldb/test/lang/objc/blocks/TestObjCIvarsInBlocks.py index 55ab980..b91c9e8 100644 --- a/lldb/test/lang/objc/blocks/TestObjCIvarsInBlocks.py +++ b/lldb/test/lang/objc/blocks/TestObjCIvarsInBlocks.py @@ -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 diff --git a/lldb/test/lang/objc/blocks/ivars-in-blocks.h b/lldb/test/lang/objc/blocks/ivars-in-blocks.h index a91793d..1ceac33 100644 --- a/lldb/test/lang/objc/blocks/ivars-in-blocks.h +++ b/lldb/test/lang/objc/blocks/ivars-in-blocks.h @@ -5,6 +5,7 @@ @public int blocky_ivar; } ++ (void) classMethod; - (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 index 0d2aa73..1098a91 100644 --- a/lldb/test/lang/objc/blocks/ivars-in-blocks.m +++ b/lldb/test/lang/objc/blocks/ivars-in-blocks.m @@ -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 -- 2.7.4