forked from OSchip/llvm-project
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. <rdar://problem/12905561> llvm-svn: 172880
This commit is contained in:
parent
97e77abf83
commit
a2868d4c2e
|
@ -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());
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
@public
|
||||
int blocky_ivar;
|
||||
}
|
||||
+ (void) classMethod;
|
||||
- (IAmBlocky *) init;
|
||||
- (int) callABlock: (int) block_value;
|
||||
@end
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue