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