111
|
1 /* GNU Objective C Runtime message lookup
|
145
|
2 Copyright (C) 1993-2020 Free Software Foundation, Inc.
|
111
|
3 Contributed by Kresten Krab Thorup
|
|
4
|
|
5 This file is part of GCC.
|
|
6
|
|
7 GCC is free software; you can redistribute it and/or modify it under the
|
|
8 terms of the GNU General Public License as published by the Free Software
|
|
9 Foundation; either version 3, or (at your option) any later version.
|
|
10
|
|
11 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
13 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
14 details.
|
|
15
|
|
16 Under Section 7 of GPL version 3, you are granted additional
|
|
17 permissions described in the GCC Runtime Library Exception, version
|
|
18 3.1, as published by the Free Software Foundation.
|
|
19
|
|
20 You should have received a copy of the GNU General Public License and
|
|
21 a copy of the GCC Runtime Library Exception along with this program;
|
|
22 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
|
23 <http://www.gnu.org/licenses/>. */
|
|
24
|
|
25 /* Uncommented the following line to enable debug logging. Use this
|
|
26 only while debugging the runtime. */
|
|
27 /* #define DEBUG 1 */
|
|
28
|
|
29 /* FIXME: This should be using libffi instead of __builtin_apply
|
|
30 and friends. */
|
|
31
|
|
32 #include "objc-private/common.h"
|
|
33 #include "objc-private/error.h"
|
|
34 #include "tconfig.h"
|
|
35 #include "coretypes.h"
|
|
36 #include "objc/runtime.h"
|
|
37 #include "objc/message.h" /* For objc_msg_lookup(), objc_msg_lookup_super(). */
|
|
38 #include "objc/thr.h"
|
|
39 #include "objc-private/module-abi-8.h"
|
|
40 #include "objc-private/runtime.h"
|
|
41 #include "objc-private/hash.h"
|
|
42 #include "objc-private/sarray.h"
|
|
43 #include "objc-private/selector.h" /* For sel_is_mapped() */
|
|
44 #include "runtime-info.h"
|
|
45 #include <assert.h> /* For assert */
|
|
46 #include <string.h> /* For strlen */
|
|
47
|
|
48 #define INVISIBLE_STRUCT_RETURN 1
|
|
49
|
|
50 /* The uninstalled dispatch table. If a class' dispatch table points
|
|
51 to __objc_uninstalled_dtable then that means it needs its dispatch
|
|
52 table to be installed. */
|
|
53 struct sarray *__objc_uninstalled_dtable = 0; /* !T:MUTEX */
|
|
54
|
|
55 /* Two hooks for method forwarding. If either is set, it is invoked to
|
|
56 * return a function that performs the real forwarding. If both are
|
|
57 * set, the result of __objc_msg_forward2 will be preferred over that
|
|
58 * of __objc_msg_forward. If both return NULL or are unset, the
|
|
59 * libgcc based functions (__builtin_apply and friends) are used. */
|
|
60 IMP (*__objc_msg_forward) (SEL) = NULL;
|
|
61 IMP (*__objc_msg_forward2) (id, SEL) = NULL;
|
|
62
|
|
63 /* Send +initialize to class. */
|
|
64 static void __objc_send_initialize (Class);
|
|
65
|
|
66 /* Forward declare some functions */
|
|
67 static void __objc_install_dtable_for_class (Class cls);
|
|
68 static void __objc_prepare_dtable_for_class (Class cls);
|
|
69 static void __objc_install_prepared_dtable_for_class (Class cls);
|
|
70
|
|
71 static struct sarray *__objc_prepared_dtable_for_class (Class cls);
|
|
72 static IMP __objc_get_prepared_imp (Class cls,SEL sel);
|
|
73
|
|
74
|
|
75 /* Various forwarding functions that are used based upon the
|
|
76 return type for the selector.
|
|
77 __objc_block_forward for structures.
|
|
78 __objc_double_forward for floats/doubles.
|
|
79 __objc_word_forward for pointers or types that fit in registers. */
|
|
80 static double __objc_double_forward (id, SEL, ...);
|
|
81 static id __objc_word_forward (id, SEL, ...);
|
|
82 typedef struct { id many[8]; } __big;
|
|
83 #if INVISIBLE_STRUCT_RETURN
|
|
84 static __big
|
|
85 #else
|
|
86 static id
|
|
87 #endif
|
|
88 __objc_block_forward (id, SEL, ...);
|
|
89 static struct objc_method * search_for_method_in_hierarchy (Class class, SEL sel);
|
|
90 struct objc_method * search_for_method_in_list (struct objc_method_list * list, SEL op);
|
|
91 id nil_method (id, SEL);
|
|
92
|
|
93 /* Make sure this inline function is exported regardless of GNU89 or C99
|
|
94 inlining semantics as it is part of the libobjc ABI. */
|
|
95 extern IMP __objc_get_forward_imp (id, SEL);
|
|
96
|
|
97 /* Given a selector, return the proper forwarding implementation. */
|
|
98 inline
|
|
99 IMP
|
|
100 __objc_get_forward_imp (id rcv, SEL sel)
|
|
101 {
|
|
102 /* If a custom forwarding hook was registered, try getting a
|
|
103 forwarding function from it. There are two forward routine hooks,
|
|
104 one that takes the receiver as an argument and one that does
|
|
105 not. */
|
|
106 if (__objc_msg_forward2)
|
|
107 {
|
|
108 IMP result;
|
|
109 if ((result = __objc_msg_forward2 (rcv, sel)) != NULL)
|
|
110 return result;
|
|
111 }
|
|
112 if (__objc_msg_forward)
|
|
113 {
|
|
114 IMP result;
|
|
115 if ((result = __objc_msg_forward (sel)) != NULL)
|
|
116 return result;
|
|
117 }
|
|
118
|
|
119 /* In all other cases, use the default forwarding functions built
|
|
120 using __builtin_apply and friends. */
|
|
121 {
|
|
122 const char *t = sel->sel_types;
|
|
123
|
|
124 if (t && (*t == '[' || *t == '(' || *t == '{')
|
|
125 #ifdef OBJC_MAX_STRUCT_BY_VALUE
|
|
126 && objc_sizeof_type (t) > OBJC_MAX_STRUCT_BY_VALUE
|
|
127 #endif
|
|
128 )
|
|
129 return (IMP)__objc_block_forward;
|
|
130 else if (t && (*t == 'f' || *t == 'd'))
|
|
131 return (IMP)__objc_double_forward;
|
|
132 else
|
|
133 return (IMP)__objc_word_forward;
|
|
134 }
|
|
135 }
|
|
136
|
|
137 /* Selectors for +resolveClassMethod: and +resolveInstanceMethod:.
|
|
138 These are set up at startup. */
|
|
139 static SEL selector_resolveClassMethod = NULL;
|
|
140 static SEL selector_resolveInstanceMethod = NULL;
|
|
141
|
|
142 /* Internal routines use to resolve a class method using
|
|
143 +resolveClassMethod:. 'class' is always a non-Nil class (*not* a
|
|
144 meta-class), and 'sel' is the selector that we are trying to
|
|
145 resolve. This must be called when class is not Nil, and the
|
|
146 dispatch table for class methods has already been installed.
|
|
147
|
|
148 This routine tries to call +resolveClassMethod: to give an
|
|
149 opportunity to resolve the method. If +resolveClassMethod: returns
|
|
150 YES, it tries looking up the method again, and if found, it returns
|
|
151 it. Else, it returns NULL. */
|
|
152 static inline
|
|
153 IMP
|
|
154 __objc_resolve_class_method (Class class, SEL sel)
|
|
155 {
|
|
156 /* We need to lookup +resolveClassMethod:. */
|
|
157 BOOL (*resolveMethodIMP) (id, SEL, SEL);
|
|
158
|
|
159 /* The dispatch table for class methods is already installed and we
|
|
160 don't want any forwarding to happen when looking up this method,
|
|
161 so we just look it up directly. Note that if 'sel' is precisely
|
|
162 +resolveClassMethod:, this would look it up yet again and find
|
|
163 nothing. That's no problem and there's no recursion. */
|
|
164 resolveMethodIMP = (BOOL (*) (id, SEL, SEL))sarray_get_safe
|
|
165 (class->class_pointer->dtable, (size_t) selector_resolveClassMethod->sel_id);
|
|
166
|
|
167 if (resolveMethodIMP && resolveMethodIMP ((id)class, selector_resolveClassMethod, sel))
|
|
168 {
|
|
169 /* +resolveClassMethod: returned YES. Look the method up again.
|
|
170 We already know the dtable is installed. */
|
|
171
|
|
172 /* TODO: There is the case where +resolveClassMethod: is buggy
|
|
173 and returned YES without actually adding the method. We
|
|
174 could maybe print an error message. */
|
|
175 return sarray_get_safe (class->class_pointer->dtable, (size_t) sel->sel_id);
|
|
176 }
|
|
177
|
|
178 return NULL;
|
|
179 }
|
|
180
|
|
181 /* Internal routines use to resolve a instance method using
|
|
182 +resolveInstanceMethod:. 'class' is always a non-Nil class, and
|
|
183 'sel' is the selector that we are trying to resolve. This must be
|
|
184 called when class is not Nil, and the dispatch table for instance
|
|
185 methods has already been installed.
|
|
186
|
|
187 This routine tries to call +resolveInstanceMethod: to give an
|
|
188 opportunity to resolve the method. If +resolveInstanceMethod:
|
|
189 returns YES, it tries looking up the method again, and if found, it
|
|
190 returns it. Else, it returns NULL. */
|
|
191 static inline
|
|
192 IMP
|
|
193 __objc_resolve_instance_method (Class class, SEL sel)
|
|
194 {
|
|
195 /* We need to lookup +resolveInstanceMethod:. */
|
|
196 BOOL (*resolveMethodIMP) (id, SEL, SEL);
|
|
197
|
|
198 /* The dispatch table for class methods may not be already installed
|
|
199 so we have to install it if needed. */
|
|
200 resolveMethodIMP = sarray_get_safe (class->class_pointer->dtable,
|
|
201 (size_t) selector_resolveInstanceMethod->sel_id);
|
|
202 if (resolveMethodIMP == 0)
|
|
203 {
|
|
204 /* Try again after installing the dtable. */
|
|
205 if (class->class_pointer->dtable == __objc_uninstalled_dtable)
|
|
206 {
|
|
207 objc_mutex_lock (__objc_runtime_mutex);
|
|
208 if (class->class_pointer->dtable == __objc_uninstalled_dtable)
|
|
209 __objc_install_dtable_for_class (class->class_pointer);
|
|
210 objc_mutex_unlock (__objc_runtime_mutex);
|
|
211 }
|
|
212 resolveMethodIMP = sarray_get_safe (class->class_pointer->dtable,
|
|
213 (size_t) selector_resolveInstanceMethod->sel_id);
|
|
214 }
|
|
215
|
|
216 if (resolveMethodIMP && resolveMethodIMP ((id)class, selector_resolveInstanceMethod, sel))
|
|
217 {
|
|
218 /* +resolveInstanceMethod: returned YES. Look the method up
|
|
219 again. We already know the dtable is installed. */
|
|
220
|
|
221 /* TODO: There is the case where +resolveInstanceMethod: is
|
|
222 buggy and returned YES without actually adding the method.
|
|
223 We could maybe print an error message. */
|
|
224 return sarray_get_safe (class->dtable, (size_t) sel->sel_id);
|
|
225 }
|
|
226
|
|
227 return NULL;
|
|
228 }
|
|
229
|
|
230 /* Given a CLASS and selector, return the implementation corresponding
|
|
231 to the method of the selector.
|
|
232
|
|
233 If CLASS is a class, the instance method is returned.
|
|
234 If CLASS is a meta class, the class method is returned.
|
|
235
|
|
236 Since this requires the dispatch table to be installed, this function
|
|
237 will implicitly invoke +initialize for CLASS if it hasn't been
|
|
238 invoked yet. This also insures that +initialize has been invoked
|
|
239 when the returned implementation is called directly.
|
|
240
|
|
241 The forwarding hooks require the receiver as an argument (if they are to
|
|
242 perform dynamic lookup in proxy objects etc), so this function has a
|
|
243 receiver argument to be used with those hooks. */
|
|
244 static inline
|
|
245 IMP
|
|
246 get_implementation (id receiver, Class class, SEL sel)
|
|
247 {
|
|
248 void *res;
|
|
249
|
|
250 if (class->dtable == __objc_uninstalled_dtable)
|
|
251 {
|
|
252 /* The dispatch table needs to be installed. */
|
|
253 objc_mutex_lock (__objc_runtime_mutex);
|
|
254
|
|
255 /* Double-checked locking pattern: Check
|
|
256 __objc_uninstalled_dtable again in case another thread
|
|
257 installed the dtable while we were waiting for the lock to be
|
|
258 released. */
|
|
259 if (class->dtable == __objc_uninstalled_dtable)
|
|
260 __objc_install_dtable_for_class (class);
|
|
261
|
|
262 /* If the dispatch table is not yet installed, we are still in
|
|
263 the process of executing +initialize. But the implementation
|
|
264 pointer should be available in the prepared ispatch table if
|
|
265 it exists at all. */
|
|
266 if (class->dtable == __objc_uninstalled_dtable)
|
|
267 {
|
|
268 assert (__objc_prepared_dtable_for_class (class) != 0);
|
|
269 res = __objc_get_prepared_imp (class, sel);
|
|
270 }
|
|
271 else
|
|
272 res = 0;
|
|
273
|
|
274 objc_mutex_unlock (__objc_runtime_mutex);
|
|
275 /* Call ourselves with the installed dispatch table and get the
|
|
276 real method. */
|
|
277 if (!res)
|
|
278 res = get_implementation (receiver, class, sel);
|
|
279 }
|
|
280 else
|
|
281 {
|
|
282 /* The dispatch table has been installed. */
|
|
283 res = sarray_get_safe (class->dtable, (size_t) sel->sel_id);
|
|
284 if (res == 0)
|
|
285 {
|
|
286 /* The dispatch table has been installed, and the method is
|
|
287 not in the dispatch table. So the method just doesn't
|
|
288 exist for the class. */
|
|
289
|
|
290 /* Try going through the +resolveClassMethod: or
|
|
291 +resolveInstanceMethod: process. */
|
|
292 if (CLS_ISMETA (class))
|
|
293 {
|
|
294 /* We have the meta class, but we need to invoke the
|
|
295 +resolveClassMethod: method on the class. So, we
|
|
296 need to obtain the class from the meta class, which
|
|
297 we do using the fact that both the class and the
|
|
298 meta-class have the same name. */
|
|
299 Class realClass = objc_lookUpClass (class->name);
|
|
300 if (realClass)
|
|
301 res = __objc_resolve_class_method (realClass, sel);
|
|
302 }
|
|
303 else
|
|
304 res = __objc_resolve_instance_method (class, sel);
|
|
305
|
|
306 if (res == 0)
|
|
307 res = __objc_get_forward_imp (receiver, sel);
|
|
308 }
|
|
309 }
|
|
310 return res;
|
|
311 }
|
|
312
|
|
313 /* Make sure this inline function is exported regardless of GNU89 or C99
|
|
314 inlining semantics as it is part of the libobjc ABI. */
|
|
315 extern IMP get_imp (Class, SEL);
|
|
316
|
|
317 inline
|
|
318 IMP
|
|
319 get_imp (Class class, SEL sel)
|
|
320 {
|
|
321 /* In a vanilla implementation we would first check if the dispatch
|
|
322 table is installed. Here instead, to get more speed in the
|
|
323 standard case (that the dispatch table is installed) we first try
|
|
324 to get the imp using brute force. Only if that fails, we do what
|
|
325 we should have been doing from the very beginning, that is, check
|
|
326 if the dispatch table needs to be installed, install it if it's
|
|
327 not installed, and retrieve the imp from the table if it's
|
|
328 installed. */
|
|
329 void *res = sarray_get_safe (class->dtable, (size_t) sel->sel_id);
|
|
330 if (res == 0)
|
|
331 {
|
|
332 res = get_implementation(nil, class, sel);
|
|
333 }
|
|
334 return res;
|
|
335 }
|
|
336
|
|
337 /* The new name of get_imp(). */
|
|
338 IMP
|
|
339 class_getMethodImplementation (Class class_, SEL selector)
|
|
340 {
|
|
341 if (class_ == Nil || selector == NULL)
|
|
342 return NULL;
|
|
343
|
|
344 /* get_imp is inlined, so we're good. */
|
|
345 return get_imp (class_, selector);
|
|
346 }
|
|
347
|
|
348 /* Given a method, return its implementation. This has been replaced
|
|
349 by method_getImplementation() in the modern API. */
|
|
350 IMP
|
|
351 method_get_imp (struct objc_method * method)
|
|
352 {
|
|
353 return (method != (struct objc_method *)0) ? method->method_imp : (IMP)0;
|
|
354 }
|
|
355
|
|
356 /* Query if an object can respond to a selector, returns YES if the
|
|
357 object implements the selector otherwise NO. Does not check if the
|
|
358 method can be forwarded. Since this requires the dispatch table to
|
|
359 installed, this function will implicitly invoke +initialize for the
|
|
360 class of OBJECT if it hasn't been invoked yet. */
|
|
361 inline
|
|
362 BOOL
|
|
363 __objc_responds_to (id object, SEL sel)
|
|
364 {
|
|
365 void *res;
|
|
366 struct sarray *dtable;
|
|
367
|
|
368 /* Install dispatch table if need be */
|
|
369 dtable = object->class_pointer->dtable;
|
|
370 if (dtable == __objc_uninstalled_dtable)
|
|
371 {
|
|
372 objc_mutex_lock (__objc_runtime_mutex);
|
|
373 if (object->class_pointer->dtable == __objc_uninstalled_dtable)
|
|
374 __objc_install_dtable_for_class (object->class_pointer);
|
|
375
|
|
376 /* If the dispatch table is not yet installed, we are still in
|
|
377 the process of executing +initialize. Yet the dispatch table
|
|
378 should be available. */
|
|
379 if (object->class_pointer->dtable == __objc_uninstalled_dtable)
|
|
380 {
|
|
381 dtable = __objc_prepared_dtable_for_class (object->class_pointer);
|
|
382 assert (dtable);
|
|
383 }
|
|
384 else
|
|
385 dtable = object->class_pointer->dtable;
|
|
386
|
|
387 objc_mutex_unlock (__objc_runtime_mutex);
|
|
388 }
|
|
389
|
|
390 /* Get the method from the dispatch table. */
|
|
391 res = sarray_get_safe (dtable, (size_t) sel->sel_id);
|
|
392 return (res != 0) ? YES : NO;
|
|
393 }
|
|
394
|
|
395 BOOL
|
|
396 class_respondsToSelector (Class class_, SEL selector)
|
|
397 {
|
|
398 struct sarray *dtable;
|
|
399 void *res;
|
|
400
|
|
401 if (class_ == Nil || selector == NULL)
|
|
402 return NO;
|
|
403
|
|
404 /* Install dispatch table if need be. */
|
|
405 dtable = class_->dtable;
|
|
406 if (dtable == __objc_uninstalled_dtable)
|
|
407 {
|
|
408 objc_mutex_lock (__objc_runtime_mutex);
|
|
409 if (class_->dtable == __objc_uninstalled_dtable)
|
|
410 __objc_install_dtable_for_class (class_);
|
|
411
|
|
412 /* If the dispatch table is not yet installed,
|
|
413 we are still in the process of executing +initialize.
|
|
414 Yet the dispatch table should be available. */
|
|
415 if (class_->dtable == __objc_uninstalled_dtable)
|
|
416 {
|
|
417 dtable = __objc_prepared_dtable_for_class (class_);
|
|
418 assert (dtable);
|
|
419 }
|
|
420 else
|
|
421 dtable = class_->dtable;
|
|
422
|
|
423 objc_mutex_unlock (__objc_runtime_mutex);
|
|
424 }
|
|
425
|
|
426 /* Get the method from the dispatch table. */
|
|
427 res = sarray_get_safe (dtable, (size_t) selector->sel_id);
|
|
428 return (res != 0) ? YES : NO;
|
|
429 }
|
|
430
|
|
431 /* This is the lookup function. All entries in the table are either a
|
|
432 valid method *or* zero. If zero then either the dispatch table
|
|
433 needs to be installed or it doesn't exist and forwarding is
|
|
434 attempted. */
|
|
435 IMP
|
|
436 objc_msg_lookup (id receiver, SEL op)
|
|
437 {
|
|
438 IMP result;
|
|
439 if (receiver)
|
|
440 {
|
|
441 /* First try a quick lookup assuming the dispatch table exists. */
|
|
442 result = sarray_get_safe (receiver->class_pointer->dtable,
|
|
443 (sidx)op->sel_id);
|
|
444 if (result == 0)
|
|
445 {
|
|
446 /* Not found ... call get_implementation () to install the
|
|
447 dispatch table and call +initialize as required,
|
|
448 providing the method implementation or a forwarding
|
|
449 function. */
|
|
450 result = get_implementation (receiver, receiver->class_pointer, op);
|
|
451 }
|
|
452 return result;
|
|
453 }
|
|
454 else
|
|
455 return (IMP)nil_method;
|
|
456 }
|
|
457
|
|
458 IMP
|
|
459 objc_msg_lookup_super (struct objc_super *super, SEL sel)
|
|
460 {
|
|
461 if (super->self)
|
|
462 return get_imp (super->super_class, sel);
|
|
463 else
|
|
464 return (IMP)nil_method;
|
|
465 }
|
|
466
|
|
467 void
|
|
468 __objc_init_dispatch_tables ()
|
|
469 {
|
|
470 __objc_uninstalled_dtable = sarray_new (200, 0);
|
|
471
|
|
472 /* TODO: It would be cool to register typed selectors here. */
|
|
473 selector_resolveClassMethod = sel_registerName ("resolveClassMethod:");
|
|
474 selector_resolveInstanceMethod = sel_registerName ("resolveInstanceMethod:");
|
|
475 }
|
|
476
|
|
477
|
|
478 /* Install dummy table for class which causes the first message to
|
|
479 that class (or instances hereof) to be initialized properly. */
|
|
480 void
|
|
481 __objc_install_premature_dtable (Class class)
|
|
482 {
|
|
483 assert (__objc_uninstalled_dtable);
|
|
484 class->dtable = __objc_uninstalled_dtable;
|
|
485 }
|
|
486
|
|
487 /* Send +initialize to class if not already done. */
|
|
488 static void
|
|
489 __objc_send_initialize (Class class)
|
|
490 {
|
|
491 /* This *must* be a class object. */
|
|
492 assert (CLS_ISCLASS (class));
|
|
493 assert (! CLS_ISMETA (class));
|
|
494
|
|
495 /* class_add_method_list/__objc_update_dispatch_table_for_class may
|
|
496 have reset the dispatch table. The canonical way to insure that
|
|
497 we send +initialize just once, is this flag. */
|
|
498 if (! CLS_ISINITIALIZED (class))
|
|
499 {
|
|
500 DEBUG_PRINTF ("+initialize: need to initialize class '%s'\n", class->name);
|
|
501 CLS_SETINITIALIZED (class);
|
|
502 CLS_SETINITIALIZED (class->class_pointer);
|
|
503
|
|
504 /* Create the garbage collector type memory description. */
|
|
505 __objc_generate_gc_type_description (class);
|
|
506
|
|
507 if (class->super_class)
|
|
508 __objc_send_initialize (class->super_class);
|
|
509
|
|
510 {
|
|
511 SEL op = sel_registerName ("initialize");
|
|
512 struct objc_method *method = search_for_method_in_hierarchy (class->class_pointer,
|
|
513 op);
|
|
514
|
|
515 if (method)
|
|
516 {
|
|
517 DEBUG_PRINTF (" begin of [%s +initialize]\n", class->name);
|
|
518 (*method->method_imp) ((id)class, op);
|
|
519 DEBUG_PRINTF (" end of [%s +initialize]\n", class->name);
|
|
520 }
|
|
521 #ifdef DEBUG
|
|
522 else
|
|
523 {
|
|
524 DEBUG_PRINTF (" class '%s' has no +initialize method\n", class->name);
|
|
525 }
|
|
526 #endif
|
|
527 }
|
|
528 }
|
|
529 }
|
|
530
|
|
531 /* Walk on the methods list of class and install the methods in the
|
|
532 reverse order of the lists. Since methods added by categories are
|
|
533 before the methods of class in the methods list, this allows
|
|
534 categories to substitute methods declared in class. However if
|
|
535 more than one category replaces the same method nothing is
|
|
536 guaranteed about what method will be used. Assumes that
|
|
537 __objc_runtime_mutex is locked down. */
|
|
538 static void
|
|
539 __objc_install_methods_in_dtable (struct sarray *dtable, struct objc_method_list * method_list)
|
|
540 {
|
|
541 int i;
|
|
542
|
|
543 if (! method_list)
|
|
544 return;
|
|
545
|
|
546 if (method_list->method_next)
|
|
547 __objc_install_methods_in_dtable (dtable, method_list->method_next);
|
|
548
|
|
549 for (i = 0; i < method_list->method_count; i++)
|
|
550 {
|
|
551 struct objc_method * method = &(method_list->method_list[i]);
|
|
552 sarray_at_put_safe (dtable,
|
|
553 (sidx) method->method_name->sel_id,
|
|
554 method->method_imp);
|
|
555 }
|
|
556 }
|
|
557
|
|
558 void
|
|
559 __objc_update_dispatch_table_for_class (Class class)
|
|
560 {
|
|
561 Class next;
|
|
562 struct sarray *arr;
|
|
563
|
|
564 DEBUG_PRINTF (" _objc_update_dtable_for_class (%s)\n", class->name);
|
|
565
|
|
566 objc_mutex_lock (__objc_runtime_mutex);
|
|
567
|
|
568 /* Not yet installed -- skip it unless in +initialize. */
|
|
569 if (class->dtable == __objc_uninstalled_dtable)
|
|
570 {
|
|
571 if (__objc_prepared_dtable_for_class (class))
|
|
572 {
|
|
573 /* There is a prepared table so we must be initialising this
|
|
574 class ... we must re-do the table preparation. */
|
|
575 __objc_prepare_dtable_for_class (class);
|
|
576 }
|
|
577 objc_mutex_unlock (__objc_runtime_mutex);
|
|
578 return;
|
|
579 }
|
|
580
|
|
581 arr = class->dtable;
|
|
582 __objc_install_premature_dtable (class); /* someone might require it... */
|
|
583 sarray_free (arr); /* release memory */
|
|
584
|
|
585 /* Could have been lazy... */
|
|
586 __objc_install_dtable_for_class (class);
|
|
587
|
|
588 if (class->subclass_list) /* Traverse subclasses. */
|
|
589 for (next = class->subclass_list; next; next = next->sibling_class)
|
|
590 __objc_update_dispatch_table_for_class (next);
|
|
591
|
|
592 objc_mutex_unlock (__objc_runtime_mutex);
|
|
593 }
|
|
594
|
|
595 /* This function adds a method list to a class. This function is
|
|
596 typically called by another function specific to the run-time. As
|
|
597 such this function does not worry about thread safe issues.
|
|
598
|
|
599 This one is only called for categories. Class objects have their
|
|
600 methods installed right away, and their selectors are made into
|
|
601 SEL's by the function __objc_register_selectors_from_class. */
|
|
602 void
|
|
603 class_add_method_list (Class class, struct objc_method_list * list)
|
|
604 {
|
|
605 /* Passing of a linked list is not allowed. Do multiple calls. */
|
|
606 assert (! list->method_next);
|
|
607
|
|
608 __objc_register_selectors_from_list(list);
|
|
609
|
|
610 /* Add the methods to the class's method list. */
|
|
611 list->method_next = class->methods;
|
|
612 class->methods = list;
|
|
613
|
|
614 /* Update the dispatch table of class. */
|
|
615 __objc_update_dispatch_table_for_class (class);
|
|
616 }
|
|
617
|
|
618 struct objc_method *
|
|
619 class_getInstanceMethod (Class class_, SEL selector)
|
|
620 {
|
|
621 struct objc_method *m;
|
|
622
|
|
623 if (class_ == Nil || selector == NULL)
|
|
624 return NULL;
|
|
625
|
|
626 m = search_for_method_in_hierarchy (class_, selector);
|
|
627 if (m)
|
|
628 return m;
|
|
629
|
|
630 /* Try going through +resolveInstanceMethod:, and do the search
|
|
631 again if successful. */
|
|
632 if (__objc_resolve_instance_method (class_, selector))
|
|
633 return search_for_method_in_hierarchy (class_, selector);
|
|
634
|
|
635 return NULL;
|
|
636 }
|
|
637
|
|
638 struct objc_method *
|
|
639 class_getClassMethod (Class class_, SEL selector)
|
|
640 {
|
|
641 struct objc_method *m;
|
|
642
|
|
643 if (class_ == Nil || selector == NULL)
|
|
644 return NULL;
|
|
645
|
|
646 m = search_for_method_in_hierarchy (class_->class_pointer,
|
|
647 selector);
|
|
648 if (m)
|
|
649 return m;
|
|
650
|
|
651 /* Try going through +resolveClassMethod:, and do the search again
|
|
652 if successful. */
|
|
653 if (__objc_resolve_class_method (class_, selector))
|
|
654 return search_for_method_in_hierarchy (class_->class_pointer,
|
|
655 selector);
|
|
656
|
|
657 return NULL;
|
|
658 }
|
|
659
|
|
660 BOOL
|
|
661 class_addMethod (Class class_, SEL selector, IMP implementation,
|
|
662 const char *method_types)
|
|
663 {
|
|
664 struct objc_method_list *method_list;
|
|
665 struct objc_method *method;
|
|
666 const char *method_name;
|
|
667
|
|
668 if (class_ == Nil || selector == NULL || implementation == NULL
|
|
669 || method_types == NULL || (strcmp (method_types, "") == 0))
|
|
670 return NO;
|
|
671
|
|
672 method_name = sel_getName (selector);
|
|
673 if (method_name == NULL)
|
|
674 return NO;
|
|
675
|
|
676 /* If the method already exists in the class, return NO. It is fine
|
|
677 if the method already exists in the superclass; in that case, we
|
|
678 are overriding it. */
|
|
679 if (CLS_IS_IN_CONSTRUCTION (class_))
|
|
680 {
|
|
681 /* The class only contains a list of methods; they have not been
|
|
682 registered yet, ie, the method_name of each of them is still
|
|
683 a string, not a selector. Iterate manually over them to
|
|
684 check if we have already added the method. */
|
|
685 struct objc_method_list * method_list = class_->methods;
|
|
686 while (method_list)
|
|
687 {
|
|
688 int i;
|
|
689
|
|
690 /* Search the method list. */
|
|
691 for (i = 0; i < method_list->method_count; ++i)
|
|
692 {
|
|
693 struct objc_method * method = &method_list->method_list[i];
|
|
694
|
|
695 if (method->method_name
|
|
696 && strcmp ((char *)method->method_name, method_name) == 0)
|
|
697 return NO;
|
|
698 }
|
|
699
|
|
700 /* The method wasn't found. Follow the link to the next list of
|
|
701 methods. */
|
|
702 method_list = method_list->method_next;
|
|
703 }
|
|
704 /* The method wasn't found. It's a new one. Go ahead and add
|
|
705 it. */
|
|
706 }
|
|
707 else
|
|
708 {
|
|
709 /* Do the standard lookup. This assumes the selectors are
|
|
710 mapped. */
|
|
711 if (search_for_method_in_list (class_->methods, selector))
|
|
712 return NO;
|
|
713 }
|
|
714
|
|
715 method_list = (struct objc_method_list *)objc_calloc (1, sizeof (struct objc_method_list));
|
|
716 method_list->method_count = 1;
|
|
717
|
|
718 method = &(method_list->method_list[0]);
|
|
719 method->method_name = objc_malloc (strlen (method_name) + 1);
|
|
720 strcpy ((char *)method->method_name, method_name);
|
|
721
|
|
722 method->method_types = objc_malloc (strlen (method_types) + 1);
|
|
723 strcpy ((char *)method->method_types, method_types);
|
|
724
|
|
725 method->method_imp = implementation;
|
|
726
|
|
727 if (CLS_IS_IN_CONSTRUCTION (class_))
|
|
728 {
|
|
729 /* We only need to add the method to the list. It will be
|
|
730 registered with the runtime when the class pair is registered
|
|
731 (if ever). */
|
|
732 method_list->method_next = class_->methods;
|
|
733 class_->methods = method_list;
|
|
734 }
|
|
735 else
|
|
736 {
|
|
737 /* Add the method to a live class. */
|
|
738 objc_mutex_lock (__objc_runtime_mutex);
|
|
739 class_add_method_list (class_, method_list);
|
|
740 objc_mutex_unlock (__objc_runtime_mutex);
|
|
741 }
|
|
742
|
|
743 return YES;
|
|
744 }
|
|
745
|
|
746 IMP
|
|
747 class_replaceMethod (Class class_, SEL selector, IMP implementation,
|
|
748 const char *method_types)
|
|
749 {
|
|
750 struct objc_method * method;
|
|
751
|
|
752 if (class_ == Nil || selector == NULL || implementation == NULL
|
|
753 || method_types == NULL)
|
|
754 return NULL;
|
|
755
|
|
756 method = search_for_method_in_hierarchy (class_, selector);
|
|
757
|
|
758 if (method)
|
|
759 {
|
|
760 return method_setImplementation (method, implementation);
|
|
761 }
|
|
762 else
|
|
763 {
|
|
764 class_addMethod (class_, selector, implementation, method_types);
|
|
765 return NULL;
|
|
766 }
|
|
767 }
|
|
768
|
|
769 /* Search for a method starting from the current class up its
|
|
770 hierarchy. Return a pointer to the method's method structure if
|
|
771 found. NULL otherwise. */
|
|
772 static struct objc_method *
|
|
773 search_for_method_in_hierarchy (Class cls, SEL sel)
|
|
774 {
|
|
775 struct objc_method * method = NULL;
|
|
776 Class class;
|
|
777
|
|
778 if (! sel_is_mapped (sel))
|
|
779 return NULL;
|
|
780
|
|
781 /* Scan the method list of the class. If the method isn't found in
|
|
782 the list then step to its super class. */
|
|
783 for (class = cls; ((! method) && class); class = class->super_class)
|
|
784 method = search_for_method_in_list (class->methods, sel);
|
|
785
|
|
786 return method;
|
|
787 }
|
|
788
|
|
789
|
|
790
|
|
791 /* Given a linked list of method and a method's name. Search for the
|
|
792 named method's method structure. Return a pointer to the method's
|
|
793 method structure if found. NULL otherwise. */
|
|
794 struct objc_method *
|
|
795 search_for_method_in_list (struct objc_method_list * list, SEL op)
|
|
796 {
|
|
797 struct objc_method_list * method_list = list;
|
|
798
|
|
799 if (! sel_is_mapped (op))
|
|
800 return NULL;
|
|
801
|
|
802 /* If not found then we'll search the list. */
|
|
803 while (method_list)
|
|
804 {
|
|
805 int i;
|
|
806
|
|
807 /* Search the method list. */
|
|
808 for (i = 0; i < method_list->method_count; ++i)
|
|
809 {
|
|
810 struct objc_method * method = &method_list->method_list[i];
|
|
811
|
|
812 if (method->method_name)
|
|
813 if (method->method_name->sel_id == op->sel_id)
|
|
814 return method;
|
|
815 }
|
|
816
|
|
817 /* The method wasn't found. Follow the link to the next list of
|
|
818 methods. */
|
|
819 method_list = method_list->method_next;
|
|
820 }
|
|
821
|
|
822 return NULL;
|
|
823 }
|
|
824
|
|
825 typedef void * retval_t;
|
|
826 typedef void * arglist_t;
|
|
827
|
|
828 static retval_t __objc_forward (id object, SEL sel, arglist_t args);
|
|
829
|
|
830 /* Forwarding pointers/integers through the normal registers. */
|
|
831 static id
|
|
832 __objc_word_forward (id rcv, SEL op, ...)
|
|
833 {
|
|
834 void *args, *res;
|
|
835
|
|
836 args = __builtin_apply_args ();
|
|
837 res = __objc_forward (rcv, op, args);
|
|
838 if (res)
|
|
839 __builtin_return (res);
|
|
840 else
|
|
841 return res;
|
|
842 }
|
|
843
|
|
844 /* Specific routine for forwarding floats/double because of
|
|
845 architectural differences on some processors. i386s for example
|
|
846 which uses a floating point stack versus general registers for
|
|
847 floating point numbers. This forward routine makes sure that GCC
|
|
848 restores the proper return values. */
|
|
849 static double
|
|
850 __objc_double_forward (id rcv, SEL op, ...)
|
|
851 {
|
|
852 void *args, *res;
|
|
853
|
|
854 args = __builtin_apply_args ();
|
|
855 res = __objc_forward (rcv, op, args);
|
|
856 __builtin_return (res);
|
|
857 }
|
|
858
|
|
859 #if INVISIBLE_STRUCT_RETURN
|
|
860 static __big
|
|
861 #else
|
|
862 static id
|
|
863 #endif
|
|
864 __objc_block_forward (id rcv, SEL op, ...)
|
|
865 {
|
|
866 void *args, *res;
|
|
867
|
|
868 args = __builtin_apply_args ();
|
|
869 res = __objc_forward (rcv, op, args);
|
|
870 if (res)
|
|
871 __builtin_return (res);
|
|
872 else
|
|
873 #if INVISIBLE_STRUCT_RETURN
|
|
874 return (__big) {{0, 0, 0, 0, 0, 0, 0, 0}};
|
|
875 #else
|
|
876 return nil;
|
|
877 #endif
|
|
878 }
|
|
879
|
|
880
|
|
881 /* This function is called for methods which are not implemented,
|
|
882 unless a custom forwarding routine has been installed. Please note
|
|
883 that most serious users of libobjc (eg, GNUstep base) do install
|
|
884 their own forwarding routines, and hence this is never actually
|
|
885 used. But, if no custom forwarding routine is installed, this is
|
|
886 called when a selector is not recognized. */
|
|
887 static retval_t
|
|
888 __objc_forward (id object, SEL sel, arglist_t args)
|
|
889 {
|
|
890 IMP imp;
|
|
891 static SEL frwd_sel = 0; /* !T:SAFE2 */
|
|
892 SEL err_sel;
|
|
893
|
|
894 /* First try if the object understands forward::. */
|
|
895 if (! frwd_sel)
|
|
896 frwd_sel = sel_get_any_uid ("forward::");
|
|
897
|
|
898 if (__objc_responds_to (object, frwd_sel))
|
|
899 {
|
|
900 imp = get_implementation (object, object->class_pointer, frwd_sel);
|
|
901 return (*imp) (object, frwd_sel, sel, args);
|
|
902 }
|
|
903
|
|
904 /* If the object recognizes the doesNotRecognize: method then we're
|
|
905 going to send it. */
|
|
906 err_sel = sel_get_any_uid ("doesNotRecognize:");
|
|
907 if (__objc_responds_to (object, err_sel))
|
|
908 {
|
|
909 imp = get_implementation (object, object->class_pointer, err_sel);
|
|
910 return (*imp) (object, err_sel, sel);
|
|
911 }
|
|
912
|
|
913 /* The object doesn't recognize the method. Check for responding to
|
|
914 error:. If it does then sent it. */
|
|
915 {
|
|
916 char msg[256 + strlen ((const char *) sel_getName (sel))
|
|
917 + strlen ((const char *) object->class_pointer->name)];
|
|
918
|
|
919 sprintf (msg, "(%s) %s does not recognize %s",
|
|
920 (CLS_ISMETA (object->class_pointer)
|
|
921 ? "class"
|
|
922 : "instance" ),
|
|
923 object->class_pointer->name, sel_getName (sel));
|
|
924
|
|
925 /* The object doesn't respond to doesNotRecognize:. Therefore, a
|
|
926 default action is taken. */
|
|
927 _objc_abort ("%s\n", msg);
|
|
928
|
|
929 return 0;
|
|
930 }
|
|
931 }
|
|
932
|
|
933 void
|
|
934 __objc_print_dtable_stats (void)
|
|
935 {
|
|
936 int total = 0;
|
|
937
|
|
938 objc_mutex_lock (__objc_runtime_mutex);
|
|
939
|
|
940 #ifdef OBJC_SPARSE2
|
|
941 printf ("memory usage: (%s)\n", "2-level sparse arrays");
|
|
942 #else
|
|
943 printf ("memory usage: (%s)\n", "3-level sparse arrays");
|
|
944 #endif
|
|
945
|
|
946 printf ("arrays: %d = %ld bytes\n", narrays,
|
|
947 (long) ((size_t) narrays * sizeof (struct sarray)));
|
|
948 total += narrays * sizeof (struct sarray);
|
|
949 printf ("buckets: %d = %ld bytes\n", nbuckets,
|
|
950 (long) ((size_t) nbuckets * sizeof (struct sbucket)));
|
|
951 total += nbuckets * sizeof (struct sbucket);
|
|
952
|
|
953 printf ("idxtables: %d = %ld bytes\n",
|
|
954 idxsize, (long) ((size_t) idxsize * sizeof (void *)));
|
|
955 total += idxsize * sizeof (void *);
|
|
956 printf ("-----------------------------------\n");
|
|
957 printf ("total: %d bytes\n", total);
|
|
958 printf ("===================================\n");
|
|
959
|
|
960 objc_mutex_unlock (__objc_runtime_mutex);
|
|
961 }
|
|
962
|
|
963 static cache_ptr prepared_dtable_table = 0;
|
|
964
|
|
965 /* This function is called by: objc_msg_lookup, get_imp and
|
|
966 __objc_responds_to (and the dispatch table installation functions
|
|
967 themselves) to install a dispatch table for a class.
|
|
968
|
|
969 If CLS is a class, it installs instance methods.
|
|
970 If CLS is a meta class, it installs class methods.
|
|
971
|
|
972 In either case +initialize is invoked for the corresponding class.
|
|
973
|
|
974 The implementation must insure that the dispatch table is not
|
|
975 installed until +initialize completes. Otherwise it opens a
|
|
976 potential race since the installation of the dispatch table is used
|
|
977 as gate in regular method dispatch and we need to guarantee that
|
|
978 +initialize is the first method invoked an that no other thread my
|
|
979 dispatch messages to the class before +initialize completes. */
|
|
980 static void
|
|
981 __objc_install_dtable_for_class (Class cls)
|
|
982 {
|
|
983 /* If the class has not yet had its class links resolved, we must
|
|
984 re-compute all class links. */
|
|
985 if (! CLS_ISRESOLV (cls))
|
|
986 __objc_resolve_class_links ();
|
|
987
|
|
988 /* Make sure the super class has its dispatch table installed or is
|
|
989 at least preparing. We do not need to send initialize for the
|
|
990 super class since __objc_send_initialize will insure that. */
|
|
991 if (cls->super_class
|
|
992 && cls->super_class->dtable == __objc_uninstalled_dtable
|
|
993 && !__objc_prepared_dtable_for_class (cls->super_class))
|
|
994 {
|
|
995 __objc_install_dtable_for_class (cls->super_class);
|
|
996 /* The superclass initialisation may have also initialised the
|
|
997 current class, in which case there is no more to do. */
|
|
998 if (cls->dtable != __objc_uninstalled_dtable)
|
|
999 return;
|
|
1000 }
|
|
1001
|
|
1002 /* We have already been prepared but +initialize hasn't completed.
|
|
1003 The +initialize implementation is probably sending 'self'
|
|
1004 messages. We rely on _objc_get_prepared_imp to retrieve the
|
|
1005 implementation pointers. */
|
|
1006 if (__objc_prepared_dtable_for_class (cls))
|
|
1007 return;
|
|
1008
|
|
1009 /* We have this function cache the implementation pointers for
|
|
1010 _objc_get_prepared_imp but the dispatch table won't be initilized
|
|
1011 until __objc_send_initialize completes. */
|
|
1012 __objc_prepare_dtable_for_class (cls);
|
|
1013
|
|
1014 /* We may have already invoked +initialize but
|
|
1015 __objc_update_dispatch_table_for_class invoked by
|
|
1016 class_add_method_list may have reset dispatch table. */
|
|
1017
|
|
1018 /* Call +initialize. If we are a real class, we are installing
|
|
1019 instance methods. If we are a meta class, we are installing
|
|
1020 class methods. The __objc_send_initialize itself will insure
|
|
1021 that the message is called only once per class. */
|
|
1022 if (CLS_ISCLASS (cls))
|
|
1023 __objc_send_initialize (cls);
|
|
1024 else
|
|
1025 {
|
|
1026 /* Retrieve the class from the meta class. */
|
|
1027 Class c = objc_getClass (cls->name);
|
|
1028 assert (CLS_ISMETA (cls));
|
|
1029 assert (c);
|
|
1030 __objc_send_initialize (c);
|
|
1031 }
|
|
1032
|
|
1033 /* We install the dispatch table correctly when +initialize completed. */
|
|
1034 __objc_install_prepared_dtable_for_class (cls);
|
|
1035 }
|
|
1036
|
|
1037 /* Builds the dispatch table for the class CLS and stores it in a
|
|
1038 place where it can be retrieved by __objc_get_prepared_imp until
|
|
1039 __objc_install_prepared_dtable_for_class installs it into the
|
|
1040 class. The dispatch table should not be installed into the class
|
|
1041 until +initialize has completed. */
|
|
1042 static void
|
|
1043 __objc_prepare_dtable_for_class (Class cls)
|
|
1044 {
|
|
1045 struct sarray *dtable;
|
|
1046 struct sarray *super_dtable;
|
|
1047
|
145
|
1048 /* This table could be initialized in init.c. We cannot use the
|
111
|
1049 class name since the class maintains the instance methods and the
|
|
1050 meta class maintains the the class methods yet both share the
|
|
1051 same name. Classes should be unique in any program. */
|
|
1052 if (! prepared_dtable_table)
|
|
1053 prepared_dtable_table
|
|
1054 = objc_hash_new (32,
|
|
1055 (hash_func_type) objc_hash_ptr,
|
|
1056 (compare_func_type) objc_compare_ptrs);
|
|
1057
|
|
1058 /* If the class has not yet had its class links resolved, we must
|
|
1059 re-compute all class links. */
|
|
1060 if (! CLS_ISRESOLV (cls))
|
|
1061 __objc_resolve_class_links ();
|
|
1062
|
|
1063 assert (cls);
|
|
1064 assert (cls->dtable == __objc_uninstalled_dtable);
|
|
1065
|
|
1066 /* If there is already a prepared dtable for this class, we must
|
|
1067 replace it with a new version (since there must have been methods
|
|
1068 added to or otherwise modified in the class while executing
|
|
1069 +initialize, and the table needs to be recomputed. */
|
|
1070 dtable = __objc_prepared_dtable_for_class (cls);
|
|
1071 if (dtable != 0)
|
|
1072 {
|
|
1073 objc_hash_remove (prepared_dtable_table, cls);
|
|
1074 sarray_free (dtable);
|
|
1075 }
|
|
1076
|
|
1077 /* Now prepare the dtable for population. */
|
|
1078 assert (cls != cls->super_class);
|
|
1079 if (cls->super_class)
|
|
1080 {
|
|
1081 /* Inherit the method list from the super class. Yet the super
|
|
1082 class may still be initializing in the case when a class
|
|
1083 cluster sub class initializes its super classes. */
|
|
1084 if (cls->super_class->dtable == __objc_uninstalled_dtable)
|
|
1085 __objc_install_dtable_for_class (cls->super_class);
|
|
1086
|
|
1087 super_dtable = cls->super_class->dtable;
|
|
1088 /* If the dispatch table is not yet installed, we are still in
|
|
1089 the process of executing +initialize. Yet the dispatch table
|
|
1090 should be available. */
|
|
1091 if (super_dtable == __objc_uninstalled_dtable)
|
|
1092 super_dtable = __objc_prepared_dtable_for_class (cls->super_class);
|
|
1093
|
|
1094 assert (super_dtable);
|
|
1095 dtable = sarray_lazy_copy (super_dtable);
|
|
1096 }
|
|
1097 else
|
|
1098 dtable = sarray_new (__objc_selector_max_index, 0);
|
|
1099
|
|
1100 __objc_install_methods_in_dtable (dtable, cls->methods);
|
|
1101
|
|
1102 objc_hash_add (&prepared_dtable_table,
|
|
1103 cls,
|
|
1104 dtable);
|
|
1105 }
|
|
1106
|
|
1107 /* This wrapper only exists to allow an easy replacement of the lookup
|
|
1108 implementation and it is expected that the compiler will optimize
|
|
1109 it away. */
|
|
1110 static struct sarray *
|
|
1111 __objc_prepared_dtable_for_class (Class cls)
|
|
1112 {
|
|
1113 struct sarray *dtable = 0;
|
|
1114 assert (cls);
|
|
1115 if (prepared_dtable_table)
|
|
1116 dtable = objc_hash_value_for_key (prepared_dtable_table, cls);
|
|
1117 /* dtable my be nil, since we call this to check whether we are
|
|
1118 currently preparing before we start preparing. */
|
|
1119 return dtable;
|
|
1120 }
|
|
1121
|
|
1122 /* Helper function for messages sent to CLS or implementation pointers
|
|
1123 retrieved from CLS during +initialize before the dtable is
|
|
1124 installed. When a class implicitly initializes another class which
|
|
1125 in turn implicitly invokes methods in this class, before the
|
|
1126 implementation of +initialize of CLS completes, this returns the
|
|
1127 expected implementation. Forwarding remains the responsibility of
|
|
1128 objc_msg_lookup. This function should only be called under the
|
|
1129 global lock. */
|
|
1130 static IMP
|
|
1131 __objc_get_prepared_imp (Class cls,SEL sel)
|
|
1132 {
|
|
1133 struct sarray *dtable;
|
|
1134 IMP imp;
|
|
1135
|
|
1136 assert (cls);
|
|
1137 assert (sel);
|
|
1138 assert (cls->dtable == __objc_uninstalled_dtable);
|
|
1139 dtable = __objc_prepared_dtable_for_class (cls);
|
|
1140
|
|
1141 assert (dtable);
|
|
1142 assert (dtable != __objc_uninstalled_dtable);
|
|
1143 imp = sarray_get_safe (dtable, (size_t) sel->sel_id);
|
|
1144
|
|
1145 /* imp may be Nil if the method does not exist and we may fallback
|
|
1146 to the forwarding implementation later. */
|
|
1147 return imp;
|
|
1148 }
|
|
1149
|
|
1150 /* When this function is called +initialize should be completed. So
|
|
1151 now we are safe to install the dispatch table for the class so that
|
|
1152 they become available for other threads that may be waiting in the
|
|
1153 lock. */
|
|
1154 static void
|
|
1155 __objc_install_prepared_dtable_for_class (Class cls)
|
|
1156 {
|
|
1157 assert (cls);
|
|
1158 assert (cls->dtable == __objc_uninstalled_dtable);
|
|
1159 cls->dtable = __objc_prepared_dtable_for_class (cls);
|
|
1160
|
|
1161 assert (cls->dtable);
|
|
1162 assert (cls->dtable != __objc_uninstalled_dtable);
|
|
1163 objc_hash_remove (prepared_dtable_table, cls);
|
|
1164 }
|