Tidied up some bits by creating private headers for private data structures.

Imported selector table code frm the Étoilé runtime.  We can now make dispatch type dependent with a -D switch.  Not enabled yet, but it will be enabled in a warning mode soon - I consider preferable to the existing GNU and Apple solution of corrupting the stack.
main
theraven 16 years ago
parent a6b5cd15dc
commit 2d84b96a72

@ -19,6 +19,7 @@ class_table.c
hash_table.h
lock.h
mutation.m
pool.h
runtime.c
string_hash.c
sync.m

@ -33,7 +33,9 @@ libobjc_C_FILES = \
protocol.c\
runtime.c\
sarray.c\
sarray2.c\
selector.c\
selector_table.c\
sendmsg.c\
thr.c

@ -0,0 +1,109 @@
struct objc_class
{
/**
* Pointer to the metaclass for this class. The metaclass defines the
* methods use when a message is sent to the class, rather than an
* instance.
*/
struct objc_class *isa;
/**
* Pointer to the superclass. The compiler will set this to the name of
* the superclass, the runtime will initialize it to point to the real
* class.
*/
struct objc_class *super_class;
/**
* The name of this class. Set to the same value for both the class and
* its associated metaclass.
*/
const char* name;
/**
* The version of this class. This is not used by the language, but may be
* set explicitly at class load time.
*/
long version;
/**
* A bitfield containing various flags.
*/
unsigned long info;
/**
* The size of this class. For classes using the non-fragile ABI, the
* compiler will set this to a negative value The absolute value will be
* the size of the instance variables defined on just this class. When
* using the fragile ABI, the instance size is the size of instances of
* this class, including any instance variables defined on superclasses.
*
* In both cases, this will be set to the size of an instance of the class
* after the class is registered with the runtime.
*/
long instance_size;
/**
* Metadata describing the instance variables in this class.
*/
struct objc_ivar_list* ivars;
/**
* Metadata for for defining the mappings from selectors to IMPs. Linked
* list of method list structures, one per class and one per category.
*/
struct objc_method_list* methods;
/**
* The dispatch table for this class. Intialized and maintained by the
* runtime.
*/
struct sarray * dtable;
/**
* A pointer to the first subclass for this class. Filled in by the
* runtime.
*/
struct objc_class* subclass_list;
/**
* A pointer to the next sibling class to this. You may find all
* subclasses of a given class by following the subclass_list pointer and
* then subsequently following the sibling_class pointers in the
* subclasses.
*/
struct objc_class* sibling_class;
/**
* Metadata describing the protocols adopted by this class. Not used by
* the runtime.
*/
struct objc_protocol_list *protocols;
/**
* Pointer used by the Boehm GC.
*/
void* gc_object_type;
/**
* New ABI. The following fields are only available with classes compiled to
* support the new ABI. You may test whether any given class supports this
* ABI by using the CLS_ISNEW_ABI() macro.
*/
/**
* The version of the ABI used for this class. This is currently always zero.
*/
long abi_version;
/**
* Array of pointers to variables where the runtime will store the ivar
* offset. These may be used for faster access to non-fragile ivars if all
* of the code is compiled for the new ABI. Each of these pointers should
* have the mangled name __objc_ivar_offset_value_{class name}.{ivar name}
*
* When using the compatible non-fragile ABI, this faster form should only be
* used for classes declared in the same compilation unit.
*
* The compiler should also emit symbols of the form
* __objc_ivar_offset_{class name}.{ivar name} which are pointers to the
* offset values. These should be emitted as weak symbols in every module
* where they are used. The legacy-compatible ABI uses these with a double
* layer of indirection.
*/
int **ivar_offsets;
/**
* List of declared properties on this class (NULL if none). This contains
* the accessor methods for each property.
*/
struct objc_property_list *properties;
};

@ -63,9 +63,8 @@ static BOOL PREFIX(_is_null)(void *value)
}
# define MAP_TABLE_VALUE_NULL PREFIX(_is_null)
# define MAP_TABLE_VALUE_PLACEHOLDER NULL
#else
# define MAP_TABLE_ACCESS_BY_REFERENCE
#endif
typedef struct PREFIX(_table_cell_struct)
{
uint32_t secondMaps;

@ -73,9 +73,7 @@ void (*_objc_load_callback) (Class class, Category *category); /* !T:SAFE */
/* Is all categories/classes resolved? */
BOOL __objc_dangling_categories = NO; /* !T:UNUSED */
extern SEL
__sel_register_typed_name (const char *name, const char *types,
struct objc_selector *orig, BOOL is_const);
void __objc_register_selector_array(SEL selectors, unsigned long count);
/* Sends +load to all classes and categories in certain situations. */
static void objc_send_load (void);
@ -535,9 +533,6 @@ __objc_exec_class (Module_t module)
/* Entry used to traverse hash lists */
struct objc_list **cell;
/* The table of selector references for this module */
SEL selectors = symtab->refs;
/* dummy counter */
int i;
@ -572,19 +567,9 @@ __objc_exec_class (Module_t module)
__objc_module_list = list_cons (module, __objc_module_list);
/* Replace referenced selectors from names to SEL's. */
if (selectors)
if (symtab->refs)
{
for (i = 0; selectors[i].sel_id; ++i)
{
const char *name, *type;
name = (char *) selectors[i].sel_id;
type = (char *) selectors[i].sel_types;
/* Constructors are constant static data so we can safely store
pointers to them in the runtime structures. is_const == YES */
__sel_register_typed_name (name, type,
(struct objc_selector *) &(selectors[i]),
YES);
}
__objc_register_selector_array(symtab->refs, symtab->sel_ref_cnt);
}
/* Parse the classes in the load module and gather selector information. */

@ -0,0 +1,43 @@
/**
* Metadata structure describing a method.
*/
struct objc_method
{
/**
* Selector used to send messages to this method. The type encoding of
* this method should match the types field.
*/
SEL selector;
/**
* The type encoding for this selector. Used only for introspection, and
* only required because of the stupid selector handling in the old GNU
* runtime. In future, this field may be reused for something else.
*/
const char *types;
/**
* A pointer to the function implementing this method.
*/
IMP imp;
};
/**
* Method list. Each class or category defines a new one of these and they are
* all chained together in a linked list, with new ones inserted at the head.
* When constructing the dispatch table, methods in the start of the list are
* used in preference to ones at the end.
*/
struct objc_method_list
{
/**
* The next group of methods in the list.
*/
struct objc_method_list *next;
/**
* The number of methods in this list.
*/
int count;
/**
* An array of methods. Note that the actual size of this is count, not 1.
*/
struct objc_method methods[1];
};

@ -36,7 +36,7 @@ typedef struct objc_ivar* Ivar;
// Don't redefine these types if the old GNU header was included first.
#ifndef __objc_INCLUDE_GNU
typedef const struct objc_selector *SEL;
typedef struct objc_selector *SEL;
typedef struct objc_class *Class;
@ -272,49 +272,28 @@ BOOL sel_isEqual(SEL sel1, SEL sel2);
SEL sel_registerName(const char *selName);
/**
* Register a typed selector.
*/
SEL sel_registerTypedName_np(const char *selName, const char *types);
/**
* Returns the type encoding associated with a selector, or the empty string is
* there is no such type.
*/
const char *sel_getType_np(SEL aSel);
/**
* Copies all of the type encodings associated with a given selector name into
* types, returning the number of types.
*/
unsigned sel_copyTypes(const char *selName, const char **types, unsigned count);
#else
#include "runtime-legacy.h"
#endif // __LEGACY_GNU_MODE__
#include "slot.h"
/**
* The objc_slot structure is used to permit safe IMP caching. It is returned
* by the new lookup APIs. When you cache an IMP, you should store a copy of
* the version field and a pointer to the slot.
*
* The slot version is guaranteed never to be 0. When updating a cache, you
* should use code of the following form:
*
* 1) version = 0;
* 2) slot->cachedFor = receiver->isa;
* 3) slot_cache = slot;
* 4) version = slot->version;
*
* The runtime guarantees that the version in any cachable slot will never be
* 0. This should ensure that, if the version and cache pointer mismatch, the
* next access will cause a cache miss.
*
* When using a cached slot, you should compare the owner pointer to the isa
* pointer of the receiver and the message and the version of the slot to your
* cached version.
*/
struct objc_slot
{
/** The class to which this slot is attached (used internally). */
Class owner;
/** The class for which this slot was cached. Note that this can be
* modified by different cache owners, in different threads. Doing so may
* cause some cache misses, but if different methods are sending messages
* to the same object and sharing a cached slot then it may also improve
* cache hits. Profiling is probably required here. */
Class cachedFor;
/** The type encoding for the method identified by this slot. */
char *types;
/** The current version. This changes if the method changes or if a
* subclass overrides this method, potentially invalidating this cache. */
int version;
/** The method pointer for this method. */
IMP method;
} OBJC_NONPORTABLE;
/**
* New ABI lookup function. Receiver may be modified during lookup or proxy
* forwarding and the sender may affect how lookup occurs.

@ -0,0 +1,39 @@
/**
* The objc_slot structure is used to permit safe IMP caching. It is returned
* by the new lookup APIs. When you cache an IMP, you should store a copy of
* the version field and a pointer to the slot.
*
* The slot version is guaranteed never to be 0. When updating a cache, you
* should use code of the following form:
*
* 1) version = 0;
* 2) slot->cachedFor = receiver->isa;
* 3) slot_cache = slot;
* 4) version = slot->version;
*
* The runtime guarantees that the version in any cachable slot will never be
* 0. This should ensure that, if the version and cache pointer mismatch, the
* next access will cause a cache miss.
*
* When using a cached slot, you should compare the owner pointer to the isa
* pointer of the receiver and the message and the version of the slot to your
* cached version.
*/
struct objc_slot
{
/** The class to which this slot is attached (used internally). */
Class owner;
/** The class for which this slot was cached. Note that this can be
* modified by different cache owners, in different threads. Doing so may
* cause some cache misses, but if different methods are sending messages
* to the same object and sharing a cached slot then it may also improve
* cache hits. Profiling is probably required here. */
Class cachedFor;
/** The type encoding for the method identified by this slot. */
const char *types;
/** The current version. This changes if the method changes or if a
* subclass overrides this method, potentially invalidating this cache. */
int version;
/** The method pointer for this method. */
IMP method;
} OBJC_NONPORTABLE;

@ -0,0 +1,53 @@
#include <stdlib.h>
#include "lock.h"
#ifndef POOL_TYPE
#error POOL_TYPE must be defined
#endif
#ifndef POOL_TYPE
#error POOL_NAME must be defined
#endif
// Horrible multiple indirection to satisfy the weird precedence rules in cpp
#define REALLY_PREFIX_SUFFIX(x,y) x ## y
#define PREFIX_SUFFIX(x, y) REALLY_PREFIX_SUFFIX(x, y)
#define NAME(x) PREFIX_SUFFIX(POOL_NAME, x)
#define PAGE_SIZE 4096
// Malloc one page at a time.
#define POOL_SIZE ((PAGE_SIZE) / sizeof(POOL_TYPE))
static POOL_TYPE* NAME(_pool);
static int NAME(_pool_next_index) = -1;
#ifdef THREAD_SAFE_POOL
static mutex_t NAME(_lock);
#define LOCK_POOL() LOCK(&POOL_NAME##_lock)
#define UNLOCK_POOL() LOCK(&POOL_NAME##_lock)
#else
#define LOCK_POOL()
#define UNLOCK_POOL()
#endif
static inline POOL_TYPE*NAME(_pool_alloc)(void)
{
LOCK_POOL();
if (0 > NAME(_pool_next_index))
{
NAME(_pool) = malloc(PAGE_SIZE);
NAME(_pool_next_index) = POOL_SIZE - 1;
}
POOL_TYPE* new = &NAME(_pool)[NAME(_pool_next_index)--];
UNLOCK_POOL();
return new;
}
#undef NAME
#undef POOL_SIZE
#undef PAGE_SIZE
#undef POOL_NAME
#undef POOL_TYPE
#undef LOCK_POOL
#undef UNLOCK_POOL
#ifdef THREAD_SAFE_POOL
#undef THREAD_SAFE_POOL
#endif

@ -602,7 +602,7 @@ IMP method_getImplementation(Method method)
SEL method_getName(Method method)
{
return method->method_name;
return (SEL)method->method_name;
}
unsigned method_getNumberOfArguments(Method method)
@ -845,23 +845,3 @@ void objc_registerClassPair(Class cls)
objc_resolve_class(cls);
}
const char *sel_getName(SEL sel)
{
return sel_get_name(sel);
}
SEL sel_getUid(const char *selName)
{
return sel_get_uid(selName);
}
BOOL sel_isEqual(SEL sel1, SEL sel2)
{
return sel_eq(sel1, sel2);
}
SEL sel_registerName(const char *selName)
{
return sel_register_name(selName);
}

@ -0,0 +1,105 @@
#include <stdlib.h>
#ifdef BUILD_TESTS
#include <stdio.h>
#endif
#include "sarray2.h"
static void *EmptyArrayData[256];
static SparseArray EmptyArray = { 0, 0xff, (void**)&EmptyArrayData};
static void init_pointers(SparseArray * sarray)
{
sarray->data = calloc(256, sizeof(void*));
if(sarray->shift != 0)
{
for(unsigned i=0 ; i<256 ; i++)
{
sarray->data[i] = &EmptyArray;
}
}
}
SparseArray * SparseArrayNew()
{
SparseArray * sarray = calloc(1, sizeof(SparseArray));
sarray->shift = 24;
sarray->mask = 0xff000000;
init_pointers(sarray);
return sarray;
}
void * SparseArrayNext(SparseArray * sarray, uint32_t * index)
{
uint32_t j = MASK_INDEX((*index));
uint32_t max = (sarray->mask >> sarray->shift) + 1;
if(sarray->shift == 0)
{
while(j<max)
{
(*index)++;
if(sarray->data[j] != SARRAY_EMPTY)
{
return sarray->data[j];
}
j++;
}
}
else while(j<max)
{
uint32_t zeromask = ~(sarray->mask >> 8);
while(j<max)
{
//Look in child nodes
if(sarray->data[j] != SARRAY_EMPTY)
{
void * ret = SparseArrayNext(sarray->data[j], index);
if(ret != SARRAY_EMPTY)
{
return ret;
}
}
//Go to the next child
j++;
//Add 2^n to index so j is still correct
(*index) += 1<<sarray->shift;
//Zero off the next component of the index so we don't miss any.
*index &= zeromask;
}
}
return SARRAY_EMPTY;
}
void SparseArrayInsert(SparseArray * sarray, uint32_t index, void * value)
{
while(sarray->shift > 0)
{
uint32_t i = MASK_INDEX(index);
if(sarray->data[i] == &EmptyArray)
{
SparseArray * newsarray = calloc(1, sizeof(SparseArray));
newsarray->shift = sarray->shift - 8;
newsarray->mask = sarray->mask >> 8;
init_pointers(newsarray);
sarray->data[i] = newsarray;
}
sarray = sarray->data[i];
}
sarray->data[index & sarray->mask] = value;
}
void SparseArrayDestroy(SparseArray * sarray)
{
if(sarray->shift > 0)
{
uint32_t max = (sarray->mask >> sarray->shift) + 1;
for(uint32_t i=0 ; i<max ; i++)
{
SparseArrayDestroy((SparseArray*)sarray->data[i]);
}
}
free(sarray->data);
free(sarray);
}

@ -0,0 +1,76 @@
/**
* Sparse Array
*
* Author: David Chisnall
*
* License: See COPYING.MIT
*
*/
#ifndef _SARRAY_H_INCLUDED_
#define _SARRAY_H_INCLUDED_
#include <stdint.h>
#include <stdlib.h>
/**
* Sparse arrays, used to implement dispatch tables. Current implementation is
* quite RAM-intensive and could be optimised. Maps 32-bit integers to pointers.
*
* Note that deletion from the array is not supported. This allows accesses to
* be done without locking; the worst that can happen is that the caller gets
* an old value (and if this is important to you then you should be doing your
* own locking). For this reason, you should be very careful when deleting a
* sparse array that there are no references to it held by other threads.
*/
typedef struct
{
uint32_t mask;
uint32_t shift;
void ** data;
} SparseArray;
/**
* Turn an index in the array into an index in the current depth.
*/
#define MASK_INDEX(index) \
((index & sarray->mask) >> sarray->shift)
#define SARRAY_EMPTY ((void*)0)
/**
* Look up the specified value in the sparse array. This is used in message
* dispatch and so has been put in the header to allow compilers to inline it,
* even though this breaks the abstraction.
*/
static inline void* SparseArrayLookup(SparseArray * sarray, uint32_t index)
{
while(sarray->shift > 0)
{
uint32_t i = MASK_INDEX(index);
sarray = (SparseArray*) sarray->data[i];
}
uint32_t i = index & sarray->mask;
return sarray->data[i];
}
/**
* Create a new sparse array.
*/
SparseArray * SparseArrayNew();
/**
* Insert a value at the specified index.
*/
void SparseArrayInsert(SparseArray * sarray, uint32_t index, void * value);
/**
* Destroy the sparse array. Note that calling this while other threads are
* performing lookups is guaranteed to break.
*/
void SparseArrayDestroy(SparseArray * sarray);
/**
* Iterate through the array. Returns the next non-NULL value after index and
* sets index to the following value. For example, an array containing values
* at 0 and 10 will, if called with index set to 0 first return the value at 0
* and set index to 1. A subsequent call with index set to 1 will return the
* value at 10 and set index to 11.
*/
void * SparseArrayNext(SparseArray * sarray, uint32_t * index);
#endif //_SARRAY_H_INCLUDED_

@ -27,77 +27,12 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
#include "objc/sarray.h"
#include "objc/encoding.h"
/* Initial selector hash table size. Value doesn't matter much */
#define SELECTOR_HASH_SIZE 128
/* Tables mapping selector names to uid and opposite */
static struct sarray *__objc_selector_array = 0; /* uid -> sel !T:MUTEX */
static struct sarray *__objc_selector_names = 0; /* uid -> name !T:MUTEX */
static cache_ptr __objc_selector_hash = 0; /* name -> uid !T:MUTEX */
/* Number of selectors stored in each of the above tables */
unsigned int __objc_selector_max_index = 0; /* !T:MUTEX */
void __objc_init_selector_tables (void)
{
__objc_selector_array = sarray_new (SELECTOR_HASH_SIZE, 0);
__objc_selector_names = sarray_new (SELECTOR_HASH_SIZE, 0);
__objc_selector_hash
= objc_hash_new (SELECTOR_HASH_SIZE,
(hash_func_type) objc_hash_string,
(compare_func_type) objc_compare_strings);
}
/* This routine is given a class and records all of the methods in its class
structure in the record table. */
void
__objc_register_selectors_from_class (Class class)
{
MethodList_t method_list;
method_list = class->methods;
while (method_list)
{
__objc_register_selectors_from_list (method_list);
method_list = method_list->method_next;
}
}
/* This routine is given a list of methods and records each of the methods in
the record table. This is the routine that does the actual recording
work.
The name and type pointers in the method list must be permanent and
immutable.
*/
void
__objc_register_selectors_from_list (MethodList_t method_list)
{
int i = 0;
objc_mutex_lock (__objc_runtime_mutex);
while (i < method_list->method_count)
{
Method_t method = &method_list->method_list[i];
if (method->method_name)
{
method->method_name
= __sel_register_typed_name ((const char *) method->method_name,
method->method_types, 0, YES);
}
i += 1;
}
objc_mutex_unlock (__objc_runtime_mutex);
}
/* Register instance methods as class methods for root classes */
void __objc_register_instance_methods_to_class (Class class)
{
MethodList_t method_list;
MethodList_t class_method_list;
int max_methods_no = 16;
int max_methods_no = 32;
MethodList_t new_list;
Method_t curr_method;
@ -157,338 +92,3 @@ void __objc_register_instance_methods_to_class (Class class)
__objc_update_dispatch_table_for_class (class->class_pointer);
}
/* Returns YES iff t1 and t2 have same method types, but we ignore
the argframe layout */
BOOL
sel_types_match (const char *t1, const char *t2)
{
if (! t1 || ! t2)
return NO;
while (*t1 && *t2)
{
if (*t1 == '+') t1++;
if (*t2 == '+') t2++;
while (isdigit ((unsigned char) *t1)) t1++;
while (isdigit ((unsigned char) *t2)) t2++;
/* xxx Remove these next two lines when qualifiers are put in
all selectors, not just Protocol selectors. */
t1 = objc_skip_type_qualifiers (t1);
t2 = objc_skip_type_qualifiers (t2);
if (! *t1 && ! *t2)
return YES;
if (*t1 != *t2)
return NO;
t1++;
t2++;
}
return NO;
}
/* return selector representing name */
SEL
sel_get_typed_uid (const char *name, const char *types)
{
struct objc_list *l;
sidx i;
objc_mutex_lock (__objc_runtime_mutex);
i = (sidx) objc_hash_value_for_key (__objc_selector_hash, name);
if (i == 0)
{
objc_mutex_unlock (__objc_runtime_mutex);
return 0;
}
for (l = (struct objc_list *) sarray_get_safe (__objc_selector_array, i);
l; l = l->tail)
{
SEL s = (SEL) l->head;
if (types == 0 || s->sel_types == 0)
{
if (s->sel_types == types)
{
objc_mutex_unlock (__objc_runtime_mutex);
return s;
}
}
else if (sel_types_match (s->sel_types, types))
{
objc_mutex_unlock (__objc_runtime_mutex);
return s;
}
}
objc_mutex_unlock (__objc_runtime_mutex);
return 0;
}
/* Return selector representing name; prefer a selector with non-NULL type */
SEL
sel_get_any_typed_uid (const char *name)
{
struct objc_list *l;
sidx i;
SEL s = NULL;
objc_mutex_lock (__objc_runtime_mutex);
i = (sidx) objc_hash_value_for_key (__objc_selector_hash, name);
if (i == 0)
{
objc_mutex_unlock (__objc_runtime_mutex);
return 0;
}
for (l = (struct objc_list *) sarray_get_safe (__objc_selector_array, i);
l; l = l->tail)
{
s = (SEL) l->head;
if (s->sel_types)
{
objc_mutex_unlock (__objc_runtime_mutex);
return s;
}
}
objc_mutex_unlock (__objc_runtime_mutex);
return s;
}
/* return selector representing name */
SEL
sel_get_any_uid (const char *name)
{
struct objc_list *l;
sidx i;
objc_mutex_lock (__objc_runtime_mutex);
i = (sidx) objc_hash_value_for_key (__objc_selector_hash, name);
if (soffset_decode (i) == 0)
{
objc_mutex_unlock (__objc_runtime_mutex);
return 0;
}
l = (struct objc_list *) sarray_get_safe (__objc_selector_array, i);
objc_mutex_unlock (__objc_runtime_mutex);
if (l == 0)
return 0;
return (SEL) l->head;
}
/* return selector representing name */
SEL
sel_get_uid (const char *name)
{
return sel_register_typed_name (name, 0);
}
/* Get name of selector. If selector is unknown, the empty string ""
is returned */
const char *sel_get_name (SEL selector)
{
const char *ret;
if (selector == 0) return "<null selector>";
objc_mutex_lock (__objc_runtime_mutex);
if ((soffset_decode ((sidx)selector->sel_id) > 0)
&& (soffset_decode ((sidx)selector->sel_id) <= __objc_selector_max_index))
ret = sarray_get_safe (__objc_selector_names, (sidx) selector->sel_id);
else
ret = 0;
objc_mutex_unlock (__objc_runtime_mutex);
return ret;
}
BOOL
sel_is_mapped (SEL selector)
{
unsigned int idx = soffset_decode ((sidx)selector->sel_id);
return ((idx > 0) && (idx <= __objc_selector_max_index));
}
const char *sel_get_type (SEL selector)
{
if (selector)
return selector->sel_types;
else
return 0;
}
/* The uninstalled dispatch table */
extern struct sarray *__objc_uninstalled_dtable;
/* __sel_register_typed_name allocates lots of struct objc_selector:s
of 8 (16, if pointers are 64 bits) bytes at startup. To reduce the number
of malloc calls and memory lost to malloc overhead, we allocate
objc_selector:s in blocks here. This is only called from
__sel_register_typed_name, and __sel_register_typed_name may only be
called when __objc_runtime_mutex is locked.
Note that the objc_selector:s allocated from __sel_register_typed_name
are never freed.
62 because 62 * sizeof (struct objc_selector) = 496 (992). This should
let malloc add some overhead and use a nice, round 512 (1024) byte chunk.
*/
#define SELECTOR_POOL_SIZE 62
static struct objc_selector *selector_pool;
static int selector_pool_left;
static struct objc_selector *
pool_alloc_selector(void)
{
if (!selector_pool_left)
{
selector_pool = objc_malloc (sizeof (struct objc_selector)
* SELECTOR_POOL_SIZE);
selector_pool_left = SELECTOR_POOL_SIZE;
}
return &selector_pool[--selector_pool_left];
}
/* Store the passed selector name in the selector record and return its
selector value (value returned by sel_get_uid).
Assumes that the calling function has locked down __objc_runtime_mutex. */
/* is_const parameter tells us if the name and types parameters
are really constant or not. If YES then they are constant and
we can just store the pointers. If NO then we need to copy
name and types because the pointers may disappear later on. */
SEL
__sel_register_typed_name (const char *name, const char *types,
struct objc_selector *orig, BOOL is_const)
{
struct objc_selector *j;
sidx i;
struct objc_list *l;
// FIXME: The linker is now going to be uniquing most of our selectors for
// us, so we want to eliminate this hash calculation except as a fallback
if (name == 0) return 0;
i = (sidx) objc_hash_value_for_key (__objc_selector_hash, name);
if (soffset_decode (i) != 0)
{
for (l = (struct objc_list *) sarray_get_safe (__objc_selector_array, i);
l; l = l->tail)
{
SEL s = (SEL) l->head;
if (types == 0 || s->sel_types == 0)
{
if (s->sel_types == types)
{
if (orig)
{
orig->sel_id = (void *) i;
return orig;
}
else
return s;
}
}
else if (! strcmp (s->sel_types, types))
{
if (orig)
{
orig->sel_id = (void *) i;
return orig;
}
else
return s;
}
}
if (orig)
j = orig;
else
j = pool_alloc_selector ();
j->sel_id = (void *) i;
/* Can we use the pointer or must copy types? Don't copy if NULL */
if ((is_const) || (types == 0))
j->sel_types = (const char *) types;
else {
j->sel_types = (char *) objc_malloc (strlen (types) + 1);
strcpy ((char *) j->sel_types, types);
}
l = (struct objc_list *) sarray_get_safe (__objc_selector_array, i);
}
else
{
__objc_selector_max_index += 1;
i = soffset_encode (__objc_selector_max_index);
if (orig)
j = orig;
else
j = pool_alloc_selector ();
j->sel_id = (void *) i;
/* Can we use the pointer or must copy types? Don't copy if NULL */
if ((is_const) || (types == 0))
j->sel_types = (const char *) types;
else {
j->sel_types = (char *) objc_malloc (strlen (types) + 1);
strcpy ((char *) j->sel_types, types);
}
l = 0;
}
DEBUG_PRINTF ("Record selector %s[%s] as: %ld\n", name, types,
(long) soffset_decode (i));
{
int is_new = (l == 0);
const char *new_name;
/* Can we use the pointer or must copy name? Don't copy if NULL */
if ((is_const) || (name == 0))
new_name = name;
else {
new_name = (char *) objc_malloc (strlen (name) + 1);
strcpy ((char *) new_name, name);
}
l = list_cons ((void *) j, l);
sarray_at_put_safe (__objc_selector_names, i, (void *) new_name);
sarray_at_put_safe (__objc_selector_array, i, (void *) l);
if (is_new)
objc_hash_add (&__objc_selector_hash, (void *) new_name, (void *) i);
}
sarray_realloc (__objc_uninstalled_dtable, __objc_selector_max_index + 1);
return (SEL) j;
}
SEL
sel_register_name (const char *name)
{
SEL ret;
objc_mutex_lock (__objc_runtime_mutex);
/* Assume that name is not constant static memory and needs to be
copied before put into a runtime structure. is_const == NO */
ret = __sel_register_typed_name (name, 0, 0, NO);
objc_mutex_unlock (__objc_runtime_mutex);
return ret;
}
SEL
sel_register_typed_name (const char *name, const char *type)
{
SEL ret;
objc_mutex_lock (__objc_runtime_mutex);
/* Assume that name and type are not constant static memory and need to
be copied before put into a runtime structure. is_const == NO */
ret = __sel_register_typed_name (name, type, 0, NO);
objc_mutex_unlock (__objc_runtime_mutex);
return ret;
}

@ -0,0 +1,26 @@
/**
* Structure used to store the types for a selector. This allows for a quick
* test to see whether a selector is polymorphic and allows enumeration of all
* type encodings for a given selector.
*
* This is the same size as an objc_selector, so we can allocate them from the
* objc_selector pool.
*
* Note: For ABI v10, we can probably do something a bit more sensible here and
* make selectors into a linked list.
*/
struct sel_type_list
{
const char *value;
struct sel_type_list *next;
};
/**
* Structure used to store selectors in the list.
*/
struct objc_selector
{
const char * name;
const char * types;
};

@ -0,0 +1,433 @@
/**
* Handle selector uniquing.
*
* When building, you may define TYPE_DEPENDENT_DISPATCH to enable message
* sends to depend on their types.
*/
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "lock.h"
#include "sarray2.h"
#include "objc/runtime.h"
#include "method_list.h"
#include "class.h"
#include "selector.h"
#ifdef TYPE_DEPENDENT_DISPATCH
# define TDD(x) x
#else
# define TDD(x)
#endif
// Define the pool allocator for selectors. This is a simple bump-the-pointer
// allocator for low-overhead allocation.
#define POOL_NAME selector
#define POOL_TYPE struct objc_selector
#include "pool.h"
/**
* The number of selectors currently registered. When a selector is
* registered, its name field is replaced with its index in the selector_list
* array.
*/
uint32_t __objc_selector_max_index;
/**
* Mapping from selector numbers to selector names.
*/
static SparseArray *selector_list = NULL;
// Get the functions for string hashing
#include "string_hash.h"
inline static BOOL isSelRegistered(SEL sel)
{
if ((uintptr_t)sel->name < (uintptr_t)__objc_selector_max_index)
{
return YES;
}
return NO;
}
/**
* Compare selectors based on whether they are treated as equivalent for the
* purpose of dispatch.
*/
static int selector_equal(const void *k,
const SEL value)
{
SEL key = (SEL)k;
return string_compare(sel_getName(key), sel_getName(value)) TDD(&&
string_compare(sel_getType_np(key), sel_getType_np(value)));
}
/**
* Compare whether two selectors are identical.
*/
static int selector_identical(const SEL key,
const SEL value)
{
return string_compare(sel_getName(key), sel_getName(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.
*/
static inline uint32_t hash_selector(const void *s)
{
SEL sel = (SEL)s;
uint32_t hash = 5381;
hash = addStringToHash(hash, sel_getName(sel));
hash = addStringToHash(hash, sel->types);
return hash;
}
#define MAP_TABLE_NAME selector
#define MAP_TABLE_COMPARE_FUNCTION selector_equal
#define MAP_TABLE_HASH_KEY hash_selector
#define MAP_TABLE_HASH_VALUE hash_selector
#include "hash_table.h"
/**
* Table of registered selector. Maps from selector to selector.
*/
static selector_table *sel_table;
/**
* Lock protecting the selector table.
*/
mutex_t selector_table_lock;
/**
* Hack to make the uninstalled dtable the right size. Won't be needed with sarray2.
*/
void objc_resize_uninstalled_dtable(void);
/**
* Create data structures to store selectors.
*/
void __objc_init_selector_tables()
{
selector_list = SparseArrayNew();
INIT_LOCK(selector_table_lock);
sel_table = selector_create(40960);
}
static SEL selector_lookup(const char *name, const char *types)
{
struct objc_selector sel = {name, types};
return selector_table_get(sel_table, &sel);
}
static inline void add_selector_to_table(SEL aSel, int32_t uid, uint32_t idx)
{
struct sel_type_list *typeList =
(struct sel_type_list *)selector_pool_alloc();
typeList->value = aSel->name;
typeList->next = 0;
// Store the name.
SparseArrayInsert(selector_list, idx, typeList);
// Store the selector.
selector_insert(sel_table, aSel);
// Set the selector's name to the uid.
aSel->name = (const char*)uid;
}
/**
* Really registers a selector. Must be called with the selector table locked.
*/
static inline void register_selector_locked(SEL aSel)
{
uintptr_t idx = __objc_selector_max_index++;
if (NULL == aSel->types)
{
add_selector_to_table(aSel, idx, idx);
objc_resize_uninstalled_dtable();
return;
}
SEL untyped = selector_lookup(aSel->name, 0);
// If this has a type encoding, store the untyped version too.
if (untyped == NULL)
{
untyped = selector_pool_alloc();
untyped->name = aSel->name;
untyped->types = 0;
add_selector_to_table(untyped, idx, idx);
// If we are in type dependent dispatch mode, the uid for the typed
// and untyped versions will be different
idx++; __objc_selector_max_index++;
}
uintptr_t uid = (uintptr_t)untyped->name;
TDD(uid = idx);
add_selector_to_table(aSel, uid, idx);
// Add this set of types to the list.
// This is quite horrible. Most selectors will only have one type
// encoding, so we're wasting a lot of memory like this.
struct sel_type_list *typeListHead =
SparseArrayLookup(selector_list, (uint32_t)(uintptr_t)untyped->name);
struct sel_type_list *typeList =
(struct sel_type_list *)selector_pool_alloc();
typeList->value = aSel->types;
typeList->next = typeListHead->next;
typeListHead->next = typeList;
objc_resize_uninstalled_dtable();
}
/**
* Registers a selector. This assumes that the argument is never deallocated.
*/
static SEL objc_register_selector(SEL aSel)
{
if (isSelRegistered(aSel))
{
return aSel;
}
// Check that this isn't already registered, before we try
SEL registered = selector_lookup(aSel->name, aSel->types);
if (NULL != registered && (selector_identical(aSel, registered) || NULL == aSel->types))
{
aSel->name = registered->name;
return registered;
}
LOCK(&selector_table_lock);
register_selector_locked(aSel);
UNLOCK(&selector_table_lock);
return aSel;
}
/**
* Registers a selector by copying the argument.
*/
static SEL objc_register_selector_copy(SEL aSel)
{
// If an identical selector is already registered, return it.
SEL copy = selector_lookup(aSel->name, aSel->types);
if (NULL != copy && (selector_identical(aSel, copy) || NULL == aSel->types))
{
return copy;
}
LOCK(&selector_table_lock);
// Create a copy of this selector.
copy = selector_pool_alloc();
copy->name = strdup(aSel->name);
copy->types = (NULL == aSel->types) ? NULL : strdup(aSel->types);
// Try to register the copy as the authoritative version
register_selector_locked(copy);
UNLOCK(&selector_table_lock);
return copy;
}
/**
* Public API functions.
*/
const char *sel_getName(SEL sel)
{
const char *name = sel->name;
if (isSelRegistered(sel))
{
struct sel_type_list * list =
SparseArrayLookup(selector_list, (uint32_t)(uintptr_t)sel->name);
name = (list == NULL) ? NULL : list->value;
}
if (NULL == name)
{
name = "";
}
return name;
}
SEL sel_getUid(const char *selName)
{
return selector_lookup(selName, 0);
}
BOOL sel_isEqual(SEL sel1, SEL sel2)
{
return selector_equal(sel1, sel2);
}
SEL sel_registerName(const char *selName)
{
struct objc_selector sel = {selName, 0};
return objc_register_selector_copy(&sel);
}
SEL sel_registerTypedName_np(const char *selName, const char *types)
{
struct objc_selector sel = {selName, types};
return objc_register_selector_copy(&sel);
}
const char *sel_getType_np(SEL aSel)
{
return (NULL == aSel->types) ? "" : aSel->types;
}
unsigned sel_copyTypes(const char *selName, const char **types, unsigned count)
{
SEL untyped = selector_lookup(selName, 0);
if (untyped == NULL) { return 0; }
struct sel_type_list *l =
SparseArrayLookup(selector_list, (uint32_t)(uintptr_t)untyped->name);
// Skip the head, which just contains the name, not the types.
l = l->next;
if (count == 0)
{
while (NULL != l)
{
count++;
l = l->next;
}
return count;
}
unsigned found = 0;
while (NULL != l && found<count)
{
types[found++] = l->value;
l = l->next;
}
return found;
}
void __objc_register_selectors_from_list(struct objc_method_list *l)
{
for (int i=0 ; i<l->count ; i++)
{
Method m = &l->methods[i];
struct objc_selector sel = { (const char*)m->selector, m->types };
m->selector = objc_register_selector_copy(&sel);
}
}
/**
* Register all of the (unregistered) selectors that are used in a class.
*/
void __objc_register_selectors_from_class(Class class)
{
for (struct objc_method_list *l=class->methods ; NULL!=l ; l=l->next)
{
__objc_register_selectors_from_list(l);
}
}
void __objc_register_selector_array(SEL selectors, unsigned long count)
{
for (unsigned long i=0 ; (i<count) && (NULL != selectors[i].name) ; i++)
{
objc_register_selector(&selectors[i]);
}
}
// FIXME: This is a stupid idea. We should be inserting root class instance
// methods into the dtable, not duplicating the metadata stuff. Yuck!
void __objc_register_instance_methods_to_class (Class class);
/**
* Legacy GNU runtime compatibility.
*
* All of the functions in this section are deprecated and should not be used
* in new code.
*/
SEL sel_get_typed_uid (const char *name, const char *types)
{
SEL sel = selector_lookup(name, types);
struct sel_type_list *l =
SparseArrayLookup(selector_list, (uint32_t)(uintptr_t)sel->name);
// Skip the head, which just contains the name, not the types.
l = l->next;
if (NULL != l)
{
sel = selector_lookup(name, l->value);
}
return sel;
}
SEL sel_get_any_typed_uid (const char *name)
{
return selector_lookup(name, 0);
}
SEL sel_get_any_uid (const char *name)
{
return selector_lookup(name, 0);
}
SEL sel_get_uid(const char *name)
{
return selector_lookup(name, 0);
}
const char *sel_get_name(SEL selector)
{
return sel_getName(selector);
}
BOOL sel_is_mapped(SEL selector)
{
return isSelRegistered(selector);
}
const char *sel_get_type(SEL selector)
{
return sel_getType_np(selector);
}
SEL sel_register_name(const char *name)
{
return sel_registerName(name);
}
SEL sel_register_typed_name (const char *name, const char *type)
{
return sel_registerTypedName_np(name, type);
}
/*
* Some simple sanity tests.
*/
#ifdef SEL_TEST
static void logSelector(SEL sel)
{
fprintf(stderr, "%s = {%p, %s}\n", sel_getName(sel), sel->name, sel_getType_np(sel));
}
void objc_resize_uninstalled_dtable(void) {}
int main(void)
{
__objc_init_selector_tables();
SEL a = sel_registerTypedName_np("foo:", "1234");
logSelector(a);
a = sel_registerName("foo:");
logSelector(a);
logSelector(sel_get_any_typed_uid("foo:"));
a = sel_registerTypedName_np("foo:", "1234");
logSelector(a);
logSelector(sel_get_any_typed_uid("foo:"));
a = sel_registerTypedName_np("foo:", "456");
logSelector(a);
unsigned count = sel_copyTypes("foo:", NULL, 0);
const char *types[count];
sel_copyTypes("foo:", types, count);
for (unsigned i=0 ; i<count ; i++)
{
fprintf(stderr, "Found type %s\n", types[i]);
}
fprintf(stderr, "Number of types: %d\n", count);
SEL sel;
}
#endif

@ -26,9 +26,13 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
#include <stdlib.h>
#include "objc/runtime-legacy.h"
#include "objc/slot.h"
#include "objc/sarray.h"
#include "objc/encoding.h"
#include "lock.h"
#define POOL_NAME slot
#define POOL_TYPE struct objc_slot
#include "pool.h"
void objc_resolve_class(Class);
@ -96,31 +100,6 @@ __objc_get_forward_imp (id rcv, SEL sel)
abort();
}
struct objc_slot
{
Class owner;
Class cachedFor;
const char *types;
int version;
IMP method;
};
// Malloc slots a page at a time.
#define SLOT_POOL_SIZE ((4096) / sizeof(struct objc_slot))
static struct objc_slot *slot_pool;
static int slot_pool_left;
static struct objc_slot *
pool_alloc_slot(void)
{
if (!slot_pool_left)
{
slot_pool = objc_malloc (sizeof (struct objc_slot)
* SLOT_POOL_SIZE);
slot_pool_left = SLOT_POOL_SIZE;
}
return &slot_pool[--slot_pool_left];
}
static inline IMP sarray_get_imp (struct sarray *dtable, size_t key)
{
@ -478,7 +457,7 @@ __objc_send_initialize (Class class)
}
static struct objc_slot *new_slot_for_method_in_class(Method *method, Class class)
{
struct objc_slot *slot = pool_alloc_slot();
struct objc_slot *slot = slot_pool_alloc();
slot->owner = class;
slot->types = method->method_types;
slot->method = method->method_imp;
@ -588,12 +567,12 @@ static void merge_method_list_to_class (Class class,
}
return;
}
//struct objc_slot *firstslot =
/*
struct objc_slot *firstslot =
// sarray_get_safe(dtable, (size_t)method_list->method_list[0].method_name->sel_id);
// If we've already got the methods from this method list, we also have all
// of the methods from all of the ones further along the chain, so don't
// bother adding them again.
/*
* FIXME: This doesn't take into account the lazy copying stuff in the sarray.
if (NULL != firstslot &&
firstslot->owner == class &&
@ -842,6 +821,12 @@ objc_get_uninstalled_dtable ()
return __objc_uninstalled_dtable;
}
void objc_resize_uninstalled_dtable(void)
{
assert(__objc_uninstalled_dtable != NULL);
sarray_realloc (__objc_uninstalled_dtable, __objc_selector_max_index + 1);
}
// This is an ugly hack to make sure that the compiler can do inlining into
// sendmsg2.c from here. It should be removed and the two compiled separately
// once we drop support for compilers that are too primitive to do cross-module

@ -1,6 +1,7 @@
sendmsg.o: sendmsg.c objc/runtime-legacy.h objc/objc.h objc/objc-api.h \
objc/hash.h objc/Availability.h objc/thr.h objc/objc-decls.h \
objc/objc-list.h objc/sarray.h objc/encoding.h sendmsg2.c
objc/objc-list.h objc/slot.h objc/sarray.h objc/encoding.h lock.h \
pool.h sendmsg2.c
objc/runtime-legacy.h:
@ -18,8 +19,14 @@ objc/objc-decls.h:
objc/objc-list.h:
objc/slot.h:
objc/sarray.h:
objc/encoding.h:
lock.h:
pool.h:
sendmsg2.c:

@ -0,0 +1,16 @@
/**
* A slot, stored in the dispatch table. A pointer to a slot is returned by
* every lookup function. The slot may be safely cached.
*/
struct objc_slot
{
/**
* The class that owns this slot.
*/
Class owner;
Class cachedFor;
const char *types;
int version;
IMP method;
};
Loading…
Cancel
Save