xref: /netbsd-src/external/gpl3/gcc/dist/libobjc/sendmsg.c (revision e9e6e0f6fbc36b8de7586170291cf5fc97cab8b6)
14fee23f9Smrg /* GNU Objective C Runtime message lookup
2*e9e6e0f6Smrg    Copyright (C) 1993-2022 Free Software Foundation, Inc.
34fee23f9Smrg    Contributed by Kresten Krab Thorup
44fee23f9Smrg 
54fee23f9Smrg This file is part of GCC.
64fee23f9Smrg 
74fee23f9Smrg GCC is free software; you can redistribute it and/or modify it under the
84fee23f9Smrg terms of the GNU General Public License as published by the Free Software
94fee23f9Smrg Foundation; either version 3, or (at your option) any later version.
104fee23f9Smrg 
114fee23f9Smrg GCC is distributed in the hope that it will be useful, but WITHOUT ANY
124fee23f9Smrg WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
134fee23f9Smrg FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
144fee23f9Smrg details.
154fee23f9Smrg 
164fee23f9Smrg Under Section 7 of GPL version 3, you are granted additional
174fee23f9Smrg permissions described in the GCC Runtime Library Exception, version
184fee23f9Smrg 3.1, as published by the Free Software Foundation.
194fee23f9Smrg 
204fee23f9Smrg You should have received a copy of the GNU General Public License and
214fee23f9Smrg a copy of the GCC Runtime Library Exception along with this program;
224fee23f9Smrg see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
234fee23f9Smrg <http://www.gnu.org/licenses/>.  */
244fee23f9Smrg 
25d77920ccSmrg /* Uncommented the following line to enable debug logging.  Use this
26d77920ccSmrg    only while debugging the runtime.  */
27d77920ccSmrg /* #define DEBUG 1 */
284fee23f9Smrg 
294fee23f9Smrg /* FIXME: This should be using libffi instead of __builtin_apply
304fee23f9Smrg    and friends.  */
314fee23f9Smrg 
32d77920ccSmrg #include "objc-private/common.h"
33d77920ccSmrg #include "objc-private/error.h"
344fee23f9Smrg #include "tconfig.h"
354fee23f9Smrg #include "coretypes.h"
364fee23f9Smrg #include "objc/runtime.h"
37d77920ccSmrg #include "objc/message.h"          /* For objc_msg_lookup(), objc_msg_lookup_super().  */
38d77920ccSmrg #include "objc/thr.h"
39d77920ccSmrg #include "objc-private/module-abi-8.h"
40d77920ccSmrg #include "objc-private/runtime.h"
41d77920ccSmrg #include "objc-private/hash.h"
42d77920ccSmrg #include "objc-private/sarray.h"
43d77920ccSmrg #include "objc-private/selector.h" /* For sel_is_mapped() */
444fee23f9Smrg #include "runtime-info.h"
45d77920ccSmrg #include <assert.h> /* For assert */
46d77920ccSmrg #include <string.h> /* For strlen */
474fee23f9Smrg 
484fee23f9Smrg #define INVISIBLE_STRUCT_RETURN 1
494fee23f9Smrg 
50d77920ccSmrg /* The uninstalled dispatch table.  If a class' dispatch table points
51d77920ccSmrg    to __objc_uninstalled_dtable then that means it needs its dispatch
52d77920ccSmrg    table to be installed.  */
534fee23f9Smrg struct sarray *__objc_uninstalled_dtable = 0;   /* !T:MUTEX */
544fee23f9Smrg 
55d77920ccSmrg /* Two hooks for method forwarding. If either is set, it is invoked to
56d77920ccSmrg  * return a function that performs the real forwarding.  If both are
57d77920ccSmrg  * set, the result of __objc_msg_forward2 will be preferred over that
58d77920ccSmrg  * of __objc_msg_forward.  If both return NULL or are unset, the
59d77920ccSmrg  * libgcc based functions (__builtin_apply and friends) are used.  */
604fee23f9Smrg IMP (*__objc_msg_forward) (SEL) = NULL;
614fee23f9Smrg IMP (*__objc_msg_forward2) (id, SEL) = NULL;
624fee23f9Smrg 
63d77920ccSmrg /* Send +initialize to class.  */
644fee23f9Smrg static void __objc_send_initialize (Class);
654fee23f9Smrg 
664fee23f9Smrg /* Forward declare some functions */
67d77920ccSmrg static void __objc_install_dtable_for_class (Class cls);
68d77920ccSmrg static void __objc_prepare_dtable_for_class (Class cls);
69d77920ccSmrg static void __objc_install_prepared_dtable_for_class (Class cls);
70d77920ccSmrg 
71d77920ccSmrg static struct sarray *__objc_prepared_dtable_for_class (Class cls);
72d77920ccSmrg static IMP __objc_get_prepared_imp (Class cls,SEL sel);
73d77920ccSmrg 
744fee23f9Smrg 
754fee23f9Smrg /* Various forwarding functions that are used based upon the
764fee23f9Smrg    return type for the selector.
774fee23f9Smrg    __objc_block_forward for structures.
784fee23f9Smrg    __objc_double_forward for floats/doubles.
794fee23f9Smrg    __objc_word_forward for pointers or types that fit in registers.  */
804fee23f9Smrg static double __objc_double_forward (id, SEL, ...);
814fee23f9Smrg static id __objc_word_forward (id, SEL, ...);
824fee23f9Smrg typedef struct { id many[8]; } __big;
834fee23f9Smrg #if INVISIBLE_STRUCT_RETURN
844fee23f9Smrg static __big
854fee23f9Smrg #else
864fee23f9Smrg static id
874fee23f9Smrg #endif
884fee23f9Smrg __objc_block_forward (id, SEL, ...);
89d77920ccSmrg static struct objc_method * search_for_method_in_hierarchy (Class class, SEL sel);
90d77920ccSmrg struct objc_method * search_for_method_in_list (struct objc_method_list * list, SEL op);
914fee23f9Smrg id nil_method (id, SEL);
924fee23f9Smrg 
93d35849d0Smrg /* Make sure this inline function is exported regardless of GNU89 or C99
94d35849d0Smrg    inlining semantics as it is part of the libobjc ABI.  */
95d35849d0Smrg extern IMP __objc_get_forward_imp (id, SEL);
96d35849d0Smrg 
974fee23f9Smrg /* Given a selector, return the proper forwarding implementation.  */
984fee23f9Smrg IMP
__objc_get_forward_imp(id rcv,SEL sel)994fee23f9Smrg __objc_get_forward_imp (id rcv, SEL sel)
1004fee23f9Smrg {
101d77920ccSmrg   /* If a custom forwarding hook was registered, try getting a
102d77920ccSmrg      forwarding function from it. There are two forward routine hooks,
103d77920ccSmrg      one that takes the receiver as an argument and one that does
104d77920ccSmrg      not.  */
1054fee23f9Smrg   if (__objc_msg_forward2)
1064fee23f9Smrg     {
1074fee23f9Smrg       IMP result;
1084fee23f9Smrg       if ((result = __objc_msg_forward2 (rcv, sel)) != NULL)
1094fee23f9Smrg        return result;
1104fee23f9Smrg     }
1114fee23f9Smrg   if (__objc_msg_forward)
1124fee23f9Smrg     {
1134fee23f9Smrg       IMP result;
1144fee23f9Smrg       if ((result = __objc_msg_forward (sel)) != NULL)
1154fee23f9Smrg 	return result;
1164fee23f9Smrg     }
1174fee23f9Smrg 
118d77920ccSmrg   /* In all other cases, use the default forwarding functions built
119d77920ccSmrg      using __builtin_apply and friends.  */
1204fee23f9Smrg     {
1214fee23f9Smrg       const char *t = sel->sel_types;
1224fee23f9Smrg 
1234fee23f9Smrg       if (t && (*t == '[' || *t == '(' || *t == '{')
1244fee23f9Smrg #ifdef OBJC_MAX_STRUCT_BY_VALUE
1254fee23f9Smrg           && objc_sizeof_type (t) > OBJC_MAX_STRUCT_BY_VALUE
1264fee23f9Smrg #endif
1274fee23f9Smrg           )
1284fee23f9Smrg         return (IMP)__objc_block_forward;
1294fee23f9Smrg       else if (t && (*t == 'f' || *t == 'd'))
1304fee23f9Smrg         return (IMP)__objc_double_forward;
1314fee23f9Smrg       else
1324fee23f9Smrg         return (IMP)__objc_word_forward;
1334fee23f9Smrg     }
1344fee23f9Smrg }
1354fee23f9Smrg 
136d77920ccSmrg /* Selectors for +resolveClassMethod: and +resolveInstanceMethod:.
137d77920ccSmrg    These are set up at startup.  */
138d77920ccSmrg static SEL selector_resolveClassMethod = NULL;
139d77920ccSmrg static SEL selector_resolveInstanceMethod = NULL;
140d77920ccSmrg 
141d77920ccSmrg /* Internal routines use to resolve a class method using
142d77920ccSmrg    +resolveClassMethod:.  'class' is always a non-Nil class (*not* a
143d77920ccSmrg    meta-class), and 'sel' is the selector that we are trying to
144d77920ccSmrg    resolve.  This must be called when class is not Nil, and the
145d77920ccSmrg    dispatch table for class methods has already been installed.
146d77920ccSmrg 
147d77920ccSmrg    This routine tries to call +resolveClassMethod: to give an
148d77920ccSmrg    opportunity to resolve the method.  If +resolveClassMethod: returns
149d77920ccSmrg    YES, it tries looking up the method again, and if found, it returns
150d77920ccSmrg    it.  Else, it returns NULL.  */
151d77920ccSmrg static inline
152d77920ccSmrg IMP
__objc_resolve_class_method(Class class,SEL sel)153d77920ccSmrg __objc_resolve_class_method (Class class, SEL sel)
154d77920ccSmrg {
155d77920ccSmrg   /* We need to lookup +resolveClassMethod:.  */
156d77920ccSmrg   BOOL (*resolveMethodIMP) (id, SEL, SEL);
157d77920ccSmrg 
158d77920ccSmrg   /* The dispatch table for class methods is already installed and we
159d77920ccSmrg      don't want any forwarding to happen when looking up this method,
160d77920ccSmrg      so we just look it up directly.  Note that if 'sel' is precisely
161d77920ccSmrg      +resolveClassMethod:, this would look it up yet again and find
162d77920ccSmrg      nothing.  That's no problem and there's no recursion.  */
163d77920ccSmrg   resolveMethodIMP = (BOOL (*) (id, SEL, SEL))sarray_get_safe
164d77920ccSmrg     (class->class_pointer->dtable, (size_t) selector_resolveClassMethod->sel_id);
165d77920ccSmrg 
166d77920ccSmrg   if (resolveMethodIMP && resolveMethodIMP ((id)class, selector_resolveClassMethod, sel))
167d77920ccSmrg     {
168d77920ccSmrg       /* +resolveClassMethod: returned YES.  Look the method up again.
169d77920ccSmrg 	 We already know the dtable is installed.  */
170d77920ccSmrg 
171d77920ccSmrg       /* TODO: There is the case where +resolveClassMethod: is buggy
172d77920ccSmrg 	 and returned YES without actually adding the method.  We
173d77920ccSmrg 	 could maybe print an error message.  */
174d77920ccSmrg       return sarray_get_safe (class->class_pointer->dtable, (size_t) sel->sel_id);
175d77920ccSmrg     }
176d77920ccSmrg 
177d77920ccSmrg   return NULL;
178d77920ccSmrg }
179d77920ccSmrg 
180d77920ccSmrg /* Internal routines use to resolve a instance method using
181d77920ccSmrg    +resolveInstanceMethod:.  'class' is always a non-Nil class, and
182d77920ccSmrg    'sel' is the selector that we are trying to resolve.  This must be
183d77920ccSmrg    called when class is not Nil, and the dispatch table for instance
184d77920ccSmrg    methods has already been installed.
185d77920ccSmrg 
186d77920ccSmrg    This routine tries to call +resolveInstanceMethod: to give an
187d77920ccSmrg    opportunity to resolve the method.  If +resolveInstanceMethod:
188d77920ccSmrg    returns YES, it tries looking up the method again, and if found, it
189d77920ccSmrg    returns it.  Else, it returns NULL.  */
190d77920ccSmrg static inline
191d77920ccSmrg IMP
__objc_resolve_instance_method(Class class,SEL sel)192d77920ccSmrg __objc_resolve_instance_method (Class class, SEL sel)
193d77920ccSmrg {
194d77920ccSmrg   /* We need to lookup +resolveInstanceMethod:.  */
195d77920ccSmrg   BOOL (*resolveMethodIMP) (id, SEL, SEL);
196d77920ccSmrg 
197d77920ccSmrg   /* The dispatch table for class methods may not be already installed
198d77920ccSmrg      so we have to install it if needed.  */
199d77920ccSmrg   resolveMethodIMP = sarray_get_safe (class->class_pointer->dtable,
200d77920ccSmrg 				      (size_t) selector_resolveInstanceMethod->sel_id);
201d77920ccSmrg   if (resolveMethodIMP == 0)
202d77920ccSmrg     {
203d77920ccSmrg       /* Try again after installing the dtable.  */
204d77920ccSmrg       if (class->class_pointer->dtable == __objc_uninstalled_dtable)
205d77920ccSmrg 	{
206d77920ccSmrg 	  objc_mutex_lock (__objc_runtime_mutex);
207d77920ccSmrg 	  if (class->class_pointer->dtable == __objc_uninstalled_dtable)
208d77920ccSmrg 	    __objc_install_dtable_for_class (class->class_pointer);
209d77920ccSmrg 	  objc_mutex_unlock (__objc_runtime_mutex);
210d77920ccSmrg 	}
211d77920ccSmrg       resolveMethodIMP = sarray_get_safe (class->class_pointer->dtable,
212d77920ccSmrg 					  (size_t) selector_resolveInstanceMethod->sel_id);
213d77920ccSmrg     }
214d77920ccSmrg 
215d77920ccSmrg   if (resolveMethodIMP && resolveMethodIMP ((id)class, selector_resolveInstanceMethod, sel))
216d77920ccSmrg     {
217d77920ccSmrg       /* +resolveInstanceMethod: returned YES.  Look the method up
218d77920ccSmrg 	 again.  We already know the dtable is installed.  */
219d77920ccSmrg 
220d77920ccSmrg       /* TODO: There is the case where +resolveInstanceMethod: is
221d77920ccSmrg 	 buggy and returned YES without actually adding the method.
222d77920ccSmrg 	 We could maybe print an error message.  */
223d77920ccSmrg       return sarray_get_safe (class->dtable, (size_t) sel->sel_id);
224d77920ccSmrg     }
225d77920ccSmrg 
226d77920ccSmrg   return NULL;
227d77920ccSmrg }
228d77920ccSmrg 
229d77920ccSmrg /* Given a CLASS and selector, return the implementation corresponding
230d77920ccSmrg    to the method of the selector.
231d77920ccSmrg 
232d77920ccSmrg    If CLASS is a class, the instance method is returned.
233d77920ccSmrg    If CLASS is a meta class, the class method is returned.
234d77920ccSmrg 
235d77920ccSmrg    Since this requires the dispatch table to be installed, this function
236d77920ccSmrg    will implicitly invoke +initialize for CLASS if it hasn't been
237d77920ccSmrg    invoked yet.  This also insures that +initialize has been invoked
238d77920ccSmrg    when the returned implementation is called directly.
239d77920ccSmrg 
240d77920ccSmrg    The forwarding hooks require the receiver as an argument (if they are to
241d77920ccSmrg    perform dynamic lookup in proxy objects etc), so this function has a
242d77920ccSmrg    receiver argument to be used with those hooks.  */
243d77920ccSmrg static inline
244d77920ccSmrg IMP
get_implementation(id receiver,Class class,SEL sel)245d77920ccSmrg get_implementation (id receiver, Class class, SEL sel)
246d77920ccSmrg {
247d77920ccSmrg   void *res;
248d77920ccSmrg 
249d77920ccSmrg   if (class->dtable == __objc_uninstalled_dtable)
250d77920ccSmrg     {
251d77920ccSmrg       /* The dispatch table needs to be installed.  */
252d77920ccSmrg       objc_mutex_lock (__objc_runtime_mutex);
253d77920ccSmrg 
254d77920ccSmrg       /* Double-checked locking pattern: Check
255d77920ccSmrg 	 __objc_uninstalled_dtable again in case another thread
256d77920ccSmrg 	 installed the dtable while we were waiting for the lock to be
257d77920ccSmrg 	 released.  */
258d77920ccSmrg       if (class->dtable == __objc_uninstalled_dtable)
259d77920ccSmrg 	__objc_install_dtable_for_class (class);
260d77920ccSmrg 
261d77920ccSmrg       /* If the dispatch table is not yet installed, we are still in
262d77920ccSmrg 	 the process of executing +initialize.  But the implementation
263d77920ccSmrg 	 pointer should be available in the prepared ispatch table if
264d77920ccSmrg 	 it exists at all.  */
265d77920ccSmrg       if (class->dtable == __objc_uninstalled_dtable)
266d77920ccSmrg 	{
267d77920ccSmrg 	  assert (__objc_prepared_dtable_for_class (class) != 0);
268d77920ccSmrg 	  res = __objc_get_prepared_imp (class, sel);
269d77920ccSmrg 	}
270d77920ccSmrg       else
271d77920ccSmrg 	res = 0;
272d77920ccSmrg 
273d77920ccSmrg       objc_mutex_unlock (__objc_runtime_mutex);
274d77920ccSmrg       /* Call ourselves with the installed dispatch table and get the
275d77920ccSmrg 	 real method.  */
276d77920ccSmrg       if (!res)
277d77920ccSmrg 	res = get_implementation (receiver, class, sel);
278d77920ccSmrg     }
279d77920ccSmrg   else
280d77920ccSmrg     {
281d77920ccSmrg       /* The dispatch table has been installed.  */
282d77920ccSmrg       res = sarray_get_safe (class->dtable, (size_t) sel->sel_id);
283d77920ccSmrg       if (res == 0)
284d77920ccSmrg 	{
285d77920ccSmrg 	  /* The dispatch table has been installed, and the method is
286d77920ccSmrg 	     not in the dispatch table.  So the method just doesn't
287d77920ccSmrg 	     exist for the class.  */
288d77920ccSmrg 
289d77920ccSmrg 	  /* Try going through the +resolveClassMethod: or
290d77920ccSmrg 	     +resolveInstanceMethod: process.  */
291d77920ccSmrg 	  if (CLS_ISMETA (class))
292d77920ccSmrg 	    {
293d77920ccSmrg 	      /* We have the meta class, but we need to invoke the
294d77920ccSmrg 		 +resolveClassMethod: method on the class.  So, we
295d77920ccSmrg 		 need to obtain the class from the meta class, which
296d77920ccSmrg 		 we do using the fact that both the class and the
297d77920ccSmrg 		 meta-class have the same name.  */
298d77920ccSmrg 	      Class realClass = objc_lookUpClass (class->name);
299d77920ccSmrg 	      if (realClass)
300d77920ccSmrg 		res = __objc_resolve_class_method (realClass, sel);
301d77920ccSmrg 	    }
302d77920ccSmrg 	  else
303d77920ccSmrg 	    res = __objc_resolve_instance_method (class, sel);
304d77920ccSmrg 
305d77920ccSmrg 	  if (res == 0)
306d77920ccSmrg 	    res = __objc_get_forward_imp (receiver, sel);
307d77920ccSmrg 	}
308d77920ccSmrg     }
309d77920ccSmrg   return res;
310d77920ccSmrg }
311d77920ccSmrg 
312d35849d0Smrg /* Make sure this inline function is exported regardless of GNU89 or C99
313d35849d0Smrg    inlining semantics as it is part of the libobjc ABI.  */
314d35849d0Smrg extern IMP get_imp (Class, SEL);
315d35849d0Smrg 
316d35849d0Smrg inline
3174fee23f9Smrg IMP
get_imp(Class class,SEL sel)3184fee23f9Smrg get_imp (Class class, SEL sel)
3194fee23f9Smrg {
3204fee23f9Smrg   /* In a vanilla implementation we would first check if the dispatch
3214fee23f9Smrg      table is installed.  Here instead, to get more speed in the
3224fee23f9Smrg      standard case (that the dispatch table is installed) we first try
3234fee23f9Smrg      to get the imp using brute force.  Only if that fails, we do what
3244fee23f9Smrg      we should have been doing from the very beginning, that is, check
3254fee23f9Smrg      if the dispatch table needs to be installed, install it if it's
3264fee23f9Smrg      not installed, and retrieve the imp from the table if it's
3274fee23f9Smrg      installed.  */
3284fee23f9Smrg   void *res = sarray_get_safe (class->dtable, (size_t) sel->sel_id);
3294fee23f9Smrg   if (res == 0)
3304fee23f9Smrg     {
331d77920ccSmrg       res = get_implementation(nil, class, sel);
3324fee23f9Smrg     }
3334fee23f9Smrg   return res;
3344fee23f9Smrg }
3354fee23f9Smrg 
336d77920ccSmrg /* The new name of get_imp().  */
337d77920ccSmrg IMP
class_getMethodImplementation(Class class_,SEL selector)338d77920ccSmrg class_getMethodImplementation (Class class_, SEL selector)
339d77920ccSmrg {
340d77920ccSmrg   if (class_ == Nil  ||  selector == NULL)
341d77920ccSmrg     return NULL;
342d77920ccSmrg 
343d77920ccSmrg   /* get_imp is inlined, so we're good.  */
344d77920ccSmrg   return get_imp (class_, selector);
345d77920ccSmrg }
346d77920ccSmrg 
347d77920ccSmrg /* Given a method, return its implementation.  This has been replaced
348d77920ccSmrg    by method_getImplementation() in the modern API.  */
349d77920ccSmrg IMP
method_get_imp(struct objc_method * method)350d77920ccSmrg method_get_imp (struct objc_method * method)
351d77920ccSmrg {
352d77920ccSmrg   return (method != (struct objc_method *)0) ? method->method_imp : (IMP)0;
353d77920ccSmrg }
354d77920ccSmrg 
3554fee23f9Smrg /* Query if an object can respond to a selector, returns YES if the
3564fee23f9Smrg    object implements the selector otherwise NO.  Does not check if the
357d77920ccSmrg    method can be forwarded.  Since this requires the dispatch table to
358d77920ccSmrg    installed, this function will implicitly invoke +initialize for the
359d77920ccSmrg    class of OBJECT if it hasn't been invoked yet.  */
3604fee23f9Smrg BOOL
__objc_responds_to(id object,SEL sel)3614fee23f9Smrg __objc_responds_to (id object, SEL sel)
3624fee23f9Smrg {
3634fee23f9Smrg   void *res;
364d77920ccSmrg   struct sarray *dtable;
3654fee23f9Smrg 
3664fee23f9Smrg   /* Install dispatch table if need be */
367d77920ccSmrg   dtable = object->class_pointer->dtable;
368d77920ccSmrg   if (dtable == __objc_uninstalled_dtable)
3694fee23f9Smrg     {
3704fee23f9Smrg       objc_mutex_lock (__objc_runtime_mutex);
3714fee23f9Smrg       if (object->class_pointer->dtable == __objc_uninstalled_dtable)
372d77920ccSmrg         __objc_install_dtable_for_class (object->class_pointer);
373d77920ccSmrg 
374d77920ccSmrg       /* If the dispatch table is not yet installed, we are still in
375d77920ccSmrg          the process of executing +initialize.  Yet the dispatch table
376d77920ccSmrg          should be available.  */
377d77920ccSmrg       if (object->class_pointer->dtable == __objc_uninstalled_dtable)
3784fee23f9Smrg         {
379d77920ccSmrg           dtable = __objc_prepared_dtable_for_class (object->class_pointer);
380d77920ccSmrg           assert (dtable);
3814fee23f9Smrg         }
382d77920ccSmrg       else
383d77920ccSmrg         dtable = object->class_pointer->dtable;
384d77920ccSmrg 
3854fee23f9Smrg       objc_mutex_unlock (__objc_runtime_mutex);
3864fee23f9Smrg     }
3874fee23f9Smrg 
388d77920ccSmrg   /* Get the method from the dispatch table.  */
389d77920ccSmrg   res = sarray_get_safe (dtable, (size_t) sel->sel_id);
390d77920ccSmrg   return (res != 0) ? YES : NO;
391d77920ccSmrg }
392d77920ccSmrg 
393d77920ccSmrg BOOL
class_respondsToSelector(Class class_,SEL selector)394d77920ccSmrg class_respondsToSelector (Class class_, SEL selector)
395d77920ccSmrg {
396d77920ccSmrg   struct sarray *dtable;
397d77920ccSmrg   void *res;
398d77920ccSmrg 
399d77920ccSmrg   if (class_ == Nil  ||  selector == NULL)
400d77920ccSmrg     return NO;
401d77920ccSmrg 
402d77920ccSmrg   /* Install dispatch table if need be.  */
403d77920ccSmrg   dtable = class_->dtable;
404d77920ccSmrg   if (dtable == __objc_uninstalled_dtable)
405d77920ccSmrg     {
406d77920ccSmrg       objc_mutex_lock (__objc_runtime_mutex);
407d77920ccSmrg       if (class_->dtable == __objc_uninstalled_dtable)
408d77920ccSmrg 	__objc_install_dtable_for_class (class_);
409d77920ccSmrg 
410d77920ccSmrg       /* If the dispatch table is not yet installed,
411d77920ccSmrg          we are still in the process of executing +initialize.
412d77920ccSmrg          Yet the dispatch table should be available.  */
413d77920ccSmrg       if (class_->dtable == __objc_uninstalled_dtable)
414d77920ccSmrg         {
415d77920ccSmrg           dtable = __objc_prepared_dtable_for_class (class_);
416d77920ccSmrg           assert (dtable);
417d77920ccSmrg         }
418d77920ccSmrg       else
419d77920ccSmrg         dtable = class_->dtable;
420d77920ccSmrg 
421d77920ccSmrg       objc_mutex_unlock (__objc_runtime_mutex);
422d77920ccSmrg     }
423d77920ccSmrg 
424d77920ccSmrg   /* Get the method from the dispatch table.  */
425d77920ccSmrg   res = sarray_get_safe (dtable, (size_t) selector->sel_id);
426d77920ccSmrg   return (res != 0) ? YES : NO;
4274fee23f9Smrg }
4284fee23f9Smrg 
4294fee23f9Smrg /* This is the lookup function.  All entries in the table are either a
4304fee23f9Smrg    valid method *or* zero.  If zero then either the dispatch table
431d77920ccSmrg    needs to be installed or it doesn't exist and forwarding is
432d77920ccSmrg    attempted.  */
4334fee23f9Smrg IMP
objc_msg_lookup(id receiver,SEL op)4344fee23f9Smrg objc_msg_lookup (id receiver, SEL op)
4354fee23f9Smrg {
4364fee23f9Smrg   IMP result;
4374fee23f9Smrg   if (receiver)
4384fee23f9Smrg     {
439d77920ccSmrg       /* First try a quick lookup assuming the dispatch table exists.  */
4404fee23f9Smrg       result = sarray_get_safe (receiver->class_pointer->dtable,
4414fee23f9Smrg 				(sidx)op->sel_id);
4424fee23f9Smrg       if (result == 0)
4434fee23f9Smrg 	{
444d77920ccSmrg 	  /* Not found ... call get_implementation () to install the
445d77920ccSmrg              dispatch table and call +initialize as required,
446d77920ccSmrg              providing the method implementation or a forwarding
447d77920ccSmrg              function.  */
448d77920ccSmrg 	  result = get_implementation (receiver, receiver->class_pointer, op);
4494fee23f9Smrg 	}
4504fee23f9Smrg       return result;
4514fee23f9Smrg     }
4524fee23f9Smrg   else
4534fee23f9Smrg     return (IMP)nil_method;
4544fee23f9Smrg }
4554fee23f9Smrg 
4564fee23f9Smrg IMP
objc_msg_lookup_super(struct objc_super * super,SEL sel)457d77920ccSmrg objc_msg_lookup_super (struct objc_super *super, SEL sel)
4584fee23f9Smrg {
4594fee23f9Smrg   if (super->self)
460d77920ccSmrg     return get_imp (super->super_class, sel);
4614fee23f9Smrg   else
4624fee23f9Smrg     return (IMP)nil_method;
4634fee23f9Smrg }
4644fee23f9Smrg 
4654fee23f9Smrg void
__objc_init_dispatch_tables()4664fee23f9Smrg __objc_init_dispatch_tables ()
4674fee23f9Smrg {
4684fee23f9Smrg   __objc_uninstalled_dtable = sarray_new (200, 0);
469d77920ccSmrg 
470d77920ccSmrg   /* TODO: It would be cool to register typed selectors here.  */
471d77920ccSmrg   selector_resolveClassMethod = sel_registerName ("resolveClassMethod:");
472d77920ccSmrg   selector_resolveInstanceMethod = sel_registerName ("resolveInstanceMethod:");
4734fee23f9Smrg }
4744fee23f9Smrg 
4754fee23f9Smrg 
4764fee23f9Smrg /* Install dummy table for class which causes the first message to
477d77920ccSmrg    that class (or instances hereof) to be initialized properly.  */
4784fee23f9Smrg void
__objc_install_premature_dtable(Class class)4794fee23f9Smrg __objc_install_premature_dtable (Class class)
4804fee23f9Smrg {
4814fee23f9Smrg   assert (__objc_uninstalled_dtable);
4824fee23f9Smrg   class->dtable = __objc_uninstalled_dtable;
4834fee23f9Smrg }
4844fee23f9Smrg 
485d77920ccSmrg /* Send +initialize to class if not already done.  */
4864fee23f9Smrg static void
__objc_send_initialize(Class class)4874fee23f9Smrg __objc_send_initialize (Class class)
4884fee23f9Smrg {
489d77920ccSmrg   /* This *must* be a class object.  */
4904fee23f9Smrg   assert (CLS_ISCLASS (class));
4914fee23f9Smrg   assert (! CLS_ISMETA (class));
4924fee23f9Smrg 
493d77920ccSmrg   /* class_add_method_list/__objc_update_dispatch_table_for_class may
494d77920ccSmrg      have reset the dispatch table.  The canonical way to insure that
495d77920ccSmrg      we send +initialize just once, is this flag.  */
4964fee23f9Smrg   if (! CLS_ISINITIALIZED (class))
4974fee23f9Smrg     {
498d77920ccSmrg       DEBUG_PRINTF ("+initialize: need to initialize class '%s'\n", class->name);
4994fee23f9Smrg       CLS_SETINITIALIZED (class);
5004fee23f9Smrg       CLS_SETINITIALIZED (class->class_pointer);
5014fee23f9Smrg 
502d77920ccSmrg       /* Create the garbage collector type memory description.  */
5034fee23f9Smrg       __objc_generate_gc_type_description (class);
5044fee23f9Smrg 
5054fee23f9Smrg       if (class->super_class)
5064fee23f9Smrg 	__objc_send_initialize (class->super_class);
5074fee23f9Smrg 
5084fee23f9Smrg       {
509d77920ccSmrg 	SEL op = sel_registerName ("initialize");
510d77920ccSmrg         struct objc_method *method = search_for_method_in_hierarchy (class->class_pointer,
511d77920ccSmrg 								     op);
5124fee23f9Smrg 
513d77920ccSmrg 	if (method)
514d77920ccSmrg 	  {
515d77920ccSmrg 	    DEBUG_PRINTF (" begin of [%s +initialize]\n", class->name);
516d77920ccSmrg 	    (*method->method_imp) ((id)class, op);
517d77920ccSmrg 	    DEBUG_PRINTF (" end of [%s +initialize]\n", class->name);
5184fee23f9Smrg 	  }
519d77920ccSmrg #ifdef DEBUG
520d77920ccSmrg 	else
521d77920ccSmrg 	  {
522d77920ccSmrg 	    DEBUG_PRINTF (" class '%s' has no +initialize method\n", class->name);
5234fee23f9Smrg 	  }
524d77920ccSmrg #endif
5254fee23f9Smrg       }
5264fee23f9Smrg     }
5274fee23f9Smrg }
5284fee23f9Smrg 
529d77920ccSmrg /* Walk on the methods list of class and install the methods in the
530d77920ccSmrg    reverse order of the lists.  Since methods added by categories are
531d77920ccSmrg    before the methods of class in the methods list, this allows
532d77920ccSmrg    categories to substitute methods declared in class.  However if
533d77920ccSmrg    more than one category replaces the same method nothing is
534d77920ccSmrg    guaranteed about what method will be used.  Assumes that
535d77920ccSmrg    __objc_runtime_mutex is locked down.  */
5364fee23f9Smrg static void
__objc_install_methods_in_dtable(struct sarray * dtable,struct objc_method_list * method_list)537d77920ccSmrg __objc_install_methods_in_dtable (struct sarray *dtable, struct objc_method_list * method_list)
5384fee23f9Smrg {
5394fee23f9Smrg   int i;
5404fee23f9Smrg 
5414fee23f9Smrg   if (! method_list)
5424fee23f9Smrg     return;
5434fee23f9Smrg 
5444fee23f9Smrg   if (method_list->method_next)
545d77920ccSmrg     __objc_install_methods_in_dtable (dtable, method_list->method_next);
5464fee23f9Smrg 
5474fee23f9Smrg   for (i = 0; i < method_list->method_count; i++)
5484fee23f9Smrg     {
549d77920ccSmrg       struct objc_method * method = &(method_list->method_list[i]);
550d77920ccSmrg       sarray_at_put_safe (dtable,
5514fee23f9Smrg 			  (sidx) method->method_name->sel_id,
5524fee23f9Smrg 			  method->method_imp);
5534fee23f9Smrg     }
5544fee23f9Smrg }
5554fee23f9Smrg 
5564fee23f9Smrg void
__objc_update_dispatch_table_for_class(Class class)5574fee23f9Smrg __objc_update_dispatch_table_for_class (Class class)
5584fee23f9Smrg {
5594fee23f9Smrg   Class next;
5604fee23f9Smrg   struct sarray *arr;
5614fee23f9Smrg 
562d77920ccSmrg   DEBUG_PRINTF (" _objc_update_dtable_for_class (%s)\n", class->name);
5634fee23f9Smrg 
5644fee23f9Smrg   objc_mutex_lock (__objc_runtime_mutex);
5654fee23f9Smrg 
566d77920ccSmrg   /* Not yet installed -- skip it unless in +initialize.  */
567d77920ccSmrg   if (class->dtable == __objc_uninstalled_dtable)
568d77920ccSmrg     {
569d77920ccSmrg       if (__objc_prepared_dtable_for_class (class))
570d77920ccSmrg 	{
571d77920ccSmrg 	  /* There is a prepared table so we must be initialising this
572d77920ccSmrg 	     class ... we must re-do the table preparation.  */
573d77920ccSmrg 	  __objc_prepare_dtable_for_class (class);
574d77920ccSmrg 	}
575d77920ccSmrg       objc_mutex_unlock (__objc_runtime_mutex);
576d77920ccSmrg       return;
577d77920ccSmrg     }
578d77920ccSmrg 
5794fee23f9Smrg   arr = class->dtable;
5804fee23f9Smrg   __objc_install_premature_dtable (class); /* someone might require it... */
5814fee23f9Smrg   sarray_free (arr);			   /* release memory */
5824fee23f9Smrg 
583d77920ccSmrg   /* Could have been lazy...  */
584d77920ccSmrg   __objc_install_dtable_for_class (class);
5854fee23f9Smrg 
586d77920ccSmrg   if (class->subclass_list)	/* Traverse subclasses.  */
5874fee23f9Smrg     for (next = class->subclass_list; next; next = next->sibling_class)
5884fee23f9Smrg       __objc_update_dispatch_table_for_class (next);
5894fee23f9Smrg 
5904fee23f9Smrg   objc_mutex_unlock (__objc_runtime_mutex);
5914fee23f9Smrg }
5924fee23f9Smrg 
5934fee23f9Smrg /* This function adds a method list to a class.  This function is
5944fee23f9Smrg    typically called by another function specific to the run-time.  As
5954fee23f9Smrg    such this function does not worry about thread safe issues.
5964fee23f9Smrg 
5974fee23f9Smrg    This one is only called for categories. Class objects have their
5984fee23f9Smrg    methods installed right away, and their selectors are made into
5994fee23f9Smrg    SEL's by the function __objc_register_selectors_from_class.  */
6004fee23f9Smrg void
class_add_method_list(Class class,struct objc_method_list * list)601d77920ccSmrg class_add_method_list (Class class, struct objc_method_list * list)
6024fee23f9Smrg {
6034fee23f9Smrg   /* Passing of a linked list is not allowed.  Do multiple calls.  */
6044fee23f9Smrg   assert (! list->method_next);
6054fee23f9Smrg 
6064fee23f9Smrg   __objc_register_selectors_from_list(list);
6074fee23f9Smrg 
6084fee23f9Smrg   /* Add the methods to the class's method list.  */
6094fee23f9Smrg   list->method_next = class->methods;
6104fee23f9Smrg   class->methods = list;
6114fee23f9Smrg 
612d77920ccSmrg   /* Update the dispatch table of class.  */
6134fee23f9Smrg   __objc_update_dispatch_table_for_class (class);
6144fee23f9Smrg }
6154fee23f9Smrg 
616d77920ccSmrg struct objc_method *
class_getInstanceMethod(Class class_,SEL selector)617d77920ccSmrg class_getInstanceMethod (Class class_, SEL selector)
6184fee23f9Smrg {
619d77920ccSmrg   struct objc_method *m;
620d77920ccSmrg 
621d77920ccSmrg   if (class_ == Nil  ||  selector == NULL)
622d77920ccSmrg     return NULL;
623d77920ccSmrg 
624d77920ccSmrg   m = search_for_method_in_hierarchy (class_, selector);
625d77920ccSmrg   if (m)
626d77920ccSmrg     return m;
627d77920ccSmrg 
628d77920ccSmrg   /* Try going through +resolveInstanceMethod:, and do the search
629d77920ccSmrg      again if successful.  */
630d77920ccSmrg   if (__objc_resolve_instance_method (class_, selector))
631d77920ccSmrg     return search_for_method_in_hierarchy (class_, selector);
632d77920ccSmrg 
633d77920ccSmrg   return NULL;
6344fee23f9Smrg }
6354fee23f9Smrg 
636d77920ccSmrg struct objc_method *
class_getClassMethod(Class class_,SEL selector)637d77920ccSmrg class_getClassMethod (Class class_, SEL selector)
6384fee23f9Smrg {
639d77920ccSmrg   struct objc_method *m;
640d77920ccSmrg 
641d77920ccSmrg   if (class_ == Nil  ||  selector == NULL)
642d77920ccSmrg     return NULL;
643d77920ccSmrg 
644d77920ccSmrg   m = search_for_method_in_hierarchy (class_->class_pointer,
645d77920ccSmrg 				      selector);
646d77920ccSmrg   if (m)
647d77920ccSmrg     return m;
648d77920ccSmrg 
649d77920ccSmrg   /* Try going through +resolveClassMethod:, and do the search again
650d77920ccSmrg      if successful.  */
651d77920ccSmrg   if (__objc_resolve_class_method (class_, selector))
652d77920ccSmrg     return search_for_method_in_hierarchy (class_->class_pointer,
653d77920ccSmrg 					   selector);
654d77920ccSmrg 
655d77920ccSmrg   return NULL;
6564fee23f9Smrg }
6574fee23f9Smrg 
658d77920ccSmrg BOOL
class_addMethod(Class class_,SEL selector,IMP implementation,const char * method_types)659d77920ccSmrg class_addMethod (Class class_, SEL selector, IMP implementation,
660d77920ccSmrg 		 const char *method_types)
661d77920ccSmrg {
662d77920ccSmrg   struct objc_method_list *method_list;
663d77920ccSmrg   struct objc_method *method;
664d77920ccSmrg   const char *method_name;
6654fee23f9Smrg 
666d77920ccSmrg   if (class_ == Nil  ||  selector == NULL  ||  implementation == NULL
667d77920ccSmrg       || method_types == NULL  || (strcmp (method_types, "") == 0))
668d77920ccSmrg     return NO;
6694fee23f9Smrg 
670d77920ccSmrg   method_name = sel_getName (selector);
671d77920ccSmrg   if (method_name == NULL)
672d77920ccSmrg     return NO;
673d77920ccSmrg 
674d77920ccSmrg   /* If the method already exists in the class, return NO.  It is fine
675d77920ccSmrg      if the method already exists in the superclass; in that case, we
676d77920ccSmrg      are overriding it.  */
677d77920ccSmrg   if (CLS_IS_IN_CONSTRUCTION (class_))
678d77920ccSmrg     {
679d77920ccSmrg       /* The class only contains a list of methods; they have not been
680d77920ccSmrg 	 registered yet, ie, the method_name of each of them is still
681d77920ccSmrg 	 a string, not a selector.  Iterate manually over them to
682d77920ccSmrg 	 check if we have already added the method.  */
683d77920ccSmrg       struct objc_method_list * method_list = class_->methods;
684d77920ccSmrg       while (method_list)
685d77920ccSmrg 	{
686d77920ccSmrg 	  int i;
687d77920ccSmrg 
688d77920ccSmrg 	  /* Search the method list.  */
689d77920ccSmrg 	  for (i = 0; i < method_list->method_count; ++i)
690d77920ccSmrg 	    {
691d77920ccSmrg 	      struct objc_method * method = &method_list->method_list[i];
692d77920ccSmrg 
693d77920ccSmrg 	      if (method->method_name
694d77920ccSmrg 		  && strcmp ((char *)method->method_name, method_name) == 0)
695d77920ccSmrg 		return NO;
696d77920ccSmrg 	    }
697d77920ccSmrg 
698d77920ccSmrg 	  /* The method wasn't found.  Follow the link to the next list of
699d77920ccSmrg 	     methods.  */
700d77920ccSmrg 	  method_list = method_list->method_next;
701d77920ccSmrg 	}
702d77920ccSmrg       /* The method wasn't found.  It's a new one.  Go ahead and add
703d77920ccSmrg 	 it.  */
704d77920ccSmrg     }
705d77920ccSmrg   else
706d77920ccSmrg     {
707d77920ccSmrg       /* Do the standard lookup.  This assumes the selectors are
708d77920ccSmrg 	 mapped.  */
709d77920ccSmrg       if (search_for_method_in_list (class_->methods, selector))
710d77920ccSmrg 	return NO;
711d77920ccSmrg     }
712d77920ccSmrg 
713d77920ccSmrg   method_list = (struct objc_method_list *)objc_calloc (1, sizeof (struct objc_method_list));
714d77920ccSmrg   method_list->method_count = 1;
715d77920ccSmrg 
716d77920ccSmrg   method = &(method_list->method_list[0]);
717d77920ccSmrg   method->method_name = objc_malloc (strlen (method_name) + 1);
718d77920ccSmrg   strcpy ((char *)method->method_name, method_name);
719d77920ccSmrg 
720d77920ccSmrg   method->method_types = objc_malloc (strlen (method_types) + 1);
721d77920ccSmrg   strcpy ((char *)method->method_types, method_types);
722d77920ccSmrg 
723d77920ccSmrg   method->method_imp = implementation;
724d77920ccSmrg 
725d77920ccSmrg   if (CLS_IS_IN_CONSTRUCTION (class_))
726d77920ccSmrg     {
727d77920ccSmrg       /* We only need to add the method to the list.  It will be
728d77920ccSmrg 	 registered with the runtime when the class pair is registered
729d77920ccSmrg 	 (if ever).  */
730d77920ccSmrg       method_list->method_next = class_->methods;
731d77920ccSmrg       class_->methods = method_list;
732d77920ccSmrg     }
733d77920ccSmrg   else
734d77920ccSmrg     {
735d77920ccSmrg       /* Add the method to a live class.  */
736d77920ccSmrg       objc_mutex_lock (__objc_runtime_mutex);
737d77920ccSmrg       class_add_method_list (class_, method_list);
738d77920ccSmrg       objc_mutex_unlock (__objc_runtime_mutex);
739d77920ccSmrg     }
740d77920ccSmrg 
741d77920ccSmrg   return YES;
742d77920ccSmrg }
743d77920ccSmrg 
744d77920ccSmrg IMP
class_replaceMethod(Class class_,SEL selector,IMP implementation,const char * method_types)745d77920ccSmrg class_replaceMethod (Class class_, SEL selector, IMP implementation,
746d77920ccSmrg 		     const char *method_types)
747d77920ccSmrg {
748d77920ccSmrg   struct objc_method * method;
749d77920ccSmrg 
750d77920ccSmrg   if (class_ == Nil  ||  selector == NULL  ||  implementation == NULL
751d77920ccSmrg       || method_types == NULL)
752d77920ccSmrg     return NULL;
753d77920ccSmrg 
754d77920ccSmrg   method = search_for_method_in_hierarchy (class_, selector);
755d77920ccSmrg 
756d77920ccSmrg   if (method)
757d77920ccSmrg     {
758d77920ccSmrg       return method_setImplementation (method, implementation);
759d77920ccSmrg     }
760d77920ccSmrg   else
761d77920ccSmrg     {
762d77920ccSmrg       class_addMethod (class_, selector, implementation, method_types);
763d77920ccSmrg       return NULL;
764d77920ccSmrg     }
765d77920ccSmrg }
766d77920ccSmrg 
767d77920ccSmrg /* Search for a method starting from the current class up its
768d77920ccSmrg    hierarchy.  Return a pointer to the method's method structure if
769d77920ccSmrg    found.  NULL otherwise.  */
770d77920ccSmrg static struct objc_method *
search_for_method_in_hierarchy(Class cls,SEL sel)7714fee23f9Smrg search_for_method_in_hierarchy (Class cls, SEL sel)
7724fee23f9Smrg {
773d77920ccSmrg   struct objc_method * method = NULL;
7744fee23f9Smrg   Class class;
7754fee23f9Smrg 
7764fee23f9Smrg   if (! sel_is_mapped (sel))
7774fee23f9Smrg     return NULL;
7784fee23f9Smrg 
779d77920ccSmrg   /* Scan the method list of the class.  If the method isn't found in
780d77920ccSmrg      the list then step to its super class.  */
7814fee23f9Smrg   for (class = cls; ((! method) && class); class = class->super_class)
7824fee23f9Smrg     method = search_for_method_in_list (class->methods, sel);
7834fee23f9Smrg 
7844fee23f9Smrg   return method;
7854fee23f9Smrg }
7864fee23f9Smrg 
7874fee23f9Smrg 
7884fee23f9Smrg 
789d77920ccSmrg /* Given a linked list of method and a method's name.  Search for the
790d77920ccSmrg    named method's method structure.  Return a pointer to the method's
791d77920ccSmrg    method structure if found.  NULL otherwise.  */
792d77920ccSmrg struct objc_method *
search_for_method_in_list(struct objc_method_list * list,SEL op)793d77920ccSmrg search_for_method_in_list (struct objc_method_list * list, SEL op)
7944fee23f9Smrg {
795d77920ccSmrg   struct objc_method_list * method_list = list;
7964fee23f9Smrg 
7974fee23f9Smrg   if (! sel_is_mapped (op))
7984fee23f9Smrg     return NULL;
7994fee23f9Smrg 
8004fee23f9Smrg   /* If not found then we'll search the list.  */
8014fee23f9Smrg   while (method_list)
8024fee23f9Smrg     {
8034fee23f9Smrg       int i;
8044fee23f9Smrg 
8054fee23f9Smrg       /* Search the method list.  */
8064fee23f9Smrg       for (i = 0; i < method_list->method_count; ++i)
8074fee23f9Smrg         {
808d77920ccSmrg           struct objc_method * method = &method_list->method_list[i];
8094fee23f9Smrg 
8104fee23f9Smrg           if (method->method_name)
8114fee23f9Smrg             if (method->method_name->sel_id == op->sel_id)
8124fee23f9Smrg               return method;
8134fee23f9Smrg         }
8144fee23f9Smrg 
8154fee23f9Smrg       /* The method wasn't found.  Follow the link to the next list of
8164fee23f9Smrg          methods.  */
8174fee23f9Smrg       method_list = method_list->method_next;
8184fee23f9Smrg     }
8194fee23f9Smrg 
8204fee23f9Smrg   return NULL;
8214fee23f9Smrg }
8224fee23f9Smrg 
823d77920ccSmrg typedef void * retval_t;
824d77920ccSmrg typedef void * arglist_t;
825d77920ccSmrg 
8264fee23f9Smrg static retval_t __objc_forward (id object, SEL sel, arglist_t args);
8274fee23f9Smrg 
828d77920ccSmrg /* Forwarding pointers/integers through the normal registers.  */
8294fee23f9Smrg static id
__objc_word_forward(id rcv,SEL op,...)8304fee23f9Smrg __objc_word_forward (id rcv, SEL op, ...)
8314fee23f9Smrg {
8324fee23f9Smrg   void *args, *res;
8334fee23f9Smrg 
8344fee23f9Smrg   args = __builtin_apply_args ();
8354fee23f9Smrg   res = __objc_forward (rcv, op, args);
8364fee23f9Smrg   if (res)
8374fee23f9Smrg     __builtin_return (res);
8384fee23f9Smrg   else
8394fee23f9Smrg     return res;
8404fee23f9Smrg }
8414fee23f9Smrg 
8424fee23f9Smrg /* Specific routine for forwarding floats/double because of
843d77920ccSmrg    architectural differences on some processors.  i386s for example
844d77920ccSmrg    which uses a floating point stack versus general registers for
845d77920ccSmrg    floating point numbers.  This forward routine makes sure that GCC
846d77920ccSmrg    restores the proper return values.  */
8474fee23f9Smrg static double
__objc_double_forward(id rcv,SEL op,...)8484fee23f9Smrg __objc_double_forward (id rcv, SEL op, ...)
8494fee23f9Smrg {
8504fee23f9Smrg   void *args, *res;
8514fee23f9Smrg 
8524fee23f9Smrg   args = __builtin_apply_args ();
8534fee23f9Smrg   res = __objc_forward (rcv, op, args);
8544fee23f9Smrg   __builtin_return (res);
8554fee23f9Smrg }
8564fee23f9Smrg 
8574fee23f9Smrg #if INVISIBLE_STRUCT_RETURN
8584fee23f9Smrg static __big
8594fee23f9Smrg #else
8604fee23f9Smrg static id
8614fee23f9Smrg #endif
__objc_block_forward(id rcv,SEL op,...)8624fee23f9Smrg __objc_block_forward (id rcv, SEL op, ...)
8634fee23f9Smrg {
8644fee23f9Smrg   void *args, *res;
8654fee23f9Smrg 
8664fee23f9Smrg   args = __builtin_apply_args ();
8674fee23f9Smrg   res = __objc_forward (rcv, op, args);
8684fee23f9Smrg   if (res)
8694fee23f9Smrg     __builtin_return (res);
8704fee23f9Smrg   else
8714fee23f9Smrg #if INVISIBLE_STRUCT_RETURN
8724fee23f9Smrg     return (__big) {{0, 0, 0, 0, 0, 0, 0, 0}};
8734fee23f9Smrg #else
8744fee23f9Smrg     return nil;
8754fee23f9Smrg #endif
8764fee23f9Smrg }
8774fee23f9Smrg 
8784fee23f9Smrg 
879d77920ccSmrg /* This function is called for methods which are not implemented,
880d77920ccSmrg    unless a custom forwarding routine has been installed.  Please note
881d77920ccSmrg    that most serious users of libobjc (eg, GNUstep base) do install
882d77920ccSmrg    their own forwarding routines, and hence this is never actually
883d77920ccSmrg    used.  But, if no custom forwarding routine is installed, this is
884d77920ccSmrg    called when a selector is not recognized.  */
8854fee23f9Smrg static retval_t
__objc_forward(id object,SEL sel,arglist_t args)8864fee23f9Smrg __objc_forward (id object, SEL sel, arglist_t args)
8874fee23f9Smrg {
8884fee23f9Smrg   IMP imp;
8894fee23f9Smrg   static SEL frwd_sel = 0;                      /* !T:SAFE2 */
8904fee23f9Smrg   SEL err_sel;
8914fee23f9Smrg 
892d77920ccSmrg   /* First try if the object understands forward::.  */
8934fee23f9Smrg   if (! frwd_sel)
8944fee23f9Smrg     frwd_sel = sel_get_any_uid ("forward::");
8954fee23f9Smrg 
8964fee23f9Smrg   if (__objc_responds_to (object, frwd_sel))
8974fee23f9Smrg     {
898d77920ccSmrg       imp = get_implementation (object, object->class_pointer, frwd_sel);
8994fee23f9Smrg       return (*imp) (object, frwd_sel, sel, args);
9004fee23f9Smrg     }
9014fee23f9Smrg 
902d77920ccSmrg   /* If the object recognizes the doesNotRecognize: method then we're
903d77920ccSmrg      going to send it.  */
9044fee23f9Smrg   err_sel = sel_get_any_uid ("doesNotRecognize:");
9054fee23f9Smrg   if (__objc_responds_to (object, err_sel))
9064fee23f9Smrg     {
907d77920ccSmrg       imp = get_implementation (object, object->class_pointer, err_sel);
9084fee23f9Smrg       return (*imp) (object, err_sel, sel);
9094fee23f9Smrg     }
9104fee23f9Smrg 
9114fee23f9Smrg   /* The object doesn't recognize the method.  Check for responding to
9124fee23f9Smrg      error:.  If it does then sent it.  */
9134fee23f9Smrg   {
914d77920ccSmrg     char msg[256 + strlen ((const char *) sel_getName (sel))
9154fee23f9Smrg              + strlen ((const char *) object->class_pointer->name)];
9164fee23f9Smrg 
9174fee23f9Smrg     sprintf (msg, "(%s) %s does not recognize %s",
9184fee23f9Smrg 	     (CLS_ISMETA (object->class_pointer)
9194fee23f9Smrg 	      ? "class"
9204fee23f9Smrg 	      : "instance" ),
921d77920ccSmrg              object->class_pointer->name, sel_getName (sel));
9224fee23f9Smrg 
923d77920ccSmrg     /* The object doesn't respond to doesNotRecognize:.  Therefore, a
924d77920ccSmrg        default action is taken.  */
925d77920ccSmrg     _objc_abort ("%s\n", msg);
9264fee23f9Smrg 
9274fee23f9Smrg     return 0;
9284fee23f9Smrg   }
9294fee23f9Smrg }
9304fee23f9Smrg 
9314fee23f9Smrg void
__objc_print_dtable_stats(void)932d77920ccSmrg __objc_print_dtable_stats (void)
9334fee23f9Smrg {
9344fee23f9Smrg   int total = 0;
9354fee23f9Smrg 
9364fee23f9Smrg   objc_mutex_lock (__objc_runtime_mutex);
9374fee23f9Smrg 
9384fee23f9Smrg #ifdef OBJC_SPARSE2
9394fee23f9Smrg   printf ("memory usage: (%s)\n", "2-level sparse arrays");
9404fee23f9Smrg #else
9414fee23f9Smrg   printf ("memory usage: (%s)\n", "3-level sparse arrays");
9424fee23f9Smrg #endif
9434fee23f9Smrg 
9444fee23f9Smrg   printf ("arrays: %d = %ld bytes\n", narrays,
9454fee23f9Smrg 	  (long) ((size_t) narrays * sizeof (struct sarray)));
9464fee23f9Smrg   total += narrays * sizeof (struct sarray);
9474fee23f9Smrg   printf ("buckets: %d = %ld bytes\n", nbuckets,
9484fee23f9Smrg 	  (long) ((size_t) nbuckets * sizeof (struct sbucket)));
9494fee23f9Smrg   total += nbuckets * sizeof (struct sbucket);
9504fee23f9Smrg 
9514fee23f9Smrg   printf ("idxtables: %d = %ld bytes\n",
9524fee23f9Smrg 	  idxsize, (long) ((size_t) idxsize * sizeof (void *)));
9534fee23f9Smrg   total += idxsize * sizeof (void *);
9544fee23f9Smrg   printf ("-----------------------------------\n");
9554fee23f9Smrg   printf ("total: %d bytes\n", total);
9564fee23f9Smrg   printf ("===================================\n");
9574fee23f9Smrg 
9584fee23f9Smrg   objc_mutex_unlock (__objc_runtime_mutex);
9594fee23f9Smrg }
9604fee23f9Smrg 
961d77920ccSmrg static cache_ptr prepared_dtable_table = 0;
962d77920ccSmrg 
963d77920ccSmrg /* This function is called by: objc_msg_lookup, get_imp and
964d77920ccSmrg    __objc_responds_to (and the dispatch table installation functions
965d77920ccSmrg    themselves) to install a dispatch table for a class.
966d77920ccSmrg 
967d77920ccSmrg    If CLS is a class, it installs instance methods.
968d77920ccSmrg    If CLS is a meta class, it installs class methods.
969d77920ccSmrg 
970d77920ccSmrg    In either case +initialize is invoked for the corresponding class.
971d77920ccSmrg 
972d77920ccSmrg    The implementation must insure that the dispatch table is not
973d77920ccSmrg    installed until +initialize completes.  Otherwise it opens a
974d77920ccSmrg    potential race since the installation of the dispatch table is used
975d77920ccSmrg    as gate in regular method dispatch and we need to guarantee that
976d77920ccSmrg    +initialize is the first method invoked an that no other thread my
977d77920ccSmrg    dispatch messages to the class before +initialize completes.  */
978d77920ccSmrg static void
__objc_install_dtable_for_class(Class cls)979d77920ccSmrg __objc_install_dtable_for_class (Class cls)
9804fee23f9Smrg {
981d77920ccSmrg   /* If the class has not yet had its class links resolved, we must
982d77920ccSmrg      re-compute all class links.  */
983d77920ccSmrg   if (! CLS_ISRESOLV (cls))
984d77920ccSmrg     __objc_resolve_class_links ();
985d77920ccSmrg 
986d77920ccSmrg   /* Make sure the super class has its dispatch table installed or is
987d77920ccSmrg      at least preparing.  We do not need to send initialize for the
988d77920ccSmrg      super class since __objc_send_initialize will insure that.  */
989d77920ccSmrg   if (cls->super_class
990d77920ccSmrg       && cls->super_class->dtable == __objc_uninstalled_dtable
991d77920ccSmrg       && !__objc_prepared_dtable_for_class (cls->super_class))
992d77920ccSmrg     {
993d77920ccSmrg       __objc_install_dtable_for_class (cls->super_class);
994d77920ccSmrg       /* The superclass initialisation may have also initialised the
995d77920ccSmrg          current class, in which case there is no more to do.  */
996d77920ccSmrg       if (cls->dtable != __objc_uninstalled_dtable)
997d77920ccSmrg 	return;
998d77920ccSmrg     }
999d77920ccSmrg 
1000d77920ccSmrg   /* We have already been prepared but +initialize hasn't completed.
1001d77920ccSmrg      The +initialize implementation is probably sending 'self'
1002d77920ccSmrg      messages.  We rely on _objc_get_prepared_imp to retrieve the
1003d77920ccSmrg      implementation pointers.  */
1004d77920ccSmrg   if (__objc_prepared_dtable_for_class (cls))
1005d77920ccSmrg     return;
1006d77920ccSmrg 
1007d77920ccSmrg   /* We have this function cache the implementation pointers for
1008d77920ccSmrg      _objc_get_prepared_imp but the dispatch table won't be initilized
1009d77920ccSmrg      until __objc_send_initialize completes.  */
1010d77920ccSmrg   __objc_prepare_dtable_for_class (cls);
1011d77920ccSmrg 
1012d77920ccSmrg   /* We may have already invoked +initialize but
1013d77920ccSmrg      __objc_update_dispatch_table_for_class invoked by
1014d77920ccSmrg      class_add_method_list may have reset dispatch table.  */
1015d77920ccSmrg 
1016d77920ccSmrg   /* Call +initialize.  If we are a real class, we are installing
1017d77920ccSmrg      instance methods.  If we are a meta class, we are installing
1018d77920ccSmrg      class methods.  The __objc_send_initialize itself will insure
1019d77920ccSmrg      that the message is called only once per class.  */
1020d77920ccSmrg   if (CLS_ISCLASS (cls))
1021d77920ccSmrg     __objc_send_initialize (cls);
1022d77920ccSmrg   else
1023d77920ccSmrg     {
1024d77920ccSmrg       /* Retrieve the class from the meta class.  */
1025d77920ccSmrg       Class c = objc_getClass (cls->name);
1026d77920ccSmrg       assert (CLS_ISMETA (cls));
1027d77920ccSmrg       assert (c);
1028d77920ccSmrg       __objc_send_initialize (c);
1029d77920ccSmrg     }
1030d77920ccSmrg 
1031d77920ccSmrg   /* We install the dispatch table correctly when +initialize completed.  */
1032d77920ccSmrg   __objc_install_prepared_dtable_for_class (cls);
1033d77920ccSmrg }
1034d77920ccSmrg 
1035d77920ccSmrg /* Builds the dispatch table for the class CLS and stores it in a
1036d77920ccSmrg    place where it can be retrieved by __objc_get_prepared_imp until
1037d77920ccSmrg    __objc_install_prepared_dtable_for_class installs it into the
1038d77920ccSmrg    class.  The dispatch table should not be installed into the class
1039d77920ccSmrg    until +initialize has completed.  */
1040d77920ccSmrg static void
__objc_prepare_dtable_for_class(Class cls)1041d77920ccSmrg __objc_prepare_dtable_for_class (Class cls)
1042d77920ccSmrg {
1043d77920ccSmrg   struct sarray *dtable;
1044d77920ccSmrg   struct sarray *super_dtable;
1045d77920ccSmrg 
1046d77920ccSmrg   /* This table could be initialized in init.c.  We cannot use the
1047d77920ccSmrg      class name since the class maintains the instance methods and the
1048d77920ccSmrg      meta class maintains the the class methods yet both share the
1049d77920ccSmrg      same name.  Classes should be unique in any program.  */
1050d77920ccSmrg   if (! prepared_dtable_table)
1051d77920ccSmrg     prepared_dtable_table
1052d77920ccSmrg       = objc_hash_new (32,
1053d77920ccSmrg 		       (hash_func_type) objc_hash_ptr,
1054d77920ccSmrg 		       (compare_func_type) objc_compare_ptrs);
1055d77920ccSmrg 
1056d77920ccSmrg   /* If the class has not yet had its class links resolved, we must
1057d77920ccSmrg      re-compute all class links.  */
1058d77920ccSmrg   if (! CLS_ISRESOLV (cls))
1059d77920ccSmrg     __objc_resolve_class_links ();
1060d77920ccSmrg 
1061d77920ccSmrg   assert (cls);
1062d77920ccSmrg   assert (cls->dtable == __objc_uninstalled_dtable);
1063d77920ccSmrg 
1064d77920ccSmrg   /* If there is already a prepared dtable for this class, we must
1065d77920ccSmrg      replace it with a new version (since there must have been methods
1066d77920ccSmrg      added to or otherwise modified in the class while executing
1067d77920ccSmrg      +initialize, and the table needs to be recomputed.  */
1068d77920ccSmrg   dtable = __objc_prepared_dtable_for_class (cls);
1069d77920ccSmrg   if (dtable != 0)
1070d77920ccSmrg     {
1071d77920ccSmrg       objc_hash_remove (prepared_dtable_table, cls);
1072d77920ccSmrg       sarray_free (dtable);
1073d77920ccSmrg     }
1074d77920ccSmrg 
1075d77920ccSmrg   /* Now prepare the dtable for population.  */
1076d77920ccSmrg   assert (cls != cls->super_class);
1077d77920ccSmrg   if (cls->super_class)
1078d77920ccSmrg     {
1079d77920ccSmrg       /* Inherit the method list from the super class.  Yet the super
1080d77920ccSmrg          class may still be initializing in the case when a class
1081d77920ccSmrg          cluster sub class initializes its super classes.  */
1082d77920ccSmrg       if (cls->super_class->dtable == __objc_uninstalled_dtable)
1083d77920ccSmrg 	__objc_install_dtable_for_class (cls->super_class);
1084d77920ccSmrg 
1085d77920ccSmrg       super_dtable = cls->super_class->dtable;
1086d77920ccSmrg       /* If the dispatch table is not yet installed, we are still in
1087d77920ccSmrg 	 the process of executing +initialize.  Yet the dispatch table
1088d77920ccSmrg 	 should be available.  */
1089d77920ccSmrg       if (super_dtable == __objc_uninstalled_dtable)
1090d77920ccSmrg 	super_dtable = __objc_prepared_dtable_for_class (cls->super_class);
1091d77920ccSmrg 
1092d77920ccSmrg       assert (super_dtable);
1093d77920ccSmrg       dtable = sarray_lazy_copy (super_dtable);
1094d77920ccSmrg     }
1095d77920ccSmrg   else
1096d77920ccSmrg     dtable = sarray_new (__objc_selector_max_index, 0);
1097d77920ccSmrg 
1098d77920ccSmrg   __objc_install_methods_in_dtable (dtable, cls->methods);
1099d77920ccSmrg 
1100d77920ccSmrg   objc_hash_add (&prepared_dtable_table,
1101d77920ccSmrg 		 cls,
1102d77920ccSmrg 		 dtable);
1103d77920ccSmrg }
1104d77920ccSmrg 
1105d77920ccSmrg /* This wrapper only exists to allow an easy replacement of the lookup
1106d77920ccSmrg    implementation and it is expected that the compiler will optimize
1107d77920ccSmrg    it away.  */
1108d77920ccSmrg static struct sarray *
__objc_prepared_dtable_for_class(Class cls)1109d77920ccSmrg __objc_prepared_dtable_for_class (Class cls)
1110d77920ccSmrg {
1111d77920ccSmrg   struct sarray *dtable = 0;
1112d77920ccSmrg   assert (cls);
1113d77920ccSmrg   if (prepared_dtable_table)
1114d77920ccSmrg     dtable = objc_hash_value_for_key (prepared_dtable_table, cls);
1115d77920ccSmrg   /* dtable my be nil, since we call this to check whether we are
1116d77920ccSmrg      currently preparing before we start preparing.  */
1117d77920ccSmrg   return dtable;
1118d77920ccSmrg }
1119d77920ccSmrg 
1120d77920ccSmrg /* Helper function for messages sent to CLS or implementation pointers
1121d77920ccSmrg    retrieved from CLS during +initialize before the dtable is
1122d77920ccSmrg    installed.  When a class implicitly initializes another class which
1123d77920ccSmrg    in turn implicitly invokes methods in this class, before the
1124d77920ccSmrg    implementation of +initialize of CLS completes, this returns the
1125d77920ccSmrg    expected implementation.  Forwarding remains the responsibility of
1126d77920ccSmrg    objc_msg_lookup.  This function should only be called under the
1127d77920ccSmrg    global lock.  */
1128d77920ccSmrg static IMP
__objc_get_prepared_imp(Class cls,SEL sel)1129d77920ccSmrg __objc_get_prepared_imp (Class cls,SEL sel)
1130d77920ccSmrg {
1131d77920ccSmrg   struct sarray *dtable;
1132d77920ccSmrg   IMP imp;
1133d77920ccSmrg 
1134d77920ccSmrg   assert (cls);
1135d77920ccSmrg   assert (sel);
1136d77920ccSmrg   assert (cls->dtable == __objc_uninstalled_dtable);
1137d77920ccSmrg   dtable = __objc_prepared_dtable_for_class (cls);
1138d77920ccSmrg 
1139d77920ccSmrg   assert (dtable);
1140d77920ccSmrg   assert (dtable != __objc_uninstalled_dtable);
1141d77920ccSmrg   imp = sarray_get_safe (dtable, (size_t) sel->sel_id);
1142d77920ccSmrg 
1143d77920ccSmrg   /* imp may be Nil if the method does not exist and we may fallback
1144d77920ccSmrg      to the forwarding implementation later.  */
1145d77920ccSmrg   return imp;
1146d77920ccSmrg }
1147d77920ccSmrg 
1148d77920ccSmrg /* When this function is called +initialize should be completed.  So
1149d77920ccSmrg    now we are safe to install the dispatch table for the class so that
1150d77920ccSmrg    they become available for other threads that may be waiting in the
1151d77920ccSmrg    lock.  */
1152d77920ccSmrg static void
__objc_install_prepared_dtable_for_class(Class cls)1153d77920ccSmrg __objc_install_prepared_dtable_for_class (Class cls)
1154d77920ccSmrg {
1155d77920ccSmrg   assert (cls);
1156d77920ccSmrg   assert (cls->dtable == __objc_uninstalled_dtable);
1157d77920ccSmrg   cls->dtable = __objc_prepared_dtable_for_class (cls);
1158d77920ccSmrg 
1159d77920ccSmrg   assert (cls->dtable);
1160d77920ccSmrg   assert (cls->dtable != __objc_uninstalled_dtable);
1161d77920ccSmrg   objc_hash_remove (prepared_dtable_table, cls);
11624fee23f9Smrg }
1163