| /* GNU Objective C Runtime selector related functions |
| Copyright (C) 1993, 1995, 1996, 1997 Free Software Foundation, Inc. |
| Contributed by Kresten Krab Thorup |
| |
| This file is part of GNU CC. |
| |
| GNU CC is free software; you can redistribute it and/or modify it under the |
| terms of the GNU General Public License as published by the Free Software |
| Foundation; either version 2, or (at your option) any later version. |
| |
| GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
| details. |
| |
| You should have received a copy of the GNU General Public License along with |
| GNU CC; see the file COPYING. If not, write to the Free Software |
| Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ |
| |
| /* As a special exception, if you link this library with files compiled with |
| GCC to produce an executable, this does not cause the resulting executable |
| to be covered by the GNU General Public License. This exception does not |
| however invalidate any other reasons why the executable file might be |
| covered by the GNU General Public License. */ |
| |
| #include "runtime.h" |
| #include "objc/sarray.h" |
| #include "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 */ |
| |
| static void register_selectors_from_list(MethodList_t); |
| |
| /* Number of selectors stored in each of the above tables */ |
| int __objc_selector_max_index = 0; /* !T:MUTEX */ |
| |
| void __objc_init_selector_tables() |
| { |
| __objc_selector_array = sarray_new (SELECTOR_HASH_SIZE, 0); |
| __objc_selector_names = sarray_new (SELECTOR_HASH_SIZE, 0); |
| __objc_selector_hash |
| = hash_new (SELECTOR_HASH_SIZE, |
| (hash_func_type) hash_string, |
| (compare_func_type) 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) |
| { |
| 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. |
| |
| This one is only called for Class objects. For categories, |
| class_add_method_list is called. |
| */ |
| static void |
| register_selectors_from_list (MethodList_t method_list) |
| { |
| int i = 0; |
| while (i < method_list->method_count) |
| { |
| Method_t method = &method_list->method_list[i]; |
| method->method_name |
| = sel_register_typed_name ((const char*)method->method_name, |
| method->method_types); |
| i += 1; |
| } |
| } |
| |
| |
| /* 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; |
| MethodList_t new_list; |
| Method_t curr_method; |
| |
| /* Only if a root class. */ |
| if(class->super_class) |
| return; |
| |
| /* Allocate a method list to hold the new class methods */ |
| new_list = objc_calloc(sizeof(struct objc_method_list) |
| + sizeof(struct objc_method[max_methods_no]), 1); |
| method_list = class->methods; |
| class_method_list = class->class_pointer->methods; |
| curr_method = &new_list->method_list[0]; |
| |
| /* Iterate through the method lists for the class */ |
| while (method_list) |
| { |
| int i; |
| |
| /* Iterate through the methods from this method list */ |
| for (i = 0; i < method_list->method_count; i++) |
| { |
| Method_t mth = &method_list->method_list[i]; |
| if (mth->method_name |
| && !search_for_method_in_list (class_method_list, |
| mth->method_name)) |
| { |
| /* This instance method isn't a class method. |
| Add it into the new_list. */ |
| *curr_method = *mth; |
| |
| /* Reallocate the method list if necessary */ |
| if(++new_list->method_count == max_methods_no) |
| new_list = |
| objc_realloc(new_list, sizeof(struct objc_method_list) |
| + sizeof(struct |
| objc_method[max_methods_no += 16])); |
| curr_method = &new_list->method_list[new_list->method_count]; |
| } |
| } |
| |
| method_list = method_list->method_next; |
| } |
| |
| /* If we created any new class methods |
| then attach the method list to the class */ |
| if (new_list->method_count) |
| { |
| new_list = |
| objc_realloc(new_list, sizeof(struct objc_method_list) |
| + sizeof(struct objc_method[new_list->method_count])); |
| new_list->method_next = class->class_pointer->methods; |
| class->class_pointer->methods = new_list; |
| } |
| |
| __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(*t1)) t1++; |
| while (isdigit(*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) 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 (__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; |
| |
| objc_mutex_lock(__objc_runtime_mutex); |
| |
| i = (sidx) 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 (__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) 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 (__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; |
| |
| 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 (__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; |
| |
| /* 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; |
| |
| i = (sidx) hash_value_for_key (__objc_selector_hash, name); |
| if (soffset_decode (i) != 0) |
| { |
| for (l = (struct objc_list*)sarray_get (__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 = objc_malloc (sizeof (struct objc_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 (__objc_selector_array, i); |
| } |
| else |
| { |
| __objc_selector_max_index += 1; |
| i = soffset_encode(__objc_selector_max_index); |
| if (orig) |
| j = orig; |
| else |
| j = objc_malloc (sizeof (struct objc_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, |
| 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) |
| 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; |
| } |
| |