2010-06-03 15:17:12 +00:00
|
|
|
#include "lock.h"
|
|
|
|
#include "class.h"
|
|
|
|
#include "sarray2.h"
|
2010-08-15 22:30:32 +00:00
|
|
|
#include "objc/slot.h"
|
2011-04-14 16:44:34 +00:00
|
|
|
#include "visibility.h"
|
2010-08-15 22:30:32 +00:00
|
|
|
#include <stdint.h>
|
2011-04-17 17:31:37 +00:00
|
|
|
#include <stdio.h>
|
2010-08-15 11:17:00 +00:00
|
|
|
|
2010-08-15 22:30:32 +00:00
|
|
|
#ifdef __OBJC_LOW_MEMORY__
|
|
|
|
typedef struct objc_dtable* dtable_t;
|
|
|
|
struct objc_slot* objc_dtable_lookup(dtable_t dtable, uint32_t uid);
|
2010-08-15 11:17:00 +00:00
|
|
|
#else
|
|
|
|
typedef SparseArray* dtable_t;
|
|
|
|
# define objc_dtable_lookup SparseArrayLookup
|
|
|
|
#endif
|
|
|
|
|
2010-06-03 15:17:12 +00:00
|
|
|
/**
|
|
|
|
* Pointer to the sparse array representing the pretend (uninstalled) dtable.
|
|
|
|
*/
|
2011-04-14 16:44:34 +00:00
|
|
|
PRIVATE extern dtable_t uninstalled_dtable;
|
2010-06-03 15:17:12 +00:00
|
|
|
/**
|
|
|
|
* Structure for maintaining a linked list of temporary dtables. When sending
|
|
|
|
* an +initialize message to a class, we create a temporary dtables and store
|
|
|
|
* it in a linked list. This is then used when sending other messages to
|
|
|
|
* instances of classes in the middle of initialisation.
|
|
|
|
*/
|
|
|
|
typedef struct _InitializingDtable
|
|
|
|
{
|
|
|
|
/** The class that owns the dtable. */
|
|
|
|
Class class;
|
|
|
|
/** The dtable for this class. */
|
2010-08-15 11:17:00 +00:00
|
|
|
dtable_t dtable;
|
2010-06-03 15:17:12 +00:00
|
|
|
/** The next uninstalled dtable in the list. */
|
|
|
|
struct _InitializingDtable *next;
|
|
|
|
} InitializingDtable;
|
|
|
|
|
|
|
|
/** Head of the list of temporary dtables. Protected by initialize_lock. */
|
|
|
|
extern InitializingDtable *temporary_dtables;
|
2010-08-15 11:17:00 +00:00
|
|
|
extern mutex_t initialize_lock;
|
2010-06-03 15:17:12 +00:00
|
|
|
|
2010-06-06 21:11:25 +00:00
|
|
|
/**
|
|
|
|
* Returns whether a class has an installed dtable.
|
|
|
|
*/
|
2010-06-03 15:17:12 +00:00
|
|
|
static inline int classHasInstalledDtable(struct objc_class *cls)
|
|
|
|
{
|
2011-04-14 16:44:34 +00:00
|
|
|
return (cls->dtable != uninstalled_dtable);
|
2010-06-03 15:17:12 +00:00
|
|
|
}
|
|
|
|
|
2011-04-18 17:30:24 +00:00
|
|
|
int objc_sync_enter(id object);
|
|
|
|
int objc_sync_exit(id object);
|
2010-06-03 15:17:12 +00:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2010-08-15 11:17:00 +00:00
|
|
|
static inline dtable_t dtable_for_class(Class cls)
|
2010-06-03 15:17:12 +00:00
|
|
|
{
|
|
|
|
if (classHasInstalledDtable(cls))
|
|
|
|
{
|
2010-08-15 11:17:00 +00:00
|
|
|
return cls->dtable;
|
2010-06-03 15:17:12 +00:00
|
|
|
}
|
2011-04-17 17:31:37 +00:00
|
|
|
|
2011-04-14 16:44:34 +00:00
|
|
|
dtable_t dtable = uninstalled_dtable;
|
2011-04-18 17:30:24 +00:00
|
|
|
|
2010-06-03 15:17:12 +00:00
|
|
|
{
|
2011-04-18 17:30:24 +00:00
|
|
|
LOCK_FOR_SCOPE(&initialize_lock);
|
|
|
|
if (classHasInstalledDtable(cls))
|
|
|
|
{
|
|
|
|
return 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. */
|
|
|
|
InitializingDtable *buffer = temporary_dtables;
|
|
|
|
while (NULL != buffer)
|
2010-06-03 15:17:12 +00:00
|
|
|
{
|
2011-04-18 17:30:24 +00:00
|
|
|
if (buffer->class == cls)
|
|
|
|
{
|
|
|
|
dtable = buffer->dtable;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
buffer = buffer->next;
|
2010-06-03 15:17:12 +00:00
|
|
|
}
|
|
|
|
}
|
2011-04-18 17:30:24 +00:00
|
|
|
|
|
|
|
if (dtable != uninstalled_dtable)
|
2010-06-03 15:17:12 +00:00
|
|
|
{
|
2011-04-18 17:30:24 +00:00
|
|
|
// Make sure that we block if +initialize is still running. We do this
|
|
|
|
// after we've released the initialize lock, so that the real dtable
|
|
|
|
// can be installed. This acquires / releases a recursive mutex, so if
|
|
|
|
// this mutex is already held by this thread then this will proceed
|
|
|
|
// immediately. If it's held by another thread (i.e. the one running
|
|
|
|
// +initialize) then we block here until it's run. We don't need to do
|
|
|
|
// this if the dtable is the uninstalled dtable, because that means
|
|
|
|
// +initialize has not yet been sent, so we can wait until something
|
|
|
|
// triggers it before needing any synchronisation.
|
|
|
|
objc_sync_enter((id)cls);
|
|
|
|
objc_sync_exit((id)cls);
|
2010-06-03 15:17:12 +00:00
|
|
|
}
|
|
|
|
return dtable;
|
|
|
|
}
|
|
|
|
|
2010-06-06 21:11:25 +00:00
|
|
|
/**
|
|
|
|
* Returns whether a class has had a dtable created. The dtable may be
|
|
|
|
* installed, or stored in the look-aside buffer.
|
|
|
|
*/
|
2010-06-03 15:17:12 +00:00
|
|
|
static inline int classHasDtable(struct objc_class *cls)
|
|
|
|
{
|
2011-04-14 16:44:34 +00:00
|
|
|
return (dtable_for_class(cls) != uninstalled_dtable);
|
2010-06-03 15:17:12 +00:00
|
|
|
}
|
2010-08-15 22:30:32 +00:00
|
|
|
|
2010-06-06 21:11:25 +00:00
|
|
|
/**
|
|
|
|
* Updates the dtable for a class and its subclasses. Must be called after
|
|
|
|
* modifying a class's method list.
|
|
|
|
*/
|
|
|
|
void objc_update_dtable_for_class(Class);
|
2011-04-19 09:50:41 +00:00
|
|
|
/**
|
|
|
|
* Adds a single method list to a class. This is used when loading categories,
|
|
|
|
* and is faster than completely rebuilding the dtable.
|
|
|
|
*/
|
|
|
|
void add_method_list_to_class(Class cls,
|
|
|
|
struct objc_method_list *list);
|
2010-10-04 17:44:17 +00:00
|
|
|
|
|
|
|
/**
|
2011-04-19 10:55:10 +00:00
|
|
|
* Destroys a dtable.
|
2010-10-04 17:44:17 +00:00
|
|
|
*/
|
2011-04-19 10:55:10 +00:00
|
|
|
void free_dtable(dtable_t dtable);
|