diff --git a/Test/CMakeLists.txt b/Test/CMakeLists.txt index 88c9d52..6425136 100644 --- a/Test/CMakeLists.txt +++ b/Test/CMakeLists.txt @@ -25,6 +25,7 @@ set(TESTS RuntimeTest.m WeakBlock_arc.m WeakReferences_arc.m + ivar_arc.m objc_msgSend.m msgInterpose.m NilException.m diff --git a/Test/ivar_arc.m b/Test/ivar_arc.m new file mode 100644 index 0000000..d45775d --- /dev/null +++ b/Test/ivar_arc.m @@ -0,0 +1,52 @@ +#include "Test.h" +#include "../objc/runtime.h" + +@interface Foo : Test +{ + @public + __weak id w; + __unsafe_unretained id u; + __strong id s; +} +@end +@implementation Foo @end +@interface Dealloc : Test +@end +int dealloc = 0; +@implementation Dealloc +- (void)dealloc +{ + dealloc++; +} +@end + +void setIvar(id obj, const char * name, id val) +{ + object_setIvar(obj, class_getInstanceVariable(object_getClass(obj), name), val); +} + +int main(void) +{ + Foo *f = [Foo new]; + Dealloc *d = [Dealloc new]; + __unsafe_unretained Dealloc *dead; + setIvar(f, "w", d); + assert(f->w == d); + assert(dealloc == 0); + d = 0; + assert(dealloc == 1); + assert(f->w == nil); + dealloc = 0; + d = [Dealloc new]; + dead = d; + setIvar(f, "s", d); + assert(dealloc == 0); + assert(f->s == d); + d = nil; + assert(dealloc == 0); + assert(f->s == dead); + setIvar(f, "s", nil); + assert(dealloc == 1); + assert(f->s == nil); + return 0; +} diff --git a/class.h b/class.h index e37ab8b..8da94eb 100644 --- a/class.h +++ b/class.h @@ -18,6 +18,24 @@ struct objc_bitfield int32_t values[0]; }; +static inline BOOL objc_bitfield_test(uintptr_t bitfield, uint64_t field) +{ + if (bitfield & 1) + { + uint64_t bit = 1<<(field+1); + return (bitfield & bit) == bit; + } + struct objc_bitfield *bf = (struct objc_bitfield*)bitfield; + uint64_t byte = field / 32; + if (byte >= bf->length) + { + return NO; + } + uint64_t bit = 1<<(field%32); + return (bf->values[byte] & bit) == bit; +} + + struct objc_class { /** @@ -141,13 +159,13 @@ struct objc_class * bits are set, from low to high, for each ivar in the object that is a * strong pointer. */ - intptr_t strong_pointers; + uintptr_t strong_pointers; /** * The location of all zeroing weak pointer ivars declared by this class. * The format of this field is the same as the format of the * strong_pointers field. */ - intptr_t weak_pointers; + uintptr_t weak_pointers; }; /** diff --git a/ivar.c b/ivar.c index c587bf2..8504dae 100644 --- a/ivar.c +++ b/ivar.c @@ -2,6 +2,7 @@ #include #include #include "objc/runtime.h" +#include "objc/objc-arc.h" #include "class.h" #include "ivar.h" #include "visibility.h" @@ -143,18 +144,69 @@ PRIVATE void objc_compute_ivar_offsets(Class class) } } +typedef enum { + ownership_invalid, + ownership_strong, + ownership_weak, + ownership_unsafe +} ownership; + +ownership ownershipForIvar(Class cls, Ivar ivar) +{ + struct objc_ivar_list *list = cls->ivars; + if ((ivar < list->ivar_list) || (ivar >= &list->ivar_list[list->count])) + { + // Try the superclass + if (cls->super_class) + { + return ownershipForIvar(cls->super_class, ivar); + } + return ownership_invalid; + } + if (!objc_test_class_flag(cls, objc_class_flag_new_abi)) + { + return ownership_unsafe; + } + if (cls->abi_version < 1) + { + return ownership_unsafe; + } + if (objc_bitfield_test(cls->strong_pointers, (ivar - list->ivar_list))) + { + return ownership_strong; + } + if (objc_bitfield_test(cls->weak_pointers, (ivar - list->ivar_list))) + { + return ownership_weak; + } + return ownership_unsafe; +} + //////////////////////////////////////////////////////////////////////////////// // Public API functions //////////////////////////////////////////////////////////////////////////////// void object_setIvar(id object, Ivar ivar, id value) { - char *addr = (char*)object; - addr += ivar_getOffset(ivar); - // FIXME: ARC ownership! We don't have enough information to do this - // correctly with the current ABI. We need to fix it with the next ABI - // bump. - *(id*)addr = value; + ownershipForIvar(object_getClass(object), ivar); + id *addr = (id*)((char*)object + ivar_getOffset(ivar)); + switch (ownershipForIvar(object_getClass(object), ivar)) + { + case ownership_strong: + objc_storeStrong(addr, value); + break; + case ownership_weak: + objc_storeWeak(addr, value); + break; + case ownership_unsafe: + *addr = value; + break; + case ownership_invalid: +#ifndef NDEBUG + fprintf(stderr, "Ivar does not belong to this class!\n"); +#endif + break; + } } Ivar object_setInstanceVariable(id obj, const char *name, void *value) @@ -174,7 +226,23 @@ Ivar object_setInstanceVariable(id obj, const char *name, void *value) id object_getIvar(id object, Ivar ivar) { - return *(id*)(((char*)object) + ivar_getOffset(ivar)); + ownershipForIvar(object_getClass(object), ivar); + id *addr = (id*)((char*)object + ivar_getOffset(ivar)); + switch (ownershipForIvar(object_getClass(object), ivar)) + { + case ownership_strong: + return objc_retainAutoreleaseReturnValue(*addr); + case ownership_weak: + return objc_loadWeak(addr); + break; + case ownership_unsafe: + return *addr; + case ownership_invalid: +#ifndef NDEBUG + fprintf(stderr, "Ivar does not belong to this class!\n"); +#endif + return nil; + } } Ivar object_getInstanceVariable(id obj, const char *name, void **outValue)