123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179 |
- /* GNU Objective C Runtime message lookup
- Copyright (C) 1993-2015 Free Software Foundation, Inc.
- Contributed by Kresten Krab Thorup
- This file is part of GCC.
- GCC 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 3, or (at your option) any later version.
- GCC 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.
- Under Section 7 of GPL version 3, you are granted additional
- permissions described in the GCC Runtime Library Exception, version
- 3.1, as published by the Free Software Foundation.
- You should have received a copy of the GNU General Public License and
- a copy of the GCC Runtime Library Exception along with this program;
- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
- <http://www.gnu.org/licenses/>. */
- /* Uncommented the following line to enable debug logging. Use this
- only while debugging the runtime. */
- /* #define DEBUG 1 */
- /* FIXME: This file has no business including tm.h. */
- /* FIXME: This should be using libffi instead of __builtin_apply
- and friends. */
- #include "objc-private/common.h"
- #include "objc-private/error.h"
- #include "tconfig.h"
- #include "coretypes.h"
- #include "tm.h"
- #include "objc/runtime.h"
- #include "objc/message.h" /* For objc_msg_lookup(), objc_msg_lookup_super(). */
- #include "objc/thr.h"
- #include "objc-private/module-abi-8.h"
- #include "objc-private/runtime.h"
- #include "objc-private/hash.h"
- #include "objc-private/sarray.h"
- #include "objc-private/selector.h" /* For sel_is_mapped() */
- #include "runtime-info.h"
- #include <assert.h> /* For assert */
- #include <string.h> /* For strlen */
- /* This is how we hack STRUCT_VALUE to be 1 or 0. */
- #define gen_rtx(args...) 1
- #define gen_rtx_MEM(args...) 1
- #define gen_rtx_REG(args...) 1
- /* Already defined in gcc/coretypes.h. So prevent double definition warning. */
- #undef rtx
- #define rtx int
- #if ! defined (STRUCT_VALUE) || STRUCT_VALUE == 0
- #define INVISIBLE_STRUCT_RETURN 1
- #else
- #define INVISIBLE_STRUCT_RETURN 0
- #endif
- /* The uninstalled dispatch table. If a class' dispatch table points
- to __objc_uninstalled_dtable then that means it needs its dispatch
- table to be installed. */
- struct sarray *__objc_uninstalled_dtable = 0; /* !T:MUTEX */
- /* Two hooks for method forwarding. If either is set, it is invoked to
- * return a function that performs the real forwarding. If both are
- * set, the result of __objc_msg_forward2 will be preferred over that
- * of __objc_msg_forward. If both return NULL or are unset, the
- * libgcc based functions (__builtin_apply and friends) are used. */
- IMP (*__objc_msg_forward) (SEL) = NULL;
- IMP (*__objc_msg_forward2) (id, SEL) = NULL;
- /* Send +initialize to class. */
- static void __objc_send_initialize (Class);
- /* Forward declare some functions */
- static void __objc_install_dtable_for_class (Class cls);
- static void __objc_prepare_dtable_for_class (Class cls);
- static void __objc_install_prepared_dtable_for_class (Class cls);
- static struct sarray *__objc_prepared_dtable_for_class (Class cls);
- static IMP __objc_get_prepared_imp (Class cls,SEL sel);
-
- /* Various forwarding functions that are used based upon the
- return type for the selector.
- __objc_block_forward for structures.
- __objc_double_forward for floats/doubles.
- __objc_word_forward for pointers or types that fit in registers. */
- static double __objc_double_forward (id, SEL, ...);
- static id __objc_word_forward (id, SEL, ...);
- typedef struct { id many[8]; } __big;
- #if INVISIBLE_STRUCT_RETURN
- static __big
- #else
- static id
- #endif
- __objc_block_forward (id, SEL, ...);
- static struct objc_method * search_for_method_in_hierarchy (Class class, SEL sel);
- struct objc_method * search_for_method_in_list (struct objc_method_list * list, SEL op);
- id nil_method (id, SEL);
- /* Make sure this inline function is exported regardless of GNU89 or C99
- inlining semantics as it is part of the libobjc ABI. */
- extern IMP __objc_get_forward_imp (id, SEL);
- /* Given a selector, return the proper forwarding implementation. */
- inline
- IMP
- __objc_get_forward_imp (id rcv, SEL sel)
- {
- /* If a custom forwarding hook was registered, try getting a
- forwarding function from it. There are two forward routine hooks,
- one that takes the receiver as an argument and one that does
- not. */
- if (__objc_msg_forward2)
- {
- IMP result;
- if ((result = __objc_msg_forward2 (rcv, sel)) != NULL)
- return result;
- }
- if (__objc_msg_forward)
- {
- IMP result;
- if ((result = __objc_msg_forward (sel)) != NULL)
- return result;
- }
- /* In all other cases, use the default forwarding functions built
- using __builtin_apply and friends. */
- {
- const char *t = sel->sel_types;
-
- if (t && (*t == '[' || *t == '(' || *t == '{')
- #ifdef OBJC_MAX_STRUCT_BY_VALUE
- && objc_sizeof_type (t) > OBJC_MAX_STRUCT_BY_VALUE
- #endif
- )
- return (IMP)__objc_block_forward;
- else if (t && (*t == 'f' || *t == 'd'))
- return (IMP)__objc_double_forward;
- else
- return (IMP)__objc_word_forward;
- }
- }
- /* Selectors for +resolveClassMethod: and +resolveInstanceMethod:.
- These are set up at startup. */
- static SEL selector_resolveClassMethod = NULL;
- static SEL selector_resolveInstanceMethod = NULL;
- /* Internal routines use to resolve a class method using
- +resolveClassMethod:. 'class' is always a non-Nil class (*not* a
- meta-class), and 'sel' is the selector that we are trying to
- resolve. This must be called when class is not Nil, and the
- dispatch table for class methods has already been installed.
- This routine tries to call +resolveClassMethod: to give an
- opportunity to resolve the method. If +resolveClassMethod: returns
- YES, it tries looking up the method again, and if found, it returns
- it. Else, it returns NULL. */
- static inline
- IMP
- __objc_resolve_class_method (Class class, SEL sel)
- {
- /* We need to lookup +resolveClassMethod:. */
- BOOL (*resolveMethodIMP) (id, SEL, SEL);
- /* The dispatch table for class methods is already installed and we
- don't want any forwarding to happen when looking up this method,
- so we just look it up directly. Note that if 'sel' is precisely
- +resolveClassMethod:, this would look it up yet again and find
- nothing. That's no problem and there's no recursion. */
- resolveMethodIMP = (BOOL (*) (id, SEL, SEL))sarray_get_safe
- (class->class_pointer->dtable, (size_t) selector_resolveClassMethod->sel_id);
- if (resolveMethodIMP && resolveMethodIMP ((id)class, selector_resolveClassMethod, sel))
- {
- /* +resolveClassMethod: returned YES. Look the method up again.
- We already know the dtable is installed. */
-
- /* TODO: There is the case where +resolveClassMethod: is buggy
- and returned YES without actually adding the method. We
- could maybe print an error message. */
- return sarray_get_safe (class->class_pointer->dtable, (size_t) sel->sel_id);
- }
- return NULL;
- }
- /* Internal routines use to resolve a instance method using
- +resolveInstanceMethod:. 'class' is always a non-Nil class, and
- 'sel' is the selector that we are trying to resolve. This must be
- called when class is not Nil, and the dispatch table for instance
- methods has already been installed.
- This routine tries to call +resolveInstanceMethod: to give an
- opportunity to resolve the method. If +resolveInstanceMethod:
- returns YES, it tries looking up the method again, and if found, it
- returns it. Else, it returns NULL. */
- static inline
- IMP
- __objc_resolve_instance_method (Class class, SEL sel)
- {
- /* We need to lookup +resolveInstanceMethod:. */
- BOOL (*resolveMethodIMP) (id, SEL, SEL);
- /* The dispatch table for class methods may not be already installed
- so we have to install it if needed. */
- resolveMethodIMP = sarray_get_safe (class->class_pointer->dtable,
- (size_t) selector_resolveInstanceMethod->sel_id);
- if (resolveMethodIMP == 0)
- {
- /* Try again after installing the dtable. */
- if (class->class_pointer->dtable == __objc_uninstalled_dtable)
- {
- objc_mutex_lock (__objc_runtime_mutex);
- if (class->class_pointer->dtable == __objc_uninstalled_dtable)
- __objc_install_dtable_for_class (class->class_pointer);
- objc_mutex_unlock (__objc_runtime_mutex);
- }
- resolveMethodIMP = sarray_get_safe (class->class_pointer->dtable,
- (size_t) selector_resolveInstanceMethod->sel_id);
- }
- if (resolveMethodIMP && resolveMethodIMP ((id)class, selector_resolveInstanceMethod, sel))
- {
- /* +resolveInstanceMethod: returned YES. Look the method up
- again. We already know the dtable is installed. */
-
- /* TODO: There is the case where +resolveInstanceMethod: is
- buggy and returned YES without actually adding the method.
- We could maybe print an error message. */
- return sarray_get_safe (class->dtable, (size_t) sel->sel_id);
- }
- return NULL;
- }
- /* Given a CLASS and selector, return the implementation corresponding
- to the method of the selector.
- If CLASS is a class, the instance method is returned.
- If CLASS is a meta class, the class method is returned.
- Since this requires the dispatch table to be installed, this function
- will implicitly invoke +initialize for CLASS if it hasn't been
- invoked yet. This also insures that +initialize has been invoked
- when the returned implementation is called directly.
- The forwarding hooks require the receiver as an argument (if they are to
- perform dynamic lookup in proxy objects etc), so this function has a
- receiver argument to be used with those hooks. */
- static inline
- IMP
- get_implementation (id receiver, Class class, SEL sel)
- {
- void *res;
- if (class->dtable == __objc_uninstalled_dtable)
- {
- /* The dispatch table needs to be installed. */
- objc_mutex_lock (__objc_runtime_mutex);
- /* Double-checked locking pattern: Check
- __objc_uninstalled_dtable again in case another thread
- installed the dtable while we were waiting for the lock to be
- released. */
- if (class->dtable == __objc_uninstalled_dtable)
- __objc_install_dtable_for_class (class);
- /* If the dispatch table is not yet installed, we are still in
- the process of executing +initialize. But the implementation
- pointer should be available in the prepared ispatch table if
- it exists at all. */
- if (class->dtable == __objc_uninstalled_dtable)
- {
- assert (__objc_prepared_dtable_for_class (class) != 0);
- res = __objc_get_prepared_imp (class, sel);
- }
- else
- res = 0;
- objc_mutex_unlock (__objc_runtime_mutex);
- /* Call ourselves with the installed dispatch table and get the
- real method. */
- if (!res)
- res = get_implementation (receiver, class, sel);
- }
- else
- {
- /* The dispatch table has been installed. */
- res = sarray_get_safe (class->dtable, (size_t) sel->sel_id);
- if (res == 0)
- {
- /* The dispatch table has been installed, and the method is
- not in the dispatch table. So the method just doesn't
- exist for the class. */
- /* Try going through the +resolveClassMethod: or
- +resolveInstanceMethod: process. */
- if (CLS_ISMETA (class))
- {
- /* We have the meta class, but we need to invoke the
- +resolveClassMethod: method on the class. So, we
- need to obtain the class from the meta class, which
- we do using the fact that both the class and the
- meta-class have the same name. */
- Class realClass = objc_lookUpClass (class->name);
- if (realClass)
- res = __objc_resolve_class_method (realClass, sel);
- }
- else
- res = __objc_resolve_instance_method (class, sel);
- if (res == 0)
- res = __objc_get_forward_imp (receiver, sel);
- }
- }
- return res;
- }
- /* Make sure this inline function is exported regardless of GNU89 or C99
- inlining semantics as it is part of the libobjc ABI. */
- extern IMP get_imp (Class, SEL);
- inline
- IMP
- get_imp (Class class, SEL sel)
- {
- /* In a vanilla implementation we would first check if the dispatch
- table is installed. Here instead, to get more speed in the
- standard case (that the dispatch table is installed) we first try
- to get the imp using brute force. Only if that fails, we do what
- we should have been doing from the very beginning, that is, check
- if the dispatch table needs to be installed, install it if it's
- not installed, and retrieve the imp from the table if it's
- installed. */
- void *res = sarray_get_safe (class->dtable, (size_t) sel->sel_id);
- if (res == 0)
- {
- res = get_implementation(nil, class, sel);
- }
- return res;
- }
- /* The new name of get_imp(). */
- IMP
- class_getMethodImplementation (Class class_, SEL selector)
- {
- if (class_ == Nil || selector == NULL)
- return NULL;
- /* get_imp is inlined, so we're good. */
- return get_imp (class_, selector);
- }
- /* Given a method, return its implementation. This has been replaced
- by method_getImplementation() in the modern API. */
- IMP
- method_get_imp (struct objc_method * method)
- {
- return (method != (struct objc_method *)0) ? method->method_imp : (IMP)0;
- }
- /* Query if an object can respond to a selector, returns YES if the
- object implements the selector otherwise NO. Does not check if the
- method can be forwarded. Since this requires the dispatch table to
- installed, this function will implicitly invoke +initialize for the
- class of OBJECT if it hasn't been invoked yet. */
- inline
- BOOL
- __objc_responds_to (id object, SEL sel)
- {
- void *res;
- struct sarray *dtable;
- /* Install dispatch table if need be */
- dtable = object->class_pointer->dtable;
- if (dtable == __objc_uninstalled_dtable)
- {
- objc_mutex_lock (__objc_runtime_mutex);
- if (object->class_pointer->dtable == __objc_uninstalled_dtable)
- __objc_install_dtable_for_class (object->class_pointer);
- /* If the dispatch table is not yet installed, we are still in
- the process of executing +initialize. Yet the dispatch table
- should be available. */
- if (object->class_pointer->dtable == __objc_uninstalled_dtable)
- {
- dtable = __objc_prepared_dtable_for_class (object->class_pointer);
- assert (dtable);
- }
- else
- dtable = object->class_pointer->dtable;
- objc_mutex_unlock (__objc_runtime_mutex);
- }
- /* Get the method from the dispatch table. */
- res = sarray_get_safe (dtable, (size_t) sel->sel_id);
- return (res != 0) ? YES : NO;
- }
- BOOL
- class_respondsToSelector (Class class_, SEL selector)
- {
- struct sarray *dtable;
- void *res;
- if (class_ == Nil || selector == NULL)
- return NO;
- /* Install dispatch table if need be. */
- dtable = class_->dtable;
- if (dtable == __objc_uninstalled_dtable)
- {
- objc_mutex_lock (__objc_runtime_mutex);
- if (class_->dtable == __objc_uninstalled_dtable)
- __objc_install_dtable_for_class (class_);
- /* If the dispatch table is not yet installed,
- we are still in the process of executing +initialize.
- Yet the dispatch table should be available. */
- if (class_->dtable == __objc_uninstalled_dtable)
- {
- dtable = __objc_prepared_dtable_for_class (class_);
- assert (dtable);
- }
- else
- dtable = class_->dtable;
- objc_mutex_unlock (__objc_runtime_mutex);
- }
- /* Get the method from the dispatch table. */
- res = sarray_get_safe (dtable, (size_t) selector->sel_id);
- return (res != 0) ? YES : NO;
- }
- /* This is the lookup function. All entries in the table are either a
- valid method *or* zero. If zero then either the dispatch table
- needs to be installed or it doesn't exist and forwarding is
- attempted. */
- IMP
- objc_msg_lookup (id receiver, SEL op)
- {
- IMP result;
- if (receiver)
- {
- /* First try a quick lookup assuming the dispatch table exists. */
- result = sarray_get_safe (receiver->class_pointer->dtable,
- (sidx)op->sel_id);
- if (result == 0)
- {
- /* Not found ... call get_implementation () to install the
- dispatch table and call +initialize as required,
- providing the method implementation or a forwarding
- function. */
- result = get_implementation (receiver, receiver->class_pointer, op);
- }
- return result;
- }
- else
- return (IMP)nil_method;
- }
- IMP
- objc_msg_lookup_super (struct objc_super *super, SEL sel)
- {
- if (super->self)
- return get_imp (super->super_class, sel);
- else
- return (IMP)nil_method;
- }
- void
- __objc_init_dispatch_tables ()
- {
- __objc_uninstalled_dtable = sarray_new (200, 0);
- /* TODO: It would be cool to register typed selectors here. */
- selector_resolveClassMethod = sel_registerName ("resolveClassMethod:");
- selector_resolveInstanceMethod = sel_registerName ("resolveInstanceMethod:");
- }
- /* Install dummy table for class which causes the first message to
- that class (or instances hereof) to be initialized properly. */
- void
- __objc_install_premature_dtable (Class class)
- {
- assert (__objc_uninstalled_dtable);
- class->dtable = __objc_uninstalled_dtable;
- }
- /* Send +initialize to class if not already done. */
- static void
- __objc_send_initialize (Class class)
- {
- /* This *must* be a class object. */
- assert (CLS_ISCLASS (class));
- assert (! CLS_ISMETA (class));
- /* class_add_method_list/__objc_update_dispatch_table_for_class may
- have reset the dispatch table. The canonical way to insure that
- we send +initialize just once, is this flag. */
- if (! CLS_ISINITIALIZED (class))
- {
- DEBUG_PRINTF ("+initialize: need to initialize class '%s'\n", class->name);
- CLS_SETINITIALIZED (class);
- CLS_SETINITIALIZED (class->class_pointer);
- /* Create the garbage collector type memory description. */
- __objc_generate_gc_type_description (class);
- if (class->super_class)
- __objc_send_initialize (class->super_class);
- {
- SEL op = sel_registerName ("initialize");
- struct objc_method *method = search_for_method_in_hierarchy (class->class_pointer,
- op);
- if (method)
- {
- DEBUG_PRINTF (" begin of [%s +initialize]\n", class->name);
- (*method->method_imp) ((id)class, op);
- DEBUG_PRINTF (" end of [%s +initialize]\n", class->name);
- }
- #ifdef DEBUG
- else
- {
- DEBUG_PRINTF (" class '%s' has no +initialize method\n", class->name);
- }
- #endif
- }
- }
- }
- /* Walk on the methods list of class and install the methods in the
- reverse order of the lists. Since methods added by categories are
- before the methods of class in the methods list, this allows
- categories to substitute methods declared in class. However if
- more than one category replaces the same method nothing is
- guaranteed about what method will be used. Assumes that
- __objc_runtime_mutex is locked down. */
- static void
- __objc_install_methods_in_dtable (struct sarray *dtable, struct objc_method_list * method_list)
- {
- int i;
-
- if (! method_list)
- return;
-
- if (method_list->method_next)
- __objc_install_methods_in_dtable (dtable, method_list->method_next);
-
- for (i = 0; i < method_list->method_count; i++)
- {
- struct objc_method * method = &(method_list->method_list[i]);
- sarray_at_put_safe (dtable,
- (sidx) method->method_name->sel_id,
- method->method_imp);
- }
- }
- void
- __objc_update_dispatch_table_for_class (Class class)
- {
- Class next;
- struct sarray *arr;
- DEBUG_PRINTF (" _objc_update_dtable_for_class (%s)\n", class->name);
- objc_mutex_lock (__objc_runtime_mutex);
- /* Not yet installed -- skip it unless in +initialize. */
- if (class->dtable == __objc_uninstalled_dtable)
- {
- if (__objc_prepared_dtable_for_class (class))
- {
- /* There is a prepared table so we must be initialising this
- class ... we must re-do the table preparation. */
- __objc_prepare_dtable_for_class (class);
- }
- objc_mutex_unlock (__objc_runtime_mutex);
- return;
- }
- arr = class->dtable;
- __objc_install_premature_dtable (class); /* someone might require it... */
- sarray_free (arr); /* release memory */
-
- /* Could have been lazy... */
- __objc_install_dtable_for_class (class);
- if (class->subclass_list) /* Traverse subclasses. */
- for (next = class->subclass_list; next; next = next->sibling_class)
- __objc_update_dispatch_table_for_class (next);
- objc_mutex_unlock (__objc_runtime_mutex);
- }
- /* This function adds a method list to a class. This function is
- typically called by another function specific to the run-time. As
- such this function does not worry about thread safe issues.
- This one is only called for categories. Class objects have their
- methods installed right away, and their selectors are made into
- SEL's by the function __objc_register_selectors_from_class. */
- void
- class_add_method_list (Class class, struct objc_method_list * list)
- {
- /* Passing of a linked list is not allowed. Do multiple calls. */
- assert (! list->method_next);
- __objc_register_selectors_from_list(list);
- /* Add the methods to the class's method list. */
- list->method_next = class->methods;
- class->methods = list;
- /* Update the dispatch table of class. */
- __objc_update_dispatch_table_for_class (class);
- }
- struct objc_method *
- class_getInstanceMethod (Class class_, SEL selector)
- {
- struct objc_method *m;
- if (class_ == Nil || selector == NULL)
- return NULL;
- m = search_for_method_in_hierarchy (class_, selector);
- if (m)
- return m;
- /* Try going through +resolveInstanceMethod:, and do the search
- again if successful. */
- if (__objc_resolve_instance_method (class_, selector))
- return search_for_method_in_hierarchy (class_, selector);
- return NULL;
- }
- struct objc_method *
- class_getClassMethod (Class class_, SEL selector)
- {
- struct objc_method *m;
- if (class_ == Nil || selector == NULL)
- return NULL;
-
- m = search_for_method_in_hierarchy (class_->class_pointer,
- selector);
- if (m)
- return m;
- /* Try going through +resolveClassMethod:, and do the search again
- if successful. */
- if (__objc_resolve_class_method (class_, selector))
- return search_for_method_in_hierarchy (class_->class_pointer,
- selector);
- return NULL;
- }
- BOOL
- class_addMethod (Class class_, SEL selector, IMP implementation,
- const char *method_types)
- {
- struct objc_method_list *method_list;
- struct objc_method *method;
- const char *method_name;
- if (class_ == Nil || selector == NULL || implementation == NULL
- || method_types == NULL || (strcmp (method_types, "") == 0))
- return NO;
- method_name = sel_getName (selector);
- if (method_name == NULL)
- return NO;
- /* If the method already exists in the class, return NO. It is fine
- if the method already exists in the superclass; in that case, we
- are overriding it. */
- if (CLS_IS_IN_CONSTRUCTION (class_))
- {
- /* The class only contains a list of methods; they have not been
- registered yet, ie, the method_name of each of them is still
- a string, not a selector. Iterate manually over them to
- check if we have already added the method. */
- struct objc_method_list * method_list = class_->methods;
- while (method_list)
- {
- int i;
-
- /* Search the method list. */
- for (i = 0; i < method_list->method_count; ++i)
- {
- struct objc_method * method = &method_list->method_list[i];
-
- if (method->method_name
- && strcmp ((char *)method->method_name, method_name) == 0)
- return NO;
- }
-
- /* The method wasn't found. Follow the link to the next list of
- methods. */
- method_list = method_list->method_next;
- }
- /* The method wasn't found. It's a new one. Go ahead and add
- it. */
- }
- else
- {
- /* Do the standard lookup. This assumes the selectors are
- mapped. */
- if (search_for_method_in_list (class_->methods, selector))
- return NO;
- }
- method_list = (struct objc_method_list *)objc_calloc (1, sizeof (struct objc_method_list));
- method_list->method_count = 1;
- method = &(method_list->method_list[0]);
- method->method_name = objc_malloc (strlen (method_name) + 1);
- strcpy ((char *)method->method_name, method_name);
- method->method_types = objc_malloc (strlen (method_types) + 1);
- strcpy ((char *)method->method_types, method_types);
-
- method->method_imp = implementation;
-
- if (CLS_IS_IN_CONSTRUCTION (class_))
- {
- /* We only need to add the method to the list. It will be
- registered with the runtime when the class pair is registered
- (if ever). */
- method_list->method_next = class_->methods;
- class_->methods = method_list;
- }
- else
- {
- /* Add the method to a live class. */
- objc_mutex_lock (__objc_runtime_mutex);
- class_add_method_list (class_, method_list);
- objc_mutex_unlock (__objc_runtime_mutex);
- }
- return YES;
- }
- IMP
- class_replaceMethod (Class class_, SEL selector, IMP implementation,
- const char *method_types)
- {
- struct objc_method * method;
- if (class_ == Nil || selector == NULL || implementation == NULL
- || method_types == NULL)
- return NULL;
- method = search_for_method_in_hierarchy (class_, selector);
- if (method)
- {
- return method_setImplementation (method, implementation);
- }
- else
- {
- class_addMethod (class_, selector, implementation, method_types);
- return NULL;
- }
- }
- /* Search for a method starting from the current class up its
- hierarchy. Return a pointer to the method's method structure if
- found. NULL otherwise. */
- static struct objc_method *
- search_for_method_in_hierarchy (Class cls, SEL sel)
- {
- struct objc_method * method = NULL;
- Class class;
- if (! sel_is_mapped (sel))
- return NULL;
- /* Scan the method list of the class. If the method isn't found in
- the list then step to its super class. */
- for (class = cls; ((! method) && class); class = class->super_class)
- method = search_for_method_in_list (class->methods, sel);
- return method;
- }
- /* Given a linked list of method and a method's name. Search for the
- named method's method structure. Return a pointer to the method's
- method structure if found. NULL otherwise. */
- struct objc_method *
- search_for_method_in_list (struct objc_method_list * list, SEL op)
- {
- struct objc_method_list * method_list = list;
- if (! sel_is_mapped (op))
- return NULL;
- /* If not found then we'll search the list. */
- while (method_list)
- {
- int i;
- /* Search the method list. */
- for (i = 0; i < method_list->method_count; ++i)
- {
- struct objc_method * method = &method_list->method_list[i];
- if (method->method_name)
- if (method->method_name->sel_id == op->sel_id)
- return method;
- }
- /* The method wasn't found. Follow the link to the next list of
- methods. */
- method_list = method_list->method_next;
- }
- return NULL;
- }
- typedef void * retval_t;
- typedef void * arglist_t;
- static retval_t __objc_forward (id object, SEL sel, arglist_t args);
- /* Forwarding pointers/integers through the normal registers. */
- static id
- __objc_word_forward (id rcv, SEL op, ...)
- {
- void *args, *res;
- args = __builtin_apply_args ();
- res = __objc_forward (rcv, op, args);
- if (res)
- __builtin_return (res);
- else
- return res;
- }
- /* Specific routine for forwarding floats/double because of
- architectural differences on some processors. i386s for example
- which uses a floating point stack versus general registers for
- floating point numbers. This forward routine makes sure that GCC
- restores the proper return values. */
- static double
- __objc_double_forward (id rcv, SEL op, ...)
- {
- void *args, *res;
- args = __builtin_apply_args ();
- res = __objc_forward (rcv, op, args);
- __builtin_return (res);
- }
- #if INVISIBLE_STRUCT_RETURN
- static __big
- #else
- static id
- #endif
- __objc_block_forward (id rcv, SEL op, ...)
- {
- void *args, *res;
- args = __builtin_apply_args ();
- res = __objc_forward (rcv, op, args);
- if (res)
- __builtin_return (res);
- else
- #if INVISIBLE_STRUCT_RETURN
- return (__big) {{0, 0, 0, 0, 0, 0, 0, 0}};
- #else
- return nil;
- #endif
- }
- /* This function is called for methods which are not implemented,
- unless a custom forwarding routine has been installed. Please note
- that most serious users of libobjc (eg, GNUstep base) do install
- their own forwarding routines, and hence this is never actually
- used. But, if no custom forwarding routine is installed, this is
- called when a selector is not recognized. */
- static retval_t
- __objc_forward (id object, SEL sel, arglist_t args)
- {
- IMP imp;
- static SEL frwd_sel = 0; /* !T:SAFE2 */
- SEL err_sel;
- /* First try if the object understands forward::. */
- if (! frwd_sel)
- frwd_sel = sel_get_any_uid ("forward::");
- if (__objc_responds_to (object, frwd_sel))
- {
- imp = get_implementation (object, object->class_pointer, frwd_sel);
- return (*imp) (object, frwd_sel, sel, args);
- }
- /* If the object recognizes the doesNotRecognize: method then we're
- going to send it. */
- err_sel = sel_get_any_uid ("doesNotRecognize:");
- if (__objc_responds_to (object, err_sel))
- {
- imp = get_implementation (object, object->class_pointer, err_sel);
- return (*imp) (object, err_sel, sel);
- }
-
- /* The object doesn't recognize the method. Check for responding to
- error:. If it does then sent it. */
- {
- char msg[256 + strlen ((const char *) sel_getName (sel))
- + strlen ((const char *) object->class_pointer->name)];
- sprintf (msg, "(%s) %s does not recognize %s",
- (CLS_ISMETA (object->class_pointer)
- ? "class"
- : "instance" ),
- object->class_pointer->name, sel_getName (sel));
- /* The object doesn't respond to doesNotRecognize:. Therefore, a
- default action is taken. */
- _objc_abort ("%s\n", msg);
- return 0;
- }
- }
- void
- __objc_print_dtable_stats (void)
- {
- int total = 0;
- objc_mutex_lock (__objc_runtime_mutex);
- #ifdef OBJC_SPARSE2
- printf ("memory usage: (%s)\n", "2-level sparse arrays");
- #else
- printf ("memory usage: (%s)\n", "3-level sparse arrays");
- #endif
- printf ("arrays: %d = %ld bytes\n", narrays,
- (long) ((size_t) narrays * sizeof (struct sarray)));
- total += narrays * sizeof (struct sarray);
- printf ("buckets: %d = %ld bytes\n", nbuckets,
- (long) ((size_t) nbuckets * sizeof (struct sbucket)));
- total += nbuckets * sizeof (struct sbucket);
- printf ("idxtables: %d = %ld bytes\n",
- idxsize, (long) ((size_t) idxsize * sizeof (void *)));
- total += idxsize * sizeof (void *);
- printf ("-----------------------------------\n");
- printf ("total: %d bytes\n", total);
- printf ("===================================\n");
- objc_mutex_unlock (__objc_runtime_mutex);
- }
- static cache_ptr prepared_dtable_table = 0;
- /* This function is called by: objc_msg_lookup, get_imp and
- __objc_responds_to (and the dispatch table installation functions
- themselves) to install a dispatch table for a class.
- If CLS is a class, it installs instance methods.
- If CLS is a meta class, it installs class methods.
- In either case +initialize is invoked for the corresponding class.
- The implementation must insure that the dispatch table is not
- installed until +initialize completes. Otherwise it opens a
- potential race since the installation of the dispatch table is used
- as gate in regular method dispatch and we need to guarantee that
- +initialize is the first method invoked an that no other thread my
- dispatch messages to the class before +initialize completes. */
- static void
- __objc_install_dtable_for_class (Class cls)
- {
- /* If the class has not yet had its class links resolved, we must
- re-compute all class links. */
- if (! CLS_ISRESOLV (cls))
- __objc_resolve_class_links ();
- /* Make sure the super class has its dispatch table installed or is
- at least preparing. We do not need to send initialize for the
- super class since __objc_send_initialize will insure that. */
- if (cls->super_class
- && cls->super_class->dtable == __objc_uninstalled_dtable
- && !__objc_prepared_dtable_for_class (cls->super_class))
- {
- __objc_install_dtable_for_class (cls->super_class);
- /* The superclass initialisation may have also initialised the
- current class, in which case there is no more to do. */
- if (cls->dtable != __objc_uninstalled_dtable)
- return;
- }
- /* We have already been prepared but +initialize hasn't completed.
- The +initialize implementation is probably sending 'self'
- messages. We rely on _objc_get_prepared_imp to retrieve the
- implementation pointers. */
- if (__objc_prepared_dtable_for_class (cls))
- return;
- /* We have this function cache the implementation pointers for
- _objc_get_prepared_imp but the dispatch table won't be initilized
- until __objc_send_initialize completes. */
- __objc_prepare_dtable_for_class (cls);
- /* We may have already invoked +initialize but
- __objc_update_dispatch_table_for_class invoked by
- class_add_method_list may have reset dispatch table. */
- /* Call +initialize. If we are a real class, we are installing
- instance methods. If we are a meta class, we are installing
- class methods. The __objc_send_initialize itself will insure
- that the message is called only once per class. */
- if (CLS_ISCLASS (cls))
- __objc_send_initialize (cls);
- else
- {
- /* Retrieve the class from the meta class. */
- Class c = objc_getClass (cls->name);
- assert (CLS_ISMETA (cls));
- assert (c);
- __objc_send_initialize (c);
- }
- /* We install the dispatch table correctly when +initialize completed. */
- __objc_install_prepared_dtable_for_class (cls);
- }
- /* Builds the dispatch table for the class CLS and stores it in a
- place where it can be retrieved by __objc_get_prepared_imp until
- __objc_install_prepared_dtable_for_class installs it into the
- class. The dispatch table should not be installed into the class
- until +initialize has completed. */
- static void
- __objc_prepare_dtable_for_class (Class cls)
- {
- struct sarray *dtable;
- struct sarray *super_dtable;
- /* This table could be initialized in init.c. We can not use the
- class name since the class maintains the instance methods and the
- meta class maintains the the class methods yet both share the
- same name. Classes should be unique in any program. */
- if (! prepared_dtable_table)
- prepared_dtable_table
- = objc_hash_new (32,
- (hash_func_type) objc_hash_ptr,
- (compare_func_type) objc_compare_ptrs);
-
- /* If the class has not yet had its class links resolved, we must
- re-compute all class links. */
- if (! CLS_ISRESOLV (cls))
- __objc_resolve_class_links ();
- assert (cls);
- assert (cls->dtable == __objc_uninstalled_dtable);
- /* If there is already a prepared dtable for this class, we must
- replace it with a new version (since there must have been methods
- added to or otherwise modified in the class while executing
- +initialize, and the table needs to be recomputed. */
- dtable = __objc_prepared_dtable_for_class (cls);
- if (dtable != 0)
- {
- objc_hash_remove (prepared_dtable_table, cls);
- sarray_free (dtable);
- }
- /* Now prepare the dtable for population. */
- assert (cls != cls->super_class);
- if (cls->super_class)
- {
- /* Inherit the method list from the super class. Yet the super
- class may still be initializing in the case when a class
- cluster sub class initializes its super classes. */
- if (cls->super_class->dtable == __objc_uninstalled_dtable)
- __objc_install_dtable_for_class (cls->super_class);
- super_dtable = cls->super_class->dtable;
- /* If the dispatch table is not yet installed, we are still in
- the process of executing +initialize. Yet the dispatch table
- should be available. */
- if (super_dtable == __objc_uninstalled_dtable)
- super_dtable = __objc_prepared_dtable_for_class (cls->super_class);
- assert (super_dtable);
- dtable = sarray_lazy_copy (super_dtable);
- }
- else
- dtable = sarray_new (__objc_selector_max_index, 0);
- __objc_install_methods_in_dtable (dtable, cls->methods);
- objc_hash_add (&prepared_dtable_table,
- cls,
- dtable);
- }
- /* This wrapper only exists to allow an easy replacement of the lookup
- implementation and it is expected that the compiler will optimize
- it away. */
- static struct sarray *
- __objc_prepared_dtable_for_class (Class cls)
- {
- struct sarray *dtable = 0;
- assert (cls);
- if (prepared_dtable_table)
- dtable = objc_hash_value_for_key (prepared_dtable_table, cls);
- /* dtable my be nil, since we call this to check whether we are
- currently preparing before we start preparing. */
- return dtable;
- }
- /* Helper function for messages sent to CLS or implementation pointers
- retrieved from CLS during +initialize before the dtable is
- installed. When a class implicitly initializes another class which
- in turn implicitly invokes methods in this class, before the
- implementation of +initialize of CLS completes, this returns the
- expected implementation. Forwarding remains the responsibility of
- objc_msg_lookup. This function should only be called under the
- global lock. */
- static IMP
- __objc_get_prepared_imp (Class cls,SEL sel)
- {
- struct sarray *dtable;
- IMP imp;
- assert (cls);
- assert (sel);
- assert (cls->dtable == __objc_uninstalled_dtable);
- dtable = __objc_prepared_dtable_for_class (cls);
- assert (dtable);
- assert (dtable != __objc_uninstalled_dtable);
- imp = sarray_get_safe (dtable, (size_t) sel->sel_id);
- /* imp may be Nil if the method does not exist and we may fallback
- to the forwarding implementation later. */
- return imp;
- }
- /* When this function is called +initialize should be completed. So
- now we are safe to install the dispatch table for the class so that
- they become available for other threads that may be waiting in the
- lock. */
- static void
- __objc_install_prepared_dtable_for_class (Class cls)
- {
- assert (cls);
- assert (cls->dtable == __objc_uninstalled_dtable);
- cls->dtable = __objc_prepared_dtable_for_class (cls);
- assert (cls->dtable);
- assert (cls->dtable != __objc_uninstalled_dtable);
- objc_hash_remove (prepared_dtable_table, cls);
- }
|