mirror of
https://github.com/darlinghq/darling-libobjc2.git
synced 2024-11-27 14:10:33 +00:00
273 lines
7.5 KiB
C
273 lines
7.5 KiB
C
#include "sarray2.h"
|
|
#define isa class_pointer
|
|
#include "class.h"
|
|
#include "lock.h"
|
|
#define objc_method_list method_list_new
|
|
#define objc_method method_new
|
|
#include "method_list.h"
|
|
|
|
SparseArray *__objc_uninstalled_dtable;
|
|
|
|
typedef struct _InitializingDtable
|
|
{
|
|
Class class;
|
|
void *dtable;
|
|
struct _InitializingDtable *next;
|
|
} InitializingDtable;
|
|
|
|
/** Protected by initialize_lock */
|
|
InitializingDtable *temporary_dtables;
|
|
|
|
static SparseArray *create_dtable_for_class (Class class);
|
|
|
|
static uint32_t dtable_depth = 8;
|
|
|
|
void __objc_init_dispatch_tables ()
|
|
{
|
|
INIT_LOCK(initialize_lock);
|
|
__objc_uninstalled_dtable = SparseArrayNewWithDepth(dtable_depth);
|
|
}
|
|
|
|
static inline int classHasInstalledDtable(struct objc_class *cls)
|
|
{
|
|
return ((void*)cls->dtable != __objc_uninstalled_dtable);
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the dtable for a given class. If we are currently in an +initialize
|
|
* method then this will block if called from a thread other than the one
|
|
* running the +initialize method.
|
|
*/
|
|
static inline SparseArray *dtable_for_class(Class cls)
|
|
{
|
|
if (classHasInstalledDtable(cls))
|
|
{
|
|
return (SparseArray*)cls->dtable;
|
|
}
|
|
LOCK_UNTIL_RETURN(&initialize_lock);
|
|
if (classHasInstalledDtable(cls))
|
|
{
|
|
return (SparseArray*)cls->dtable;
|
|
}
|
|
/* This is a linear search, and so, in theory, could be very slow. It is
|
|
* O(n) where n is the number of +initialize methods on the stack. In
|
|
* practice, this is a very small number. Profiling with GNUstep showed that
|
|
* this peaks at 8. */
|
|
SparseArray *dtable = __objc_uninstalled_dtable;
|
|
InitializingDtable *buffer = temporary_dtables;
|
|
while (NULL != buffer)
|
|
{
|
|
if (buffer->class == cls)
|
|
{
|
|
dtable = (SparseArray*)buffer->dtable;
|
|
break;
|
|
}
|
|
buffer = buffer->next;
|
|
}
|
|
UNLOCK(&initialize_lock);
|
|
if (dtable == 0)
|
|
{
|
|
dtable = __objc_uninstalled_dtable;
|
|
}
|
|
return dtable;
|
|
}
|
|
|
|
static inline int classHasDtable(struct objc_class *cls)
|
|
{
|
|
return (dtable_for_class(cls) != __objc_uninstalled_dtable);
|
|
}
|
|
|
|
static void collectMethodsForMethodListToSparseArray(
|
|
struct objc_method_list *list,
|
|
SparseArray *sarray)
|
|
{
|
|
if (NULL != list->next)
|
|
{
|
|
collectMethodsForMethodListToSparseArray(list->next, sarray);
|
|
}
|
|
for (unsigned i=0 ; i<list->count ; i++)
|
|
{
|
|
SparseArrayInsert(sarray, PTR_TO_IDX(list->methods[i].selector->sel_id),
|
|
(void*)&list->methods[i]);
|
|
}
|
|
}
|
|
|
|
static BOOL installMethodInDtable(Class class,
|
|
SparseArray *dtable,
|
|
struct objc_method *method,
|
|
BOOL replaceExisting)
|
|
{
|
|
assert(__objc_uninstalled_dtable != dtable);
|
|
uint32_t sel_id = PTR_TO_IDX(method->selector->sel_id);
|
|
struct objc_slot *slot = SparseArrayLookup(dtable, sel_id);
|
|
if (NULL != slot && slot->owner == class)
|
|
{
|
|
if (slot->method == method->imp || replaceExisting) { return NO; }
|
|
//fprintf(stderr, "Installing method %p %s in %s\n", method->imp, sel_get_name(method->selector), class->name);
|
|
slot->method = method->imp;
|
|
slot->version++;
|
|
}
|
|
else
|
|
{
|
|
//fprintf(stderr, "Installing method %p %s in %s\n", method->imp, sel_get_name(method->selector), class->name);
|
|
slot = new_slot_for_method_in_class((void*)method, class);
|
|
SparseArrayInsert(dtable, sel_id, slot);
|
|
// Invalidate the superclass's slot, if it has one.
|
|
if (NULL != class->super_class)
|
|
{
|
|
if (NULL != (slot =
|
|
SparseArrayLookup(dtable_for_class(class->super_class), sel_id)))
|
|
{
|
|
slot->version++;
|
|
}
|
|
}
|
|
}
|
|
return YES;
|
|
}
|
|
|
|
static void installMethodsInClass(Class cls,
|
|
SparseArray *methods,
|
|
BOOL replaceExisting)
|
|
{
|
|
SparseArray *dtable = dtable_for_class(cls);
|
|
assert(__objc_uninstalled_dtable != dtable);
|
|
|
|
uint32_t idx = 0;
|
|
struct objc_method *m;
|
|
while ((m = SparseArrayNext(methods, &idx)))
|
|
{
|
|
if (!installMethodInDtable(cls, dtable, m, replaceExisting))
|
|
{
|
|
// Remove this method from the list, if it wasn't actually installed
|
|
SparseArrayInsert(methods, idx, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void mergeMethodsFromSuperclass(Class cls, SparseArray *methods)
|
|
{
|
|
for (struct objc_class *subclass=cls->subclass_list ;
|
|
Nil != subclass ; subclass = subclass->sibling_class)
|
|
{
|
|
// Don't bother updating dtables for subclasses that haven't been
|
|
// initialized yet
|
|
if (!classHasDtable(subclass)) { return; }
|
|
|
|
// Create a new (copy-on-write) array to pass down to children
|
|
SparseArray *newMethods = SparseArrayCopy(methods);
|
|
// Install all of these methods except ones that are overridden in the
|
|
// subclass. All of the methods that we are updating were added in a
|
|
// superclass, so we don't replace versions registered to the subclass.
|
|
installMethodsInClass(subclass, newMethods, NO);
|
|
// Recursively add the methods to the subclass's subclasses.
|
|
mergeMethodsFromSuperclass(subclass, newMethods);
|
|
SparseArrayDestroy(newMethods);
|
|
}
|
|
}
|
|
|
|
Class class_getSuperclass(Class);
|
|
|
|
void __objc_update_dispatch_table_for_class(Class cls)
|
|
{
|
|
// Only update real dtables
|
|
if (!classHasDtable(cls)) { return; }
|
|
|
|
LOCK_UNTIL_RETURN(__objc_runtime_mutex);
|
|
|
|
SparseArray *methods = SparseArrayNewWithDepth(dtable_depth);
|
|
collectMethodsForMethodListToSparseArray((void*)cls->methods, methods);
|
|
installMethodsInClass(cls, methods, YES);
|
|
// Methods now contains only the new methods for this class.
|
|
mergeMethodsFromSuperclass(cls, methods);
|
|
SparseArrayDestroy(methods);
|
|
}
|
|
|
|
static SparseArray *create_dtable_for_class(Class class)
|
|
{
|
|
// Don't create a dtable for a class that already has one
|
|
if (classHasDtable(class)) { return dtable_for_class(class); }
|
|
|
|
LOCK_UNTIL_RETURN(__objc_runtime_mutex);
|
|
|
|
// 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); }
|
|
|
|
Class super = class_getSuperclass(class);
|
|
|
|
if (Nil != super && !classHasInstalledDtable(super))
|
|
{
|
|
__objc_install_dispatch_table_for_class(super);
|
|
}
|
|
|
|
SparseArray *dtable;
|
|
|
|
/* Allocate dtable if necessary */
|
|
if (Nil == super)
|
|
{
|
|
dtable = SparseArrayNewWithDepth(dtable_depth);
|
|
}
|
|
else
|
|
{
|
|
assert(__objc_uninstalled_dtable != dtable_for_class(super));
|
|
dtable = SparseArrayCopy(dtable_for_class(super));
|
|
}
|
|
|
|
// When constructing the initial dtable for a class, we iterate along the
|
|
// method list in forward-traversal order. The first method that we
|
|
// encounter is always the one that we want to keep, so we instruct
|
|
// installMethodInDtable() not to replace methods that are already
|
|
// associated with this class.
|
|
struct objc_method_list *list = (void*)class->methods;
|
|
|
|
while (NULL != list)
|
|
{
|
|
for (unsigned i=0 ; i<list->count ; i++)
|
|
{
|
|
installMethodInDtable(class, dtable, &list->methods[i], NO);
|
|
}
|
|
list = list->next;
|
|
}
|
|
|
|
return dtable;
|
|
}
|
|
|
|
static void __objc_install_dispatch_table_for_class(Class class)
|
|
{
|
|
class->dtable = (void*)create_dtable_for_class(class);
|
|
}
|
|
|
|
Class class_table_next(void **e);
|
|
|
|
void objc_resize_dtables(uint32_t newSize)
|
|
{
|
|
// If dtables already have enough space to store all registered selectors, do nothing
|
|
if (1<<dtable_depth > newSize) { return; }
|
|
|
|
LOCK_UNTIL_RETURN(__objc_runtime_mutex);
|
|
|
|
dtable_depth <<= 1;
|
|
|
|
uint32_t oldMask = __objc_uninstalled_dtable->mask;
|
|
|
|
SparseArrayExpandingArray(__objc_uninstalled_dtable);
|
|
// Resize all existing dtables
|
|
void *e = NULL;
|
|
struct objc_class *next;
|
|
while ((next = class_table_next(&e)))
|
|
{
|
|
if (next->dtable != (void*)__objc_uninstalled_dtable &&
|
|
NULL != next->dtable &&
|
|
((SparseArray*)next->dtable)->mask == oldMask)
|
|
{
|
|
SparseArrayExpandingArray((void*)next->dtable);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#define sarray_get_safe(x,y) SparseArrayLookup((SparseArray*)x, y)
|
|
#undef objc_method_list
|
|
#undef objc_method
|