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