|
|
|
|
@ -160,270 +160,6 @@ static void collectMethodsForMethodListToSparseArray(
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef __OBJC_LOW_MEMORY__
|
|
|
|
|
|
|
|
|
|
struct objc_dtable
|
|
|
|
|
{
|
|
|
|
|
struct cache_line
|
|
|
|
|
{
|
|
|
|
|
uint32_t idx;
|
|
|
|
|
uint32_t version;
|
|
|
|
|
struct objc_slot *slot;
|
|
|
|
|
} cache[8];
|
|
|
|
|
mutex_t lock;
|
|
|
|
|
struct objc_slot **slots;
|
|
|
|
|
int slot_count;
|
|
|
|
|
int slot_size;
|
|
|
|
|
Class cls;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void update_dtable(dtable_t dtable);
|
|
|
|
|
|
|
|
|
|
PRIVATE void init_dispatch_tables ()
|
|
|
|
|
{
|
|
|
|
|
INIT_LOCK(initialize_lock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Class class_getSuperclass(Class);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static dtable_t create_dtable_for_class(Class class, dtable_t root_dtable)
|
|
|
|
|
{
|
|
|
|
|
// Don't create a dtable for a class that already has one
|
|
|
|
|
if (classHasDtable(class)) { return dtable_for_class(class); }
|
|
|
|
|
|
|
|
|
|
LOCK_RUNTIME_FOR_SCOPE();
|
|
|
|
|
|
|
|
|
|
// Make sure that another thread didn't create the dtable while we were
|
|
|
|
|
// waiting on the lock.
|
|
|
|
|
if (classHasDtable(class)) { return dtable_for_class(class); }
|
|
|
|
|
|
|
|
|
|
// Allocate the dtable
|
|
|
|
|
dtable_t dtable = calloc(1, sizeof(struct objc_dtable));
|
|
|
|
|
dtable->cls = class;
|
|
|
|
|
INIT_LOCK(dtable->lock);
|
|
|
|
|
|
|
|
|
|
// Initialise it
|
|
|
|
|
update_dtable(dtable);
|
|
|
|
|
|
|
|
|
|
return dtable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PRIVATE void objc_resize_dtables(uint32_t newSize)
|
|
|
|
|
{
|
|
|
|
|
if (1<<dtable_depth > newSize) { return; }
|
|
|
|
|
dtable_depth <<= 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define HASH_UID(uid) ((uid >> 2) & 7)
|
|
|
|
|
|
|
|
|
|
static struct objc_slot* check_cache(dtable_t dtable, uint32_t uid)
|
|
|
|
|
{
|
|
|
|
|
int i = HASH_UID(uid);
|
|
|
|
|
volatile struct cache_line *cache = &dtable->cache[i];
|
|
|
|
|
int32_t initial_idx = cache->idx;
|
|
|
|
|
|
|
|
|
|
if (initial_idx != uid)
|
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct objc_slot *slot;
|
|
|
|
|
int32_t idx;
|
|
|
|
|
int32_t version;
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
initial_idx = cache->idx;
|
|
|
|
|
version = cache->version;
|
|
|
|
|
slot = cache->slot;
|
|
|
|
|
__sync_synchronize();
|
|
|
|
|
idx = cache->idx;
|
|
|
|
|
} while (idx != initial_idx);
|
|
|
|
|
|
|
|
|
|
return (idx == uid) && (slot->version == version) ? slot : NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct objc_slot *find_slot(uint32_t uid,
|
|
|
|
|
struct objc_slot **slots, int slot_count)
|
|
|
|
|
{
|
|
|
|
|
if (slot_count == 0) { return NULL; }
|
|
|
|
|
int idx = slot_count >> 1;
|
|
|
|
|
struct objc_slot *slot = slots[idx];
|
|
|
|
|
if (slot_count == 1)
|
|
|
|
|
{
|
|
|
|
|
if (slot->selector->index == uid)
|
|
|
|
|
{
|
|
|
|
|
return slot;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
if (slot->selector->index > uid)
|
|
|
|
|
{
|
|
|
|
|
return find_slot(uid, slots, idx);
|
|
|
|
|
}
|
|
|
|
|
if (slot->selector->index < uid)
|
|
|
|
|
{
|
|
|
|
|
return find_slot(uid, slots+idx, slot_count - idx);
|
|
|
|
|
}
|
|
|
|
|
if (slot->selector->index == uid)
|
|
|
|
|
{
|
|
|
|
|
return slot;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int slot_cmp(const void *l, const void *r)
|
|
|
|
|
{
|
|
|
|
|
return (*(struct objc_slot**)l)->selector->index
|
|
|
|
|
- (*(struct objc_slot**)r)->selector->index;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void insert_slot(dtable_t dtable, struct objc_slot *slot, uint32_t idx)
|
|
|
|
|
{
|
|
|
|
|
if (dtable->slot_size == dtable->slot_count)
|
|
|
|
|
{
|
|
|
|
|
dtable->slot_size += 16;
|
|
|
|
|
dtable->slots = realloc(dtable->slots, dtable->slot_size *
|
|
|
|
|
sizeof(struct objc_slot));
|
|
|
|
|
assert(NULL != dtable->slots && "Out of memory!");
|
|
|
|
|
}
|
|
|
|
|
dtable->slots[dtable->slot_count++] = slot;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void add_slot_to_dtable(SEL sel, dtable_t dtable, uint32_t
|
|
|
|
|
old_slot_count, struct objc_method *m, Class cls)
|
|
|
|
|
{
|
|
|
|
|
uint32_t idx = sel->index;
|
|
|
|
|
struct objc_slot *s = find_slot(idx, dtable->slots, old_slot_count);
|
|
|
|
|
if (NULL != s)
|
|
|
|
|
{
|
|
|
|
|
s->method = m->imp;
|
|
|
|
|
s->version++;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
struct objc_slot *slot = new_slot_for_method_in_class(m, cls);
|
|
|
|
|
slot->selector = sel;
|
|
|
|
|
insert_slot(dtable, slot, idx);
|
|
|
|
|
if (Nil != cls->super_class)
|
|
|
|
|
{
|
|
|
|
|
slot = objc_dtable_lookup(dtable_for_class(cls->super_class), idx);
|
|
|
|
|
if (NULL != slot)
|
|
|
|
|
{
|
|
|
|
|
slot->version++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
static void update_dtable(dtable_t dtable)
|
|
|
|
|
{
|
|
|
|
|
Class cls = dtable->cls;
|
|
|
|
|
|
|
|
|
|
if (NULL == cls->methods) { return; }
|
|
|
|
|
|
|
|
|
|
SparseArray *methods = SparseArrayNewWithDepth(dtable_depth);
|
|
|
|
|
collectMethodsForMethodListToSparseArray((void*)cls->methods, methods, YES);
|
|
|
|
|
|
|
|
|
|
if (NULL == dtable->slots)
|
|
|
|
|
{
|
|
|
|
|
dtable->slots = calloc(sizeof(struct objc_slot), 16);
|
|
|
|
|
dtable->slot_size = 16;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t old_slot_count = dtable->slot_count;
|
|
|
|
|
struct objc_method *m;
|
|
|
|
|
uint32_t idx = 0;
|
|
|
|
|
while ((m = SparseArrayNext(methods, &idx)))
|
|
|
|
|
{
|
|
|
|
|
add_slot_to_dtable(m->selector, dtable, old_slot_count, m, cls);
|
|
|
|
|
#ifdef TYPE_DEPENDENT_DISPATCH
|
|
|
|
|
add_slot_to_dtable(sel_getUntyped(m->selector), dtable, old_slot_count, m, cls);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
mergesort(dtable->slots, dtable->slot_count, sizeof(struct objc_slot*),
|
|
|
|
|
slot_cmp);
|
|
|
|
|
SparseArrayDestroy(methods);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PRIVATE void objc_update_dtable_for_class(Class cls)
|
|
|
|
|
{
|
|
|
|
|
dtable_t dtable = dtable_for_class(cls);
|
|
|
|
|
// Be lazy about constructing the slot list - don't do it unless we actually
|
|
|
|
|
// need to access it
|
|
|
|
|
if ((NULL == dtable) || (NULL == dtable->slots)) { return; }
|
|
|
|
|
|
|
|
|
|
LOCK_FOR_SCOPE(&dtable->lock);
|
|
|
|
|
|
|
|
|
|
update_dtable(dtable);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
PRIVATE void add_method_list_to_class(Class cls,
|
|
|
|
|
struct objc_method_list *list)
|
|
|
|
|
{
|
|
|
|
|
objc_update_dtable_for_class(cls);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PRIVATE struct objc_slot* objc_dtable_lookup(dtable_t dtable, uint32_t uid)
|
|
|
|
|
{
|
|
|
|
|
if (NULL == dtable) { return NULL; }
|
|
|
|
|
|
|
|
|
|
struct objc_slot *slot = check_cache(dtable, uid);
|
|
|
|
|
|
|
|
|
|
if (NULL != slot)
|
|
|
|
|
{
|
|
|
|
|
return slot;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCK_FOR_SCOPE(&dtable->lock);
|
|
|
|
|
if (NULL == dtable->slots)
|
|
|
|
|
{
|
|
|
|
|
update_dtable(dtable);
|
|
|
|
|
}
|
|
|
|
|
slot = find_slot(uid, dtable->slots, dtable->slot_count);
|
|
|
|
|
if (NULL != slot)
|
|
|
|
|
{
|
|
|
|
|
int i = HASH_UID(uid);
|
|
|
|
|
volatile struct cache_line *cache = &dtable->cache[i];
|
|
|
|
|
// Simplified multiword atomic exchange. First we write a value that
|
|
|
|
|
// is an invalid but recognisable UID and then a memory barrier. Then
|
|
|
|
|
// we complete the update and set the index pointer if and only if
|
|
|
|
|
// there have been no other modifications in the meantime
|
|
|
|
|
cache->idx = -uid;
|
|
|
|
|
__sync_synchronize();
|
|
|
|
|
cache->version = slot->version;
|
|
|
|
|
cache->slot = slot;
|
|
|
|
|
__sync_bool_compare_and_swap(&cache->idx, -uid, uid);
|
|
|
|
|
return slot;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (NULL != dtable->cls->super_class)
|
|
|
|
|
{
|
|
|
|
|
return objc_dtable_lookup(dtable_for_class(dtable->cls->super_class), uid);
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
PRIVATE dtable_t objc_copy_dtable_for_class(dtable_t old, Class cls)
|
|
|
|
|
{
|
|
|
|
|
dtable_t dtable = calloc(1, sizeof(struct objc_dtable));
|
|
|
|
|
dtable->cls = cls;
|
|
|
|
|
INIT_LOCK(dtable->lock);
|
|
|
|
|
return dtable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PRIVATE void free_dtable(dtable_t dtable)
|
|
|
|
|
{
|
|
|
|
|
if (NULL != dtable->slots)
|
|
|
|
|
{
|
|
|
|
|
free(dtable->slots);
|
|
|
|
|
}
|
|
|
|
|
DESTROY_LOCK(&dtable->lock);
|
|
|
|
|
free(dtable);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PRIVATE void init_dispatch_tables ()
|
|
|
|
|
{
|
|
|
|
|
INIT_LOCK(initialize_lock);
|
|
|
|
|
@ -750,8 +486,6 @@ PRIVATE void free_dtable(dtable_t dtable)
|
|
|
|
|
SparseArrayDestroy(dtable);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif // __OBJC_LOW_MEMORY__
|
|
|
|
|
|
|
|
|
|
LEGACY void update_dispatch_table_for_class(Class cls)
|
|
|
|
|
{
|
|
|
|
|
static BOOL warned = NO;
|
|
|
|
|
|