3 Star 0 Fork 0

Gitee 极速下载 / gnustep-objective-c-runtime

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
此仓库是为了提升国内下载速度的镜像仓库,每日同步一次。 原始仓库: https://github.com/gnustep/libobjc2
克隆/下载
protocol.c 18.42 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
#include "objc/runtime.h"
#include "protocol.h"
#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"
// Get the functions for string hashing
#include "string_hash.h"
static int protocol_compare(const char *name,
const struct objc_protocol *protocol)
{
return string_compare(name, protocol->name);
}
static int protocol_hash(const struct objc_protocol *protocol)
{
return string_hash(protocol->name);
}
#define MAP_TABLE_NAME protocol
#define MAP_TABLE_COMPARE_FUNCTION protocol_compare
#define MAP_TABLE_HASH_KEY string_hash
#define MAP_TABLE_HASH_VALUE protocol_hash
#include "hash_table.h"
static protocol_table *known_protocol_table;
mutex_t protocol_table_lock;
PRIVATE void init_protocol_table(void)
{
protocol_initialize(&known_protocol_table, 128);
INIT_LOCK(protocol_table_lock);
}
static void protocol_table_insert(const struct objc_protocol *protocol)
{
protocol_insert(known_protocol_table, (void*)protocol);
}
struct objc_protocol *protocol_for_name(const char *name)
{
return protocol_table_get(known_protocol_table, name);
}
static id incompleteProtocolClass(void)
{
static id IncompleteProtocolClass = 0;
if (IncompleteProtocolClass == nil)
{
IncompleteProtocolClass = objc_getClass("__IncompleteProtocol");
}
return IncompleteProtocolClass;
}
/**
* Class used for legacy GCC protocols (`ProtocolGCC`).
*/
static id protocol_class_gcc;
/**
* Class used for legacy GNUstep V1 ABI protocols (`ProtocolGSv1`).
*/
static id protocol_class_gsv1;
/**
* Class used for protocols (`Protocol`).
*/
static id protocol_class_gsv2;
static BOOL init_protocol_classes(void)
{
if (nil == protocol_class_gcc)
{
protocol_class_gcc = objc_getClass("ProtocolGCC");
}
if (nil == protocol_class_gsv1)
{
protocol_class_gsv1 = objc_getClass("ProtocolGSv1");
}
if (nil == protocol_class_gsv2)
{
protocol_class_gsv2 = objc_getClass("Protocol");
}
if ((nil == protocol_class_gcc) ||
(nil == protocol_class_gsv1) ||
(nil == protocol_class_gsv2))
{
return NO;
}
return YES;
}
static BOOL protocol_hasClassProperties(struct objc_protocol *p)
{
if (!init_protocol_classes())
{
return NO;
}
return p->isa == protocol_class_gsv2;
}
static BOOL protocol_hasOptionalMethodsAndProperties(struct objc_protocol *p)
{
if (!init_protocol_classes())
{
return NO;
}
if (p->isa == protocol_class_gcc)
{
return NO;
}
return YES;
}
static int isEmptyProtocol(struct objc_protocol *aProto)
{
int isEmpty =
((aProto->instance_methods == NULL) ||
(aProto->instance_methods->count == 0)) &&
((aProto->class_methods == NULL) ||
(aProto->class_methods->count == 0)) &&
((aProto->protocol_list == NULL) ||
(aProto->protocol_list->count == 0));
if (protocol_hasOptionalMethodsAndProperties(aProto))
{
isEmpty &= (aProto->optional_instance_methods == NULL) ||
(aProto->optional_instance_methods->count == 0);
isEmpty &= (aProto->optional_class_methods == NULL) ||
(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_protocol *p1,
struct objc_protocol *p2)
{
#define COPY(x) p1->x = p2->x
COPY(instance_methods);
COPY(class_methods);
COPY(protocol_list);
if (protocol_hasOptionalMethodsAndProperties(p1) &&
protocol_hasOptionalMethodsAndProperties(p2))
{
COPY(optional_instance_methods);
COPY(optional_class_methods);
COPY(properties);
COPY(optional_properties);
}
#undef COPY
}
static struct objc_protocol *unique_protocol(struct objc_protocol *aProto)
{
struct objc_protocol *oldProtocol =
protocol_for_name(aProto->name);
if (NULL == oldProtocol)
{
// This is the first time we've seen this protocol, so add it to the
// hash table and ignore it.
protocol_table_insert(aProto);
return aProto;
}
if (isEmptyProtocol(oldProtocol))
{
if (isEmptyProtocol(aProto))
{
return aProto;
// Add protocol to a list somehow.
}
else
{
// This protocol is not empty, so we use its definitions
makeProtocolEqualToProtocol(oldProtocol, aProto);
return aProto;
}
}
else
{
if (isEmptyProtocol(aProto))
{
makeProtocolEqualToProtocol(aProto, oldProtocol);
return oldProtocol;
}
else
{
return oldProtocol;
//FIXME: We should really perform a check here to make sure the
//protocols are actually the same.
}
}
}
static BOOL init_protocols(struct objc_protocol_list *protocols)
{
if (!init_protocol_classes())
{
return NO;
}
for (unsigned i=0 ; i<protocols->count ; i++)
{
struct objc_protocol *aProto = protocols->list[i];
// Don't initialise a protocol twice
if ((aProto->isa == protocol_class_gcc) ||
(aProto->isa == protocol_class_gsv1) ||
(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.
enum protocol_version version =
(enum protocol_version)(uintptr_t)aProto->isa;
switch (version)
{
default:
fprintf(stderr, "Unknown protocol version");
abort();
#ifdef OLDABI_COMPAT
case protocol_version_gcc:
protocols->list[i] = objc_upgrade_protocol_gcc((struct objc_protocol_gcc *)aProto);
assert(aProto->isa == protocol_class_gcc);
assert(protocols->list[i]->isa == protocol_class_gsv2);
aProto = protocols->list[i];
break;
case protocol_version_gsv1:
protocols->list[i] = objc_upgrade_protocol_gsv1((struct objc_protocol_gsv1 *)aProto);
assert(aProto->isa == protocol_class_gsv1);
assert(protocols->list[i]->isa == protocol_class_gsv2);
aProto = protocols->list[i];
break;
#endif
case protocol_version_gsv2:
aProto->isa = protocol_class_gsv2;
break;
}
// Initialize all of the protocols that this protocol refers to
if (NULL != aProto->protocol_list)
{
init_protocols(aProto->protocol_list);
}
// Replace this protocol with a unique version of it.
protocols->list[i] = unique_protocol(aProto);
}
return YES;
}
PRIVATE void objc_init_protocols(struct objc_protocol_list *protocols)
{
LOCK_FOR_SCOPE(&protocol_table_lock);
if (!init_protocols(protocols))
{
set_buffered_object_at_index(protocols, buffered_objects++);
return;
}
if (buffered_objects == 0) { return; }
// If we can load one protocol, then we can load all of them.
for (unsigned i=0 ; i<buffered_objects ; i++)
{
struct objc_protocol_list *c = buffered_object_at_index(i);
if (NULL != c)
{
init_protocols(c);
set_buffered_object_at_index(NULL, i);
}
}
compact_buffer();
}
// Public functions:
Protocol *objc_getProtocol(const char *name)
{
if (NULL == name) { return NULL; }
LOCK_FOR_SCOPE(&protocol_table_lock);
return (Protocol*)protocol_for_name(name);
}
BOOL protocol_conformsToProtocol(Protocol *p1, Protocol *p2)
{
if (NULL == p1 || NULL == p2) { return NO; }
// A protocol trivially conforms to itself
if (strcmp(p1->name, p2->name) == 0) { return YES; }
for (struct objc_protocol_list *list = p1->protocol_list ;
list != NULL ; list = list->next)
{
for (int i=0 ; i<list->count ; i++)
{
if (strcmp(list->list[i]->name, p2->name) == 0)
{
return YES;
}
if (protocol_conformsToProtocol((Protocol*)list->list[i], p2))
{
return YES;
}
}
}
return NO;
}
BOOL class_conformsToProtocol(Class cls, Protocol *protocol)
{
if (Nil == cls || NULL == protocol) { return NO; }
for ( ; Nil != cls ; cls = class_getSuperclass(cls))
{
for (struct objc_protocol_list *protocols = cls->protocols;
protocols != NULL ; protocols = protocols->next)
{
for (int i=0 ; i<protocols->count ; i++)
{
Protocol *p1 = (Protocol*)protocols->list[i];
if (protocol_conformsToProtocol(p1, protocol))
{
return YES;
}
}
}
}
return NO;
}
static struct objc_protocol_method_description_list *
get_method_list(Protocol *p,
BOOL isRequiredMethod,
BOOL isInstanceMethod)
{
struct objc_protocol_method_description_list *list;
if (isRequiredMethod)
{
if (isInstanceMethod)
{
list = p->instance_methods;
}
else
{
list = p->class_methods;
}
}
else
{
if (!protocol_hasOptionalMethodsAndProperties(p)) { return NULL; }
if (isInstanceMethod)
{
list = p->optional_instance_methods;
}
else
{
list = p->optional_class_methods;
}
}
return list;
}
struct objc_method_description *protocol_copyMethodDescriptionList(Protocol *p,
BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *count)
{
if ((NULL == p) || (NULL == count)){ return NULL; }
struct objc_protocol_method_description_list *list =
get_method_list(p, isRequiredMethod, isInstanceMethod);
*count = 0;
if (NULL == list || list->count == 0) { return NULL; }
*count = list->count;
struct objc_method_description *out =
calloc(sizeof(struct objc_method_description), list->count);
for (int i=0 ; i < (list->count) ; i++)
{
out[i].name = protocol_method_at_index(list, i)->selector;
out[i].types = sel_getType_np(protocol_method_at_index(list, i)->selector);
}
return out;
}
Protocol*__unsafe_unretained* protocol_copyProtocolList(Protocol *p, unsigned int *count)
{
if (NULL == p) { return NULL; }
*count = 0;
if (p->protocol_list == NULL || p->protocol_list->count ==0)
{
return NULL;
}
*count = p->protocol_list->count;
Protocol **out = calloc(sizeof(Protocol*), p->protocol_list->count);
for (int i=0 ; i<p->protocol_list->count ; i++)
{
out[i] = (Protocol*)p->protocol_list->list[i];
}
return out;
}
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 it's an old protocol, it won't have any of the other options.
if (!isRequiredProperty && !isInstanceProperty &&
!protocol_hasOptionalMethodsAndProperties(p))
{
return NULL;
}
if (properties == NULL)
{
return NULL;
}
unsigned int count = 0;
for (struct objc_property_list *l=properties ; l!=NULL ; l=l->next)
{
count += l->count;
}
if (0 == count)
{
return NULL;
}
objc_property_t *list = calloc(sizeof(objc_property_t), count);
unsigned int out = 0;
for (struct objc_property_list *l=properties ; l!=NULL ; l=l->next)
{
for (int i=0 ; i<l->count ; 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,
BOOL isInstanceProperty)
{
if (NULL == p) { return NULL; }
if (!protocol_hasOptionalMethodsAndProperties(p))
{
return NULL;
}
if (!isInstanceProperty && !protocol_hasClassProperties(p))
{
return NULL;
}
struct objc_property_list *properties =
isInstanceProperty ?
(isRequiredProperty ? p->properties : p->optional_properties) :
(isRequiredProperty ? p->class_properties : p->optional_class_properties);
while (NULL != properties)
{
for (int i=0 ; i<properties->count ; i++)
{
objc_property_t prop = property_at_index(properties, i);
if (strcmp(property_getName(prop), name) == 0)
{
return prop;
}
}
properties = properties->next;
}
return NULL;
}
static struct objc_protocol_method_description *
get_method_description(Protocol *p,
SEL aSel,
BOOL isRequiredMethod,
BOOL isInstanceMethod)
{
struct objc_protocol_method_description_list *list =
get_method_list(p, isRequiredMethod, isInstanceMethod);
if (NULL == list)
{
return NULL;
}
for (int i=0 ; i<list->count ; i++)
{
SEL s = protocol_method_at_index(list, i)->selector;
if (sel_isEqual(s, aSel))
{
return protocol_method_at_index(list, i);
}
}
return NULL;
}
struct objc_method_description
protocol_getMethodDescription(Protocol *p,
SEL aSel,
BOOL isRequiredMethod,
BOOL isInstanceMethod)
{
struct objc_method_description d = {0,0};
struct objc_protocol_method_description *m =
get_method_description(p, aSel, isRequiredMethod, isInstanceMethod);
if (m != NULL)
{
SEL s = m->selector;
d.name = s;
d.types = sel_getType_np(s);
}
return d;
}
const char *_protocol_getMethodTypeEncoding(Protocol *p,
SEL aSel,
BOOL isRequiredMethod,
BOOL isInstanceMethod)
{
struct objc_protocol_method_description *m =
get_method_description(p, aSel, isRequiredMethod, isInstanceMethod);
if (m != NULL)
{
return m->types;
}
return NULL;
}
const char *protocol_getName(Protocol *p)
{
if (NULL != p)
{
return p->name;
}
return NULL;
}
BOOL protocol_isEqual(Protocol *p, Protocol *other)
{
if (NULL == p || NULL == other)
{
return NO;
}
if (p == other ||
p->name == other->name ||
0 == strcmp(p->name, other->name))
{
return YES;
}
return NO;
}
Protocol*__unsafe_unretained* objc_copyProtocolList(unsigned int *outCount)
{
LOCK_FOR_SCOPE(&protocol_table_lock);
unsigned int total = known_protocol_table->table_used;
Protocol **p = calloc(sizeof(Protocol*), known_protocol_table->table_used);
struct protocol_table_enumerator *e = NULL;
Protocol *next;
unsigned int count = 0;
while ((count < total) && (next = protocol_next(known_protocol_table, &e)))
{
p[count++] = next;
}
if (NULL != outCount)
{
*outCount = total;
}
return p;
}
Protocol *objc_allocateProtocol(const char *name)
{
if (objc_getProtocol(name) != NULL) { return NULL; }
// 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;
}
void objc_registerProtocol(Protocol *proto)
{
if (NULL == proto) { return; }
LOCK_FOR_SCOPE(&protocol_table_lock);
if (objc_getProtocol(proto->name) != NULL) { return; }
if (incompleteProtocolClass() != proto->isa) { return; }
init_protocol_classes();
proto->isa = protocol_class_gsv2;
protocol_table_insert(proto);
}
PRIVATE void registerProtocol(Protocol *proto)
{
init_protocol_classes();
LOCK_FOR_SCOPE(&protocol_table_lock);
proto->isa = protocol_class_gsv2;
if (protocol_for_name(proto->name) == NULL)
{
protocol_table_insert(proto);
}
}
void protocol_addMethodDescription(Protocol *aProtocol,
SEL name,
const char *types,
BOOL isRequiredMethod,
BOOL isInstanceMethod)
{
if ((NULL == aProtocol) || (NULL == name) || (NULL == types)) { return; }
if (incompleteProtocolClass() != aProtocol->isa) { return; }
struct objc_protocol_method_description_list **listPtr;
if (isInstanceMethod)
{
if (isRequiredMethod)
{
listPtr = &aProtocol->instance_methods;
}
else
{
listPtr = &aProtocol->optional_instance_methods;
}
}
else
{
if (isRequiredMethod)
{
listPtr = &aProtocol->class_methods;
}
else
{
listPtr = &aProtocol->optional_class_methods;
}
}
if (NULL == *listPtr)
{
// FIXME: Factor this out, we do the same thing in multiple places.
*listPtr = calloc(1, sizeof(struct objc_protocol_method_description_list) +
sizeof(struct objc_protocol_method_description));
(*listPtr)->count = 1;
(*listPtr)->size = sizeof(struct objc_protocol_method_description);
}
else
{
(*listPtr)->count++;
*listPtr = realloc(*listPtr, sizeof(struct objc_protocol_method_description_list) +
sizeof(struct objc_protocol_method_description) * (*listPtr)->count);
}
struct objc_protocol_method_description_list *list = *listPtr;
int index = list->count-1;
protocol_method_at_index(list, index)->selector = sel_registerTypedName_np(sel_getName(name), types);
protocol_method_at_index(list, index)->types = types;
}
void protocol_addProtocol(Protocol *aProtocol, Protocol *addition)
{
if ((NULL == aProtocol) || (NULL == addition)) { return; }
if (incompleteProtocolClass() != aProtocol->isa) { return; }
if (NULL == aProtocol->protocol_list)
{
aProtocol->protocol_list = calloc(1, sizeof(struct objc_property_list) + sizeof(Protocol*));
aProtocol->protocol_list->count = 1;
}
else
{
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->list[aProtocol->protocol_list->count-1] = (Protocol*)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 (incompleteProtocolClass() != aProtocol->isa) { return; }
if (!isInstanceProperty) { return; }
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));
(*listPtr)->size = 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;
struct objc_property p = propertyFromAttrs(attributes, attributeCount, name);
assert(list->size == sizeof(p));
memcpy(&(list->properties[index]), &p, sizeof(p));
}
C/C++
1
https://gitee.com/mirrors/gnustep-objective-c-runtime.git
git@gitee.com:mirrors/gnustep-objective-c-runtime.git
mirrors
gnustep-objective-c-runtime
gnustep-objective-c-runtime
master

搜索帮助