Add support for improved property structure.

As a side effect, we need to upgrade properties in class and protocols
(but not categories because we didn't have class properties in earlier
ABIs).
main
David Chisnall 8 years ago
parent b7ef58bc59
commit 65b34a196f

@ -19,8 +19,7 @@
+ (Class)class { return self; }
- (id)self { return self; }
@end
@implementation Protocol2 @end
@interface __IncompleteProtocol : Protocol2 @end
@interface __IncompleteProtocol : Protocol @end
@implementation __IncompleteProtocol @end
/**
@ -34,3 +33,5 @@
@implementation __ObjC_Protocol_Holder_Ugly_Hack @end
@implementation Object @end
@implementation ProtocolGCC @end

@ -254,7 +254,7 @@ struct legacy_gnustep_objc_class
* List of declared properties on this class (NULL if none). This contains
* the accessor methods for each property.
*/
struct objc_property_list_legacy *properties;
struct objc_property_list_gsv1 *properties;
/**
* GC / ARC ABI: Fields below this point only exist if abi_version is >= 1.

@ -1,5 +1,6 @@
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "objc/runtime.h"
@ -9,6 +10,8 @@
#include "class.h"
#include "loader.h"
PRIVATE size_t lengthOfTypeEncoding(const char *types);
static ivar_ownership ownershipForIvar(struct legacy_gnustep_objc_class *cls, int idx)
{
if (objc_get_class_version_legacy(cls) < 2)
@ -88,7 +91,143 @@ static struct objc_method_list *upgradeMethodList(struct objc_method_list_legacy
return l;
}
static struct objc_property_list *upgradePropertyList(struct objc_property_list_legacy *l)
static inline BOOL checkAttribute(char field, int attr)
{
return (field & attr) == attr;
}
static void upgradeProperty(struct objc_property *n, struct objc_property_gsv1 *o)
{
size_t typeSize = lengthOfTypeEncoding(o->getter_types);
char *typeEncoding = malloc(typeSize + 1);
memcpy(typeEncoding, o->getter_types, typeSize);
typeEncoding[typeSize] = 0;
n->type = typeEncoding;
if (o->getter_name)
{
n->getter = sel_registerTypedName_np(o->getter_name, o->getter_types);
}
if (o->setter_name)
{
n->setter = sel_registerTypedName_np(o->setter_name, o->setter_types);
}
if (o->name[0] == '\0')
{
n->name = o->name + o->name[1];
n->attributes = o->name + 2;
return;
}
n->name = o->name;
const char *name = o->name;
size_t nameSize = (NULL == name) ? 0 : strlen(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.
size_t encodingSize = typeSize + nameSize + 6;
char flags[20];
size_t i = 0;
// Flags that are a comma then a character
if (checkAttribute(o->attributes, OBJC_PR_readonly))
{
flags[i++] = ',';
flags[i++] = 'R';
}
if (checkAttribute(o->attributes, OBJC_PR_retain))
{
flags[i++] = ',';
flags[i++] = '&';
}
if (checkAttribute(o->attributes, OBJC_PR_copy))
{
flags[i++] = ',';
flags[i++] = 'C';
}
if (checkAttribute(o->attributes2, OBJC_PR_weak))
{
flags[i++] = ',';
flags[i++] = 'W';
}
if (checkAttribute(o->attributes2, OBJC_PR_dynamic))
{
flags[i++] = ',';
flags[i++] = 'D';
}
if ((o->attributes & OBJC_PR_nonatomic) == OBJC_PR_nonatomic)
{
flags[i++] = ',';
flags[i++] = 'N';
}
encodingSize += i;
flags[i] = '\0';
size_t setterLength = 0;
size_t getterLength = 0;
if ((o->attributes & OBJC_PR_getter) == OBJC_PR_getter)
{
getterLength = strlen(o->getter_name);
encodingSize += 2 + getterLength;
}
if ((o->attributes & OBJC_PR_setter) == OBJC_PR_setter)
{
setterLength = strlen(o->setter_name);
encodingSize += 2 + setterLength;
}
unsigned char *encoding = malloc(encodingSize);
// Set the leading 0 and the offset of the name
unsigned char *insert = encoding;
BOOL needsComma = NO;
*(insert++) = 0;
*(insert++) = 0;
// Set the type encoding
if (NULL != typeEncoding)
{
*(insert++) = 'T';
memcpy(insert, typeEncoding, typeSize);
insert += typeSize;
needsComma = YES;
}
// Set the flags
memcpy(insert, flags, i);
insert += i;
if ((o->attributes & OBJC_PR_getter) == OBJC_PR_getter)
{
if (needsComma)
{
*(insert++) = ',';
}
i++;
needsComma = YES;
*(insert++) = 'G';
memcpy(insert, o->getter_name, getterLength);
insert += getterLength;
}
if ((o->attributes & OBJC_PR_setter) == OBJC_PR_setter)
{
if (needsComma)
{
*(insert++) = ',';
}
i++;
needsComma = YES;
*(insert++) = 'S';
memcpy(insert, o->setter_name, setterLength);
insert += setterLength;
}
if (needsComma)
{
*(insert++) = ',';
}
*(insert++) = 'V';
memcpy(insert, name, nameSize);
insert += nameSize;
*(insert++) = '\0';
n->attributes = (const char*)encoding;
}
static struct objc_property_list *upgradePropertyList(struct objc_property_list_gsv1 *l)
{
if (l == NULL)
{
@ -98,7 +237,10 @@ static struct objc_property_list *upgradePropertyList(struct objc_property_list_
struct objc_property_list *n = calloc(1, sizeof(struct objc_property_list) + data_size);
n->count = l->count;
n->size = sizeof(struct objc_property);
memcpy(n->properties, l->properties, data_size);
for (int i=0 ; i<l->count ; i++)
{
upgradeProperty(&n->properties[i], &l->properties[i]);
}
return n;
}
@ -140,3 +282,44 @@ PRIVATE struct objc_category *objc_upgrade_category(struct objc_category_legacy
cat->class_methods = upgradeMethodList(old->class_methods);
return cat;
}
PRIVATE struct objc_protocol *objc_upgrade_protocol_gcc(struct objc_protocol_gcc *p)
{
// If the protocol has already been upgraded, the don't try to upgrade it twice.
if (p->isa == objc_getClass("ProtocolGCC"))
{
return objc_getProtocol(p->name);
}
p->isa = objc_getClass("ProtocolGCC");
Protocol *proto =
(Protocol*)class_createInstance((Class)objc_getClass("Protocol"),
sizeof(struct objc_protocol) - sizeof(id));
proto->name = p->name;
// Aliasing this means that when this returns these will all be updated.
proto->protocol_list = p->protocol_list;
proto->instance_methods = p->instance_methods;
proto->class_methods = p->class_methods;
assert(proto->isa);
return proto;
}
PRIVATE struct objc_protocol *objc_upgrade_protocol_gsv1(struct objc_protocol_gsv1 *p)
{
// If the protocol has already been upgraded, the don't try to upgrade it twice.
if (p->isa == objc_getClass("Protocol"))
{
return (struct objc_protocol*)p;
}
if (p->properties)
{
p->properties = (struct objc_property_list_gsv1*)upgradePropertyList(p->properties);
}
if (p->optional_properties)
{
p->optional_properties = (struct objc_property_list_gsv1*)upgradePropertyList(p->optional_properties);
}
p->isa = objc_getClass("Protocol");
assert(p->isa);
return (struct objc_protocol*)p;
}

@ -3,9 +3,13 @@
#include "ivar.h"
#include "class.h"
#include "category.h"
#include "protocol.h"
PRIVATE Class objc_upgrade_class(struct legacy_gnustep_objc_class *oldClass);
PRIVATE struct objc_category *objc_upgrade_category(struct objc_category_legacy *);
PRIVATE struct legacy_gnustep_objc_class* objc_legacy_class_for_class(Class);
PRIVATE struct objc_protocol *objc_upgrade_protocol_gcc(struct objc_protocol_gcc*);
PRIVATE struct objc_protocol *objc_upgrade_protocol_gsv1(struct objc_protocol_gsv1*);

@ -118,10 +118,10 @@ struct objc_init
Class *cls_ref_end;
struct objc_category *cat_begin;
struct objc_category *cat_end;
struct objc_protocol2 *proto_begin;
struct objc_protocol2 *proto_end;
struct objc_protocol2 **proto_ref_begin;
struct objc_protocol2 **proto_ref_end;
struct objc_protocol *proto_begin;
struct objc_protocol *proto_end;
struct objc_protocol **proto_ref_begin;
struct objc_protocol **proto_ref_end;
struct objc_alias *alias_begin;
struct objc_alias *alias_end;
};
@ -158,7 +158,7 @@ void __objc_load(struct objc_init *init)
objc_register_selector(sel);
}
int i = 0;
for (struct objc_protocol2 *proto = init->proto_begin ; proto < init->proto_end ;
for (struct objc_protocol *proto = init->proto_begin ; proto < init->proto_end ;
proto++)
{
if (proto->name == NULL)

@ -91,6 +91,33 @@ enum PropertyAttributeKind2
* documentation.
*/
struct objc_property
{
/**
* Name of this property.
*/
const char *name;
/**
* The type encoding of the property.
*/
const char *attributes;
/**
* The type encoding of the property.
*/
const char *type;
/**
* The selector for the getter for this property.
*/
SEL getter;
/**
* The selector for the setter for this property.
*/
SEL setter;
};
/**
* GNUstep v1 ABI version of `struct objc_property`
*/
struct objc_property_gsv1
{
/**
* Name of this property.
@ -136,7 +163,7 @@ struct objc_property
/**
* List of property introspection data.
*/
struct objc_property_list_legacy
struct objc_property_list_gsv1
{
/**
* Number of properties in this array.
@ -149,7 +176,7 @@ struct objc_property_list_legacy
/**
* List of properties.
*/
struct objc_property properties[];
struct objc_property_gsv1 properties[];
};
/**
@ -183,7 +210,7 @@ struct objc_property_list
*/
PRIVATE struct objc_property propertyFromAttrs(const objc_property_attribute_t *attributes,
unsigned int attributeCount,
const char **iVarName);
const char *name);
/**
* Constructs and installs a property attribute string from the property

@ -15,11 +15,6 @@
PRIVATE int spinlocks[spinlock_count];
static inline BOOL checkAttribute(char field, int attr)
{
return (field & attr) == attr;
}
/**
* Public function for getting a property.
*/
@ -326,9 +321,7 @@ const char *property_getName(objc_property_t property)
return name;
}
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.
@ -336,167 +329,13 @@ PRIVATE size_t lengthOfTypeEncoding(const char *types);
static const char *property_getTypeEncoding(objc_property_t property)
{
if (NULL == property) { return NULL; }
if (NULL == property->getter_types) { 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];
}
PRIVATE const char *constructPropertyAttributes(objc_property_t property,
const char *iVarName)
{
const char *name = (char*)property->name;
const char *typeEncoding = property_getTypeEncoding(property);
size_t typeSize = (NULL == typeEncoding) ? 0 : strlen(typeEncoding);
size_t nameSize = (NULL == name) ? 0 : strlen(name);
size_t iVarNameSize = (NULL == iVarName) ? 0 : strlen(iVarName);
// 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.
size_t encodingSize = typeSize + nameSize + 6;
char flags[20];
size_t i = 0;
// Flags that are a comma then a character
if (checkAttribute(property->attributes, OBJC_PR_readonly))
{
flags[i++] = ',';
flags[i++] = 'R';
}
if (checkAttribute(property->attributes, OBJC_PR_retain))
{
flags[i++] = ',';
flags[i++] = '&';
}
if (checkAttribute(property->attributes, OBJC_PR_copy))
{
flags[i++] = ',';
flags[i++] = 'C';
}
if (checkAttribute(property->attributes2, OBJC_PR_weak))
{
flags[i++] = ',';
flags[i++] = 'W';
}
if (checkAttribute(property->attributes2, OBJC_PR_dynamic))
{
flags[i++] = ',';
flags[i++] = 'D';
}
if ((property->attributes & OBJC_PR_nonatomic) == OBJC_PR_nonatomic)
{
flags[i++] = ',';
flags[i++] = 'N';
}
encodingSize += i;
flags[i] = '\0';
size_t setterLength = 0;
size_t getterLength = 0;
if ((property->attributes & OBJC_PR_getter) == OBJC_PR_getter)
{
getterLength = strlen(property->getter_name);
encodingSize += 2 + getterLength;
}
if ((property->attributes & OBJC_PR_setter) == OBJC_PR_setter)
{
setterLength = strlen(property->setter_name);
encodingSize += 2 + setterLength;
}
if (NULL != iVarName)
{
encodingSize += 2 + iVarNameSize;
}
unsigned char *encoding = malloc(encodingSize);
// Set the leading 0 and the offset of the name
unsigned char *insert = encoding;
BOOL needsComma = NO;
*(insert++) = 0;
*(insert++) = 0;
// Set the type encoding
if (NULL != typeEncoding)
{
*(insert++) = 'T';
memcpy(insert, typeEncoding, typeSize);
insert += typeSize;
needsComma = YES;
}
// Set the flags
memcpy(insert, flags, i);
insert += i;
if ((property->attributes & OBJC_PR_getter) == OBJC_PR_getter)
{
if (needsComma)
{
*(insert++) = ',';
}
i++;
needsComma = YES;
*(insert++) = 'G';
memcpy(insert, property->getter_name, getterLength);
insert += getterLength;
}
if ((property->attributes & OBJC_PR_setter) == OBJC_PR_setter)
{
if (needsComma)
{
*(insert++) = ',';
}
i++;
needsComma = YES;
*(insert++) = 'S';
memcpy(insert, property->setter_name, setterLength);
insert += setterLength;
}
// If the instance variable name is the same as the property name, then we
// use the same string for both, otherwise we write the ivar name in the
// attributes string and then a null and then the name.
if (NULL != iVarName)
{
if (needsComma)
{
*(insert++) = ',';
}
*(insert++) = 'V';
memcpy(insert, iVarName, iVarNameSize);
insert += iVarNameSize;
}
*(insert++) = '\0';
encoding[1] = (unsigned char)(uintptr_t)(insert - encoding);
memcpy(insert, name, nameSize);
insert += nameSize;
*insert = '\0';
// If another thread installed the encoding string while we were computing
// it, then discard the one that we created and return theirs.
if (!__sync_bool_compare_and_swap(&(property->name), name, (char*)encoding))
{
free(encoding);
return property->name + 2;
}
return (const char*)(encoding + 2);
return property->type;
}
const char *property_getAttributes(objc_property_t property)
{
if (NULL == property) { return NULL; }
const char *name = (char*)property->name;
if (name[0] == 0)
{
return name + 2;
}
return constructPropertyAttributes(property, NULL);
return property->attributes;
}
@ -558,12 +397,12 @@ objc_property_attribute_t *property_copyAttributeList(objc_property_t property,
break;
case 'G':
attrs[count].name = "G";
attrs[count].value = property->getter_name;
attrs[count].value = sel_getName(property->getter);
i += strlen(attrs[count].value);
break;
case 'S':
attrs[count].name = "S";
attrs[count].value = property->setter_name;
attrs[count].value = sel_getName(property->setter);
i += strlen(attrs[count].value);
break;
case 'V':
@ -586,87 +425,118 @@ objc_property_attribute_t *property_copyAttributeList(objc_property_t property,
return propAttrs;
}
PRIVATE struct objc_property propertyFromAttrs(const objc_property_attribute_t *attributes,
unsigned int attributeCount,
const char **name)
static const objc_property_attribute_t *findAttribute(char attr,
const objc_property_attribute_t *attributes,
unsigned int attributeCount)
{
struct objc_property p = { 0 };
for (unsigned int i=0 ; i<attributeCount ; i++)
// This linear scan is N^2 in the worst case, but that's still probably
// cheaper than sorting the array because N<12
for (int i=0 ; i<attributeCount ; i++)
{
switch (attributes[i].name[0])
if (attributes[i].name[0] == attr)
{
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':
{
*name = attributes[i].value;
break;
}
case 'C':
{
p.attributes |= OBJC_PR_copy;
break;
}
case 'R':
{
p.attributes |= OBJC_PR_readonly;
break;
}
case 'W':
{
p.attributes2 |= OBJC_PR_weak;
break;
}
case '&':
{
p.attributes |= OBJC_PR_retain;
break;
}
case 'N':
{
p.attributes |= OBJC_PR_nonatomic;
break;
}
case 'D':
{
p.attributes2 |= OBJC_PR_dynamic;
break;
}
return &attributes[i];
}
}
return NULL;
}
static char *addAttrIfExists(char a,
char *buffer,
const objc_property_attribute_t *attributes,
unsigned int attributeCount)
{
const objc_property_attribute_t *attr = findAttribute(a, attributes, attributeCount);
if (attr)
{
*(buffer++) = attr->name[0];
if (attr->value)
{
size_t len = strlen(attr->value);
memcpy(buffer, attr->value, len);
buffer += len;
}
*(buffer++) = ',';
}
return buffer;
}
static const char *encodingFromAttrs(const objc_property_attribute_t *attributes,
unsigned int attributeCount)
{
// Length of the attributes string (initially the number of keys and commas and trailing null)
size_t attributesSize = 2 * attributeCount;
for (int i=0 ; i<attributeCount ; i++)
{
if (attributes[i].value)
{
attributesSize += strlen(attributes[i].value);
}
}
if (attributesSize == 0)
{
return NULL;
}
char *buffer = malloc(attributesSize);
char *out = buffer;
out = addAttrIfExists('T', out, attributes, attributeCount);
out = addAttrIfExists('R', out, attributes, attributeCount);
out = addAttrIfExists('&', out, attributes, attributeCount);
out = addAttrIfExists('C', out, attributes, attributeCount);
out = addAttrIfExists('W', out, attributes, attributeCount);
out = addAttrIfExists('D', out, attributes, attributeCount);
out = addAttrIfExists('N', out, attributes, attributeCount);
out = addAttrIfExists('G', out, attributes, attributeCount);
out = addAttrIfExists('S', out, attributes, attributeCount);
out = addAttrIfExists('V', out, attributes, attributeCount);
assert(out != buffer);
out--;
*out = '\0';
return buffer;
}
PRIVATE struct objc_property propertyFromAttrs(const objc_property_attribute_t *attributes,
unsigned int attributeCount,
const char *name)
{
struct objc_property p;
p.name = strdup(name);
p.attributes = encodingFromAttrs(attributes, attributeCount);
p.type = NULL;
const objc_property_attribute_t *attr = findAttribute('T', attributes, attributeCount);
if (attr)
{
p.type = strdup(attr->value);
}
p.getter = NULL;
attr = findAttribute('G', attributes, attributeCount);
if (attr)
{
// TODO: We should be able to construct the full type encoding if we
// also have a type, but for now use an untyped selector.
p.getter = sel_registerName(attr->value);
}
p.setter = NULL;
attr = findAttribute('S', attributes, attributeCount);
if (attr)
{
// TODO: We should be able to construct the full type encoding if we
// also have a type, but for now use an untyped selector.
p.setter = sel_registerName(attr->value);
}
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; }
const char *iVarname = NULL;
struct objc_property p = propertyFromAttrs(attributes, attributeCount, &iVarname);
// If the iVar name is not the same as the name, then we need to construct
// the attributes string now, otherwise we can construct it lazily.
p.name = name;
constructPropertyAttributes(&p, iVarname);
struct objc_property p = propertyFromAttrs(attributes, attributeCount, name);
struct objc_property_list *l = calloc(1, sizeof(struct objc_property_list)
+ sizeof(struct objc_property));
@ -691,11 +561,8 @@ void class_replaceProperty(Class cls,
class_addProperty(cls, name, attributes, attributeCount);
return;
}
const char *iVarname = 0;
struct objc_property p = propertyFromAttrs(attributes, attributeCount, &iVarname);
p.name = name;
struct objc_property p = propertyFromAttrs(attributes, attributeCount, name);
LOCK_RUNTIME_FOR_SCOPE();
constructPropertyAttributes(&p, iVarname);
memcpy(old, &p, sizeof(struct objc_property));
}
char *property_copyAttributeValue(objc_property_t property,
@ -725,11 +592,11 @@ char *property_copyAttributeValue(objc_property_t property,
}
case 'S':
{
return strdup(property->setter_name);
return strdup(sel_getName(property->setter));
}
case 'G':
{
return strdup(property->getter_name);
return strdup(sel_getName(property->getter));
}
}
return 0;

@ -3,7 +3,9 @@
#include "properties.h"
#include "class.h"
#include "lock.h"
#include "legacy.h"
#include <stdlib.h>
#include <assert.h>
#define BUFFER_TYPE struct objc_protocol_list *
#include "buffer.h"
@ -12,11 +14,11 @@
#include "string_hash.h"
static int protocol_compare(const char *name,
const struct objc_protocol2 *protocol)
const struct objc_protocol *protocol)
{
return string_compare(name, protocol->name);
}
static int protocol_hash(const struct objc_protocol2 *protocol)
static int protocol_hash(const struct objc_protocol *protocol)
{
return string_hash(protocol->name);
}
@ -33,18 +35,16 @@ void init_protocol_table(void)
protocol_initialize(&known_protocol_table, 128);
}
static void protocol_table_insert(const struct objc_protocol2 *protocol)
static void protocol_table_insert(const struct objc_protocol *protocol)
{
protocol_insert(known_protocol_table, (void*)protocol);
}
struct objc_protocol2 *protocol_for_name(const char *name)
struct objc_protocol *protocol_for_name(const char *name)
{
return protocol_table_get(known_protocol_table, name);
}
static id ObjC2ProtocolClass = 0;
static id incompleteProtocolClass(void)
{
static id IncompleteProtocolClass = 0;
@ -55,8 +55,48 @@ static id incompleteProtocolClass(void)
return IncompleteProtocolClass;
}
/**
* Class used for legacy GCC protocols (`ProtocolGCC`).
*/
static id protocol_class_gcc;
/**
* Class used for protocols (`Protocol`).
*/
static id protocol_class_gsv2;
static int isEmptyProtocol(struct objc_protocol2 *aProto)
static BOOL init_protocol_classes(void)
{
if (nil == protocol_class_gcc)
{
protocol_class_gcc = objc_getClass("ProtocolGCC");
}
if (nil == protocol_class_gsv2)
{
protocol_class_gsv2 = objc_getClass("Protocol");
}
if ((nil == protocol_class_gcc) ||
(nil == protocol_class_gsv2))
{
return NO;
}
return YES;
}
static BOOL protocol_hasOptionalMethodsAndProperties(struct objc_protocol *p)
{
if (!init_protocol_classes())
{
return NO;
}
if (p->isa == protocol_class_gcc)
{
return NO;
}
assert((p->isa == protocol_class_gsv2));
return YES;
}
static int isEmptyProtocol(struct objc_protocol *aProto)
{
int isEmpty =
((aProto->instance_methods == NULL) ||
@ -65,27 +105,26 @@ static int isEmptyProtocol(struct objc_protocol2 *aProto)
(aProto->class_methods->count == 0)) &&
((aProto->protocol_list == NULL) ||
(aProto->protocol_list->count == 0));
if (aProto->isa == ObjC2ProtocolClass)
if (protocol_hasOptionalMethodsAndProperties(aProto))
{
struct objc_protocol2 *p2 = (struct objc_protocol2*)aProto;
isEmpty &= (p2->optional_instance_methods->count == 0);
isEmpty &= (p2->optional_class_methods->count == 0);
isEmpty &= (p2->properties == 0) || (p2->properties->count == 0);
isEmpty &= (p2->optional_properties == 0) || (p2->optional_properties->count == 0);
isEmpty &= (aProto->optional_instance_methods->count == 0);
isEmpty &= (aProto->optional_class_methods->count == 0);
isEmpty &= (aProto->properties == 0) || (aProto->properties->count == 0);
isEmpty &= (aProto->optional_properties == 0) || (aProto->optional_properties->count == 0);
}
return isEmpty;
}
// FIXME: Make p1 adopt all of the stuff in p2
static void makeProtocolEqualToProtocol(struct objc_protocol2 *p1,
struct objc_protocol2 *p2)
static void makeProtocolEqualToProtocol(struct objc_protocol *p1,
struct objc_protocol *p2)
{
#define COPY(x) p1->x = p2->x
COPY(instance_methods);
COPY(class_methods);
COPY(protocol_list);
if (p1->isa == ObjC2ProtocolClass &&
p2->isa == ObjC2ProtocolClass)
if (protocol_hasOptionalMethodsAndProperties(p1) &&
protocol_hasOptionalMethodsAndProperties(p2))
{
COPY(optional_instance_methods);
COPY(optional_class_methods);
@ -95,13 +134,9 @@ static void makeProtocolEqualToProtocol(struct objc_protocol2 *p1,
#undef COPY
}
static struct objc_protocol2 *unique_protocol(struct objc_protocol2 *aProto)
static struct objc_protocol *unique_protocol(struct objc_protocol *aProto)
{
if (ObjC2ProtocolClass == 0)
{
ObjC2ProtocolClass = objc_getClass("Protocol2");
}
struct objc_protocol2 *oldProtocol =
struct objc_protocol *oldProtocol =
protocol_for_name(aProto->name);
if (NULL == oldProtocol)
{
@ -140,40 +175,38 @@ static struct objc_protocol2 *unique_protocol(struct objc_protocol2 *aProto)
}
}
static id protocol_class;
static id protocol_class2;
enum protocol_version
{
/**
* Legacy (GCC-compatible) protocol version.
*/
protocol_version_legacy = 2,
protocol_version_gcc = 2,
/**
* New (Objective-C 2-compatible) protocol version.
* GNUstep V1 ABI protocol.
*/
protocol_version_objc2 = 3
protocol_version_gsv1 = 3,
/**
* GNUstep V2 ABI protocol.
*/
protocol_version_gsv2 = 4
};
static BOOL init_protocols(struct objc_protocol_list *protocols)
{
// Protocol2 is a subclass of Protocol, so if we have loaded Protocol2 we
// must have also loaded Protocol.
if (nil == protocol_class2)
{
protocol_class = objc_getClass("Protocol");
protocol_class2 = objc_getClass("Protocol2");
}
if (nil == protocol_class2 || nil == protocol_class)
if (!init_protocol_classes())
{
return NO;
}
for (unsigned i=0 ; i<protocols->count ; i++)
{
struct objc_protocol2 *aProto = protocols->list[i];
struct objc_protocol *aProto = protocols->list[i];
// Don't initialise a protocol twice
if (aProto->isa == protocol_class ||
aProto->isa == protocol_class2) { continue ;}
if ((aProto->isa == protocol_class_gcc) ||
(aProto->isa == protocol_class_gsv2))
{
continue;
}
// Protocols in the protocol list have their class pointers set to the
// version of the protocol class that they expect.
@ -184,11 +217,17 @@ static BOOL init_protocols(struct objc_protocol_list *protocols)
default:
fprintf(stderr, "Unknown protocol version");
abort();
case protocol_version_legacy:
aProto->isa = protocol_class;
case protocol_version_gcc:
protocols->list[i] = aProto = objc_upgrade_protocol_gcc((struct objc_protocol_gcc *)aProto);
assert(aProto->isa == protocol_class_gcc);
break;
case protocol_version_gsv1:
protocols->list[i] = aProto = objc_upgrade_protocol_gsv1((struct objc_protocol_gsv1 *)aProto);
// The upgrade process should have done an in-place replacement.
assert(aProto->isa == protocol_class_gsv2);
break;
case protocol_version_objc2:
aProto->isa = protocol_class2;
case protocol_version_gsv2:
aProto->isa = protocol_class_gsv2;
break;
}
// Initialize all of the protocols that this protocol refers to
@ -282,12 +321,6 @@ get_method_list(Protocol *p,
BOOL isRequiredMethod,
BOOL isInstanceMethod)
{
static id protocol2 = NULL;
if (NULL == protocol2)
{
protocol2 = objc_getClass("Protocol2");
}
struct objc_method_description_list *list;
if (isRequiredMethod)
{
@ -302,16 +335,15 @@ get_method_list(Protocol *p,
}
else
{
if (p->isa != protocol2) { return NULL; }
if (!protocol_hasOptionalMethodsAndProperties(p)) { return NULL; }
if (isInstanceMethod)
{
list = ((Protocol2*)p)->optional_instance_methods;
list = p->optional_instance_methods;
}
else
{
list = ((Protocol2*)p)->optional_class_methods;
list = p->optional_class_methods;
}
}
return list;
@ -356,15 +388,14 @@ Protocol*__unsafe_unretained* protocol_copyProtocolList(Protocol *p, unsigned in
return out;
}
objc_property_t *protocol_copyPropertyList(Protocol *protocol,
objc_property_t *protocol_copyPropertyList(Protocol *p,
unsigned int *outCount)
{
if (NULL == protocol) { return NULL; }
if (protocol->isa != ObjC2ProtocolClass)
if (NULL == p) { return NULL; }
if (!protocol_hasOptionalMethodsAndProperties(p))
{
return NULL;
}
Protocol2 *p = (Protocol2*)protocol;
struct objc_property_list *properties = p->properties;
unsigned int count = 0;
if (NULL != properties)
@ -400,20 +431,19 @@ objc_property_t *protocol_copyPropertyList(Protocol *protocol,
return list;
}
objc_property_t protocol_getProperty(Protocol *protocol,
objc_property_t protocol_getProperty(Protocol *p,
const char *name,
BOOL isRequiredProperty,
BOOL isInstanceProperty)
{
if (NULL == protocol) { return NULL; }
if (NULL == p) { return NULL; }
// Class properties are not supported yet (there is no language syntax for
// defining them!)
if (!isInstanceProperty) { return NULL; }
if (protocol->isa != ObjC2ProtocolClass)
if (!protocol_hasOptionalMethodsAndProperties(p))
{
return NULL;
}
Protocol2 *p = (Protocol2*)protocol;
struct objc_property_list *properties =
isRequiredProperty ? p->properties : p->optional_properties;
while (NULL != properties)
@ -508,7 +538,9 @@ Protocol*__unsafe_unretained* objc_copyProtocolList(unsigned int *outCount)
Protocol *objc_allocateProtocol(const char *name)
{
if (objc_getProtocol(name) != NULL) { return NULL; }
Protocol *p = (Protocol*)class_createInstance((Class)incompleteProtocolClass(), 0);
// Create this as an object and add extra space at the end for the properties.
Protocol *p = (Protocol*)class_createInstance((Class)incompleteProtocolClass(),
sizeof(struct objc_protocol) - sizeof(id));
p->name = strdup(name);
return p;
}
@ -518,13 +550,16 @@ void objc_registerProtocol(Protocol *proto)
LOCK_RUNTIME_FOR_SCOPE();
if (objc_getProtocol(proto->name) != NULL) { return; }
if (incompleteProtocolClass() != proto->isa) { return; }
proto->isa = ObjC2ProtocolClass;
protocol_table_insert((struct objc_protocol2*)proto);
init_protocol_classes();
proto->isa = protocol_class_gsv2;
protocol_table_insert(proto);
}
PRIVATE void registerProtocol(Protocol *proto)
{
init_protocol_classes();
LOCK_RUNTIME_FOR_SCOPE();
protocol_table_insert((struct objc_protocol2*)proto);
proto->isa = protocol_class_gsv2;
protocol_table_insert(proto);
}
void protocol_addMethodDescription(Protocol *aProtocol,
SEL name,
@ -534,28 +569,27 @@ void protocol_addMethodDescription(Protocol *aProtocol,
{
if ((NULL == aProtocol) || (NULL == name) || (NULL == types)) { return; }
if (incompleteProtocolClass() != aProtocol->isa) { return; }
Protocol2 *proto = (Protocol2*)aProtocol;
struct objc_method_description_list **listPtr;
if (isInstanceMethod)
{
if (isRequiredMethod)
{
listPtr = &proto->instance_methods;
listPtr = &aProtocol->instance_methods;
}
else
{
listPtr = &proto->optional_instance_methods;
listPtr = &aProtocol->optional_instance_methods;
}
}
else
{
if (isRequiredMethod)
{
listPtr = &proto->class_methods;
listPtr = &aProtocol->class_methods;
}
else
{
listPtr = &proto->optional_class_methods;
listPtr = &aProtocol->optional_class_methods;
}
}
if (NULL == *listPtr)
@ -578,20 +612,19 @@ 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)
if (NULL == aProtocol->protocol_list)
{
proto->protocol_list = calloc(1, sizeof(struct objc_property_list) + sizeof(Protocol2*));
proto->protocol_list->count = 1;
aProtocol->protocol_list = calloc(1, sizeof(struct objc_property_list) + sizeof(Protocol*));
aProtocol->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;
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;
}
proto->protocol_list->list[proto->protocol_list->count-1] = (Protocol2*)addition;
aProtocol->protocol_list->list[aProtocol->protocol_list->count-1] = (Protocol*)addition;
}
void protocol_addProperty(Protocol *aProtocol,
const char *name,
@ -603,15 +636,14 @@ void protocol_addProperty(Protocol *aProtocol,
if ((NULL == aProtocol) || (NULL == name)) { return; }
if (incompleteProtocolClass() != aProtocol->isa) { return; }
if (!isInstanceProperty) { return; }
Protocol2 *proto = (Protocol2*)aProtocol;
struct objc_property_list **listPtr;
if (isRequiredProperty)
{
listPtr = &proto->properties;
listPtr = &aProtocol->properties;
}
else
{
listPtr = &proto->optional_properties;
listPtr = &aProtocol->optional_properties;
}
if (NULL == *listPtr)
{
@ -627,9 +659,7 @@ void protocol_addProperty(Protocol *aProtocol,
struct objc_property_list *list = *listPtr;
int index = list->count-1;
const char *iVarName = NULL;
struct objc_property p = propertyFromAttrs(attributes, attributeCount, &iVarName);
p.name = name;
constructPropertyAttributes(&p, iVarName);
struct objc_property p = propertyFromAttrs(attributes, attributeCount, name);
memcpy(&(list->properties[index]), &p, sizeof(p));
}

@ -1,3 +1,6 @@
#ifndef PROTOCOL_H_INCLUDED
#define PROTOCOL_H_INCLUDED
#include "selector.h"
#include <stdlib.h>
@ -15,22 +18,39 @@ struct objc_method_description_list
struct objc_selector methods[];
};
#ifdef __OBJC__
@interface Object { id isa; } @end
/**
* Definition of the Protocol type. Protocols are objects, but are rarely used
* as such.
*/
@interface Protocol : Object
{
@public
#else
struct objc_protocol
{
/**
* Redefinition of the superclass ivars in the C version.
*/
id isa;
char *name;
struct objc_protocol_list *protocol_list;
struct objc_method_description_list *instance_methods;
struct objc_method_description_list *class_methods;
/**
* Instance methods that are declared as optional for this protocol.
*/
struct objc_method_description_list *optional_instance_methods;
/**
* Class methods that are declared as optional for this protocol.
*/
struct objc_method_description_list *optional_class_methods;
/**
* Properties that are required by this protocol.
*/
struct objc_property_list *properties;
/**
* Optional properties.
*/
struct objc_property_list *optional_properties;
};
struct objc_protocol_gcc
{
/** Class pointer. */
id isa;
#endif
/**
* The name of this protocol. Two protocols are regarded as identical if
* they have the same name.
@ -48,29 +68,18 @@ struct objc_protocol
* List of class methods required by this protocol.
*/
struct objc_method_description_list *class_methods;
}
#ifdef __OBJC__
@end
#else
;
#endif
};
#ifdef __OBJC__
@interface Protocol2 : Protocol
{
@public
#else
typedef struct objc_protocol2
struct objc_protocol_gsv1
{
/**
* Redefinition of the superclass ivars in the C version.
* The first five ivars are shared with `objc_protocol_gcc`.
*/
id isa;
char *name;
struct objc_protocol_list *protocol_list;
struct objc_method_description_list *instance_methods;
struct objc_method_description_list *class_methods;
#endif
/**
* Instance methods that are declared as optional for this protocol.
*/
@ -82,16 +91,38 @@ typedef struct objc_protocol2
/**
* Properties that are required by this protocol.
*/
struct objc_property_list *properties;
struct objc_property_list_gsv1 *properties;
/**
* Optional properties.
*/
struct objc_property_list *optional_properties;
}
struct objc_property_list_gsv1 *optional_properties;
};
// Note: If you introduce a new protocol type that is larger than the current
// one then it's fine to auto-upgrade anything using the v2 ABI, because
// protocol structures there are referenced only via the indirection layer or
// via other runtime-managed structures.
//
// Auto-upgrading GNUstep v1 ABI protocols relies on their being the same size
// as v2, so the upgrade can happen in place. If this isn't possible, then we
// will need to add a new protocol class for v1 ABI struct and make sure that
// anything accessing the missing fields checks for this class before doing so.
_Static_assert(sizeof(struct objc_protocol_gsv1) == sizeof(struct objc_protocol),
"The V1 ABI protocol strcuture has a different size to the current AIB. "
"The auto-upgrader will not be able to do in-place replacement.");
#ifdef __OBJC__
@interface Object { id isa; } @end
/**
* Definition of the Protocol type. Protocols are objects, but are rarely used
* as such.
*/
@interface Protocol : Object
@end
#else
Protocol2;
@interface ProtocolGCC : Protocol
@end
#endif
/**
@ -117,6 +148,7 @@ struct objc_protocol_list
* On load, this contains direct references to other protocols and should
* be updated to point to the canonical (possibly upgraded) version.
*/
Protocol2 *list[];
struct objc_protocol *list[];
};
#endif // PROTOCOL_H_INCLUDED

@ -189,11 +189,11 @@ BOOL class_addProtocol(Class cls, Protocol *protocol)
CHECK_ARG(protocol);
if (class_conformsToProtocol(cls, protocol)) { return NO; }
struct objc_protocol_list *protocols =
malloc(sizeof(struct objc_protocol_list) + sizeof(Protocol2*));
malloc(sizeof(struct objc_protocol_list) + sizeof(Protocol*));
if (protocols == NULL) { return NO; }
protocols->next = cls->protocols;
protocols->count = 1;
protocols->list[0] = (Protocol2*)protocol;
protocols->list[0] = protocol;
cls->protocols = protocols;
return YES;

Loading…
Cancel
Save