Add code that reads the APPLE_property debug info, and makes up properties from them.

llvm-svn: 144440
This commit is contained in:
Jim Ingham 2011-11-12 01:36:43 +00:00
parent 3a3d8e82bc
commit e3ae82af89
7 changed files with 411 additions and 24 deletions

View File

@ -80,6 +80,7 @@ namespace clang
class ObjCEncodeExpr; class ObjCEncodeExpr;
class ObjCImplicitSetterGetterRefExpr; class ObjCImplicitSetterGetterRefExpr;
class ObjCInterfaceDecl; class ObjCInterfaceDecl;
class ObjCIvarDecl;
class ObjCIvarRefExpr; class ObjCIvarRefExpr;
class ObjCMessageExpr; class ObjCMessageExpr;
class ObjCMethodDecl; class ObjCMethodDecl;

View File

@ -22,6 +22,7 @@
#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/SmallVector.h"
#include "clang/AST/TemplateBase.h" #include "clang/AST/TemplateBase.h"
// Project includes // Project includes
#include "lldb/lldb-enumerations.h" #include "lldb/lldb-enumerations.h"
#include "lldb/Core/ClangForward.h" #include "lldb/Core/ClangForward.h"
@ -247,7 +248,7 @@ public:
int kind, int kind,
lldb::LanguageType language); lldb::LanguageType language);
static bool static clang::FieldDecl *
AddFieldToRecordType (clang::ASTContext *ast, AddFieldToRecordType (clang::ASTContext *ast,
lldb::clang_type_t record_qual_type, lldb::clang_type_t record_qual_type,
const char *name, const char *name,
@ -255,7 +256,7 @@ public:
lldb::AccessType access, lldb::AccessType access,
uint32_t bitfield_bit_size); uint32_t bitfield_bit_size);
bool clang::FieldDecl *
AddFieldToRecordType (lldb::clang_type_t record_qual_type, AddFieldToRecordType (lldb::clang_type_t record_qual_type,
const char *name, const char *name,
lldb::clang_type_t field_type, lldb::clang_type_t field_type,
@ -383,7 +384,7 @@ public:
bool isForwardDecl, bool isForwardDecl,
bool isInternal); bool isInternal);
static bool static clang::FieldDecl *
AddObjCClassIVar (clang::ASTContext *ast, AddObjCClassIVar (clang::ASTContext *ast,
lldb::clang_type_t class_opaque_type, lldb::clang_type_t class_opaque_type,
const char *name, const char *name,
@ -392,7 +393,7 @@ public:
uint32_t bitfield_bit_size, uint32_t bitfield_bit_size,
bool isSynthesized); bool isSynthesized);
bool clang::FieldDecl *
AddObjCClassIVar (lldb::clang_type_t class_opaque_type, AddObjCClassIVar (lldb::clang_type_t class_opaque_type,
const char *name, const char *name,
lldb::clang_type_t ivar_opaque_type, lldb::clang_type_t ivar_opaque_type,
@ -409,6 +410,41 @@ public:
isSynthesized); isSynthesized);
} }
static bool
AddObjCClassProperty
(
clang::ASTContext *ast,
lldb::clang_type_t class_opaque_type,
const char *property_name,
lldb::clang_type_t property_opaque_type, // The property type is only required if you don't have an ivar decl
clang::ObjCIvarDecl *ivar_decl,
const char *property_setter_name,
const char *property_getter_name,
uint32_t property_attributes
);
bool
AddObjCClassProperty
(
lldb::clang_type_t class_opaque_type,
const char *property_name,
lldb::clang_type_t property_opaque_type,
clang::ObjCIvarDecl *ivar_decl,
const char *property_setter_name,
const char *property_getter_name,
uint32_t property_attributes
)
{
return ClangASTContext::AddObjCClassProperty (getASTContext(),
class_opaque_type,
property_name,
property_opaque_type,
ivar_decl,
property_setter_name,
property_getter_name,
property_attributes);
}
bool bool
SetObjCSuperClass (lldb::clang_type_t class_clang_type, SetObjCSuperClass (lldb::clang_type_t class_clang_type,
lldb::clang_type_t superclass_clang_type); lldb::clang_type_t superclass_clang_type);

View File

@ -14,6 +14,7 @@
#include "clang/AST/ASTContext.h" #include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h" #include "clang/AST/Decl.h"
#include "clang/AST/DeclGroup.h" #include "clang/AST/DeclGroup.h"
#include "clang/AST/DeclObjC.h"
#include "clang/Basic/Builtins.h" #include "clang/Basic/Builtins.h"
#include "clang/Basic/IdentifierTable.h" #include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LangOptions.h" #include "clang/Basic/LangOptions.h"
@ -1324,6 +1325,12 @@ SymbolFileDWARF::ParseChildMembers
Declaration decl; Declaration decl;
//DWARFExpression location; //DWARFExpression location;
const char *name = NULL; const char *name = NULL;
const char *prop_name = NULL;
const char *prop_getter_name = NULL;
const char *prop_setter_name = NULL;
uint32_t prop_attributes = 0;
bool is_artificial = false; bool is_artificial = false;
lldb::user_id_t encoding_uid = LLDB_INVALID_UID; lldb::user_id_t encoding_uid = LLDB_INVALID_UID;
AccessType accessibility = eAccessNone; AccessType accessibility = eAccessNone;
@ -1369,6 +1376,12 @@ SymbolFileDWARF::ParseChildMembers
case DW_AT_description: case DW_AT_description:
case DW_AT_mutable: case DW_AT_mutable:
case DW_AT_visibility: case DW_AT_visibility:
case DW_AT_APPLE_property_name: prop_name = form_value.AsCString(&get_debug_str_data()); break;
case DW_AT_APPLE_property_getter: prop_getter_name = form_value.AsCString(&get_debug_str_data()); break;
case DW_AT_APPLE_property_setter: prop_setter_name = form_value.AsCString(&get_debug_str_data()); break;
case DW_AT_APPLE_property_attribute: prop_attributes = form_value.Unsigned(); break;
default: default:
case DW_AT_sibling: case DW_AT_sibling:
break; break;
@ -1415,13 +1428,14 @@ SymbolFileDWARF::ParseChildMembers
if (is_artificial == false) if (is_artificial == false)
{ {
Type *member_type = ResolveTypeUID(encoding_uid); Type *member_type = ResolveTypeUID(encoding_uid);
clang::FieldDecl *field_decl = NULL;
if (member_type) if (member_type)
{ {
if (accessibility == eAccessNone) if (accessibility == eAccessNone)
accessibility = default_accessibility; accessibility = default_accessibility;
member_accessibilities.push_back(accessibility); member_accessibilities.push_back(accessibility);
GetClangASTContext().AddFieldToRecordType (class_clang_type, field_decl = GetClangASTContext().AddFieldToRecordType (class_clang_type,
name, name,
member_type->GetClangLayoutType(), member_type->GetClangLayoutType(),
accessibility, accessibility,
@ -1439,6 +1453,22 @@ SymbolFileDWARF::ParseChildMembers
MakeUserID(die->GetOffset()), MakeUserID(die->GetOffset()),
encoding_uid); encoding_uid);
} }
if (prop_name != NULL)
{
clang::ObjCIvarDecl *ivar_decl = clang::dyn_cast<clang::ObjCIvarDecl>(field_decl);
assert (ivar_decl != NULL);
GetClangASTContext().AddObjCClassProperty (class_clang_type,
prop_name,
0,
ivar_decl,
prop_setter_name,
prop_getter_name,
prop_attributes);
}
} }
} }
++member_idx; ++member_idx;

View File

@ -1782,7 +1782,7 @@ ClangASTContext::AddMethodToCXXRecordType
return cxx_method_decl; return cxx_method_decl;
} }
bool clang::FieldDecl *
ClangASTContext::AddFieldToRecordType ClangASTContext::AddFieldToRecordType
( (
ASTContext *ast, ASTContext *ast,
@ -1794,8 +1794,9 @@ ClangASTContext::AddFieldToRecordType
) )
{ {
if (record_clang_type == NULL || field_type == NULL) if (record_clang_type == NULL || field_type == NULL)
return false; return NULL;
FieldDecl *field = NULL;
IdentifierTable *identifier_table = &ast->Idents; IdentifierTable *identifier_table = &ast->Idents;
assert (ast != NULL); assert (ast != NULL);
@ -1818,7 +1819,7 @@ ClangASTContext::AddFieldToRecordType
APInt bitfield_bit_size_apint(ast->getTypeSize(ast->IntTy), bitfield_bit_size); APInt bitfield_bit_size_apint(ast->getTypeSize(ast->IntTy), bitfield_bit_size);
bit_width = new (*ast)IntegerLiteral (*ast, bitfield_bit_size_apint, ast->IntTy, SourceLocation()); bit_width = new (*ast)IntegerLiteral (*ast, bitfield_bit_size_apint, ast->IntTy, SourceLocation());
} }
FieldDecl *field = FieldDecl::Create (*ast, field = FieldDecl::Create (*ast,
record_decl, record_decl,
SourceLocation(), SourceLocation(),
SourceLocation(), SourceLocation(),
@ -1846,7 +1847,7 @@ ClangASTContext::AddFieldToRecordType
if (objc_class_type) if (objc_class_type)
{ {
bool is_synthesized = false; bool is_synthesized = false;
ClangASTContext::AddObjCClassIVar (ast, field = ClangASTContext::AddObjCClassIVar (ast,
record_clang_type, record_clang_type,
name, name,
field_type, field_type,
@ -1856,7 +1857,7 @@ ClangASTContext::AddFieldToRecordType
} }
} }
} }
return false; return field;
} }
bool bool
@ -2047,7 +2048,7 @@ ClangASTContext::SetObjCSuperClass (clang_type_t class_opaque_type, clang_type_t
} }
bool FieldDecl *
ClangASTContext::AddObjCClassIVar ClangASTContext::AddObjCClassIVar
( (
ASTContext *ast, ASTContext *ast,
@ -2060,7 +2061,9 @@ ClangASTContext::AddObjCClassIVar
) )
{ {
if (class_opaque_type == NULL || ivar_opaque_type == NULL) if (class_opaque_type == NULL || ivar_opaque_type == NULL)
return false; return NULL;
ObjCIvarDecl *field = NULL;
IdentifierTable *identifier_table = &ast->Idents; IdentifierTable *identifier_table = &ast->Idents;
@ -2087,7 +2090,7 @@ ClangASTContext::AddObjCClassIVar
bit_width = new (*ast)IntegerLiteral (*ast, bitfield_bit_size_apint, ast->IntTy, SourceLocation()); bit_width = new (*ast)IntegerLiteral (*ast, bitfield_bit_size_apint, ast->IntTy, SourceLocation());
} }
ObjCIvarDecl *field = ObjCIvarDecl::Create (*ast, field = ObjCIvarDecl::Create (*ast,
class_interface_decl, class_interface_decl,
SourceLocation(), SourceLocation(),
SourceLocation(), SourceLocation(),
@ -2106,6 +2109,98 @@ ClangASTContext::AddObjCClassIVar
VerifyDecl(field); VerifyDecl(field);
#endif #endif
return field;
}
}
}
}
return NULL;
}
bool
ClangASTContext::AddObjCClassProperty
(
ASTContext *ast,
clang_type_t class_opaque_type,
const char *property_name,
clang_type_t property_opaque_type,
ObjCIvarDecl *ivar_decl,
const char *property_setter_name,
const char *property_getter_name,
uint32_t property_attributes
)
{
if (class_opaque_type == NULL)
return false;
IdentifierTable *identifier_table = &ast->Idents;
assert (ast != NULL);
assert (identifier_table != NULL);
QualType class_qual_type(QualType::getFromOpaquePtr(class_opaque_type));
const clang::Type *class_type = class_qual_type.getTypePtr();
if (class_type)
{
const ObjCObjectType *objc_class_type = dyn_cast<ObjCObjectType>(class_type);
if (objc_class_type)
{
ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface();
// FIXME: For now, we don't know how to add properties if we don't have their associated ivar.
if (class_interface_decl && ivar_decl)
{
clang::TypeSourceInfo *prop_type_source;
if (ivar_decl)
prop_type_source = ast->CreateTypeSourceInfo (ivar_decl->getType());
else
prop_type_source = ast->CreateTypeSourceInfo (QualType::getFromOpaquePtr(property_opaque_type));
ObjCPropertyDecl *property_decl = ObjCPropertyDecl::Create(*ast,
class_interface_decl,
SourceLocation(), // Source Location
&identifier_table->get(property_name),
SourceLocation(), //Source Location for AT
prop_type_source
);
if (property_decl)
{
class_interface_decl->addDecl (property_decl);
if (property_setter_name != NULL)
{
std::string property_setter_no_colon(property_setter_name, strlen(property_setter_name) - 1);
clang::IdentifierInfo *setter_ident = &identifier_table->get(property_setter_no_colon.c_str());
Selector setter_sel = ast->Selectors.getSelector(1, &setter_ident);
property_decl->setSetterName(setter_sel);
property_decl->setPropertyAttributes (clang::ObjCPropertyDecl::OBJC_PR_setter);
}
if (property_getter_name != NULL)
{
clang::IdentifierInfo *getter_ident = &identifier_table->get(property_getter_name);
Selector getter_sel = ast->Selectors.getSelector(0, &getter_ident);
property_decl->setGetterName(getter_sel);
property_decl->setPropertyAttributes (clang::ObjCPropertyDecl::OBJC_PR_getter);
}
if (ivar_decl)
property_decl->setPropertyIvarDecl (ivar_decl);
if (property_attributes & DW_APPLE_PROPERTY_readonly)
property_decl->setPropertyAttributes (clang::ObjCPropertyDecl::OBJC_PR_readonly);
if (property_attributes & DW_APPLE_PROPERTY_readwrite)
property_decl->setPropertyAttributes (clang::ObjCPropertyDecl::OBJC_PR_readwrite);
if (property_attributes & DW_APPLE_PROPERTY_assign)
property_decl->setPropertyAttributes (clang::ObjCPropertyDecl::OBJC_PR_assign);
if (property_attributes & DW_APPLE_PROPERTY_retain)
property_decl->setPropertyAttributes (clang::ObjCPropertyDecl::OBJC_PR_retain);
if (property_attributes & DW_APPLE_PROPERTY_copy)
property_decl->setPropertyAttributes (clang::ObjCPropertyDecl::OBJC_PR_copy);
if (property_attributes & DW_APPLE_PROPERTY_nonatomic)
property_decl->setPropertyAttributes (clang::ObjCPropertyDecl::OBJC_PR_nonatomic);
return true; return true;
} }
} }
@ -2114,7 +2209,6 @@ ClangASTContext::AddObjCClassIVar
return false; return false;
} }
bool bool
ClangASTContext::ObjCTypeHasIVars (clang_type_t class_opaque_type, bool check_superclass) ClangASTContext::ObjCTypeHasIVars (clang_type_t class_opaque_type, bool check_superclass)
{ {

View File

@ -0,0 +1,6 @@
LEVEL = ../../../make
OBJC_SOURCES := main.m
LDFLAGS = $(CFLAGS) -lobjc -framework Foundation
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,129 @@
"""
Use lldb Python API to verify that expression evaluation for property references uses the correct getters and setters
"""
import os, time
import re
import unittest2
import lldb, lldbutil
from lldbtest import *
class ObjCDynamicValueTestCase(TestBase):
mydir = os.path.join("lang", "objc", "objc-property")
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
@python_api_test
def test_get_dynamic_objc_vals_with_dsym(self):
"""Test that expr uses the correct property getters and setters"""
self.buildDsym()
self.do_test_properties()
@python_api_test
def test_get_objc_dynamic_vals_with_dwarf(self):
"""Test that expr uses the correct property getters and setters"""
self.buildDwarf()
self.do_test_properties()
def setUp(self):
# Call super's setUp().
TestBase.setUp(self)
# Find the line number to break for main.c.
self.source_name = 'main.m'
def run_to_main (self):
"""Test that expr uses the correct property getters and setters"""
exe = os.path.join(os.getcwd(), "a.out")
# Create a target from the debugger.
target = self.dbg.CreateTarget (exe)
self.assertTrue(target, VALID_TARGET)
# Set up our breakpoints:
main_bkpt = target.BreakpointCreateBySourceRegex ("Set a breakpoint here.", lldb.SBFileSpec (self.source_name))
self.assertTrue(main_bkpt and
main_bkpt.GetNumLocations() == 1,
VALID_BREAKPOINT)
# Now launch the process, and do not stop at the entry point.
process = target.LaunchSimple (None, None, os.getcwd())
self.assertTrue(process.GetState() == lldb.eStateStopped,
PROCESS_STOPPED)
threads = lldbutil.get_threads_stopped_at_breakpoint (process, main_bkpt)
self.assertTrue (len(threads) == 1)
thread = threads[0]
return thread
def do_test_properties (self):
thread = self.run_to_main()
frame = thread.GetFrameAtIndex(0)
mine = frame.FindVariable ("mine")
self.assertTrue (mine.IsValid())
access_count = mine.GetChildMemberWithName ("_access_count")
self.assertTrue (access_count.IsValid())
start_access_count = access_count.GetValueAsUnsigned (123456)
self.assertTrue (start_access_count != 123456)
#
# The first set of tests test calling the getter & setter of
# a property that actually only has a getter & setter and no
# @property.
#
nonexistant_value = frame.EvaluateExpression("mine.nonexistantInt", False)
nonexistant_error = nonexistant_value.GetError()
self.assertTrue (nonexistant_error.Success())
nonexistant_int = nonexistant_value.GetValueAsUnsigned (123456)
self.assertTrue (nonexistant_int == 6)
# Calling the getter function would up the access count, so make sure that happened.
new_access_count = access_count.GetValueAsUnsigned (123456)
self.assertTrue (new_access_count - start_access_count == 1)
start_access_count = new_access_count
#
# Now call the setter, then make sure that
nonexistant_change = frame.EvaluateExpression("mine.nonexistantInt = 10", False)
nonexistant_error = nonexistant_change.GetError()
self.assertTrue (nonexistant_error.Success())
# Calling the setter function would up the access count, so make sure that happened.
new_access_count = access_count.GetValueAsUnsigned (123456)
self.assertTrue (new_access_count - start_access_count == 1)
start_access_count = new_access_count
#
# Now we call the getter of a property that is backed by an ivar,
# make sure it works and that we actually update the backing ivar.
#
backed_value = frame.EvaluateExpression("mine.backedInt", False)
backed_error = backed_value.GetError()
self.assertTrue (backed_error.Success())
backing_value = mine.GetChildMemberWithName ("_backedInt")
self.assertTrue (backing_value.IsValid())
self.assertTrue (backed_value.GetValueAsUnsigned (12345) == backing_value.GetValueAsUnsigned(23456))
#
# This doesn't work correctly yet, because the clang Sema::HandleExprPropertyRefExpr
# doesn't complete the class before looking up the property.
#
#unbacked_value = frame.EvaluateExpression("mine.unbackedInt", False)
#unbacked_error = unbacked_value.GetError()
#self.assertTrue (unbacked_error.Success())
if __name__ == '__main__':
import atexit
lldb.SBDebugger.Initialize()
atexit.register(lambda: lldb.SBDebugger.Terminate())
unittest2.main()

View File

@ -0,0 +1,91 @@
#import <Foundation/Foundation.h>
@interface BaseClass : NSObject
{
int _backedInt;
int _access_count;
}
- (int) nonexistantInt;
- (void) setNonexistantInt: (int) in_int;
- (int) myGetUnbackedInt;
- (void) mySetUnbackedInt: (int) in_int;
- (int) getAccessCount;
+(BaseClass *) baseClassWithBackedInt: (int) inInt andUnbackedInt: (int) inOtherInt;
@property(getter=myGetUnbackedInt,setter=mySetUnbackedInt:) int unbackedInt;
@property int backedInt;
@end
@implementation BaseClass
@synthesize unbackedInt;
@synthesize backedInt = _backedInt;
+ (BaseClass *) baseClassWithBackedInt: (int) inInt andUnbackedInt: (int) inOtherInt
{
BaseClass *new = [[BaseClass alloc] init];
new->_backedInt = inInt;
new->unbackedInt = inOtherInt;
return new;
}
- (int) myGetUnbackedInt
{
// NSLog (@"Getting BaseClass::unbackedInt - %d.\n", unbackedInt);
_access_count++;
return unbackedInt;
}
- (void) mySetUnbackedInt: (int) in_int
{
// NSLog (@"Setting BaseClass::unbackedInt from %d to %d.", unbackedInt, in_int);
_access_count++;
unbackedInt = in_int;
}
- (int) nonexistantInt
{
// NSLog (@"Getting BaseClass::nonexistantInt - %d.\n", 5);
_access_count++;
return 6;
}
- (void) setNonexistantInt: (int) in_int
{
// NSLog (@"Setting BaseClass::nonexistantInt from 7 to %d.", in_int);
_access_count++;
}
- (int) getAccessCount
{
return _access_count;
}
@end
int
main ()
{
BaseClass *mine = [BaseClass baseClassWithBackedInt: 10 andUnbackedInt: 20];
// Set a breakpoint here.
int nonexistant = mine.nonexistantInt;
int backedInt = mine.backedInt;
int unbackedInt = mine.unbackedInt;
NSLog (@"Results for %p: nonexistant: %d backed: %d unbacked: %d accessCount: %d.",
mine,
nonexistant,
backedInt,
unbackedInt,
[mine getAccessCount]);
return 0;
}