xref: /netbsd-src/external/gpl3/gcc.old/dist/libobjc/sendmsg.c (revision 8feb0f0b7eaff0608f8350bbfa3098827b4bb91b)
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