diff --git a/Test/ProtocolCreation.m b/Test/ProtocolCreation.m index 1cbf296..24fbf6b 100644 --- a/Test/ProtocolCreation.m +++ b/Test/ProtocolCreation.m @@ -4,26 +4,91 @@ #include @protocol Test2 @end +@protocol Test3 @end +@protocol Test4 @end + +void checkProtocolMethod(Protocol *p, SEL sel, BOOL isClass, BOOL isOptional) +{ + struct objc_method_description d = protocol_getMethodDescription(p, sel, isClass, isOptional); + assert(sel_isEqual(d.name, sel)); + assert(strcmp((d.types), "@:") == 0); + d = protocol_getMethodDescription(p, sel, !isClass, isOptional); + assert(d.name == NULL); + assert(d.types == NULL); + d = protocol_getMethodDescription(p, sel, isClass, !isOptional); + assert(d.name == NULL); + assert(d.types == NULL); + d = protocol_getMethodDescription(p, sel, !isClass, !isOptional); + assert(d.name == NULL); + assert(d.types == NULL); +} int main(void) { Protocol *force_reference = @protocol(Test2); Protocol *p = objc_allocateProtocol("Test"); - protocol_addMethodDescription(p, @selector(someMethod), "@:", YES, NO); + protocol_addMethodDescription(p, @selector(someClassMethod), "@:", YES, NO); + protocol_addMethodDescription(p, @selector(someOptionalClassMethod), "@:", YES, YES); + protocol_addMethodDescription(p, @selector(someMethod), "@:", NO, NO); + protocol_addMethodDescription(p, @selector(someOtherMethod), "@:", NO, NO); + protocol_addMethodDescription(p, @selector(someOptionalMethod), "@:", NO, YES); assert(objc_getProtocol("Test2")); protocol_addProtocol(p, objc_getProtocol("Test2")); - objc_property_attribute_t attrs[] = { {"T", "@" }, {"V", "foo"} }; - protocol_addProperty(p, "foo", attrs, 2, YES, YES); + protocol_addProtocol(p, @protocol(Test3)); + + // Check that this don't crash + protocol_addProtocol(p, NULL); + protocol_addProtocol(NULL, p); + protocol_addProtocol(NULL, NULL); + + objc_property_attribute_t attrs[] = { {"T", "@" }, {"V", "optional"} }; + protocol_addProperty(p, "optional", attrs, 2, NO, YES); + attrs[1].value = "required"; + protocol_addProperty(p, "required", attrs, 2, YES, YES); + attrs[1].value = "required2"; + protocol_addProperty(p, "required2", attrs, 2, YES, YES); + protocol_addProperty(p, "classOptional", attrs, 1, NO, NO); + protocol_addProperty(p, "classRequired", attrs, 1, YES, NO); + + checkProtocolMethod(p, @selector(someClassMethod), YES, NO); + checkProtocolMethod(p, @selector(someOptionalClassMethod), YES, YES); + checkProtocolMethod(p, @selector(someMethod), NO, NO); + checkProtocolMethod(p, @selector(someOtherMethod), NO, NO); + checkProtocolMethod(p, @selector(someOptionalMethod), NO, YES); objc_registerProtocol(p); + // Modifying protocols after they've been registered is not permitted. + protocol_addProtocol(p, @protocol(Test4)); + protocol_addMethodDescription(p, @selector(someUnsupportedMethod), "@:", NO, NO); + protocol_addProperty(p, "classRequired2", attrs, 1, NO, NO); + Protocol *p1 = objc_getProtocol("Test"); assert(p == p1); - struct objc_method_description d = protocol_getMethodDescription(p1, @selector(someMethod), YES, NO); - assert(strcmp(sel_getName(d.name), "someMethod") == 0); - assert(strcmp((d.types), "@:") == 0); + + checkProtocolMethod(p1, @selector(someClassMethod), YES, NO); + checkProtocolMethod(p1, @selector(someOptionalClassMethod), YES, YES); + checkProtocolMethod(p1, @selector(someMethod), NO, NO); + checkProtocolMethod(p1, @selector(someOtherMethod), NO, NO); + checkProtocolMethod(p1, @selector(someOptionalMethod), NO, YES); + // Added after the protocol was registered, shouldn't have been allowed. + struct objc_method_description d = protocol_getMethodDescription(p1, @selector(someUnsupportedMethod), NO, NO); + assert(d.name == NULL); + assert(d.types == NULL); + assert(protocol_conformsToProtocol(p1, objc_getProtocol("Test2"))); + assert(protocol_conformsToProtocol(p1, objc_getProtocol("Test3"))); + // Added after the protocol was registered, shouldn't have been allowed. + assert(!protocol_conformsToProtocol(p1, objc_getProtocol("Test4"))); unsigned int count; - objc_property_t *props = protocol_copyPropertyList(p1, &count); + protocol_copyPropertyList(p1, &count); + assert(count == 2); + protocol_copyPropertyList2(p1, &count, YES, YES); + assert(count == 2); + objc_property_t *props = protocol_copyPropertyList2(p1, &count, NO, YES); assert(count == 1); - assert(strcmp("T@,Vfoo", property_getAttributes(*props)) == 0); + assert(strcmp("T@,Voptional", property_getAttributes(*props)) == 0); + + + //objc_copyProtocolList + return 0; } diff --git a/objc/runtime.h b/objc/runtime.h index e6a9334..199bf46 100644 --- a/objc/runtime.h +++ b/objc/runtime.h @@ -715,12 +715,20 @@ struct objc_method_description *protocol_copyMethodDescriptionList(Protocol *p, BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *count); /** - * Returns an array of property metadata values, with the number being stored - * in the variable pointed to by the last argument. The caller is responsible - * for freeing the returned array. + * Returns an array of required instance properties, with the number being + * stored in the variable pointed to by the last argument. The caller is + * responsible for freeing the returned array. */ objc_property_t *protocol_copyPropertyList(Protocol *p, unsigned int *count); +/** + * Returns an array of properties specified by this class, with the number + * being stored in the variable pointed to by the last argument. The caller is + * responsible for freeing the returned array. + */ +objc_property_t *protocol_copyPropertyList2(Protocol *p, unsigned int *count, + BOOL isRequiredProperty, BOOL isInstanceProperty); + /** * Returns an array of protocols that this protocol conforms to, with the * number of protocols in the array being returned via the last argument. The diff --git a/protocol.c b/protocol.c index 91b8132..c726f8d 100644 --- a/protocol.c +++ b/protocol.c @@ -391,23 +391,28 @@ Protocol*__unsafe_unretained* protocol_copyProtocolList(Protocol *p, unsigned in return out; } -objc_property_t *protocol_copyPropertyList(Protocol *p, - unsigned int *outCount) +objc_property_t *protocol_copyPropertyList2(Protocol *p, unsigned int *outCount, + BOOL isRequiredProperty, BOOL isInstanceProperty) { + struct objc_property_list *properties = + isInstanceProperty ? + (isRequiredProperty ? p->properties : p->optional_properties) : + (isRequiredProperty ? p->class_properties : p->optional_class_properties); if (NULL == p) { return NULL; } - if (!protocol_hasOptionalMethodsAndProperties(p)) + // If it's an old protocol, it won't have any of the other options. + if (!isRequiredProperty && !isInstanceProperty && + !protocol_hasOptionalMethodsAndProperties(p)) { return NULL; } - struct objc_property_list *properties = p->properties; - unsigned int count = 0; - if (NULL != properties) + if (properties == NULL) { - count = properties->count; + return NULL; } - if (NULL != p->optional_properties) + unsigned int count = 0; + for (struct objc_property_list *l=properties ; l!=NULL ; l=l->next) { - count += p->optional_properties->count; + count += properties->count; } if (0 == count) { @@ -415,25 +420,23 @@ objc_property_t *protocol_copyPropertyList(Protocol *p, } objc_property_t *list = calloc(sizeof(objc_property_t), count); unsigned int out = 0; - if (properties) - { - for (int i=0 ; icount ; i++) - { - list[out++] = property_at_index(properties, i); - } - } - properties = p->optional_properties; - if (properties) + for (struct objc_property_list *l=properties ; l!=NULL ; l=l->next) { - for (int i=0 ; icount ; i++) + for (int i=0 ; icount ; i++) { - list[out++] = property_at_index(properties, i); + list[out++] = property_at_index(l, i); } } *outCount = count; return list; } +objc_property_t *protocol_copyPropertyList(Protocol *p, + unsigned int *outCount) +{ + return protocol_copyPropertyList2(p, outCount, YES, YES); +} + objc_property_t protocol_getProperty(Protocol *p, const char *name, BOOL isRequiredProperty, @@ -658,7 +661,6 @@ void protocol_addProtocol(Protocol *aProtocol, Protocol *addition) aProtocol->protocol_list->count++; aProtocol->protocol_list = realloc(aProtocol->protocol_list, sizeof(struct objc_property_list) + aProtocol->protocol_list->count * sizeof(Protocol*)); - aProtocol->protocol_list->count = 1; } aProtocol->protocol_list->list[aProtocol->protocol_list->count-1] = (Protocol*)addition; } @@ -672,15 +674,10 @@ void protocol_addProperty(Protocol *aProtocol, if ((NULL == aProtocol) || (NULL == name)) { return; } if (incompleteProtocolClass() != aProtocol->isa) { return; } if (!isInstanceProperty) { return; } - struct objc_property_list **listPtr; - if (isRequiredProperty) - { - listPtr = &aProtocol->properties; - } - else - { - listPtr = &aProtocol->optional_properties; - } + struct objc_property_list **listPtr = + isInstanceProperty ? + (isRequiredProperty ? &aProtocol->properties : &aProtocol->optional_properties) : + (isRequiredProperty ? &aProtocol->class_properties : &aProtocol->optional_class_properties); if (NULL == *listPtr) { *listPtr = calloc(1, sizeof(struct objc_property_list) + sizeof(struct objc_property));