2010-06-02 22:25:12 +00:00
|
|
|
#include "objc/runtime.h"
|
|
|
|
#include "protocol.h"
|
|
|
|
#include "properties.h"
|
2010-06-06 21:11:25 +00:00
|
|
|
#include "class.h"
|
2010-01-07 19:12:09 +00:00
|
|
|
#include "lock.h"
|
|
|
|
#include <stdlib.h>
|
2009-09-08 16:18:59 +00:00
|
|
|
|
2010-06-02 22:25:12 +00:00
|
|
|
#define BUFFER_TYPE struct objc_protocol_list
|
|
|
|
#include "buffer.h"
|
|
|
|
|
2010-01-07 19:12:09 +00:00
|
|
|
// Get the functions for string hashing
|
|
|
|
#include "string_hash.h"
|
2009-09-08 16:18:59 +00:00
|
|
|
|
2010-01-07 19:12:09 +00:00
|
|
|
static int protocol_compare(const char *name,
|
|
|
|
const struct objc_protocol2 *protocol)
|
2009-09-08 16:18:59 +00:00
|
|
|
{
|
2010-06-02 22:25:12 +00:00
|
|
|
return string_compare(name, protocol->name);
|
2010-01-07 19:12:09 +00:00
|
|
|
}
|
|
|
|
static int protocol_hash(const struct objc_protocol2 *protocol)
|
|
|
|
{
|
2010-06-02 22:25:12 +00:00
|
|
|
return string_hash(protocol->name);
|
2010-01-07 19:12:09 +00:00
|
|
|
}
|
|
|
|
#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"
|
2009-09-08 16:18:59 +00:00
|
|
|
|
2010-01-25 22:56:54 +00:00
|
|
|
static protocol_table *known_protocol_table;
|
2009-09-08 16:18:59 +00:00
|
|
|
|
|
|
|
void __objc_init_protocol_table(void)
|
|
|
|
{
|
2010-01-25 22:56:54 +00:00
|
|
|
known_protocol_table = protocol_create(128);
|
2009-09-08 16:18:59 +00:00
|
|
|
}
|
|
|
|
|
2010-01-07 19:12:09 +00:00
|
|
|
static void protocol_table_insert(const struct objc_protocol2 *protocol)
|
|
|
|
{
|
2010-01-25 22:56:54 +00:00
|
|
|
protocol_insert(known_protocol_table, (void*)protocol);
|
2010-01-07 19:12:09 +00:00
|
|
|
}
|
|
|
|
|
2010-06-02 22:25:12 +00:00
|
|
|
struct objc_protocol2 *protocol_for_name(const char *name)
|
2010-01-07 19:12:09 +00:00
|
|
|
{
|
2010-06-02 22:25:12 +00:00
|
|
|
return protocol_table_get(known_protocol_table, name);
|
2010-01-07 19:12:09 +00:00
|
|
|
}
|
|
|
|
|
2010-06-02 22:25:12 +00:00
|
|
|
static id ObjC2ProtocolClass = 0;
|
2009-09-08 16:18:59 +00:00
|
|
|
|
2010-01-07 19:12:09 +00:00
|
|
|
static int isEmptyProtocol(struct objc_protocol2 *aProto)
|
2009-09-08 16:18:59 +00:00
|
|
|
{
|
2010-01-25 23:34:00 +00:00
|
|
|
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));
|
2010-06-02 22:25:12 +00:00
|
|
|
if (aProto->isa == ObjC2ProtocolClass)
|
2009-09-08 16:18:59 +00:00
|
|
|
{
|
|
|
|
struct objc_protocol2 *p2 = (struct objc_protocol2*)aProto;
|
|
|
|
isEmpty &= (p2->optional_instance_methods->count == 0);
|
|
|
|
isEmpty &= (p2->optional_class_methods->count == 0);
|
2010-06-02 22:25:12 +00:00
|
|
|
isEmpty &= (p2->properties->count == 0);
|
|
|
|
isEmpty &= (p2->optional_properties->count == 0);
|
2009-09-08 16:18:59 +00:00
|
|
|
}
|
|
|
|
return isEmpty;
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: Make p1 adopt all of the stuff in p2
|
2010-01-07 19:12:09 +00:00
|
|
|
static void makeProtocolEqualToProtocol(struct objc_protocol2 *p1,
|
|
|
|
struct objc_protocol2 *p2)
|
|
|
|
{
|
|
|
|
#define COPY(x) p1->x = p2->x
|
|
|
|
COPY(instance_methods);
|
|
|
|
COPY(class_methods);
|
|
|
|
COPY(protocol_list);
|
2010-06-02 22:25:12 +00:00
|
|
|
if (p1->isa == ObjC2ProtocolClass &&
|
|
|
|
p2->isa == ObjC2ProtocolClass)
|
2010-01-07 19:12:09 +00:00
|
|
|
{
|
|
|
|
COPY(optional_instance_methods);
|
|
|
|
COPY(optional_class_methods);
|
|
|
|
COPY(properties);
|
|
|
|
COPY(optional_properties);
|
|
|
|
}
|
|
|
|
#undef COPY
|
|
|
|
}
|
2009-09-08 16:18:59 +00:00
|
|
|
|
2010-06-02 22:25:12 +00:00
|
|
|
static struct objc_protocol2 *unique_protocol(struct objc_protocol2 *aProto)
|
2009-09-08 16:18:59 +00:00
|
|
|
{
|
|
|
|
if (ObjC2ProtocolClass == 0)
|
|
|
|
{
|
2010-06-02 22:25:12 +00:00
|
|
|
ObjC2ProtocolClass = objc_getClass("Protocol2");
|
2009-09-08 16:18:59 +00:00
|
|
|
}
|
2010-01-07 19:12:09 +00:00
|
|
|
struct objc_protocol2 *oldProtocol =
|
2010-06-02 22:25:12 +00:00
|
|
|
protocol_for_name(aProto->name);
|
2009-09-08 16:18:59 +00:00
|
|
|
if (NULL == oldProtocol)
|
|
|
|
{
|
|
|
|
// This is the first time we've seen this protocol, so add it to the
|
|
|
|
// hash table and ignore it.
|
2010-01-07 19:12:09 +00:00
|
|
|
protocol_table_insert(aProto);
|
|
|
|
return aProto;
|
2009-09-08 16:18:59 +00:00
|
|
|
}
|
|
|
|
if (isEmptyProtocol(oldProtocol))
|
|
|
|
{
|
|
|
|
if (isEmptyProtocol(aProto))
|
|
|
|
{
|
2010-01-07 19:12:09 +00:00
|
|
|
return aProto;
|
2009-09-08 16:18:59 +00:00
|
|
|
// Add protocol to a list somehow.
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// This protocol is not empty, so we use its definitions
|
|
|
|
makeProtocolEqualToProtocol(oldProtocol, aProto);
|
2010-01-07 19:12:09 +00:00
|
|
|
return aProto;
|
2009-09-08 16:18:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (isEmptyProtocol(aProto))
|
|
|
|
{
|
|
|
|
makeProtocolEqualToProtocol(aProto, oldProtocol);
|
2010-01-07 19:12:09 +00:00
|
|
|
return oldProtocol;
|
2009-09-08 16:18:59 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-01-07 19:12:09 +00:00
|
|
|
return oldProtocol;
|
2009-09-08 16:18:59 +00:00
|
|
|
//FIXME: We should really perform a check here to make sure the
|
|
|
|
//protocols are actually the same.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-01-21 17:35:25 +00:00
|
|
|
|
2010-06-02 22:25:12 +00:00
|
|
|
static id protocol_class;
|
|
|
|
static id protocol_class2;
|
|
|
|
enum protocol_version
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Legacy (GCC-compatible) protocol version.
|
|
|
|
*/
|
|
|
|
protocol_version_legacy = 2,
|
|
|
|
/**
|
|
|
|
* New (Objective-C 2-compatible) protocol version.
|
|
|
|
*/
|
|
|
|
protocol_version_objc2 = 3
|
|
|
|
};
|
|
|
|
|
|
|
|
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");
|
|
|
|
}
|
2010-07-19 14:13:51 +00:00
|
|
|
if (nil == protocol_class2 || nil == protocol_class)
|
2010-06-02 22:25:12 +00:00
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (unsigned i=0 ; i<protocols->count ; i++)
|
|
|
|
{
|
|
|
|
struct objc_protocol2 *aProto = protocols->list[i];
|
|
|
|
// Don't initialise a protocol twice
|
|
|
|
if (aProto->isa == protocol_class ||
|
|
|
|
aProto->isa == protocol_class2) { 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();
|
|
|
|
case protocol_version_legacy:
|
|
|
|
aProto->isa = protocol_class;
|
|
|
|
break;
|
|
|
|
case protocol_version_objc2:
|
|
|
|
aProto->isa = protocol_class2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Initialize all of the protocols that this protocol refers to
|
2010-06-03 19:30:01 +00:00
|
|
|
if (NULL != aProto->protocol_list)
|
|
|
|
{
|
|
|
|
init_protocols(aProto->protocol_list);
|
|
|
|
}
|
2010-06-02 22:25:12 +00:00
|
|
|
// Replace this protocol with a unique version of it.
|
|
|
|
protocols->list[i] = unique_protocol(aProto);
|
|
|
|
}
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
void objc_init_protocols(struct objc_protocol_list *protocols)
|
|
|
|
{
|
|
|
|
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();
|
|
|
|
}
|
2010-01-21 17:35:25 +00:00
|
|
|
|
|
|
|
// Public functions:
|
|
|
|
Protocol *objc_getProtocol(const char *name)
|
|
|
|
{
|
|
|
|
return (Protocol*)protocol_for_name(name);
|
|
|
|
}
|
2010-01-25 23:34:00 +00:00
|
|
|
|
2010-06-06 21:11:25 +00:00
|
|
|
BOOL protocol_conformsToProtocol(Protocol *p1, Protocol *p2)
|
2010-01-25 23:34:00 +00:00
|
|
|
{
|
2010-06-06 21:11:25 +00:00
|
|
|
// A protocol trivially conforms to itself
|
|
|
|
if (strcmp(p1->name, p2->name) == 0) { return YES; }
|
2010-01-25 23:34:00 +00:00
|
|
|
|
2010-06-06 21:11:25 +00:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
while (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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cls = cls->super_class;
|
|
|
|
}
|
2010-01-25 23:34:00 +00:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2010-09-07 21:28:41 +00:00
|
|
|
static struct objc_method_description_list *
|
|
|
|
get_method_list(Protocol *p,
|
|
|
|
BOOL isRequiredMethod,
|
|
|
|
BOOL isInstanceMethod)
|
2010-01-25 23:34:00 +00:00
|
|
|
{
|
2010-09-07 20:55:26 +00:00
|
|
|
static id protocol2 = NULL;
|
|
|
|
|
|
|
|
if (NULL == protocol2)
|
|
|
|
{
|
|
|
|
protocol2 = objc_getClass("Protocol2");
|
|
|
|
}
|
|
|
|
struct objc_method_description_list *list;
|
|
|
|
if (isRequiredMethod)
|
|
|
|
{
|
|
|
|
if (isInstanceMethod)
|
|
|
|
{
|
|
|
|
list = p->instance_methods;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
list = p->class_methods;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (p->isa != protocol2) { return NULL; }
|
|
|
|
|
|
|
|
|
|
|
|
if (isInstanceMethod)
|
|
|
|
{
|
|
|
|
list = ((Protocol2*)p)->optional_instance_methods;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
list = ((Protocol2*)p)->optional_class_methods;
|
|
|
|
}
|
|
|
|
}
|
2010-09-07 21:28:41 +00:00
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
//FIXME!!!!
|
|
|
|
struct objc_method_description *protocol_copyMethodDescriptionList(Protocol *p,
|
|
|
|
BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *count)
|
|
|
|
{
|
|
|
|
|
|
|
|
struct objc_method_description_list *list =
|
|
|
|
get_method_list(p, isRequiredMethod, isInstanceMethod);
|
|
|
|
*count = 0;
|
2010-09-07 20:55:26 +00:00
|
|
|
if (NULL == list || list->count == 0) { return NULL; }
|
|
|
|
|
|
|
|
*count = list->count;
|
|
|
|
struct objc_method_description *out =
|
|
|
|
calloc(sizeof(struct objc_method_description_list), list->count);
|
|
|
|
|
|
|
|
for (int i=0 ; i<list->count ; i++)
|
|
|
|
{
|
|
|
|
out[i].name = sel_registerTypedName_np(list->methods[i].name,
|
|
|
|
list->methods[i].types);
|
|
|
|
out[i].types = list->methods[i].types;
|
|
|
|
}
|
|
|
|
return out;
|
2010-01-25 23:34:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Protocol **protocol_copyProtocolList(Protocol *p, unsigned int *count)
|
|
|
|
{
|
|
|
|
*count = 0;
|
2010-09-07 20:55:26 +00:00
|
|
|
if (p->protocol_list == NULL || p->protocol_list->count ==0)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
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];
|
|
|
|
}
|
2010-01-25 23:34:00 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2010-09-07 21:28:41 +00:00
|
|
|
struct objc_method_description
|
|
|
|
protocol_getMethodDescription(Protocol *p,
|
|
|
|
SEL aSel,
|
|
|
|
BOOL isRequiredMethod,
|
|
|
|
BOOL isInstanceMethod)
|
|
|
|
{
|
|
|
|
struct objc_method_description d = {0,0};
|
|
|
|
struct objc_method_description_list *list =
|
|
|
|
get_method_list(p, isRequiredMethod, isInstanceMethod);
|
|
|
|
if (NULL == list)
|
|
|
|
{
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
// TODO: We could make this much more efficient if
|
|
|
|
for (int i=0 ; i<list->count ; i++)
|
|
|
|
{
|
|
|
|
SEL s = sel_registerTypedName_np(list->methods[i].name, 0);
|
|
|
|
if (sel_isEqual(s, aSel))
|
|
|
|
{
|
|
|
|
d.name = s;
|
|
|
|
d.types = list->methods[i].types;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
2010-01-25 23:34:00 +00:00
|
|
|
|
|
|
|
const char *protocol_getName(Protocol *p)
|
|
|
|
{
|
|
|
|
if (NULL != p)
|
|
|
|
{
|
2010-06-02 22:25:12 +00:00
|
|
|
return p->name;
|
2010-01-25 23:34:00 +00:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL protocol_isEqual(Protocol *p, Protocol *other)
|
|
|
|
{
|
|
|
|
if (NULL == p || NULL == other)
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
if (p == other ||
|
2010-06-02 22:25:12 +00:00
|
|
|
p->name == other->name ||
|
|
|
|
0 == strcmp(p->name, other->name))
|
2010-01-25 23:34:00 +00:00
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|