From 4796a41e4e91985e4bf21dc377a783b861627e24 Mon Sep 17 00:00:00 2001 From: theraven Date: Tue, 11 Oct 2011 18:42:34 +0000 Subject: [PATCH] Finished implementing the new runtime APIs. --- ANNOUNCE | 3 +- Test/PropertyIntrospectionTest.m | 36 ++++++ Test/ProtocolCreation.m | 28 ++++ class_table.c | 11 ++ encoding2.c | 1 + objc/runtime.h | 91 +++++++++++++ properties.h | 11 +- properties.m | 211 ++++++++++++++++++++++++++++++- protocol.c | 121 +++++++++++++++++- 9 files changed, 506 insertions(+), 7 deletions(-) create mode 100644 Test/PropertyIntrospectionTest.m create mode 100644 Test/ProtocolCreation.m diff --git a/ANNOUNCE b/ANNOUNCE index d06f7f0..c3d4f1b 100644 --- a/ANNOUNCE +++ b/ANNOUNCE @@ -6,12 +6,13 @@ libobjc2). This runtime was designed to support the features of Objective-C 2 for use with GNUstep and other Objective-C programs. Highlights of this release include: +- Compatibility with the new runtime APIs introduced with Mac OS X 10.7. - Support for small objects (ones hidden inside a pointer). On 32-bit systems, the runtime permits one small object class, on 64-bit systems it permits 4. - Support for prototype-style object orientation. You can now add methods, as well as associated references, to individual objects, as well as cloning them. The runtime now supports everything required for the JavaScript object - model. + model, including the ability to use blocks as methods. You may obtain the code for this release from subversion at the following subversion branch: diff --git a/Test/PropertyIntrospectionTest.m b/Test/PropertyIntrospectionTest.m new file mode 100644 index 0000000..bcf82b7 --- /dev/null +++ b/Test/PropertyIntrospectionTest.m @@ -0,0 +1,36 @@ +#import +#include +#include +#include + +@interface Foo +@property (getter=bar, setter=setBar:, nonatomic, copy) id foo; +@end +@interface Foo(Bar) +-(id)bar; +-(void)setBar:(id)b; +@end +@implementation Foo +@synthesize foo; +@end + +int main(void) +{ + objc_property_t p = class_getProperty(objc_getClass("Foo"), "foo"); + unsigned int count; + objc_property_attribute_t *l = property_copyAttributeList(p, &count); + for (unsigned int i=0 ; i +#include +#include +#include + +@protocol Test2 @end + +int main(void) +{ + Protocol *p = objc_allocateProtocol("Test"); + protocol_addMethodDescription(p, @selector(someMethod), "@:", YES, NO); + assert(objc_getProtocol("Test2")); + protocol_addProtocol(p, objc_getProtocol("Test2")); + objc_property_attribute_t attrs[] = { {"T", "@" } }; + protocol_addProperty(p, "foo", attrs, 1, YES, YES); + objc_registerProtocol(p); + 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); + assert(protocol_conformsToProtocol(p1, objc_getProtocol("Test2"))); + unsigned int count; + objc_property_t *props = protocol_copyPropertyList(p1, &count); + assert(count == 1); + assert(strcmp("T@,Vfoo", property_getAttributes(*props)) == 0); + return 0; +} diff --git a/class_table.c b/class_table.c index d01f5b9..4694444 100644 --- a/class_table.c +++ b/class_table.c @@ -460,6 +460,17 @@ int objc_getClassList(Class *buffer, int bufferLen) } return count; } +Class *objc_copyClassList(unsigned int *outCount) +{ + int count = class_table->table_used; + Class *buffer = calloc(sizeof(Class), count); + if (NULL != outCount) + { + *outCount = count; + } + objc_getClassList(buffer, count); + return buffer; +} Class class_getSuperclass(Class cls) { diff --git a/encoding2.c b/encoding2.c index 9591c7c..5c9b4ed 100644 --- a/encoding2.c +++ b/encoding2.c @@ -41,6 +41,7 @@ const char *objc_skip_argspec(const char *type) PRIVATE size_t lengthOfTypeEncoding(const char *types) { + if ((NULL == types) || ('\0' == types[0])) { return 0; } const char *end = objc_skip_typespec(types); size_t length = end - types; return length; diff --git a/objc/runtime.h b/objc/runtime.h index df1172c..61078e5 100644 --- a/objc/runtime.h +++ b/objc/runtime.h @@ -163,6 +163,26 @@ struct objc_method_description const char *types; }; +/** + * The objc_property_attribute_t type is used to store attributes for + * properties. This is used to store a decomposed version of the property + * encoding, with each flag stored in the name and each value in the value. + * + * All of the strings that these refer to are internal to the runtime and + * should not be freed. + */ +typedef struct +{ + /** + * The flag that this attribute describes. All current flags are single characters, + */ + const char *name; + /** + */ + const char *value; +} objc_property_attribute_t; + + #ifndef YES # define YES ((BOOL)1) @@ -520,6 +540,12 @@ id objc_getClass(const char *name); * Otherwise, it copies classes and returns the number copied. */ int objc_getClassList(Class *buffer, int bufferLen); +/** + * Returns a copy of the list of all classes in the system. The caller is + * responsible for freeing this list. The number of classes is returned in the + * parameter. + */ +Class *objc_copyClassList(unsigned int *outCount); /** * Returns the metaclass with the specified name. This is equivalent to @@ -544,6 +570,41 @@ id objc_lookUpClass(const char *name); * Returns the protocol with the specified name. */ Protocol *objc_getProtocol(const char *name); +/** + * Allocates a new protocol. This returns NULL if a protocol with the same + * name already exists in the system. + * + * Protocols are immutable after they have been registered, so may only be + * modified between calling this function and calling objc_registerProtocol(). + */ +Protocol *objc_allocateProtocol(const char *name); +/** + * Registers a protocol with the runtime. After this point, the protocol may + * not be modified. + */ +void objc_registerProtocol(Protocol *proto); +/** + * Adds a method to the protocol. + */ +void protocol_addMethodDescription(Protocol *aProtocol, + SEL name, + const char *types, + BOOL isRequiredMethod, + BOOL isInstanceMethod); +/** + * Adds a protocol to the protocol. + */ +void protocol_addProtocol(Protocol *aProtocol, Protocol *addition); +/** + * Adds a property to the protocol. + */ +void protocol_addProperty(Protocol *aProtocol, + const char *name, + const objc_property_attribute_t *attributes, + unsigned int attributeCount, + BOOL isRequiredProperty, + BOOL isInstanceProperty); + /** * Registers a new class and its metaclass with the runtime. This function @@ -600,6 +661,36 @@ const char *property_getName(objc_property_t property); */ const char *property_getAttributes(objc_property_t property); +/** + * Returns an array of attributes for this property. + */ +objc_property_attribute_t *property_copyAttributeList(objc_property_t property, + unsigned int *outCount); +/** + * Adds a property to the class, given a specified set of attributes. Note + * that this only sets the property metadata. The property accessor methods + * must already be created. + */ +BOOL class_addProperty(Class cls, + const char *name, + const objc_property_attribute_t *attributes, + unsigned int attributeCount); + +/** + * Replaces property metadata. If the property does not exist, then this is + * equivalent to calling class_addProperty(). + */ +void class_replaceProperty(Class cls, + const char *name, + const objc_property_attribute_t *attributes, + unsigned int attributeCount); + +/** + * Returns a copy of a single attribute. + */ +char *property_copyAttributeValue(objc_property_t property, + const char *attributeName); + /** * Testswhether a protocol conforms to another protocol. */ diff --git a/properties.h b/properties.h index 737875f..9500ac5 100644 --- a/properties.h +++ b/properties.h @@ -1,3 +1,4 @@ +#include "visibility.h" enum PropertyAttributeKind { @@ -55,7 +56,7 @@ struct objc_property * Attributes for this property. Made by ORing together * PropertyAttributeKinds. */ - const char attributes; + char attributes; /** * Flag set if the property is synthesized. */ @@ -88,8 +89,7 @@ struct objc_property_list */ int count; /* - * UNUSED. In future, categories will be allowed to add properties and - * then this will be used to link the declarations together. + * The next property in a linked list. */ struct objc_property_list *next; /** @@ -98,3 +98,8 @@ struct objc_property_list struct objc_property properties[]; }; +/** + * Constructs a property description from a list of attributes. + */ +PRIVATE struct objc_property propertyFromAttrs(const objc_property_attribute_t *attributes, + unsigned int attributeCount); diff --git a/properties.m b/properties.m index 370f053..e4aea0f 100644 --- a/properties.m +++ b/properties.m @@ -10,6 +10,7 @@ #include "visibility.h" #include "nsobject.h" #include "gc_ops.h" +#include "lock.h" PRIVATE int spinlocks[spinlock_count]; @@ -181,6 +182,7 @@ objc_property_t class_getProperty(Class cls, const char *name) } return NULL; } + objc_property_t* class_copyPropertyList(Class cls, unsigned int *outCount) { if (Nil == cls || !objc_test_class_flag(cls, objc_class_flag_new_abi)) @@ -228,6 +230,32 @@ const char *property_getName(objc_property_t property) PRIVATE size_t lengthOfTypeEncoding(const char *types); +/** + * The compiler stores the type encoding of the getter. We replace this with + * the type encoding of the property itself. We use a 0 byte at the start to + * indicate that the swap has taken place. + */ +static const char *property_getTypeEncoding(objc_property_t property) +{ + if (NULL == property) { return NULL; } + + const char *name = property->getter_types; + if (name[0] == 0) + { + return &name[1]; + } + size_t typeSize = lengthOfTypeEncoding(name); + char *buffer = malloc(typeSize + 2); + buffer[0] = 0; + memcpy(buffer+1, name, typeSize); + buffer[typeSize+1] = 0; + if (!__sync_bool_compare_and_swap(&(property->getter_types), name, buffer)) + { + free(buffer); + } + return &property->getter_types[1]; +} + const char *property_getAttributes(objc_property_t property) { if (NULL == property) { return NULL; } @@ -238,7 +266,8 @@ const char *property_getAttributes(objc_property_t property) return name + 2; } - size_t typeSize = lengthOfTypeEncoding(property->getter_types); + const char *typeEncoding = property_getTypeEncoding(property); + size_t typeSize = strlen(typeEncoding); size_t nameSize = strlen(property->name); // Encoding is T{type},V{name}, so 4 bytes for the "T,V" that we always // need. We also need two bytes for the leading null and the length. @@ -287,7 +316,7 @@ const char *property_getAttributes(objc_property_t property) *(insert++) = 0; // Set the type encoding *(insert++) = 'T'; - memcpy(insert, property->getter_types, typeSize); + memcpy(insert, typeEncoding, typeSize); insert += typeSize; // Set the flags memcpy(insert, flags, i); @@ -322,3 +351,181 @@ const char *property_getAttributes(objc_property_t property) return (const char*)(encoding + 2); } +objc_property_attribute_t *property_copyAttributeList(objc_property_t property, + unsigned int *outCount) +{ + if (NULL == property) { return NULL; } + objc_property_attribute_t attrs[10]; + int count = 0; + + attrs[count].name = "T"; + attrs[count].value = property_getTypeEncoding(property); + count++; + if ((property->attributes & OBJC_PR_copy) == OBJC_PR_copy) + { + attrs[count].name = "C"; + attrs[count].value = ""; + count++; + } + if ((property->attributes & OBJC_PR_retain) == OBJC_PR_retain) + { + attrs[count].name = "&"; + attrs[count].value = ""; + count++; + } + if ((property->attributes & OBJC_PR_nonatomic) == OBJC_PR_nonatomic) + { + attrs[count].name = "N"; + attrs[count].value = ""; + count++; + } + if ((property->attributes & OBJC_PR_getter) == OBJC_PR_getter) + { + attrs[count].name = "G"; + attrs[count].value = property->getter_name; + count++; + } + if ((property->attributes & OBJC_PR_setter) == OBJC_PR_setter) + { + attrs[count].name = "S"; + attrs[count].value = property->setter_name; + count++; + } + attrs[count].name = "V"; + attrs[count].value = property_getName(property); + count++; + + objc_property_attribute_t *propAttrs = calloc(sizeof(objc_property_attribute_t), count); + memcpy(propAttrs, attrs, count * sizeof(objc_property_attribute_t)); + if (NULL != outCount) + { + *outCount = count; + } + return propAttrs; +} + +PRIVATE struct objc_property propertyFromAttrs(const objc_property_attribute_t *attributes, + unsigned int attributeCount) +{ + struct objc_property p = { 0 }; + for (unsigned int i=0 ; icount = 0; + memcpy(&l->properties, &p, sizeof(struct objc_property)); + LOCK_RUNTIME_FOR_SCOPE(); + l->next = cls->properties; + cls->properties = l; + return YES; +} + +void class_replaceProperty(Class cls, + const char *name, + const objc_property_attribute_t *attributes, + unsigned int attributeCount) +{ + if ((Nil == cls) || (NULL == name)) { return; } + objc_property_t old = class_getProperty(cls, name); + if (NULL == old) + { + class_addProperty(cls, name, attributes, attributeCount); + return; + } + struct objc_property p = propertyFromAttrs(attributes, attributeCount); + memcpy(old, &p, sizeof(struct objc_property)); + if (NULL == old->name) + { + old->name = name; + } +} +char *property_copyAttributeValue(objc_property_t property, + const char *attributeName) +{ + if ((NULL == property) || (NULL == attributeName)) { return NULL; } + switch (attributeName[0]) + { + case 'T': + { + return strdup(property_getTypeEncoding(property)); + } + case 'V': + { + return strdup(property_getName(property)); + } + case 'S': + { + return strdup(property->setter_name); + } + case 'G': + { + return strdup(property->getter_name); + } + case 'C': + { + return ((property->attributes |= OBJC_PR_copy) == OBJC_PR_copy) ? strdup("") : 0; + } + case '&': + { + return ((property->attributes |= OBJC_PR_retain) == OBJC_PR_retain) ? strdup("") : 0; + } + case 'N': + { + return ((property->attributes |= OBJC_PR_nonatomic) == OBJC_PR_nonatomic) ? strdup("") : 0; + } + } + return 0; +} diff --git a/protocol.c b/protocol.c index 28835ad..25bb2cf 100644 --- a/protocol.c +++ b/protocol.c @@ -3,7 +3,6 @@ #include "properties.h" #include "class.h" #include "lock.h" -#include "visibility.h" #include #define BUFFER_TYPE struct objc_protocol_list @@ -494,3 +493,123 @@ Protocol*__unsafe_unretained* objc_copyProtocolList(unsigned int *outCount) return p; } + +Protocol *objc_allocateProtocol(const char *name) +{ + if (objc_getProtocol(name) != NULL) { return NULL; } + Protocol *p = calloc(1, sizeof(Protocol2)); + p->name = strdup(name); + return p; +} +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; } + proto->isa = ObjC2ProtocolClass; + protocol_table_insert((struct objc_protocol2*)proto); +} +void protocol_addMethodDescription(Protocol *aProtocol, + SEL name, + const char *types, + BOOL isRequiredMethod, + BOOL isInstanceMethod) +{ + if ((NULL == aProtocol) || (NULL == name) || (NULL == types)) { return; } + if (nil != aProtocol->isa) { return; } + Protocol2 *proto = (Protocol2*)aProtocol; + struct objc_method_description_list **listPtr; + if (isInstanceMethod) + { + if (isRequiredMethod) + { + listPtr = &proto->instance_methods; + } + else + { + listPtr = &proto->optional_instance_methods; + } + } + else + { + if (isRequiredMethod) + { + listPtr = &proto->class_methods; + } + else + { + listPtr = &proto->optional_class_methods; + } + } + if (NULL == *listPtr) + { + *listPtr = calloc(1, sizeof(struct objc_method_description_list) + sizeof(struct objc_method_description)); + (*listPtr)->count = 1; + } + else + { + (*listPtr)->count++; + *listPtr = realloc(*listPtr, sizeof(struct objc_method_description_list) + + sizeof(struct objc_method_description) * (*listPtr)->count); + } + struct objc_method_description_list *list = *listPtr; + int index = list->count-1; + list->methods[index].name = sel_getName(name); + list->methods[index].types= types; +} +void protocol_addProtocol(Protocol *aProtocol, Protocol *addition) +{ + if ((NULL == aProtocol) || (NULL == addition)) { return; } + Protocol2 *proto = (Protocol2*)aProtocol; + if (NULL == proto->protocol_list) + { + proto->protocol_list = calloc(1, sizeof(struct objc_property_list) + sizeof(Protocol2*)); + proto->protocol_list->count = 1; + } + else + { + proto->protocol_list->count++; + proto->protocol_list = realloc(proto->protocol_list, sizeof(struct objc_property_list) + + proto->protocol_list->count * sizeof(Protocol2*)); + proto->protocol_list->count = 1; + } + proto->protocol_list->list[proto->protocol_list->count-1] = (Protocol2*)addition; +} +void protocol_addProperty(Protocol *aProtocol, + const char *name, + const objc_property_attribute_t *attributes, + unsigned int attributeCount, + BOOL isRequiredProperty, + BOOL isInstanceProperty) +{ + if ((NULL == aProtocol) || (NULL == name)) { return; } + if (nil != aProtocol->isa) { return; } + if (!isInstanceProperty) { return; } + Protocol2 *proto = (Protocol2*)aProtocol; + struct objc_property_list **listPtr; + if (isRequiredProperty) + { + listPtr = &proto->properties; + } + else + { + listPtr = &proto->optional_properties; + } + if (NULL == *listPtr) + { + *listPtr = calloc(1, sizeof(struct objc_property_list) + sizeof(struct objc_property)); + (*listPtr)->count = 1; + } + else + { + (*listPtr)->count++; + *listPtr = realloc(*listPtr, sizeof(struct objc_property_list) + + sizeof(struct objc_property) * (*listPtr)->count); + } + struct objc_property_list *list = *listPtr; + int index = list->count-1; + list->properties[index] = propertyFromAttrs(attributes, attributeCount); + list->properties[index].name = strdup(name); +} +