136ac495dSmrg /* GNU Objective C Runtime message lookup
2*8feb0f0bSmrg Copyright (C) 1993-2020 Free Software Foundation, Inc.
336ac495dSmrg Contributed by Kresten Krab Thorup
436ac495dSmrg
536ac495dSmrg This file is part of GCC.
636ac495dSmrg
736ac495dSmrg GCC is free software; you can redistribute it and/or modify it under the
836ac495dSmrg terms of the GNU General Public License as published by the Free Software
936ac495dSmrg Foundation; either version 3, or (at your option) any later version.
1036ac495dSmrg
1136ac495dSmrg GCC is distributed in the hope that it will be useful, but WITHOUT ANY
1236ac495dSmrg WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1336ac495dSmrg FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1436ac495dSmrg details.
1536ac495dSmrg
1636ac495dSmrg Under Section 7 of GPL version 3, you are granted additional
1736ac495dSmrg permissions described in the GCC Runtime Library Exception, version
1836ac495dSmrg 3.1, as published by the Free Software Foundation.
1936ac495dSmrg
2036ac495dSmrg You should have received a copy of the GNU General Public License and
2136ac495dSmrg a copy of the GCC Runtime Library Exception along with this program;
2236ac495dSmrg see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
2336ac495dSmrg <http://www.gnu.org/licenses/>. */
2436ac495dSmrg
2536ac495dSmrg /* Uncommented the following line to enable debug logging. Use this
2636ac495dSmrg only while debugging the runtime. */
2736ac495dSmrg /* #define DEBUG 1 */
2836ac495dSmrg
2936ac495dSmrg /* FIXME: This should be using libffi instead of __builtin_apply
3036ac495dSmrg and friends. */
3136ac495dSmrg
3236ac495dSmrg #include "objc-private/common.h"
3336ac495dSmrg #include "objc-private/error.h"
3436ac495dSmrg #include "tconfig.h"
3536ac495dSmrg #include "coretypes.h"
3636ac495dSmrg #include "objc/runtime.h"
3736ac495dSmrg #include "objc/message.h" /* For objc_msg_lookup(), objc_msg_lookup_super(). */
3836ac495dSmrg #include "objc/thr.h"
3936ac495dSmrg #include "objc-private/module-abi-8.h"
4036ac495dSmrg #include "objc-private/runtime.h"
4136ac495dSmrg #include "objc-private/hash.h"
4236ac495dSmrg #include "objc-private/sarray.h"
4336ac495dSmrg #include "objc-private/selector.h" /* For sel_is_mapped() */
4436ac495dSmrg #include "runtime-info.h"
4536ac495dSmrg #include <assert.h> /* For assert */
4636ac495dSmrg #include <string.h> /* For strlen */
4736ac495dSmrg
4836ac495dSmrg #define INVISIBLE_STRUCT_RETURN 1
4936ac495dSmrg
5036ac495dSmrg /* The uninstalled dispatch table. If a class' dispatch table points
5136ac495dSmrg to __objc_uninstalled_dtable then that means it needs its dispatch
5236ac495dSmrg table to be installed. */
5336ac495dSmrg struct sarray *__objc_uninstalled_dtable = 0; /* !T:MUTEX */
5436ac495dSmrg
5536ac495dSmrg /* Two hooks for method forwarding. If either is set, it is invoked to
5636ac495dSmrg * return a function that performs the real forwarding. If both are
5736ac495dSmrg * set, the result of __objc_msg_forward2 will be preferred over that
5836ac495dSmrg * of __objc_msg_forward. If both return NULL or are unset, the
5936ac495dSmrg * libgcc based functions (__builtin_apply and friends) are used. */
6036ac495dSmrg IMP (*__objc_msg_forward) (SEL) = NULL;
6136ac495dSmrg IMP (*__objc_msg_forward2) (id, SEL) = NULL;
6236ac495dSmrg
6336ac495dSmrg /* Send +initialize to class. */
6436ac495dSmrg static void __objc_send_initialize (Class);
6536ac495dSmrg
6636ac495dSmrg /* Forward declare some functions */
6736ac495dSmrg static void __objc_install_dtable_for_class (Class cls);
6836ac495dSmrg static void __objc_prepare_dtable_for_class (Class cls);
6936ac495dSmrg static void __objc_install_prepared_dtable_for_class (Class cls);
7036ac495dSmrg
7136ac495dSmrg static struct sarray *__objc_prepared_dtable_for_class (Class cls);
7236ac495dSmrg static IMP __objc_get_prepared_imp (Class cls,SEL sel);
7336ac495dSmrg
7436ac495dSmrg
7536ac495dSmrg /* Various forwarding functions that are used based upon the
7636ac495dSmrg return type for the selector.
7736ac495dSmrg __objc_block_forward for structures.
7836ac495dSmrg __objc_double_forward for floats/doubles.
7936ac495dSmrg __objc_word_forward for pointers or types that fit in registers. */
8036ac495dSmrg static double __objc_double_forward (id, SEL, ...);
8136ac495dSmrg static id __objc_word_forward (id, SEL, ...);
8236ac495dSmrg typedef struct { id many[8]; } __big;
8336ac495dSmrg #if INVISIBLE_STRUCT_RETURN
8436ac495dSmrg static __big
8536ac495dSmrg #else
8636ac495dSmrg static id
8736ac495dSmrg #endif
8836ac495dSmrg __objc_block_forward (id, SEL, ...);
8936ac495dSmrg static struct objc_method * search_for_method_in_hierarchy (Class class, SEL sel);
9036ac495dSmrg struct objc_method * search_for_method_in_list (struct objc_method_list * list, SEL op);
9136ac495dSmrg id nil_method (id, SEL);
9236ac495dSmrg
9336ac495dSmrg /* Make sure this inline function is exported regardless of GNU89 or C99
9436ac495dSmrg inlining semantics as it is part of the libobjc ABI. */
9536ac495dSmrg extern IMP __objc_get_forward_imp (id, SEL);
9636ac495dSmrg
9736ac495dSmrg /* Given a selector, return the proper forwarding implementation. */
9836ac495dSmrg IMP
__objc_get_forward_imp(id rcv,SEL sel)9936ac495dSmrg __objc_get_forward_imp (id rcv, SEL sel)
10036ac495dSmrg {
10136ac495dSmrg /* If a custom forwarding hook was registered, try getting a
10236ac495dSmrg forwarding function from it. There are two forward routine hooks,
10336ac495dSmrg one that takes the receiver as an argument and one that does
10436ac495dSmrg not. */
10536ac495dSmrg if (__objc_msg_forward2)
10636ac495dSmrg {
10736ac495dSmrg IMP result;
10836ac495dSmrg if ((result = __objc_msg_forward2 (rcv, sel)) != NULL)
10936ac495dSmrg return result;
11036ac495dSmrg }
11136ac495dSmrg if (__objc_msg_forward)
11236ac495dSmrg {
11336ac495dSmrg IMP result;
11436ac495dSmrg if ((result = __objc_msg_forward (sel)) != NULL)
11536ac495dSmrg return result;
11636ac495dSmrg }
11736ac495dSmrg
11836ac495dSmrg /* In all other cases, use the default forwarding functions built
11936ac495dSmrg using __builtin_apply and friends. */
12036ac495dSmrg {
12136ac495dSmrg const char *t = sel->sel_types;
12236ac495dSmrg
12336ac495dSmrg if (t && (*t == '[' || *t == '(' || *t == '{')
12436ac495dSmrg #ifdef OBJC_MAX_STRUCT_BY_VALUE
12536ac495dSmrg && objc_sizeof_type (t) > OBJC_MAX_STRUCT_BY_VALUE
12636ac495dSmrg #endif
12736ac495dSmrg )
12836ac495dSmrg return (IMP)__objc_block_forward;
12936ac495dSmrg else if (t && (*t == 'f' || *t == 'd'))
13036ac495dSmrg return (IMP)__objc_double_forward;
13136ac495dSmrg else
13236ac495dSmrg return (IMP)__objc_word_forward;
13336ac495dSmrg }
13436ac495dSmrg }
13536ac495dSmrg
13636ac495dSmrg /* Selectors for +resolveClassMethod: and +resolveInstanceMethod:.
13736ac495dSmrg These are set up at startup. */
13836ac495dSmrg static SEL selector_resolveClassMethod = NULL;
13936ac495dSmrg static SEL selector_resolveInstanceMethod = NULL;
14036ac495dSmrg
14136ac495dSmrg /* Internal routines use to resolve a class method using
14236ac495dSmrg +resolveClassMethod:. 'class' is always a non-Nil class (*not* a
14336ac495dSmrg meta-class), and 'sel' is the selector that we are trying to
14436ac495dSmrg resolve. This must be called when class is not Nil, and the
14536ac495dSmrg dispatch table for class methods has already been installed.
14636ac495dSmrg
14736ac495dSmrg This routine tries to call +resolveClassMethod: to give an
14836ac495dSmrg opportunity to resolve the method. If +resolveClassMethod: returns
14936ac495dSmrg YES, it tries looking up the method again, and if found, it returns
15036ac495dSmrg it. Else, it returns NULL. */
15136ac495dSmrg static inline
15236ac495dSmrg IMP
__objc_resolve_class_method(Class class,SEL sel)15336ac495dSmrg __objc_resolve_class_method (Class class, SEL sel)
15436ac495dSmrg {
15536ac495dSmrg /* We need to lookup +resolveClassMethod:. */
15636ac495dSmrg BOOL (*resolveMethodIMP) (id, SEL, SEL);
15736ac495dSmrg
15836ac495dSmrg /* The dispatch table for class methods is already installed and we
15936ac495dSmrg don't want any forwarding to happen when looking up this method,
16036ac495dSmrg so we just look it up directly. Note that if 'sel' is precisely
16136ac495dSmrg +resolveClassMethod:, this would look it up yet again and find
16236ac495dSmrg nothing. That's no problem and there's no recursion. */
16336ac495dSmrg resolveMethodIMP = (BOOL (*) (id, SEL, SEL))sarray_get_safe
16436ac495dSmrg (class->class_pointer->dtable, (size_t) selector_resolveClassMethod->sel_id);
16536ac495dSmrg
16636ac495dSmrg if (resolveMethodIMP && resolveMethodIMP ((id)class, selector_resolveClassMethod, sel))
16736ac495dSmrg {
16836ac495dSmrg /* +resolveClassMethod: returned YES. Look the method up again.
16936ac495dSmrg We already know the dtable is installed. */
17036ac495dSmrg
17136ac495dSmrg /* TODO: There is the case where +resolveClassMethod: is buggy
17236ac495dSmrg and returned YES without actually adding the method. We
17336ac495dSmrg could maybe print an error message. */
17436ac495dSmrg return sarray_get_safe (class->class_pointer->dtable, (size_t) sel->sel_id);
17536ac495dSmrg }
17636ac495dSmrg
17736ac495dSmrg return NULL;
17836ac495dSmrg }
17936ac495dSmrg
18036ac495dSmrg /* Internal routines use to resolve a instance method using
18136ac495dSmrg +resolveInstanceMethod:. 'class' is always a non-Nil class, and
18236ac495dSmrg 'sel' is the selector that we are trying to resolve. This must be
18336ac495dSmrg called when class is not Nil, and the dispatch table for instance
18436ac495dSmrg methods has already been installed.
18536ac495dSmrg
18636ac495dSmrg This routine tries to call +resolveInstanceMethod: to give an
18736ac495dSmrg opportunity to resolve the method. If +resolveInstanceMethod:
18836ac495dSmrg returns YES, it tries looking up the method again, and if found, it
18936ac495dSmrg returns it. Else, it returns NULL. */
19036ac495dSmrg static inline
19136ac495dSmrg IMP
__objc_resolve_instance_method(Class class,SEL sel)19236ac495dSmrg __objc_resolve_instance_method (Class class, SEL sel)
19336ac495dSmrg {
19436ac495dSmrg /* We need to lookup +resolveInstanceMethod:. */
19536ac495dSmrg BOOL (*resolveMethodIMP) (id, SEL, SEL);
19636ac495dSmrg
19736ac495dSmrg /* The dispatch table for class methods may not be already installed
19836ac495dSmrg so we have to install it if needed. */
19936ac495dSmrg resolveMethodIMP = sarray_get_safe (class->class_pointer->dtable,
20036ac495dSmrg (size_t) selector_resolveInstanceMethod->sel_id);
20136ac495dSmrg if (resolveMethodIMP == 0)
20236ac495dSmrg {
20336ac495dSmrg /* Try again after installing the dtable. */
20436ac495dSmrg if (class->class_pointer->dtable == __objc_uninstalled_dtable)
20536ac495dSmrg {
20636ac495dSmrg objc_mutex_lock (__objc_runtime_mutex);
20736ac495dSmrg if (class->class_pointer->dtable == __objc_uninstalled_dtable)
20836ac495dSmrg __objc_install_dtable_for_class (class->class_pointer);
20936ac495dSmrg objc_mutex_unlock (__objc_runtime_mutex);
21036ac495dSmrg }
21136ac495dSmrg resolveMethodIMP = sarray_get_safe (class->class_pointer->dtable,
21236ac495dSmrg (size_t) selector_resolveInstanceMethod->sel_id);
21336ac495dSmrg }
21436ac495dSmrg
21536ac495dSmrg if (resolveMethodIMP && resolveMethodIMP ((id)class, selector_resolveInstanceMethod, sel))
21636ac495dSmrg {
21736ac495dSmrg /* +resolveInstanceMethod: returned YES. Look the method up
21836ac495dSmrg again. We already know the dtable is installed. */
21936ac495dSmrg
22036ac495dSmrg /* TODO: There is the case where +resolveInstanceMethod: is
22136ac495dSmrg buggy and returned YES without actually adding the method.
22236ac495dSmrg We could maybe print an error message. */
22336ac495dSmrg return sarray_get_safe (class->dtable, (size_t) sel->sel_id);
22436ac495dSmrg }
22536ac495dSmrg
22636ac495dSmrg return NULL;
22736ac495dSmrg }
22836ac495dSmrg
22936ac495dSmrg /* Given a CLASS and selector, return the implementation corresponding
23036ac495dSmrg to the method of the selector.
23136ac495dSmrg
23236ac495dSmrg If CLASS is a class, the instance method is returned.
23336ac495dSmrg If CLASS is a meta class, the class method is returned.
23436ac495dSmrg
23536ac495dSmrg Since this requires the dispatch table to be installed, this function
23636ac495dSmrg will implicitly invoke +initialize for CLASS if it hasn't been
23736ac495dSmrg invoked yet. This also insures that +initialize has been invoked
23836ac495dSmrg when the returned implementation is called directly.
23936ac495dSmrg
24036ac495dSmrg The forwarding hooks require the receiver as an argument (if they are to
24136ac495dSmrg perform dynamic lookup in proxy objects etc), so this function has a
24236ac495dSmrg receiver argument to be used with those hooks. */
24336ac495dSmrg static inline
24436ac495dSmrg IMP
get_implementation(id receiver,Class class,SEL sel)24536ac495dSmrg get_implementation (id receiver, Class class, SEL sel)
24636ac495dSmrg {
24736ac495dSmrg void *res;
24836ac495dSmrg
24936ac495dSmrg if (class->dtable == __objc_uninstalled_dtable)
25036ac495dSmrg {
25136ac495dSmrg /* The dispatch table needs to be installed. */
25236ac495dSmrg objc_mutex_lock (__objc_runtime_mutex);
25336ac495dSmrg
25436ac495dSmrg /* Double-checked locking pattern: Check
25536ac495dSmrg __objc_uninstalled_dtable again in case another thread
25636ac495dSmrg installed the dtable while we were waiting for the lock to be
25736ac495dSmrg released. */
25836ac495dSmrg if (class->dtable == __objc_uninstalled_dtable)
25936ac495dSmrg __objc_install_dtable_for_class (class);
26036ac495dSmrg
26136ac495dSmrg /* If the dispatch table is not yet installed, we are still in
26236ac495dSmrg the process of executing +initialize. But the implementation
26336ac495dSmrg pointer should be available in the prepared ispatch table if
26436ac495dSmrg it exists at all. */
26536ac495dSmrg if (class->dtable == __objc_uninstalled_dtable)
26636ac495dSmrg {
26736ac495dSmrg assert (__objc_prepared_dtable_for_class (class) != 0);
26836ac495dSmrg res = __objc_get_prepared_imp (class, sel);
26936ac495dSmrg }
27036ac495dSmrg else
27136ac495dSmrg res = 0;
27236ac495dSmrg
27336ac495dSmrg objc_mutex_unlock (__objc_runtime_mutex);
27436ac495dSmrg /* Call ourselves with the installed dispatch table and get the
27536ac495dSmrg real method. */
27636ac495dSmrg if (!res)
27736ac495dSmrg res = get_implementation (receiver, class, sel);
27836ac495dSmrg }
27936ac495dSmrg else
28036ac495dSmrg {
28136ac495dSmrg /* The dispatch table has been installed. */
28236ac495dSmrg res = sarray_get_safe (class->dtable, (size_t) sel->sel_id);
28336ac495dSmrg if (res == 0)
28436ac495dSmrg {
28536ac495dSmrg /* The dispatch table has been installed, and the method is
28636ac495dSmrg not in the dispatch table. So the method just doesn't
28736ac495dSmrg exist for the class. */
28836ac495dSmrg
28936ac495dSmrg /* Try going through the +resolveClassMethod: or
29036ac495dSmrg +resolveInstanceMethod: process. */
29136ac495dSmrg if (CLS_ISMETA (class))
29236ac495dSmrg {
29336ac495dSmrg /* We have the meta class, but we need to invoke the
29436ac495dSmrg +resolveClassMethod: method on the class. So, we
29536ac495dSmrg need to obtain the class from the meta class, which
29636ac495dSmrg we do using the fact that both the class and the
29736ac495dSmrg meta-class have the same name. */
29836ac495dSmrg Class realClass = objc_lookUpClass (class->name);
29936ac495dSmrg if (realClass)
30036ac495dSmrg res = __objc_resolve_class_method (realClass, sel);
30136ac495dSmrg }
30236ac495dSmrg else
30336ac495dSmrg res = __objc_resolve_instance_method (class, sel);
30436ac495dSmrg
30536ac495dSmrg if (res == 0)
30636ac495dSmrg res = __objc_get_forward_imp (receiver, sel);
30736ac495dSmrg }
30836ac495dSmrg }
30936ac495dSmrg return res;
31036ac495dSmrg }
31136ac495dSmrg
31236ac495dSmrg /* Make sure this inline function is exported regardless of GNU89 or C99
31336ac495dSmrg inlining semantics as it is part of the libobjc ABI. */
31436ac495dSmrg extern IMP get_imp (Class, SEL);
31536ac495dSmrg
31636ac495dSmrg inline
31736ac495dSmrg IMP
get_imp(Class class,SEL sel)31836ac495dSmrg get_imp (Class class, SEL sel)
31936ac495dSmrg {
32036ac495dSmrg /* In a vanilla implementation we would first check if the dispatch
32136ac495dSmrg table is installed. Here instead, to get more speed in the
32236ac495dSmrg standard case (that the dispatch table is installed) we first try
32336ac495dSmrg to get the imp using brute force. Only if that fails, we do what
32436ac495dSmrg we should have been doing from the very beginning, that is, check
32536ac495dSmrg if the dispatch table needs to be installed, install it if it's
32636ac495dSmrg not installed, and retrieve the imp from the table if it's
32736ac495dSmrg installed. */
32836ac495dSmrg void *res = sarray_get_safe (class->dtable, (size_t) sel->sel_id);
32936ac495dSmrg if (res == 0)
33036ac495dSmrg {
33136ac495dSmrg res = get_implementation(nil, class, sel);
33236ac495dSmrg }
33336ac495dSmrg return res;
33436ac495dSmrg }
33536ac495dSmrg
33636ac495dSmrg /* The new name of get_imp(). */
33736ac495dSmrg IMP
class_getMethodImplementation(Class class_,SEL selector)33836ac495dSmrg class_getMethodImplementation (Class class_, SEL selector)
33936ac495dSmrg {
34036ac495dSmrg if (class_ == Nil || selector == NULL)
34136ac495dSmrg return NULL;
34236ac495dSmrg
34336ac495dSmrg /* get_imp is inlined, so we're good. */
34436ac495dSmrg return get_imp (class_, selector);
34536ac495dSmrg }
34636ac495dSmrg
34736ac495dSmrg /* Given a method, return its implementation. This has been replaced
34836ac495dSmrg by method_getImplementation() in the modern API. */
34936ac495dSmrg IMP
method_get_imp(struct objc_method * method)35036ac495dSmrg method_get_imp (struct objc_method * method)
35136ac495dSmrg {
35236ac495dSmrg return (method != (struct objc_method *)0) ? method->method_imp : (IMP)0;
35336ac495dSmrg }
35436ac495dSmrg
35536ac495dSmrg /* Query if an object can respond to a selector, returns YES if the
35636ac495dSmrg object implements the selector otherwise NO. Does not check if the
35736ac495dSmrg method can be forwarded. Since this requires the dispatch table to
35836ac495dSmrg installed, this function will implicitly invoke +initialize for the
35936ac495dSmrg class of OBJECT if it hasn't been invoked yet. */
36036ac495dSmrg BOOL
__objc_responds_to(id object,SEL sel)36136ac495dSmrg __objc_responds_to (id object, SEL sel)
36236ac495dSmrg {
36336ac495dSmrg void *res;
36436ac495dSmrg struct sarray *dtable;
36536ac495dSmrg
36636ac495dSmrg /* Install dispatch table if need be */
36736ac495dSmrg dtable = object->class_pointer->dtable;
36836ac495dSmrg if (dtable == __objc_uninstalled_dtable)
36936ac495dSmrg {
37036ac495dSmrg objc_mutex_lock (__objc_runtime_mutex);
37136ac495dSmrg if (object->class_pointer->dtable == __objc_uninstalled_dtable)
37236ac495dSmrg __objc_install_dtable_for_class (object->class_pointer);
37336ac495dSmrg
37436ac495dSmrg /* If the dispatch table is not yet installed, we are still in
37536ac495dSmrg the process of executing +initialize. Yet the dispatch table
37636ac495dSmrg should be available. */
37736ac495dSmrg if (object->class_pointer->dtable == __objc_uninstalled_dtable)
37836ac495dSmrg {
37936ac495dSmrg dtable = __objc_prepared_dtable_for_class (object->class_pointer);
38036ac495dSmrg assert (dtable);
38136ac495dSmrg }
38236ac495dSmrg else
38336ac495dSmrg dtable = object->class_pointer->dtable;
38436ac495dSmrg
38536ac495dSmrg objc_mutex_unlock (__objc_runtime_mutex);
38636ac495dSmrg }
38736ac495dSmrg
38836ac495dSmrg /* Get the method from the dispatch table. */
38936ac495dSmrg res = sarray_get_safe (dtable, (size_t) sel->sel_id);
39036ac495dSmrg return (res != 0) ? YES : NO;
39136ac495dSmrg }
39236ac495dSmrg
39336ac495dSmrg BOOL
class_respondsToSelector(Class class_,SEL selector)39436ac495dSmrg class_respondsToSelector (Class class_, SEL selector)
39536ac495dSmrg {
39636ac495dSmrg struct sarray *dtable;
39736ac495dSmrg void *res;
39836ac495dSmrg
39936ac495dSmrg if (class_ == Nil || selector == NULL)
40036ac495dSmrg return NO;
40136ac495dSmrg
40236ac495dSmrg /* Install dispatch table if need be. */
40336ac495dSmrg dtable = class_->dtable;
40436ac495dSmrg if (dtable == __objc_uninstalled_dtable)
40536ac495dSmrg {
40636ac495dSmrg objc_mutex_lock (__objc_runtime_mutex);
40736ac495dSmrg if (class_->dtable == __objc_uninstalled_dtable)
40836ac495dSmrg __objc_install_dtable_for_class (class_);
40936ac495dSmrg
41036ac495dSmrg /* If the dispatch table is not yet installed,
41136ac495dSmrg we are still in the process of executing +initialize.
41236ac495dSmrg Yet the dispatch table should be available. */
41336ac495dSmrg if (class_->dtable == __objc_uninstalled_dtable)
41436ac495dSmrg {
41536ac495dSmrg dtable = __objc_prepared_dtable_for_class (class_);
41636ac495dSmrg assert (dtable);
41736ac495dSmrg }
41836ac495dSmrg else
41936ac495dSmrg dtable = class_->dtable;
42036ac495dSmrg
42136ac495dSmrg objc_mutex_unlock (__objc_runtime_mutex);
42236ac495dSmrg }
42336ac495dSmrg
42436ac495dSmrg /* Get the method from the dispatch table. */
42536ac495dSmrg res = sarray_get_safe (dtable, (size_t) selector->sel_id);
42636ac495dSmrg return (res != 0) ? YES : NO;
42736ac495dSmrg }
42836ac495dSmrg
42936ac495dSmrg /* This is the lookup function. All entries in the table are either a
43036ac495dSmrg valid method *or* zero. If zero then either the dispatch table
43136ac495dSmrg needs to be installed or it doesn't exist and forwarding is
43236ac495dSmrg attempted. */
43336ac495dSmrg IMP
objc_msg_lookup(id receiver,SEL op)43436ac495dSmrg objc_msg_lookup (id receiver, SEL op)
43536ac495dSmrg {
43636ac495dSmrg IMP result;
43736ac495dSmrg if (receiver)
43836ac495dSmrg {
43936ac495dSmrg /* First try a quick lookup assuming the dispatch table exists. */
44036ac495dSmrg result = sarray_get_safe (receiver->class_pointer->dtable,
44136ac495dSmrg (sidx)op->sel_id);
44236ac495dSmrg if (result == 0)
44336ac495dSmrg {
44436ac495dSmrg /* Not found ... call get_implementation () to install the
44536ac495dSmrg dispatch table and call +initialize as required,
44636ac495dSmrg providing the method implementation or a forwarding
44736ac495dSmrg function. */
44836ac495dSmrg result = get_implementation (receiver, receiver->class_pointer, op);
44936ac495dSmrg }
45036ac495dSmrg return result;
45136ac495dSmrg }
45236ac495dSmrg else
45336ac495dSmrg return (IMP)nil_method;
45436ac495dSmrg }
45536ac495dSmrg
45636ac495dSmrg IMP
objc_msg_lookup_super(struct objc_super * super,SEL sel)45736ac495dSmrg objc_msg_lookup_super (struct objc_super *super, SEL sel)
45836ac495dSmrg {
45936ac495dSmrg if (super->self)
46036ac495dSmrg return get_imp (super->super_class, sel);
46136ac495dSmrg else
46236ac495dSmrg return (IMP)nil_method;
46336ac495dSmrg }
46436ac495dSmrg
46536ac495dSmrg void
__objc_init_dispatch_tables()46636ac495dSmrg __objc_init_dispatch_tables ()
46736ac495dSmrg {
46836ac495dSmrg __objc_uninstalled_dtable = sarray_new (200, 0);
46936ac495dSmrg
47036ac495dSmrg /* TODO: It would be cool to register typed selectors here. */
47136ac495dSmrg selector_resolveClassMethod = sel_registerName ("resolveClassMethod:");
47236ac495dSmrg selector_resolveInstanceMethod = sel_registerName ("resolveInstanceMethod:");
47336ac495dSmrg }
47436ac495dSmrg
47536ac495dSmrg
47636ac495dSmrg /* Install dummy table for class which causes the first message to
47736ac495dSmrg that class (or instances hereof) to be initialized properly. */
47836ac495dSmrg void
__objc_install_premature_dtable(Class class)47936ac495dSmrg __objc_install_premature_dtable (Class class)
48036ac495dSmrg {
48136ac495dSmrg assert (__objc_uninstalled_dtable);
48236ac495dSmrg class->dtable = __objc_uninstalled_dtable;
48336ac495dSmrg }
48436ac495dSmrg
48536ac495dSmrg /* Send +initialize to class if not already done. */
48636ac495dSmrg static void
__objc_send_initialize(Class class)48736ac495dSmrg __objc_send_initialize (Class class)
48836ac495dSmrg {
48936ac495dSmrg /* This *must* be a class object. */
49036ac495dSmrg assert (CLS_ISCLASS (class));
49136ac495dSmrg assert (! CLS_ISMETA (class));
49236ac495dSmrg
49336ac495dSmrg /* class_add_method_list/__objc_update_dispatch_table_for_class may
49436ac495dSmrg have reset the dispatch table. The canonical way to insure that
49536ac495dSmrg we send +initialize just once, is this flag. */
49636ac495dSmrg if (! CLS_ISINITIALIZED (class))
49736ac495dSmrg {
49836ac495dSmrg DEBUG_PRINTF ("+initialize: need to initialize class '%s'\n", class->name);
49936ac495dSmrg CLS_SETINITIALIZED (class);
50036ac495dSmrg CLS_SETINITIALIZED (class->class_pointer);
50136ac495dSmrg
50236ac495dSmrg /* Create the garbage collector type memory description. */
50336ac495dSmrg __objc_generate_gc_type_description (class);
50436ac495dSmrg
50536ac495dSmrg if (class->super_class)
50636ac495dSmrg __objc_send_initialize (class->super_class);
50736ac495dSmrg
50836ac495dSmrg {
50936ac495dSmrg SEL op = sel_registerName ("initialize");
51036ac495dSmrg struct objc_method *method = search_for_method_in_hierarchy (class->class_pointer,
51136ac495dSmrg op);
51236ac495dSmrg
51336ac495dSmrg if (method)
51436ac495dSmrg {
51536ac495dSmrg DEBUG_PRINTF (" begin of [%s +initialize]\n", class->name);
51636ac495dSmrg (*method->method_imp) ((id)class, op);
51736ac495dSmrg DEBUG_PRINTF (" end of [%s +initialize]\n", class->name);
51836ac495dSmrg }
51936ac495dSmrg #ifdef DEBUG
52036ac495dSmrg else
52136ac495dSmrg {
52236ac495dSmrg DEBUG_PRINTF (" class '%s' has no +initialize method\n", class->name);
52336ac495dSmrg }
52436ac495dSmrg #endif
52536ac495dSmrg }
52636ac495dSmrg }
52736ac495dSmrg }
52836ac495dSmrg
52936ac495dSmrg /* Walk on the methods list of class and install the methods in the
53036ac495dSmrg reverse order of the lists. Since methods added by categories are
53136ac495dSmrg before the methods of class in the methods list, this allows
53236ac495dSmrg categories to substitute methods declared in class. However if
53336ac495dSmrg more than one category replaces the same method nothing is
53436ac495dSmrg guaranteed about what method will be used. Assumes that
53536ac495dSmrg __objc_runtime_mutex is locked down. */
53636ac495dSmrg static void
__objc_install_methods_in_dtable(struct sarray * dtable,struct objc_method_list * method_list)53736ac495dSmrg __objc_install_methods_in_dtable (struct sarray *dtable, struct objc_method_list * method_list)
53836ac495dSmrg {
53936ac495dSmrg int i;
54036ac495dSmrg
54136ac495dSmrg if (! method_list)
54236ac495dSmrg return;
54336ac495dSmrg
54436ac495dSmrg if (method_list->method_next)
54536ac495dSmrg __objc_install_methods_in_dtable (dtable, method_list->method_next);
54636ac495dSmrg
54736ac495dSmrg for (i = 0; i < method_list->method_count; i++)
54836ac495dSmrg {
54936ac495dSmrg struct objc_method * method = &(method_list->method_list[i]);
55036ac495dSmrg sarray_at_put_safe (dtable,
55136ac495dSmrg (sidx) method->method_name->sel_id,
55236ac495dSmrg method->method_imp);
55336ac495dSmrg }
55436ac495dSmrg }
55536ac495dSmrg
55636ac495dSmrg void
__objc_update_dispatch_table_for_class(Class class)55736ac495dSmrg __objc_update_dispatch_table_for_class (Class class)
55836ac495dSmrg {
55936ac495dSmrg Class next;
56036ac495dSmrg struct sarray *arr;
56136ac495dSmrg
56236ac495dSmrg DEBUG_PRINTF (" _objc_update_dtable_for_class (%s)\n", class->name);
56336ac495dSmrg
56436ac495dSmrg objc_mutex_lock (__objc_runtime_mutex);
56536ac495dSmrg
56636ac495dSmrg /* Not yet installed -- skip it unless in +initialize. */
56736ac495dSmrg if (class->dtable == __objc_uninstalled_dtable)
56836ac495dSmrg {
56936ac495dSmrg if (__objc_prepared_dtable_for_class (class))
57036ac495dSmrg {
57136ac495dSmrg /* There is a prepared table so we must be initialising this
57236ac495dSmrg class ... we must re-do the table preparation. */
57336ac495dSmrg __objc_prepare_dtable_for_class (class);
57436ac495dSmrg }
57536ac495dSmrg objc_mutex_unlock (__objc_runtime_mutex);
57636ac495dSmrg return;
57736ac495dSmrg }
57836ac495dSmrg
57936ac495dSmrg arr = class->dtable;
58036ac495dSmrg __objc_install_premature_dtable (class); /* someone might require it... */
58136ac495dSmrg sarray_free (arr); /* release memory */
58236ac495dSmrg
58336ac495dSmrg /* Could have been lazy... */
58436ac495dSmrg __objc_install_dtable_for_class (class);
58536ac495dSmrg
58636ac495dSmrg if (class->subclass_list) /* Traverse subclasses. */
58736ac495dSmrg for (next = class->subclass_list; next; next = next->sibling_class)
58836ac495dSmrg __objc_update_dispatch_table_for_class (next);
58936ac495dSmrg
59036ac495dSmrg objc_mutex_unlock (__objc_runtime_mutex);
59136ac495dSmrg }
59236ac495dSmrg
59336ac495dSmrg /* This function adds a method list to a class. This function is
59436ac495dSmrg typically called by another function specific to the run-time. As
59536ac495dSmrg such this function does not worry about thread safe issues.
59636ac495dSmrg
59736ac495dSmrg This one is only called for categories. Class objects have their
59836ac495dSmrg methods installed right away, and their selectors are made into
59936ac495dSmrg SEL's by the function __objc_register_selectors_from_class. */
60036ac495dSmrg void
class_add_method_list(Class class,struct objc_method_list * list)60136ac495dSmrg class_add_method_list (Class class, struct objc_method_list * list)
60236ac495dSmrg {
60336ac495dSmrg /* Passing of a linked list is not allowed. Do multiple calls. */
60436ac495dSmrg assert (! list->method_next);
60536ac495dSmrg
60636ac495dSmrg __objc_register_selectors_from_list(list);
60736ac495dSmrg
60836ac495dSmrg /* Add the methods to the class's method list. */
60936ac495dSmrg list->method_next = class->methods;
61036ac495dSmrg class->methods = list;
61136ac495dSmrg
61236ac495dSmrg /* Update the dispatch table of class. */
61336ac495dSmrg __objc_update_dispatch_table_for_class (class);
61436ac495dSmrg }
61536ac495dSmrg
61636ac495dSmrg struct objc_method *
class_getInstanceMethod(Class class_,SEL selector)61736ac495dSmrg class_getInstanceMethod (Class class_, SEL selector)
61836ac495dSmrg {
61936ac495dSmrg struct objc_method *m;
62036ac495dSmrg
62136ac495dSmrg if (class_ == Nil || selector == NULL)
62236ac495dSmrg return NULL;
62336ac495dSmrg
62436ac495dSmrg m = search_for_method_in_hierarchy (class_, selector);
62536ac495dSmrg if (m)
62636ac495dSmrg return m;
62736ac495dSmrg
62836ac495dSmrg /* Try going through +resolveInstanceMethod:, and do the search
62936ac495dSmrg again if successful. */
63036ac495dSmrg if (__objc_resolve_instance_method (class_, selector))
63136ac495dSmrg return search_for_method_in_hierarchy (class_, selector);
63236ac495dSmrg
63336ac495dSmrg return NULL;
63436ac495dSmrg }
63536ac495dSmrg
63636ac495dSmrg struct objc_method *
class_getClassMethod(Class class_,SEL selector)63736ac495dSmrg class_getClassMethod (Class class_, SEL selector)
63836ac495dSmrg {
63936ac495dSmrg struct objc_method *m;
64036ac495dSmrg
64136ac495dSmrg if (class_ == Nil || selector == NULL)
64236ac495dSmrg return NULL;
64336ac495dSmrg
64436ac495dSmrg m = search_for_method_in_hierarchy (class_->class_pointer,
64536ac495dSmrg selector);
64636ac495dSmrg if (m)
64736ac495dSmrg return m;
64836ac495dSmrg
64936ac495dSmrg /* Try going through +resolveClassMethod:, and do the search again
65036ac495dSmrg if successful. */
65136ac495dSmrg if (__objc_resolve_class_method (class_, selector))
65236ac495dSmrg return search_for_method_in_hierarchy (class_->class_pointer,
65336ac495dSmrg selector);
65436ac495dSmrg
65536ac495dSmrg return NULL;
65636ac495dSmrg }
65736ac495dSmrg
65836ac495dSmrg BOOL
class_addMethod(Class class_,SEL selector,IMP implementation,const char * method_types)65936ac495dSmrg class_addMethod (Class class_, SEL selector, IMP implementation,
66036ac495dSmrg const char *method_types)
66136ac495dSmrg {
66236ac495dSmrg struct objc_method_list *method_list;
66336ac495dSmrg struct objc_method *method;
66436ac495dSmrg const char *method_name;
66536ac495dSmrg
66636ac495dSmrg if (class_ == Nil || selector == NULL || implementation == NULL
66736ac495dSmrg || method_types == NULL || (strcmp (method_types, "") == 0))
66836ac495dSmrg return NO;
66936ac495dSmrg
67036ac495dSmrg method_name = sel_getName (selector);
67136ac495dSmrg if (method_name == NULL)
67236ac495dSmrg return NO;
67336ac495dSmrg
67436ac495dSmrg /* If the method already exists in the class, return NO. It is fine
67536ac495dSmrg if the method already exists in the superclass; in that case, we
67636ac495dSmrg are overriding it. */
67736ac495dSmrg if (CLS_IS_IN_CONSTRUCTION (class_))
67836ac495dSmrg {
67936ac495dSmrg /* The class only contains a list of methods; they have not been
68036ac495dSmrg registered yet, ie, the method_name of each of them is still
68136ac495dSmrg a string, not a selector. Iterate manually over them to
68236ac495dSmrg check if we have already added the method. */
68336ac495dSmrg struct objc_method_list * method_list = class_->methods;
68436ac495dSmrg while (method_list)
68536ac495dSmrg {
68636ac495dSmrg int i;
68736ac495dSmrg
68836ac495dSmrg /* Search the method list. */
68936ac495dSmrg for (i = 0; i < method_list->method_count; ++i)
69036ac495dSmrg {
69136ac495dSmrg struct objc_method * method = &method_list->method_list[i];
69236ac495dSmrg
69336ac495dSmrg if (method->method_name
69436ac495dSmrg && strcmp ((char *)method->method_name, method_name) == 0)
69536ac495dSmrg return NO;
69636ac495dSmrg }
69736ac495dSmrg
69836ac495dSmrg /* The method wasn't found. Follow the link to the next list of
69936ac495dSmrg methods. */
70036ac495dSmrg method_list = method_list->method_next;
70136ac495dSmrg }
70236ac495dSmrg /* The method wasn't found. It's a new one. Go ahead and add
70336ac495dSmrg it. */
70436ac495dSmrg }
70536ac495dSmrg else
70636ac495dSmrg {
70736ac495dSmrg /* Do the standard lookup. This assumes the selectors are
70836ac495dSmrg mapped. */
70936ac495dSmrg if (search_for_method_in_list (class_->methods, selector))
71036ac495dSmrg return NO;
71136ac495dSmrg }
71236ac495dSmrg
71336ac495dSmrg method_list = (struct objc_method_list *)objc_calloc (1, sizeof (struct objc_method_list));
71436ac495dSmrg method_list->method_count = 1;
71536ac495dSmrg
71636ac495dSmrg method = &(method_list->method_list[0]);
71736ac495dSmrg method->method_name = objc_malloc (strlen (method_name) + 1);
71836ac495dSmrg strcpy ((char *)method->method_name, method_name);
71936ac495dSmrg
72036ac495dSmrg method->method_types = objc_malloc (strlen (method_types) + 1);
72136ac495dSmrg strcpy ((char *)method->method_types, method_types);
72236ac495dSmrg
72336ac495dSmrg method->method_imp = implementation;
72436ac495dSmrg
72536ac495dSmrg if (CLS_IS_IN_CONSTRUCTION (class_))
72636ac495dSmrg {
72736ac495dSmrg /* We only need to add the method to the list. It will be
72836ac495dSmrg registered with the runtime when the class pair is registered
72936ac495dSmrg (if ever). */
73036ac495dSmrg method_list->method_next = class_->methods;
73136ac495dSmrg class_->methods = method_list;
73236ac495dSmrg }
73336ac495dSmrg else
73436ac495dSmrg {
73536ac495dSmrg /* Add the method to a live class. */
73636ac495dSmrg objc_mutex_lock (__objc_runtime_mutex);
73736ac495dSmrg class_add_method_list (class_, method_list);
73836ac495dSmrg objc_mutex_unlock (__objc_runtime_mutex);
73936ac495dSmrg }
74036ac495dSmrg
74136ac495dSmrg return YES;
74236ac495dSmrg }
74336ac495dSmrg
74436ac495dSmrg IMP
class_replaceMethod(Class class_,SEL selector,IMP implementation,const char * method_types)74536ac495dSmrg class_replaceMethod (Class class_, SEL selector, IMP implementation,
74636ac495dSmrg const char *method_types)
74736ac495dSmrg {
74836ac495dSmrg struct objc_method * method;
74936ac495dSmrg
75036ac495dSmrg if (class_ == Nil || selector == NULL || implementation == NULL
75136ac495dSmrg || method_types == NULL)
75236ac495dSmrg return NULL;
75336ac495dSmrg
75436ac495dSmrg method = search_for_method_in_hierarchy (class_, selector);
75536ac495dSmrg
75636ac495dSmrg if (method)
75736ac495dSmrg {
75836ac495dSmrg return method_setImplementation (method, implementation);
75936ac495dSmrg }
76036ac495dSmrg else
76136ac495dSmrg {
76236ac495dSmrg class_addMethod (class_, selector, implementation, method_types);
76336ac495dSmrg return NULL;
76436ac495dSmrg }
76536ac495dSmrg }
76636ac495dSmrg
76736ac495dSmrg /* Search for a method starting from the current class up its
76836ac495dSmrg hierarchy. Return a pointer to the method's method structure if
76936ac495dSmrg found. NULL otherwise. */
77036ac495dSmrg static struct objc_method *
search_for_method_in_hierarchy(Class cls,SEL sel)77136ac495dSmrg search_for_method_in_hierarchy (Class cls, SEL sel)
77236ac495dSmrg {
77336ac495dSmrg struct objc_method * method = NULL;
77436ac495dSmrg Class class;
77536ac495dSmrg
77636ac495dSmrg if (! sel_is_mapped (sel))
77736ac495dSmrg return NULL;
77836ac495dSmrg
77936ac495dSmrg /* Scan the method list of the class. If the method isn't found in
78036ac495dSmrg the list then step to its super class. */
78136ac495dSmrg for (class = cls; ((! method) && class); class = class->super_class)
78236ac495dSmrg method = search_for_method_in_list (class->methods, sel);
78336ac495dSmrg
78436ac495dSmrg return method;
78536ac495dSmrg }
78636ac495dSmrg
78736ac495dSmrg
78836ac495dSmrg
78936ac495dSmrg /* Given a linked list of method and a method's name. Search for the
79036ac495dSmrg named method's method structure. Return a pointer to the method's
79136ac495dSmrg method structure if found. NULL otherwise. */
79236ac495dSmrg struct objc_method *
search_for_method_in_list(struct objc_method_list * list,SEL op)79336ac495dSmrg search_for_method_in_list (struct objc_method_list * list, SEL op)
79436ac495dSmrg {
79536ac495dSmrg struct objc_method_list * method_list = list;
79636ac495dSmrg
79736ac495dSmrg if (! sel_is_mapped (op))
79836ac495dSmrg return NULL;
79936ac495dSmrg
80036ac495dSmrg /* If not found then we'll search the list. */
80136ac495dSmrg while (method_list)
80236ac495dSmrg {
80336ac495dSmrg int i;
80436ac495dSmrg
80536ac495dSmrg /* Search the method list. */
80636ac495dSmrg for (i = 0; i < method_list->method_count; ++i)
80736ac495dSmrg {
80836ac495dSmrg struct objc_method * method = &method_list->method_list[i];
80936ac495dSmrg
81036ac495dSmrg if (method->method_name)
81136ac495dSmrg if (method->method_name->sel_id == op->sel_id)
81236ac495dSmrg return method;
81336ac495dSmrg }
81436ac495dSmrg
81536ac495dSmrg /* The method wasn't found. Follow the link to the next list of
81636ac495dSmrg methods. */
81736ac495dSmrg method_list = method_list->method_next;
81836ac495dSmrg }
81936ac495dSmrg
82036ac495dSmrg return NULL;
82136ac495dSmrg }
82236ac495dSmrg
82336ac495dSmrg typedef void * retval_t;
82436ac495dSmrg typedef void * arglist_t;
82536ac495dSmrg
82636ac495dSmrg static retval_t __objc_forward (id object, SEL sel, arglist_t args);
82736ac495dSmrg
82836ac495dSmrg /* Forwarding pointers/integers through the normal registers. */
82936ac495dSmrg static id
__objc_word_forward(id rcv,SEL op,...)83036ac495dSmrg __objc_word_forward (id rcv, SEL op, ...)
83136ac495dSmrg {
83236ac495dSmrg void *args, *res;
83336ac495dSmrg
83436ac495dSmrg args = __builtin_apply_args ();
83536ac495dSmrg res = __objc_forward (rcv, op, args);
83636ac495dSmrg if (res)
83736ac495dSmrg __builtin_return (res);
83836ac495dSmrg else
83936ac495dSmrg return res;
84036ac495dSmrg }
84136ac495dSmrg
84236ac495dSmrg /* Specific routine for forwarding floats/double because of
84336ac495dSmrg architectural differences on some processors. i386s for example
84436ac495dSmrg which uses a floating point stack versus general registers for
84536ac495dSmrg floating point numbers. This forward routine makes sure that GCC
84636ac495dSmrg restores the proper return values. */
84736ac495dSmrg static double
__objc_double_forward(id rcv,SEL op,...)84836ac495dSmrg __objc_double_forward (id rcv, SEL op, ...)
84936ac495dSmrg {
85036ac495dSmrg void *args, *res;
85136ac495dSmrg
85236ac495dSmrg args = __builtin_apply_args ();
85336ac495dSmrg res = __objc_forward (rcv, op, args);
85436ac495dSmrg __builtin_return (res);
85536ac495dSmrg }
85636ac495dSmrg
85736ac495dSmrg #if INVISIBLE_STRUCT_RETURN
85836ac495dSmrg static __big
85936ac495dSmrg #else
86036ac495dSmrg static id
86136ac495dSmrg #endif
__objc_block_forward(id rcv,SEL op,...)86236ac495dSmrg __objc_block_forward (id rcv, SEL op, ...)
86336ac495dSmrg {
86436ac495dSmrg void *args, *res;
86536ac495dSmrg
86636ac495dSmrg args = __builtin_apply_args ();
86736ac495dSmrg res = __objc_forward (rcv, op, args);
86836ac495dSmrg if (res)
86936ac495dSmrg __builtin_return (res);
87036ac495dSmrg else
87136ac495dSmrg #if INVISIBLE_STRUCT_RETURN
87236ac495dSmrg return (__big) {{0, 0, 0, 0, 0, 0, 0, 0}};
87336ac495dSmrg #else
87436ac495dSmrg return nil;
87536ac495dSmrg #endif
87636ac495dSmrg }
87736ac495dSmrg
87836ac495dSmrg
87936ac495dSmrg /* This function is called for methods which are not implemented,
88036ac495dSmrg unless a custom forwarding routine has been installed. Please note
88136ac495dSmrg that most serious users of libobjc (eg, GNUstep base) do install
88236ac495dSmrg their own forwarding routines, and hence this is never actually
88336ac495dSmrg used. But, if no custom forwarding routine is installed, this is
88436ac495dSmrg called when a selector is not recognized. */
88536ac495dSmrg static retval_t
__objc_forward(id object,SEL sel,arglist_t args)88636ac495dSmrg __objc_forward (id object, SEL sel, arglist_t args)
88736ac495dSmrg {
88836ac495dSmrg IMP imp;
88936ac495dSmrg static SEL frwd_sel = 0; /* !T:SAFE2 */
89036ac495dSmrg SEL err_sel;
89136ac495dSmrg
89236ac495dSmrg /* First try if the object understands forward::. */
89336ac495dSmrg if (! frwd_sel)
89436ac495dSmrg frwd_sel = sel_get_any_uid ("forward::");
89536ac495dSmrg
89636ac495dSmrg if (__objc_responds_to (object, frwd_sel))
89736ac495dSmrg {
89836ac495dSmrg imp = get_implementation (object, object->class_pointer, frwd_sel);
89936ac495dSmrg return (*imp) (object, frwd_sel, sel, args);
90036ac495dSmrg }
90136ac495dSmrg
90236ac495dSmrg /* If the object recognizes the doesNotRecognize: method then we're
90336ac495dSmrg going to send it. */
90436ac495dSmrg err_sel = sel_get_any_uid ("doesNotRecognize:");
90536ac495dSmrg if (__objc_responds_to (object, err_sel))
90636ac495dSmrg {
90736ac495dSmrg imp = get_implementation (object, object->class_pointer, err_sel);
90836ac495dSmrg return (*imp) (object, err_sel, sel);
90936ac495dSmrg }
91036ac495dSmrg
91136ac495dSmrg /* The object doesn't recognize the method. Check for responding to
91236ac495dSmrg error:. If it does then sent it. */
91336ac495dSmrg {
91436ac495dSmrg char msg[256 + strlen ((const char *) sel_getName (sel))
91536ac495dSmrg + strlen ((const char *) object->class_pointer->name)];
91636ac495dSmrg
91736ac495dSmrg sprintf (msg, "(%s) %s does not recognize %s",
91836ac495dSmrg (CLS_ISMETA (object->class_pointer)
91936ac495dSmrg ? "class"
92036ac495dSmrg : "instance" ),
92136ac495dSmrg object->class_pointer->name, sel_getName (sel));
92236ac495dSmrg
92336ac495dSmrg /* The object doesn't respond to doesNotRecognize:. Therefore, a
92436ac495dSmrg default action is taken. */
92536ac495dSmrg _objc_abort ("%s\n", msg);
92636ac495dSmrg
92736ac495dSmrg return 0;
92836ac495dSmrg }
92936ac495dSmrg }
93036ac495dSmrg
93136ac495dSmrg void
__objc_print_dtable_stats(void)93236ac495dSmrg __objc_print_dtable_stats (void)
93336ac495dSmrg {
93436ac495dSmrg int total = 0;
93536ac495dSmrg
93636ac495dSmrg objc_mutex_lock (__objc_runtime_mutex);
93736ac495dSmrg
93836ac495dSmrg #ifdef OBJC_SPARSE2
93936ac495dSmrg printf ("memory usage: (%s)\n", "2-level sparse arrays");
94036ac495dSmrg #else
94136ac495dSmrg printf ("memory usage: (%s)\n", "3-level sparse arrays");
94236ac495dSmrg #endif
94336ac495dSmrg
94436ac495dSmrg printf ("arrays: %d = %ld bytes\n", narrays,
94536ac495dSmrg (long) ((size_t) narrays * sizeof (struct sarray)));
94636ac495dSmrg total += narrays * sizeof (struct sarray);
94736ac495dSmrg printf ("buckets: %d = %ld bytes\n", nbuckets,
94836ac495dSmrg (long) ((size_t) nbuckets * sizeof (struct sbucket)));
94936ac495dSmrg total += nbuckets * sizeof (struct sbucket);
95036ac495dSmrg
95136ac495dSmrg printf ("idxtables: %d = %ld bytes\n",
95236ac495dSmrg idxsize, (long) ((size_t) idxsize * sizeof (void *)));
95336ac495dSmrg total += idxsize * sizeof (void *);
95436ac495dSmrg printf ("-----------------------------------\n");
95536ac495dSmrg printf ("total: %d bytes\n", total);
95636ac495dSmrg printf ("===================================\n");
95736ac495dSmrg
95836ac495dSmrg objc_mutex_unlock (__objc_runtime_mutex);
95936ac495dSmrg }
96036ac495dSmrg
96136ac495dSmrg static cache_ptr prepared_dtable_table = 0;
96236ac495dSmrg
96336ac495dSmrg /* This function is called by: objc_msg_lookup, get_imp and
96436ac495dSmrg __objc_responds_to (and the dispatch table installation functions
96536ac495dSmrg themselves) to install a dispatch table for a class.
96636ac495dSmrg
96736ac495dSmrg If CLS is a class, it installs instance methods.
96836ac495dSmrg If CLS is a meta class, it installs class methods.
96936ac495dSmrg
97036ac495dSmrg In either case +initialize is invoked for the corresponding class.
97136ac495dSmrg
97236ac495dSmrg The implementation must insure that the dispatch table is not
97336ac495dSmrg installed until +initialize completes. Otherwise it opens a
97436ac495dSmrg potential race since the installation of the dispatch table is used
97536ac495dSmrg as gate in regular method dispatch and we need to guarantee that
97636ac495dSmrg +initialize is the first method invoked an that no other thread my
97736ac495dSmrg dispatch messages to the class before +initialize completes. */
97836ac495dSmrg static void
__objc_install_dtable_for_class(Class cls)97936ac495dSmrg __objc_install_dtable_for_class (Class cls)
98036ac495dSmrg {
98136ac495dSmrg /* If the class has not yet had its class links resolved, we must
98236ac495dSmrg re-compute all class links. */
98336ac495dSmrg if (! CLS_ISRESOLV (cls))
98436ac495dSmrg __objc_resolve_class_links ();
98536ac495dSmrg
98636ac495dSmrg /* Make sure the super class has its dispatch table installed or is
98736ac495dSmrg at least preparing. We do not need to send initialize for the
98836ac495dSmrg super class since __objc_send_initialize will insure that. */
98936ac495dSmrg if (cls->super_class
99036ac495dSmrg && cls->super_class->dtable == __objc_uninstalled_dtable
99136ac495dSmrg && !__objc_prepared_dtable_for_class (cls->super_class))
99236ac495dSmrg {
99336ac495dSmrg __objc_install_dtable_for_class (cls->super_class);
99436ac495dSmrg /* The superclass initialisation may have also initialised the
99536ac495dSmrg current class, in which case there is no more to do. */
99636ac495dSmrg if (cls->dtable != __objc_uninstalled_dtable)
99736ac495dSmrg return;
99836ac495dSmrg }
99936ac495dSmrg
100036ac495dSmrg /* We have already been prepared but +initialize hasn't completed.
100136ac495dSmrg The +initialize implementation is probably sending 'self'
100236ac495dSmrg messages. We rely on _objc_get_prepared_imp to retrieve the
100336ac495dSmrg implementation pointers. */
100436ac495dSmrg if (__objc_prepared_dtable_for_class (cls))
100536ac495dSmrg return;
100636ac495dSmrg
100736ac495dSmrg /* We have this function cache the implementation pointers for
100836ac495dSmrg _objc_get_prepared_imp but the dispatch table won't be initilized
100936ac495dSmrg until __objc_send_initialize completes. */
101036ac495dSmrg __objc_prepare_dtable_for_class (cls);
101136ac495dSmrg
101236ac495dSmrg /* We may have already invoked +initialize but
101336ac495dSmrg __objc_update_dispatch_table_for_class invoked by
101436ac495dSmrg class_add_method_list may have reset dispatch table. */
101536ac495dSmrg
101636ac495dSmrg /* Call +initialize. If we are a real class, we are installing
101736ac495dSmrg instance methods. If we are a meta class, we are installing
101836ac495dSmrg class methods. The __objc_send_initialize itself will insure
101936ac495dSmrg that the message is called only once per class. */
102036ac495dSmrg if (CLS_ISCLASS (cls))
102136ac495dSmrg __objc_send_initialize (cls);
102236ac495dSmrg else
102336ac495dSmrg {
102436ac495dSmrg /* Retrieve the class from the meta class. */
102536ac495dSmrg Class c = objc_getClass (cls->name);
102636ac495dSmrg assert (CLS_ISMETA (cls));
102736ac495dSmrg assert (c);
102836ac495dSmrg __objc_send_initialize (c);
102936ac495dSmrg }
103036ac495dSmrg
103136ac495dSmrg /* We install the dispatch table correctly when +initialize completed. */
103236ac495dSmrg __objc_install_prepared_dtable_for_class (cls);
103336ac495dSmrg }
103436ac495dSmrg
103536ac495dSmrg /* Builds the dispatch table for the class CLS and stores it in a
103636ac495dSmrg place where it can be retrieved by __objc_get_prepared_imp until
103736ac495dSmrg __objc_install_prepared_dtable_for_class installs it into the
103836ac495dSmrg class. The dispatch table should not be installed into the class
103936ac495dSmrg until +initialize has completed. */
104036ac495dSmrg static void
__objc_prepare_dtable_for_class(Class cls)104136ac495dSmrg __objc_prepare_dtable_for_class (Class cls)
104236ac495dSmrg {
104336ac495dSmrg struct sarray *dtable;
104436ac495dSmrg struct sarray *super_dtable;
104536ac495dSmrg
104636ac495dSmrg /* This table could be initialized in init.c. We cannot use the
104736ac495dSmrg class name since the class maintains the instance methods and the
104836ac495dSmrg meta class maintains the the class methods yet both share the
104936ac495dSmrg same name. Classes should be unique in any program. */
105036ac495dSmrg if (! prepared_dtable_table)
105136ac495dSmrg prepared_dtable_table
105236ac495dSmrg = objc_hash_new (32,
105336ac495dSmrg (hash_func_type) objc_hash_ptr,
105436ac495dSmrg (compare_func_type) objc_compare_ptrs);
105536ac495dSmrg
105636ac495dSmrg /* If the class has not yet had its class links resolved, we must
105736ac495dSmrg re-compute all class links. */
105836ac495dSmrg if (! CLS_ISRESOLV (cls))
105936ac495dSmrg __objc_resolve_class_links ();
106036ac495dSmrg
106136ac495dSmrg assert (cls);
106236ac495dSmrg assert (cls->dtable == __objc_uninstalled_dtable);
106336ac495dSmrg
106436ac495dSmrg /* If there is already a prepared dtable for this class, we must
106536ac495dSmrg replace it with a new version (since there must have been methods
106636ac495dSmrg added to or otherwise modified in the class while executing
106736ac495dSmrg +initialize, and the table needs to be recomputed. */
106836ac495dSmrg dtable = __objc_prepared_dtable_for_class (cls);
106936ac495dSmrg if (dtable != 0)
107036ac495dSmrg {
107136ac495dSmrg objc_hash_remove (prepared_dtable_table, cls);
107236ac495dSmrg sarray_free (dtable);
107336ac495dSmrg }
107436ac495dSmrg
107536ac495dSmrg /* Now prepare the dtable for population. */
107636ac495dSmrg assert (cls != cls->super_class);
107736ac495dSmrg if (cls->super_class)
107836ac495dSmrg {
107936ac495dSmrg /* Inherit the method list from the super class. Yet the super
108036ac495dSmrg class may still be initializing in the case when a class
108136ac495dSmrg cluster sub class initializes its super classes. */
108236ac495dSmrg if (cls->super_class->dtable == __objc_uninstalled_dtable)
108336ac495dSmrg __objc_install_dtable_for_class (cls->super_class);
108436ac495dSmrg
108536ac495dSmrg super_dtable = cls->super_class->dtable;
108636ac495dSmrg /* If the dispatch table is not yet installed, we are still in
108736ac495dSmrg the process of executing +initialize. Yet the dispatch table
108836ac495dSmrg should be available. */
108936ac495dSmrg if (super_dtable == __objc_uninstalled_dtable)
109036ac495dSmrg super_dtable = __objc_prepared_dtable_for_class (cls->super_class);
109136ac495dSmrg
109236ac495dSmrg assert (super_dtable);
109336ac495dSmrg dtable = sarray_lazy_copy (super_dtable);
109436ac495dSmrg }
109536ac495dSmrg else
109636ac495dSmrg dtable = sarray_new (__objc_selector_max_index, 0);
109736ac495dSmrg
109836ac495dSmrg __objc_install_methods_in_dtable (dtable, cls->methods);
109936ac495dSmrg
110036ac495dSmrg objc_hash_add (&prepared_dtable_table,
110136ac495dSmrg cls,
110236ac495dSmrg dtable);
110336ac495dSmrg }
110436ac495dSmrg
110536ac495dSmrg /* This wrapper only exists to allow an easy replacement of the lookup
110636ac495dSmrg implementation and it is expected that the compiler will optimize
110736ac495dSmrg it away. */
110836ac495dSmrg static struct sarray *
__objc_prepared_dtable_for_class(Class cls)110936ac495dSmrg __objc_prepared_dtable_for_class (Class cls)
111036ac495dSmrg {
111136ac495dSmrg struct sarray *dtable = 0;
111236ac495dSmrg assert (cls);
111336ac495dSmrg if (prepared_dtable_table)
111436ac495dSmrg dtable = objc_hash_value_for_key (prepared_dtable_table, cls);
111536ac495dSmrg /* dtable my be nil, since we call this to check whether we are
111636ac495dSmrg currently preparing before we start preparing. */
111736ac495dSmrg return dtable;
111836ac495dSmrg }
111936ac495dSmrg
112036ac495dSmrg /* Helper function for messages sent to CLS or implementation pointers
112136ac495dSmrg retrieved from CLS during +initialize before the dtable is
112236ac495dSmrg installed. When a class implicitly initializes another class which
112336ac495dSmrg in turn implicitly invokes methods in this class, before the
112436ac495dSmrg implementation of +initialize of CLS completes, this returns the
112536ac495dSmrg expected implementation. Forwarding remains the responsibility of
112636ac495dSmrg objc_msg_lookup. This function should only be called under the
112736ac495dSmrg global lock. */
112836ac495dSmrg static IMP
__objc_get_prepared_imp(Class cls,SEL sel)112936ac495dSmrg __objc_get_prepared_imp (Class cls,SEL sel)
113036ac495dSmrg {
113136ac495dSmrg struct sarray *dtable;
113236ac495dSmrg IMP imp;
113336ac495dSmrg
113436ac495dSmrg assert (cls);
113536ac495dSmrg assert (sel);
113636ac495dSmrg assert (cls->dtable == __objc_uninstalled_dtable);
113736ac495dSmrg dtable = __objc_prepared_dtable_for_class (cls);
113836ac495dSmrg
113936ac495dSmrg assert (dtable);
114036ac495dSmrg assert (dtable != __objc_uninstalled_dtable);
114136ac495dSmrg imp = sarray_get_safe (dtable, (size_t) sel->sel_id);
114236ac495dSmrg
114336ac495dSmrg /* imp may be Nil if the method does not exist and we may fallback
114436ac495dSmrg to the forwarding implementation later. */
114536ac495dSmrg return imp;
114636ac495dSmrg }
114736ac495dSmrg
114836ac495dSmrg /* When this function is called +initialize should be completed. So
114936ac495dSmrg now we are safe to install the dispatch table for the class so that
115036ac495dSmrg they become available for other threads that may be waiting in the
115136ac495dSmrg lock. */
115236ac495dSmrg static void
__objc_install_prepared_dtable_for_class(Class cls)115336ac495dSmrg __objc_install_prepared_dtable_for_class (Class cls)
115436ac495dSmrg {
115536ac495dSmrg assert (cls);
115636ac495dSmrg assert (cls->dtable == __objc_uninstalled_dtable);
115736ac495dSmrg cls->dtable = __objc_prepared_dtable_for_class (cls);
115836ac495dSmrg
115936ac495dSmrg assert (cls->dtable);
116036ac495dSmrg assert (cls->dtable != __objc_uninstalled_dtable);
116136ac495dSmrg objc_hash_remove (prepared_dtable_table, cls);
116236ac495dSmrg }
1163