Added support for type-dependent dispatch to libobjc2.

main
theraven 16 years ago
parent 79899f8a46
commit 0e374db1db

@ -47,7 +47,7 @@ void objc_send_load_message(Class class)
for (int i=0 ; i<l->count ; i++) for (int i=0 ; i<l->count ; i++)
{ {
Method m = &l->methods[i]; Method m = &l->methods[i];
if (m->selector->name == loadSel->name) if (sel_isEqual(m->selector, loadSel))
{ {
if (load_messages_table_get(load_table, m->imp) == 0) if (load_messages_table_get(load_table, m->imp) == 0)
{ {

@ -84,6 +84,11 @@ static BOOL installMethodInDtable(Class class,
//fprintf(stderr, "Installing method %p (%d) %s in %s (previous slot owned by %s)\n", method->imp, sel_id, sel_getName(method->selector), class->name, slot? oldSlot->owner->name: "(no one)"); //fprintf(stderr, "Installing method %p (%d) %s in %s (previous slot owned by %s)\n", method->imp, sel_id, sel_getName(method->selector), class->name, slot? oldSlot->owner->name: "(no one)");
slot = new_slot_for_method_in_class((void*)method, owner); slot = new_slot_for_method_in_class((void*)method, owner);
SparseArrayInsert(dtable, sel_id, slot); SparseArrayInsert(dtable, sel_id, slot);
// In TDD mode, we also register the first typed method that we
// encounter as the untyped version.
#ifdef TYPE_DEPENDENT_DISPATCH
SparseArrayInsert(dtable, get_untyped_idx(method->selector), slot);
#endif
// Invalidate the old slot, if there is one. // Invalidate the old slot, if there is one.
if (NULL != oldSlot) if (NULL != oldSlot)
{ {

@ -26,4 +26,12 @@ struct objc_selector
const char * types; const char * types;
}; };
__attribute__((unused))
static uint32_t get_untyped_idx(SEL aSel)
{
SEL untyped = sel_registerTypedName_np(sel_getName(aSel), 0);
return (uint32_t)(uintptr_t)untyped->name;
}
#endif // OBJC_SELECTOR_H_INCLUDED #endif // OBJC_SELECTOR_H_INCLUDED

@ -7,6 +7,7 @@
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <assert.h> #include <assert.h>
#include <ctype.h>
#include "lock.h" #include "lock.h"
#include "sarray2.h" #include "sarray2.h"
#include "objc/runtime.h" #include "objc/runtime.h"
@ -51,6 +52,54 @@ inline static BOOL isSelRegistered(SEL sel)
return NO; return NO;
} }
/**
* Skip anything in a type encoding that is irrelevant to the comparison
* between selectors, including type qualifiers and argframe info.
*/
static const char *skip_irrelevant_type_info(const char *t)
{
switch (*t)
{
default: return t;
case 'r': case 'n': case 'N': case 'o': case 'O': case 'R':
case 'V': case '!': case '0'...'9':
return skip_irrelevant_type_info(t+1);
}
}
#ifdef TYPE_DEPENDENT_DISPATCH
static BOOL selector_types_equal(const char *t1, const char *t2)
{
if (t1 == NULL || t2 == NULL) { return t1 == t2; }
while (('\0' != *t1) && ('\0' != *t1))
{
t1 = skip_irrelevant_type_info(t1);
t2 = skip_irrelevant_type_info(t2);
if (*t1 != *t2)
{
return NO;
}
if ('\0' != *t1) { t1++; }
if ('\0' != *t2) { t2++; }
}
return YES;
}
#endif
#ifdef TYPE_DEPENDENT_DISPATCH
static BOOL selector_types_equivalent(const char *t1, const char *t2)
{
// We always treat untyped selectors as having the same type as typed
// selectors, for dispatch purposes.
if (t1 == NULL || t2 == NULL) { return YES; }
return selector_types_equal(t1, t2);
}
#endif
/** /**
* Compare selectors based on whether they are treated as equivalent for the * Compare selectors based on whether they are treated as equivalent for the
* purpose of dispatch. * purpose of dispatch.
@ -58,31 +107,23 @@ inline static BOOL isSelRegistered(SEL sel)
static int selector_equal(const void *k, static int selector_equal(const void *k,
const SEL value) const SEL value)
{ {
#ifdef TYPE_DEPENDENT_DISPATCH
return selector_identical(key, value);
#else
SEL key = (SEL)k; SEL key = (SEL)k;
return string_compare(sel_getName(key), sel_getName(value)) TDD(&& return string_compare(sel_getName(key), sel_getName(value));
string_compare(sel_getType_np(key), sel_getType_np(value))); #endif
} }
/** /**
* Compare whether two selectors are identical. * Compare whether two selectors are identical.
*/ */
static int selector_identical(const SEL key, static int selector_identical(const void *k,
const SEL value) const SEL value)
{ {
SEL key = (SEL)k;
return string_compare(sel_getName(key), sel_getName(value)) && return string_compare(sel_getName(key), sel_getName(value)) &&
string_compare(sel_getType_np(key), sel_getType_np(value)); string_compare(sel_getType_np(key), sel_getType_np(value));
} }
static inline uint32_t addStringToHash(uint32_t hash, const char *str)
{
uint32_t c;
if(str != NULL)
{
while((c = (uint32_t)*str++))
{
hash = hash * 33 + c;
}
}
return hash;
}
/** /**
* Hash a selector. * Hash a selector.
*/ */
@ -90,13 +131,32 @@ static inline uint32_t hash_selector(const void *s)
{ {
SEL sel = (SEL)s; SEL sel = (SEL)s;
uint32_t hash = 5381; uint32_t hash = 5381;
hash = addStringToHash(hash, sel_getName(sel)); const char *str = sel_getName(sel);
hash = addStringToHash(hash, sel->types); uint32_t c;
while((c = (uint32_t)*str++))
{
hash = hash * 33 + c;
}
#ifdef TYPE_DEPENDENT_DISPATCH
str = sel_getType_np(sel);
if (NULL != str)
{
while (*str != '\0')
{
str = skip_irrelevant_type_info(str);
if (*str != '\0')
{
hash = hash * 33 + (uint32_t)*str;
}
str++;
}
}
#endif //TYPE_DEPENDENT_DISPATCH
return hash; return hash;
} }
#define MAP_TABLE_NAME selector #define MAP_TABLE_NAME selector
#define MAP_TABLE_COMPARE_FUNCTION selector_equal #define MAP_TABLE_COMPARE_FUNCTION selector_identical
#define MAP_TABLE_HASH_KEY hash_selector #define MAP_TABLE_HASH_KEY hash_selector
#define MAP_TABLE_HASH_VALUE hash_selector #define MAP_TABLE_HASH_VALUE hash_selector
#include "hash_table.h" #include "hash_table.h"
@ -134,6 +194,7 @@ static SEL selector_lookup(const char *name, const char *types)
} }
static inline void add_selector_to_table(SEL aSel, int32_t uid, uint32_t idx) static inline void add_selector_to_table(SEL aSel, int32_t uid, uint32_t idx)
{ {
//fprintf(stderr, "Sel %s uid: %d, idx: %d, hash: %d\n", sel_getName(aSel), uid, idx, hash_selector(aSel));
struct sel_type_list *typeList = struct sel_type_list *typeList =
(struct sel_type_list *)selector_pool_alloc(); (struct sel_type_list *)selector_pool_alloc();
typeList->value = aSel->name; typeList->value = aSel->name;
@ -153,6 +214,7 @@ static inline void register_selector_locked(SEL aSel)
uintptr_t idx = selector_count++; uintptr_t idx = selector_count++;
if (NULL == aSel->types) if (NULL == aSel->types)
{ {
//fprintf(stderr, "Registering selector %d %s\n", idx, sel_getName(aSel));
add_selector_to_table(aSel, idx, idx); add_selector_to_table(aSel, idx, idx);
objc_resize_dtables(selector_count); objc_resize_dtables(selector_count);
return; return;
@ -164,6 +226,7 @@ static inline void register_selector_locked(SEL aSel)
untyped = selector_pool_alloc(); untyped = selector_pool_alloc();
untyped->name = aSel->name; untyped->name = aSel->name;
untyped->types = 0; untyped->types = 0;
//fprintf(stderr, "Registering selector %d %s\n", idx, sel_getName(aSel));
add_selector_to_table(untyped, idx, idx); add_selector_to_table(untyped, idx, idx);
// If we are in type dependent dispatch mode, the uid for the typed // If we are in type dependent dispatch mode, the uid for the typed
// and untyped versions will be different // and untyped versions will be different
@ -171,6 +234,7 @@ static inline void register_selector_locked(SEL aSel)
} }
uintptr_t uid = (uintptr_t)untyped->name; uintptr_t uid = (uintptr_t)untyped->name;
TDD(uid = idx); TDD(uid = idx);
//fprintf(stderr, "Registering typed selector %d %s\n", uid, sel_getName(aSel));
add_selector_to_table(aSel, uid, idx); add_selector_to_table(aSel, uid, idx);
// Add this set of types to the list. // Add this set of types to the list.
@ -196,7 +260,7 @@ static SEL objc_register_selector(SEL aSel)
} }
// Check that this isn't already registered, before we try // Check that this isn't already registered, before we try
SEL registered = selector_lookup(aSel->name, aSel->types); SEL registered = selector_lookup(aSel->name, aSel->types);
if (NULL != registered && (selector_identical(aSel, registered) || NULL == aSel->types)) if (NULL != registered && selector_equal(aSel, registered))
{ {
aSel->name = registered->name; aSel->name = registered->name;
return registered; return registered;
@ -214,18 +278,24 @@ static SEL objc_register_selector_copy(SEL aSel)
{ {
// If an identical selector is already registered, return it. // If an identical selector is already registered, return it.
SEL copy = selector_lookup(aSel->name, aSel->types); SEL copy = selector_lookup(aSel->name, aSel->types);
if (NULL != copy && (selector_identical(aSel, copy) || NULL == aSel->types)) //fprintf(stderr, "Checking if old selector is registered: %d (%d)\n", NULL != copy ? selector_equal(aSel, copy) : 0, ((NULL != copy) && selector_equal(aSel, copy)));
if ((NULL != copy) && selector_identical(aSel, copy))
{
//fprintf(stderr, "Not adding new copy\n");
return copy;
}
LOCK_UNTIL_RETURN(&selector_table_lock);
copy = selector_lookup(aSel->name, aSel->types);
if (NULL != copy && selector_identical(aSel, copy))
{ {
return copy; return copy;
} }
LOCK(&selector_table_lock);
// Create a copy of this selector. // Create a copy of this selector.
copy = selector_pool_alloc(); copy = selector_pool_alloc();
copy->name = strdup(aSel->name); copy->name = strdup(aSel->name);
copy->types = (NULL == aSel->types) ? NULL : strdup(aSel->types); copy->types = (NULL == aSel->types) ? NULL : strdup(aSel->types);
// Try to register the copy as the authoritative version // Try to register the copy as the authoritative version
register_selector_locked(copy); register_selector_locked(copy);
UNLOCK(&selector_table_lock);
return copy; return copy;
} }
@ -256,7 +326,14 @@ SEL sel_getUid(const char *selName)
BOOL sel_isEqual(SEL sel1, SEL sel2) BOOL sel_isEqual(SEL sel1, SEL sel2)
{ {
return selector_equal(sel1, sel2); if (sel1->name == sel2->name)
{
return YES;
}
// Otherwise, do a slow compare
return string_compare(sel_getName(sel1), sel_getName(sel2)) TDD(&&
(sel1->types == NULL || sel2->types == NULL ||
selector_types_equivalent(sel_getType_np(sel1), sel_getType_np(sel2))));
} }
SEL sel_registerName(const char *selName) SEL sel_registerName(const char *selName)

@ -52,6 +52,15 @@ static inline Slot_t objc_msg_lookup_internal(id *receiver,
} }
if (0 == result) if (0 == result)
{ {
if ((result = SparseArrayLookup(dtable, get_untyped_idx(selector))))
{
fprintf(stderr, "Calling %s with incorrect signature. "
"Method has %s, selector has %s\n",
sel_getName(selector),
result->types,
sel_getType_np(selector));
return result;
}
id newReceiver = objc_proxy_lookup(*receiver, selector); id newReceiver = objc_proxy_lookup(*receiver, selector);
// If some other library wants us to play forwarding games, try again // If some other library wants us to play forwarding games, try again
// with the new object. // with the new object.
@ -235,6 +244,18 @@ Slot_t objc_get_slot(Class cls, SEL selector)
// weren't looking // weren't looking
result = SparseArrayLookup(dtable, PTR_TO_IDX(selector->name)); result = SparseArrayLookup(dtable, PTR_TO_IDX(selector->name));
} }
if (NULL == result)
{
if ((result = SparseArrayLookup(dtable, get_untyped_idx(selector))))
{
fprintf(stderr, "Calling %s with incorrect signature. "
"Method has %s, selector has %s\n",
sel_getName(selector),
result->types,
sel_getType_np(selector));
return result;
}
}
} }
return result; return result;
} }

@ -26,7 +26,7 @@ static int string_compare(const char *str1, const char *str2)
{ {
return 1; return 1;
} }
if (str2 == NULL) if (str1 == NULL || str2 == NULL)
{ {
return 0; return 0;
} }

Loading…
Cancel
Save