/** * Handle selector uniquing. * * When building, you may define TYPE_DEPENDENT_DISPATCH to enable message * sends to depend on their types. */ #include #include #include #include #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. */ static uint32_t selector_count = 1; /** * 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)selector_count) { return YES; } return NO; } /** * Skip anything in a type encoding that is irrelevant to the comparison * between selectors, including type qualifiers and argframe info. */ static const char *skip_irrelevant_type_info(const char *t) { switch (*t) { default: return t; case 'r': case 'n': case 'N': case 'o': case 'O': case 'R': case 'V': case '!': case '0'...'9': return skip_irrelevant_type_info(t+1); } } #ifdef TYPE_DEPENDENT_DISPATCH static BOOL selector_types_equal(const char *t1, const char *t2) { if (t1 == NULL || t2 == NULL) { return t1 == t2; } while (('\0' != *t1) && ('\0' != *t1)) { t1 = skip_irrelevant_type_info(t1); t2 = skip_irrelevant_type_info(t2); if (*t1 != *t2) { return NO; } if ('\0' != *t1) { t1++; } if ('\0' != *t2) { t2++; } } return YES; } #endif #ifdef TYPE_DEPENDENT_DISPATCH static BOOL selector_types_equivalent(const char *t1, const char *t2) { // We always treat untyped selectors as having the same type as typed // selectors, for dispatch purposes. if (t1 == NULL || t2 == NULL) { return YES; } return selector_types_equal(t1, t2); } #endif /** * 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) { #ifdef TYPE_DEPENDENT_DISPATCH return selector_identical(key, value); #else SEL key = (SEL)k; return string_compare(sel_getName(key), sel_getName(value)); #endif } /** * Compare whether two selectors are identical. */ static int selector_identical(const void *k, const SEL value) { SEL key = (SEL)k; return string_compare(sel_getName(key), sel_getName(value)) && string_compare(sel_getType_np(key), sel_getType_np(value)); } /** * Hash a selector. */ static inline uint32_t hash_selector(const void *s) { SEL sel = (SEL)s; uint32_t hash = 5381; const char *str = sel_getName(sel); uint32_t c; while((c = (uint32_t)*str++)) { hash = hash * 33 + c; } #ifdef TYPE_DEPENDENT_DISPATCH str = sel_getType_np(sel); if (NULL != str) { while (*str != '\0') { str = skip_irrelevant_type_info(str); if (*str != '\0') { hash = hash * 33 + (uint32_t)*str; } str++; } } #endif //TYPE_DEPENDENT_DISPATCH return hash; } #define MAP_TABLE_NAME selector #define MAP_TABLE_COMPARE_FUNCTION selector_identical #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; /** * Resizes the dtables to ensure that they can store as many selectors as * exist. */ void objc_resize_dtables(uint32_t); /** * Create data structures to store selectors. */ void __objc_init_selector_tables() { selector_list = SparseArrayNew(); INIT_LOCK(selector_table_lock); sel_table = selector_create(4096); } 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) { //fprintf(stderr, "Sel %s uid: %d, idx: %d, hash: %d\n", sel_getName(aSel), uid, idx, hash_selector(aSel)); 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*)(uintptr_t)uid; } /** * Really registers a selector. Must be called with the selector table locked. */ static inline void register_selector_locked(SEL aSel) { uintptr_t idx = selector_count++; if (NULL == aSel->types) { //fprintf(stderr, "Registering selector %d %s\n", idx, sel_getName(aSel)); add_selector_to_table(aSel, idx, idx); objc_resize_dtables(selector_count); 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; //fprintf(stderr, "Registering selector %d %s\n", idx, sel_getName(aSel)); 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++; selector_count++; } uintptr_t uid = (uintptr_t)untyped->name; TDD(uid = idx); //fprintf(stderr, "Registering typed selector %d %s\n", uid, sel_getName(aSel)); 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_dtables(selector_count); } /** * 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_equal(aSel, registered)) { 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); //fprintf(stderr, "Checking if old selector is registered: %d (%d)\n", NULL != copy ? selector_equal(aSel, copy) : 0, ((NULL != copy) && selector_equal(aSel, copy))); if ((NULL != copy) && selector_identical(aSel, copy)) { //fprintf(stderr, "Not adding new copy\n"); return copy; } LOCK_UNTIL_RETURN(&selector_table_lock); copy = selector_lookup(aSel->name, aSel->types); if (NULL != copy && selector_identical(aSel, copy)) { return copy; } // 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); 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) { if (sel1->name == sel2->name) { return YES; } // Otherwise, do a slow compare return string_compare(sel_getName(sel1), sel_getName(sel2)) TDD(&& (sel1->types == NULL || sel2->types == NULL || selector_types_equivalent(sel_getType_np(sel1), sel_getType_np(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 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 && foundvalue; l = l->next; } return found; } void objc_register_selectors_from_list(struct objc_method_list *l) { for (int i=0 ; icount ; 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) { // GCC is broken and always sets the count to 0, so we ignore count until // we can throw stupid and buggy compilers in the bin. for (unsigned long i=0 ; (NULL != selectors[i].name) ; i++) { objc_register_selector(&selectors[i]); } } /** * 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); if (NULL == sel) { return sel_registerTypedName_np(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) { SEL sel = selector_lookup(name, 0); if (NULL == sel) { return sel_registerName(name); } 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_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_dtables(uint32_t ignored) {} int main(void) { __objc_init_selector_tables(); SEL a = sel_registerTypedName_np("foo:", "1234"); logSelector(a); a = sel_registerName("bar:"); 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 ; ivalue, ((struct sel_type_list *)SparseArrayLookup(selector_list, idx))->value); } fprintf(stderr, "Number of types: %d\n", count); SEL sel; } #endif