Ensure that objects that support ARC will use ARC, even if they are

created without sending a message to the class.

Also ensure that protocols are always valid objects so that ARC doesn't
become confused.
main
David Chisnall 10 years ago
parent 5c21e73f26
commit d44bf5655b

@ -20,6 +20,8 @@
- (id)self { return self; } - (id)self { return self; }
@end @end
@implementation Protocol2 @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 * This class exists for the sole reason that the legacy GNU ABI did not

@ -91,6 +91,50 @@ static void checkARCAccessors(Class cls)
objc_set_class_flag(cls, objc_class_flag_fast_arc); 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 ; i<l->count ; 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( static void collectMethodsForMethodListToSparseArray(
struct objc_method_list *list, struct objc_method_list *list,
SparseArray *sarray, SparseArray *sarray,

@ -126,3 +126,9 @@ void add_method_list_to_class(Class cls,
* Destroys a dtable. * Destroys a dtable.
*/ */
void free_dtable(dtable_t 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);

@ -45,6 +45,17 @@ struct objc_protocol2 *protocol_for_name(const char *name)
static id ObjC2ProtocolClass = 0; 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) static int isEmptyProtocol(struct objc_protocol2 *aProto)
{ {
int isEmpty = int isEmpty =
@ -497,7 +508,7 @@ Protocol*__unsafe_unretained* objc_copyProtocolList(unsigned int *outCount)
Protocol *objc_allocateProtocol(const char *name) Protocol *objc_allocateProtocol(const char *name)
{ {
if (objc_getProtocol(name) != NULL) { return NULL; } 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); p->name = strdup(name);
return p; return p;
} }
@ -506,7 +517,7 @@ void objc_registerProtocol(Protocol *proto)
if (NULL == proto) { return; } if (NULL == proto) { return; }
LOCK_RUNTIME_FOR_SCOPE(); LOCK_RUNTIME_FOR_SCOPE();
if (objc_getProtocol(proto->name) != NULL) { return; } if (objc_getProtocol(proto->name) != NULL) { return; }
if (nil != proto->isa) { return; } if (incompleteProtocolClass() != proto->isa) { return; }
proto->isa = ObjC2ProtocolClass; proto->isa = ObjC2ProtocolClass;
protocol_table_insert((struct objc_protocol2*)proto); protocol_table_insert((struct objc_protocol2*)proto);
} }
@ -517,7 +528,7 @@ void protocol_addMethodDescription(Protocol *aProtocol,
BOOL isInstanceMethod) BOOL isInstanceMethod)
{ {
if ((NULL == aProtocol) || (NULL == name) || (NULL == types)) { return; } if ((NULL == aProtocol) || (NULL == name) || (NULL == types)) { return; }
if (nil != aProtocol->isa) { return; } if (incompleteProtocolClass() != aProtocol->isa) { return; }
Protocol2 *proto = (Protocol2*)aProtocol; Protocol2 *proto = (Protocol2*)aProtocol;
struct objc_method_description_list **listPtr; struct objc_method_description_list **listPtr;
if (isInstanceMethod) if (isInstanceMethod)
@ -561,6 +572,7 @@ void protocol_addMethodDescription(Protocol *aProtocol,
void protocol_addProtocol(Protocol *aProtocol, Protocol *addition) void protocol_addProtocol(Protocol *aProtocol, Protocol *addition)
{ {
if ((NULL == aProtocol) || (NULL == addition)) { return; } if ((NULL == aProtocol) || (NULL == addition)) { return; }
if (incompleteProtocolClass() != aProtocol->isa) { return; }
Protocol2 *proto = (Protocol2*)aProtocol; Protocol2 *proto = (Protocol2*)aProtocol;
if (NULL == proto->protocol_list) if (NULL == proto->protocol_list)
{ {
@ -584,7 +596,7 @@ void protocol_addProperty(Protocol *aProtocol,
BOOL isInstanceProperty) BOOL isInstanceProperty)
{ {
if ((NULL == aProtocol) || (NULL == name)) { return; } if ((NULL == aProtocol) || (NULL == name)) { return; }
if (nil != aProtocol->isa) { return; } if (incompleteProtocolClass() != aProtocol->isa) { return; }
if (!isInstanceProperty) { return; } if (!isInstanceProperty) { return; }
Protocol2 *proto = (Protocol2*)aProtocol; Protocol2 *proto = (Protocol2*)aProtocol;
struct objc_property_list **listPtr; struct objc_property_list **listPtr;

@ -348,6 +348,7 @@ id class_createInstance(Class cls, size_t extraBytes)
if (Nil == cls) { return nil; } if (Nil == cls) { return nil; }
id obj = gc->allocate_class(cls, extraBytes); id obj = gc->allocate_class(cls, extraBytes);
obj->isa = cls; obj->isa = cls;
checkARCAccessorsSlow(cls);
call_cxx_construct(obj); call_cxx_construct(obj);
return obj; return obj;
} }

Loading…
Cancel
Save