forked from OSchip/llvm-project
Add code that reads the APPLE_property debug info, and makes up properties from them.
llvm-svn: 144440
This commit is contained in:
parent
3a3d8e82bc
commit
e3ae82af89
|
@ -80,6 +80,7 @@ namespace clang
|
|||
class ObjCEncodeExpr;
|
||||
class ObjCImplicitSetterGetterRefExpr;
|
||||
class ObjCInterfaceDecl;
|
||||
class ObjCIvarDecl;
|
||||
class ObjCIvarRefExpr;
|
||||
class ObjCMessageExpr;
|
||||
class ObjCMethodDecl;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "clang/AST/TemplateBase.h"
|
||||
|
||||
|
||||
// Project includes
|
||||
#include "lldb/lldb-enumerations.h"
|
||||
#include "lldb/Core/ClangForward.h"
|
||||
|
@ -247,7 +248,7 @@ public:
|
|||
int kind,
|
||||
lldb::LanguageType language);
|
||||
|
||||
static bool
|
||||
static clang::FieldDecl *
|
||||
AddFieldToRecordType (clang::ASTContext *ast,
|
||||
lldb::clang_type_t record_qual_type,
|
||||
const char *name,
|
||||
|
@ -255,7 +256,7 @@ public:
|
|||
lldb::AccessType access,
|
||||
uint32_t bitfield_bit_size);
|
||||
|
||||
bool
|
||||
clang::FieldDecl *
|
||||
AddFieldToRecordType (lldb::clang_type_t record_qual_type,
|
||||
const char *name,
|
||||
lldb::clang_type_t field_type,
|
||||
|
@ -383,7 +384,7 @@ public:
|
|||
bool isForwardDecl,
|
||||
bool isInternal);
|
||||
|
||||
static bool
|
||||
static clang::FieldDecl *
|
||||
AddObjCClassIVar (clang::ASTContext *ast,
|
||||
lldb::clang_type_t class_opaque_type,
|
||||
const char *name,
|
||||
|
@ -392,7 +393,7 @@ public:
|
|||
uint32_t bitfield_bit_size,
|
||||
bool isSynthesized);
|
||||
|
||||
bool
|
||||
clang::FieldDecl *
|
||||
AddObjCClassIVar (lldb::clang_type_t class_opaque_type,
|
||||
const char *name,
|
||||
lldb::clang_type_t ivar_opaque_type,
|
||||
|
@ -409,6 +410,41 @@ public:
|
|||
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
|
||||
SetObjCSuperClass (lldb::clang_type_t class_clang_type,
|
||||
lldb::clang_type_t superclass_clang_type);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/DeclGroup.h"
|
||||
#include "clang/AST/DeclObjC.h"
|
||||
#include "clang/Basic/Builtins.h"
|
||||
#include "clang/Basic/IdentifierTable.h"
|
||||
#include "clang/Basic/LangOptions.h"
|
||||
|
@ -1324,6 +1325,12 @@ SymbolFileDWARF::ParseChildMembers
|
|||
Declaration decl;
|
||||
//DWARFExpression location;
|
||||
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;
|
||||
lldb::user_id_t encoding_uid = LLDB_INVALID_UID;
|
||||
AccessType accessibility = eAccessNone;
|
||||
|
@ -1369,6 +1376,12 @@ SymbolFileDWARF::ParseChildMembers
|
|||
case DW_AT_description:
|
||||
case DW_AT_mutable:
|
||||
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:
|
||||
case DW_AT_sibling:
|
||||
break;
|
||||
|
@ -1415,13 +1428,14 @@ SymbolFileDWARF::ParseChildMembers
|
|||
if (is_artificial == false)
|
||||
{
|
||||
Type *member_type = ResolveTypeUID(encoding_uid);
|
||||
clang::FieldDecl *field_decl = NULL;
|
||||
if (member_type)
|
||||
{
|
||||
if (accessibility == eAccessNone)
|
||||
accessibility = default_accessibility;
|
||||
member_accessibilities.push_back(accessibility);
|
||||
|
||||
GetClangASTContext().AddFieldToRecordType (class_clang_type,
|
||||
field_decl = GetClangASTContext().AddFieldToRecordType (class_clang_type,
|
||||
name,
|
||||
member_type->GetClangLayoutType(),
|
||||
accessibility,
|
||||
|
@ -1439,6 +1453,22 @@ SymbolFileDWARF::ParseChildMembers
|
|||
MakeUserID(die->GetOffset()),
|
||||
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;
|
||||
|
|
|
@ -1782,7 +1782,7 @@ ClangASTContext::AddMethodToCXXRecordType
|
|||
return cxx_method_decl;
|
||||
}
|
||||
|
||||
bool
|
||||
clang::FieldDecl *
|
||||
ClangASTContext::AddFieldToRecordType
|
||||
(
|
||||
ASTContext *ast,
|
||||
|
@ -1794,8 +1794,9 @@ ClangASTContext::AddFieldToRecordType
|
|||
)
|
||||
{
|
||||
if (record_clang_type == NULL || field_type == NULL)
|
||||
return false;
|
||||
return NULL;
|
||||
|
||||
FieldDecl *field = NULL;
|
||||
IdentifierTable *identifier_table = &ast->Idents;
|
||||
|
||||
assert (ast != NULL);
|
||||
|
@ -1818,7 +1819,7 @@ ClangASTContext::AddFieldToRecordType
|
|||
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());
|
||||
}
|
||||
FieldDecl *field = FieldDecl::Create (*ast,
|
||||
field = FieldDecl::Create (*ast,
|
||||
record_decl,
|
||||
SourceLocation(),
|
||||
SourceLocation(),
|
||||
|
@ -1846,7 +1847,7 @@ ClangASTContext::AddFieldToRecordType
|
|||
if (objc_class_type)
|
||||
{
|
||||
bool is_synthesized = false;
|
||||
ClangASTContext::AddObjCClassIVar (ast,
|
||||
field = ClangASTContext::AddObjCClassIVar (ast,
|
||||
record_clang_type,
|
||||
name,
|
||||
field_type,
|
||||
|
@ -1856,7 +1857,7 @@ ClangASTContext::AddFieldToRecordType
|
|||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return field;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -2047,7 +2048,7 @@ ClangASTContext::SetObjCSuperClass (clang_type_t class_opaque_type, clang_type_t
|
|||
}
|
||||
|
||||
|
||||
bool
|
||||
FieldDecl *
|
||||
ClangASTContext::AddObjCClassIVar
|
||||
(
|
||||
ASTContext *ast,
|
||||
|
@ -2060,8 +2061,10 @@ ClangASTContext::AddObjCClassIVar
|
|||
)
|
||||
{
|
||||
if (class_opaque_type == NULL || ivar_opaque_type == NULL)
|
||||
return false;
|
||||
return NULL;
|
||||
|
||||
ObjCIvarDecl *field = NULL;
|
||||
|
||||
IdentifierTable *identifier_table = &ast->Idents;
|
||||
|
||||
assert (ast != NULL);
|
||||
|
@ -2087,16 +2090,16 @@ ClangASTContext::AddObjCClassIVar
|
|||
bit_width = new (*ast)IntegerLiteral (*ast, bitfield_bit_size_apint, ast->IntTy, SourceLocation());
|
||||
}
|
||||
|
||||
ObjCIvarDecl *field = ObjCIvarDecl::Create (*ast,
|
||||
class_interface_decl,
|
||||
SourceLocation(),
|
||||
SourceLocation(),
|
||||
&identifier_table->get(name), // Identifier
|
||||
QualType::getFromOpaquePtr(ivar_opaque_type), // Field type
|
||||
NULL, // TypeSourceInfo *
|
||||
ConvertAccessTypeToObjCIvarAccessControl (access),
|
||||
bit_width,
|
||||
is_synthesized);
|
||||
field = ObjCIvarDecl::Create (*ast,
|
||||
class_interface_decl,
|
||||
SourceLocation(),
|
||||
SourceLocation(),
|
||||
&identifier_table->get(name), // Identifier
|
||||
QualType::getFromOpaquePtr(ivar_opaque_type), // Field type
|
||||
NULL, // TypeSourceInfo *
|
||||
ConvertAccessTypeToObjCIvarAccessControl (access),
|
||||
bit_width,
|
||||
is_synthesized);
|
||||
|
||||
if (field)
|
||||
{
|
||||
|
@ -2106,15 +2109,106 @@ ClangASTContext::AddObjCClassIVar
|
|||
VerifyDecl(field);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
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 false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ClangASTContext::ObjCTypeHasIVars (clang_type_t class_opaque_type, bool check_superclass)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
LEVEL = ../../../make
|
||||
|
||||
OBJC_SOURCES := main.m
|
||||
LDFLAGS = $(CFLAGS) -lobjc -framework Foundation
|
||||
|
||||
include $(LEVEL)/Makefile.rules
|
|
@ -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()
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue