Finished implementing the new runtime APIs.

This commit is contained in:
theraven 2011-10-11 18:42:34 +00:00
parent 084bbdd877
commit 4796a41e4e
9 changed files with 506 additions and 7 deletions

View File

@ -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:

View File

@ -0,0 +1,36 @@
#import <objc/runtime.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
@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<count ; i++)
{
switch (l[i].name[0])
{
case 'T': assert(0==strcmp(l[i].value, "@")); break;
case 'C': assert(0==strcmp(l[i].value, "")); break;
case 'N': assert(0==strcmp(l[i].value, "")); break;
case 'G': assert(0==strcmp(l[i].value, "bar")); break;
case 'S': assert(0==strcmp(l[i].value, "setBar:")); break;
case 'B': assert(0==strcmp(l[i].value, "foo")); break;
}
}
assert(0 == property_copyAttributeList(0, &count));
return 0;
}

28
Test/ProtocolCreation.m Normal file
View File

@ -0,0 +1,28 @@
#import <objc/runtime.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
@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;
}

View File

@ -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)
{

View File

@ -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;

View File

@ -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.
*/

View File

@ -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);

View File

@ -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 ; i<attributeCount ; i++)
{
switch (attributes[i].name[0])
{
case 'T':
{
size_t typeSize = strlen(attributes[i].value);
char *buffer = malloc(typeSize + 2);
buffer[0] = 0;
memcpy(buffer+1, attributes[i].value, typeSize);
buffer[typeSize+1] = 0;
p.getter_types = buffer;
break;
}
case 'S':
{
p.setter_name = strdup(attributes[i].value);
break;
}
case 'G':
{
p.getter_name = strdup(attributes[i].value);
break;
}
case 'V':
{
p.name = strdup(attributes[i].value);
break;
}
case 'C':
{
p.attributes |= OBJC_PR_copy;
}
case '&':
{
p.attributes |= OBJC_PR_retain;
}
case 'N':
{
p.attributes |= OBJC_PR_nonatomic;
}
}
}
return p;
}
BOOL class_addProperty(Class cls,
const char *name,
const objc_property_attribute_t *attributes,
unsigned int attributeCount)
{
if ((Nil == cls) || (NULL == name) || (class_getProperty(cls, name) != 0)) { return NO; }
struct objc_property p = propertyFromAttrs(attributes, attributeCount);
// If there is a name mismatch, the attributes are invalid.
if ((p.name != 0) && (strcmp(name, p.name) != 0)) { return NO; }
struct objc_property_list *l = calloc(1, sizeof(struct objc_property_list));
l->count = 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;
}

View File

@ -3,7 +3,6 @@
#include "properties.h"
#include "class.h"
#include "lock.h"
#include "visibility.h"
#include <stdlib.h>
#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);
}