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
parent
a6b5cd15dc
commit
2d84b96a72
@ -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;
|
||||
};
|
||||
@ -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];
|
||||
};
|
||||
@ -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
|
||||
@ -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_
|
||||
@ -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
|
||||
@ -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…
Reference in New Issue