diff --git a/Protocol2.m b/Protocol2.m index 7897b43..f65a339 100644 --- a/Protocol2.m +++ b/Protocol2.m @@ -20,6 +20,8 @@ - (id)self { return self; } @end @implementation Protocol2 @end +@interface __IncompleteProtocol : Protocol2 @end +@implementation __IncompleteProtocol @end /** * This class exists for the sole reason that the legacy GNU ABI did not diff --git a/dtable.c b/dtable.c index 3b42382..b537e9c 100644 --- a/dtable.c +++ b/dtable.c @@ -91,6 +91,50 @@ static void checkARCAccessors(Class cls) objc_set_class_flag(cls, objc_class_flag_fast_arc); } +PRIVATE void checkARCAccessorsSlow(Class cls) +{ + if (cls->dtable != uninstalled_dtable) + { + return; + } + static SEL retain, release, autorelease, isARC; + if (NULL == retain) + { + retain = sel_registerName("retain"); + release = sel_registerName("release"); + autorelease = sel_registerName("autorelease"); + isARC = sel_registerName("_ARCCompliantRetainRelease"); + } + if (cls->super_class != Nil) + { + checkARCAccessorsSlow(cls->super_class); + } + BOOL superIsFast = objc_test_class_flag(cls, objc_class_flag_fast_arc); + BOOL selfImplementsRetainRelease = NO; + for (struct objc_method_list *l=cls->methods ; l != NULL ; l= l->next) + { + for (int i=0 ; icount ; i++) + { + SEL s = l->methods[i].selector; + if (sel_isEqual(s, retain) || + sel_isEqual(s, release) || + sel_isEqual(s, autorelease)) + { + selfImplementsRetainRelease = YES; + } + else if (sel_isEqual(s, isARC)) + { + objc_set_class_flag(cls, objc_class_flag_fast_arc); + return; + } + } + } + if (superIsFast && ! selfImplementsRetainRelease) + { + objc_set_class_flag(cls, objc_class_flag_fast_arc); + } +} + static void collectMethodsForMethodListToSparseArray( struct objc_method_list *list, SparseArray *sarray, diff --git a/dtable.h b/dtable.h index 95eae20..c374892 100644 --- a/dtable.h +++ b/dtable.h @@ -126,3 +126,9 @@ void add_method_list_to_class(Class cls, * Destroys a dtable. */ void free_dtable(dtable_t dtable); + +/** + * Checks whether the class supports ARC. This can be used before the dtable + * is installed. + */ +void checkARCAccessorsSlow(Class cls); diff --git a/protocol.c b/protocol.c index e88db48..4a58e96 100644 --- a/protocol.c +++ b/protocol.c @@ -45,6 +45,17 @@ struct objc_protocol2 *protocol_for_name(const char *name) static id ObjC2ProtocolClass = 0; +static id incompleteProtocolClass(void) +{ + static id IncompleteProtocolClass = 0; + if (IncompleteProtocolClass == nil) + { + IncompleteProtocolClass = objc_getClass("__IncompleteProtocol"); + } + return IncompleteProtocolClass; +} + + static int isEmptyProtocol(struct objc_protocol2 *aProto) { int isEmpty = @@ -497,7 +508,7 @@ Protocol*__unsafe_unretained* objc_copyProtocolList(unsigned int *outCount) Protocol *objc_allocateProtocol(const char *name) { if (objc_getProtocol(name) != NULL) { return NULL; } - Protocol *p = calloc(1, sizeof(Protocol2)); + Protocol *p = (Protocol*)class_createInstance((Class)incompleteProtocolClass(), 0); p->name = strdup(name); return p; } @@ -506,7 +517,7 @@ void objc_registerProtocol(Protocol *proto) if (NULL == proto) { return; } LOCK_RUNTIME_FOR_SCOPE(); if (objc_getProtocol(proto->name) != NULL) { return; } - if (nil != proto->isa) { return; } + if (incompleteProtocolClass() != proto->isa) { return; } proto->isa = ObjC2ProtocolClass; protocol_table_insert((struct objc_protocol2*)proto); } @@ -517,7 +528,7 @@ void protocol_addMethodDescription(Protocol *aProtocol, BOOL isInstanceMethod) { if ((NULL == aProtocol) || (NULL == name) || (NULL == types)) { return; } - if (nil != aProtocol->isa) { return; } + if (incompleteProtocolClass() != aProtocol->isa) { return; } Protocol2 *proto = (Protocol2*)aProtocol; struct objc_method_description_list **listPtr; if (isInstanceMethod) @@ -561,6 +572,7 @@ void protocol_addMethodDescription(Protocol *aProtocol, void protocol_addProtocol(Protocol *aProtocol, Protocol *addition) { if ((NULL == aProtocol) || (NULL == addition)) { return; } + if (incompleteProtocolClass() != aProtocol->isa) { return; } Protocol2 *proto = (Protocol2*)aProtocol; if (NULL == proto->protocol_list) { @@ -584,7 +596,7 @@ void protocol_addProperty(Protocol *aProtocol, BOOL isInstanceProperty) { if ((NULL == aProtocol) || (NULL == name)) { return; } - if (nil != aProtocol->isa) { return; } + if (incompleteProtocolClass() != aProtocol->isa) { return; } if (!isInstanceProperty) { return; } Protocol2 *proto = (Protocol2*)aProtocol; struct objc_property_list **listPtr; diff --git a/runtime.c b/runtime.c index 14a8d9e..27a8725 100644 --- a/runtime.c +++ b/runtime.c @@ -348,6 +348,7 @@ id class_createInstance(Class cls, size_t extraBytes) if (Nil == cls) { return nil; } id obj = gc->allocate_class(cls, extraBytes); obj->isa = cls; + checkARCAccessorsSlow(cls); call_cxx_construct(obj); return obj; }