]>
Commit | Line | Data |
---|---|---|
8a7d0ecc | 1 | /* GNU Objective C Runtime class related functions |
6bc9506f | 2 | Copyright (C) 1993, 1995, 1996, 1997, 2001, 2002, 2009 |
61776355 | 3 | Free Software Foundation, Inc. |
8a7d0ecc | 4 | Contributed by Kresten Krab Thorup and Dennis Glatting. |
5 | ||
12e22ea5 | 6 | Lock-free class table code designed and written from scratch by |
7 | Nicola Pero, 2001. | |
8 | ||
893d9197 | 9 | This file is part of GCC. |
8a7d0ecc | 10 | |
893d9197 | 11 | GCC is free software; you can redistribute it and/or modify it under the |
8a7d0ecc | 12 | terms of the GNU General Public License as published by the Free Software |
6bc9506f | 13 | Foundation; either version 3, or (at your option) any later version. |
8a7d0ecc | 14 | |
893d9197 | 15 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
8a7d0ecc | 16 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
17 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | |
18 | details. | |
19 | ||
6bc9506f | 20 | Under Section 7 of GPL version 3, you are granted additional |
21 | permissions described in the GCC Runtime Library Exception, version | |
22 | 3.1, as published by the Free Software Foundation. | |
8a7d0ecc | 23 | |
6bc9506f | 24 | You should have received a copy of the GNU General Public License and |
25 | a copy of the GCC Runtime Library Exception along with this program; | |
26 | see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
27 | <http://www.gnu.org/licenses/>. */ | |
8a7d0ecc | 28 | |
12e22ea5 | 29 | /* |
30 | The code in this file critically affects class method invocation | |
31 | speed. This long preamble comment explains why, and the issues | |
32 | involved. | |
33 | ||
34 | ||
35 | One of the traditional weaknesses of the GNU Objective-C runtime is | |
36 | that class method invocations are slow. The reason is that when you | |
37 | write | |
38 | ||
39 | array = [NSArray new]; | |
40 | ||
41 | this gets basically compiled into the equivalent of | |
42 | ||
43 | array = [(objc_get_class ("NSArray")) new]; | |
44 | ||
45 | objc_get_class returns the class pointer corresponding to the string | |
46 | `NSArray'; and because of the lookup, the operation is more | |
47 | complicated and slow than a simple instance method invocation. | |
48 | ||
49 | Most high performance Objective-C code (using the GNU Objc runtime) | |
50 | I had the opportunity to read (or write) work around this problem by | |
51 | caching the class pointer: | |
52 | ||
53 | Class arrayClass = [NSArray class]; | |
54 | ||
55 | ... later on ... | |
56 | ||
57 | array = [arrayClass new]; | |
58 | array = [arrayClass new]; | |
59 | array = [arrayClass new]; | |
60 | ||
61 | In this case, you always perform a class lookup (the first one), but | |
62 | then all the [arrayClass new] methods run exactly as fast as an | |
63 | instance method invocation. It helps if you have many class method | |
64 | invocations to the same class. | |
65 | ||
66 | The long-term solution to this problem would be to modify the | |
67 | compiler to output tables of class pointers corresponding to all the | |
68 | class method invocations, and to add code to the runtime to update | |
69 | these tables - that should in the end allow class method invocations | |
70 | to perform precisely as fast as instance method invocations, because | |
71 | no class lookup would be involved. I think the Apple Objective-C | |
72 | runtime uses this technique. Doing this involves synchronized | |
73 | modifications in the runtime and in the compiler. | |
74 | ||
75 | As a first medicine to the problem, I [NP] have redesigned and | |
76 | rewritten the way the runtime is performing class lookup. This | |
77 | doesn't give as much speed as the other (definitive) approach, but | |
78 | at least a class method invocation now takes approximately 4.5 times | |
79 | an instance method invocation on my machine (it would take approx 12 | |
80 | times before the rewriting), which is a lot better. | |
81 | ||
82 | One of the main reason the new class lookup is so faster is because | |
83 | I implemented it in a way that can safely run multithreaded without | |
84 | using locks - a so-called `lock-free' data structure. The atomic | |
85 | operation is pointer assignment. The reason why in this problem | |
86 | lock-free data structures work so well is that you never remove | |
87 | classes from the table - and the difficult thing with lock-free data | |
88 | structures is freeing data when is removed from the structures. */ | |
89 | ||
e58aa1bc | 90 | #include "objc-private/common.h" |
c3a945cd | 91 | #include "objc-private/error.h" |
f75aa158 | 92 | #include "objc/runtime.h" |
2386cda7 | 93 | #include "objc/thr.h" |
f75aa158 | 94 | #include "objc-private/module-abi-8.h" /* For CLS_ISCLASS and similar. */ |
95 | #include "objc-private/runtime.h" /* the kitchen sink */ | |
cd9fd8f4 | 96 | #include "objc-private/sarray.h" /* For sarray_put_at_safe. */ |
f75aa158 | 97 | #include <string.h> /* For memset */ |
12e22ea5 | 98 | |
99 | /* We use a table which maps a class name to the corresponding class | |
100 | * pointer. The first part of this file defines this table, and | |
101 | * functions to do basic operations on the table. The second part of | |
102 | * the file implements some higher level Objective-C functionality for | |
103 | * classes by using the functions provided in the first part to manage | |
104 | * the table. */ | |
105 | ||
106 | /** | |
107 | ** Class Table Internals | |
108 | **/ | |
109 | ||
110 | /* A node holding a class */ | |
111 | typedef struct class_node | |
112 | { | |
113 | struct class_node *next; /* Pointer to next entry on the list. | |
114 | NULL indicates end of list. */ | |
115 | ||
116 | const char *name; /* The class name string */ | |
117 | int length; /* The class name string length */ | |
118 | Class pointer; /* The Class pointer */ | |
119 | ||
120 | } *class_node_ptr; | |
121 | ||
122 | /* A table containing classes is a class_node_ptr (pointing to the | |
123 | first entry in the table - if it is NULL, then the table is | |
124 | empty). */ | |
125 | ||
126 | /* We have 1024 tables. Each table contains all class names which | |
127 | have the same hash (which is a number between 0 and 1023). To look | |
128 | up a class_name, we compute its hash, and get the corresponding | |
129 | table. Once we have the table, we simply compare strings directly | |
130 | till we find the one which we want (using the length first). The | |
131 | number of tables is quite big on purpose (a normal big application | |
132 | has less than 1000 classes), so that you shouldn't normally get any | |
133 | collisions, and get away with a single comparison (which we can't | |
134 | avoid since we need to know that you have got the right thing). */ | |
135 | #define CLASS_TABLE_SIZE 1024 | |
136 | #define CLASS_TABLE_MASK 1023 | |
137 | ||
138 | static class_node_ptr class_table_array[CLASS_TABLE_SIZE]; | |
139 | ||
140 | /* The table writing mutex - we lock on writing to avoid conflicts | |
141 | between different writers, but we read without locks. That is | |
142 | possible because we assume pointer assignment to be an atomic | |
e983fc72 | 143 | operation. TODO: This is only true under certain circumstances, |
144 | which should be clarified. */ | |
12e22ea5 | 145 | static objc_mutex_t __class_table_lock = NULL; |
146 | ||
147 | /* CLASS_TABLE_HASH is how we compute the hash of a class name. It is | |
148 | a macro - *not* a function - arguments *are* modified directly. | |
149 | ||
150 | INDEX should be a variable holding an int; | |
151 | HASH should be a variable holding an int; | |
152 | CLASS_NAME should be a variable holding a (char *) to the class_name. | |
153 | ||
154 | After the macro is executed, INDEX contains the length of the | |
155 | string, and HASH the computed hash of the string; CLASS_NAME is | |
156 | untouched. */ | |
157 | ||
158 | #define CLASS_TABLE_HASH(INDEX, HASH, CLASS_NAME) \ | |
159 | HASH = 0; \ | |
160 | for (INDEX = 0; CLASS_NAME[INDEX] != '\0'; INDEX++) \ | |
161 | { \ | |
162 | HASH = (HASH << 4) ^ (HASH >> 28) ^ CLASS_NAME[INDEX]; \ | |
163 | } \ | |
164 | \ | |
165 | HASH = (HASH ^ (HASH >> 10) ^ (HASH >> 20)) & CLASS_TABLE_MASK; | |
166 | ||
167 | /* Setup the table. */ | |
168 | static void | |
adff42e6 | 169 | class_table_setup (void) |
12e22ea5 | 170 | { |
171 | /* Start - nothing in the table. */ | |
61776355 | 172 | memset (class_table_array, 0, sizeof (class_node_ptr) * CLASS_TABLE_SIZE); |
12e22ea5 | 173 | |
174 | /* The table writing mutex. */ | |
175 | __class_table_lock = objc_mutex_allocate (); | |
176 | } | |
177 | ||
178 | ||
179 | /* Insert a class in the table (used when a new class is registered). */ | |
180 | static void | |
181 | class_table_insert (const char *class_name, Class class_pointer) | |
182 | { | |
183 | int hash, length; | |
184 | class_node_ptr new_node; | |
185 | ||
186 | /* Find out the class name's hash and length. */ | |
187 | CLASS_TABLE_HASH (length, hash, class_name); | |
188 | ||
189 | /* Prepare the new node holding the class. */ | |
190 | new_node = objc_malloc (sizeof (struct class_node)); | |
191 | new_node->name = class_name; | |
192 | new_node->length = length; | |
193 | new_node->pointer = class_pointer; | |
194 | ||
195 | /* Lock the table for modifications. */ | |
196 | objc_mutex_lock (__class_table_lock); | |
197 | ||
198 | /* Insert the new node in the table at the beginning of the table at | |
199 | class_table_array[hash]. */ | |
200 | new_node->next = class_table_array[hash]; | |
201 | class_table_array[hash] = new_node; | |
202 | ||
203 | objc_mutex_unlock (__class_table_lock); | |
204 | } | |
205 | ||
206 | /* Replace a class in the table (used only by poseAs:). */ | |
207 | static void | |
208 | class_table_replace (Class old_class_pointer, Class new_class_pointer) | |
209 | { | |
210 | int hash; | |
211 | class_node_ptr node; | |
212 | ||
213 | objc_mutex_lock (__class_table_lock); | |
214 | ||
215 | hash = 0; | |
216 | node = class_table_array[hash]; | |
217 | ||
218 | while (hash < CLASS_TABLE_SIZE) | |
219 | { | |
220 | if (node == NULL) | |
221 | { | |
222 | hash++; | |
223 | if (hash < CLASS_TABLE_SIZE) | |
224 | { | |
225 | node = class_table_array[hash]; | |
226 | } | |
227 | } | |
228 | else | |
229 | { | |
230 | Class class1 = node->pointer; | |
231 | ||
232 | if (class1 == old_class_pointer) | |
233 | { | |
234 | node->pointer = new_class_pointer; | |
235 | } | |
236 | node = node->next; | |
237 | } | |
238 | } | |
8a7d0ecc | 239 | |
12e22ea5 | 240 | objc_mutex_unlock (__class_table_lock); |
241 | } | |
242 | ||
243 | ||
244 | /* Get a class from the table. This does not need mutex protection. | |
245 | Currently, this function is called each time you call a static | |
246 | method, this is why it must be very fast. */ | |
247 | static inline Class | |
248 | class_table_get_safe (const char *class_name) | |
249 | { | |
250 | class_node_ptr node; | |
251 | int length, hash; | |
252 | ||
253 | /* Compute length and hash. */ | |
254 | CLASS_TABLE_HASH (length, hash, class_name); | |
255 | ||
256 | node = class_table_array[hash]; | |
257 | ||
258 | if (node != NULL) | |
259 | { | |
260 | do | |
261 | { | |
262 | if (node->length == length) | |
263 | { | |
264 | /* Compare the class names. */ | |
265 | int i; | |
266 | ||
267 | for (i = 0; i < length; i++) | |
268 | { | |
269 | if ((node->name)[i] != class_name[i]) | |
270 | { | |
271 | break; | |
272 | } | |
273 | } | |
274 | ||
275 | if (i == length) | |
276 | { | |
277 | /* They are equal! */ | |
278 | return node->pointer; | |
279 | } | |
280 | } | |
281 | } | |
282 | while ((node = node->next) != NULL); | |
283 | } | |
284 | ||
285 | return Nil; | |
286 | } | |
287 | ||
288 | /* Enumerate over the class table. */ | |
289 | struct class_table_enumerator | |
290 | { | |
291 | int hash; | |
292 | class_node_ptr node; | |
293 | }; | |
294 | ||
295 | ||
296 | static Class | |
297 | class_table_next (struct class_table_enumerator **e) | |
298 | { | |
299 | struct class_table_enumerator *enumerator = *e; | |
300 | class_node_ptr next; | |
301 | ||
302 | if (enumerator == NULL) | |
303 | { | |
304 | *e = objc_malloc (sizeof (struct class_table_enumerator)); | |
305 | enumerator = *e; | |
306 | enumerator->hash = 0; | |
307 | enumerator->node = NULL; | |
308 | ||
309 | next = class_table_array[enumerator->hash]; | |
310 | } | |
311 | else | |
312 | { | |
313 | next = enumerator->node->next; | |
314 | } | |
315 | ||
316 | if (next != NULL) | |
317 | { | |
318 | enumerator->node = next; | |
319 | return enumerator->node->pointer; | |
320 | } | |
321 | else | |
322 | { | |
323 | enumerator->hash++; | |
324 | ||
325 | while (enumerator->hash < CLASS_TABLE_SIZE) | |
326 | { | |
327 | next = class_table_array[enumerator->hash]; | |
328 | if (next != NULL) | |
329 | { | |
330 | enumerator->node = next; | |
331 | return enumerator->node->pointer; | |
332 | } | |
333 | enumerator->hash++; | |
334 | } | |
335 | ||
336 | /* Ok - table finished - done. */ | |
337 | objc_free (enumerator); | |
338 | return Nil; | |
339 | } | |
340 | } | |
341 | ||
342 | #if 0 /* DEBUGGING FUNCTIONS */ | |
343 | /* Debugging function - print the class table. */ | |
344 | void | |
61776355 | 345 | class_table_print (void) |
12e22ea5 | 346 | { |
347 | int i; | |
348 | ||
349 | for (i = 0; i < CLASS_TABLE_SIZE; i++) | |
350 | { | |
351 | class_node_ptr node; | |
352 | ||
353 | printf ("%d:\n", i); | |
354 | node = class_table_array[i]; | |
355 | ||
356 | while (node != NULL) | |
357 | { | |
358 | printf ("\t%s\n", node->name); | |
359 | node = node->next; | |
360 | } | |
361 | } | |
362 | } | |
363 | ||
364 | /* Debugging function - print an histogram of number of classes in | |
365 | function of hash key values. Useful to evaluate the hash function | |
366 | in real cases. */ | |
367 | void | |
61776355 | 368 | class_table_print_histogram (void) |
12e22ea5 | 369 | { |
370 | int i, j; | |
371 | int counter = 0; | |
372 | ||
373 | for (i = 0; i < CLASS_TABLE_SIZE; i++) | |
374 | { | |
375 | class_node_ptr node; | |
376 | ||
377 | node = class_table_array[i]; | |
378 | ||
379 | while (node != NULL) | |
380 | { | |
381 | counter++; | |
382 | node = node->next; | |
383 | } | |
384 | if (((i + 1) % 50) == 0) | |
385 | { | |
386 | printf ("%4d:", i + 1); | |
387 | for (j = 0; j < counter; j++) | |
388 | { | |
389 | printf ("X"); | |
390 | } | |
391 | printf ("\n"); | |
392 | counter = 0; | |
393 | } | |
394 | } | |
395 | printf ("%4d:", i + 1); | |
396 | for (j = 0; j < counter; j++) | |
397 | { | |
398 | printf ("X"); | |
399 | } | |
400 | printf ("\n"); | |
401 | } | |
402 | #endif /* DEBUGGING FUNCTIONS */ | |
403 | ||
404 | /** | |
405 | ** Objective-C runtime functions | |
406 | **/ | |
407 | ||
408 | /* From now on, the only access to the class table data structure | |
409 | should be via the class_table_* functions. */ | |
410 | ||
411 | /* This is a hook which is called by objc_get_class and | |
412 | objc_lookup_class if the runtime is not able to find the class. | |
86bde516 | 413 | This may e.g. try to load in the class using dynamic loading. |
414 | ||
415 | This hook was a public, global variable in the Traditional GNU | |
416 | Objective-C Runtime API (objc/objc-api.h). The modern GNU | |
417 | Objective-C Runtime API (objc/runtime.h) provides the | |
418 | objc_setGetUnknownClassHandler() function instead. | |
419 | */ | |
61776355 | 420 | Class (*_objc_lookup_class) (const char *name) = 0; /* !T:SAFE */ |
8a7d0ecc | 421 | |
86bde516 | 422 | /* The handler currently in use. PS: if both |
423 | __obj_get_unknown_class_handler and _objc_lookup_class are defined, | |
424 | __objc_get_unknown_class_handler is called first. */ | |
425 | static objc_get_unknown_class_handler | |
426 | __objc_get_unknown_class_handler = NULL; | |
427 | ||
428 | objc_get_unknown_class_handler | |
429 | objc_setGetUnknownClassHandler (objc_get_unknown_class_handler | |
430 | new_handler) | |
431 | { | |
432 | objc_get_unknown_class_handler old_handler | |
433 | = __objc_get_unknown_class_handler; | |
434 | __objc_get_unknown_class_handler = new_handler; | |
435 | return old_handler; | |
436 | } | |
437 | ||
8a7d0ecc | 438 | |
12e22ea5 | 439 | /* True when class links has been resolved. */ |
8a7d0ecc | 440 | BOOL __objc_class_links_resolved = NO; /* !T:UNUSED */ |
441 | ||
442 | ||
61776355 | 443 | void |
444 | __objc_init_class_tables (void) | |
8a7d0ecc | 445 | { |
12e22ea5 | 446 | /* Allocate the class hash table. */ |
447 | ||
61776355 | 448 | if (__class_table_lock) |
8a7d0ecc | 449 | return; |
12e22ea5 | 450 | |
61776355 | 451 | objc_mutex_lock (__objc_runtime_mutex); |
12e22ea5 | 452 | |
453 | class_table_setup (); | |
8a7d0ecc | 454 | |
61776355 | 455 | objc_mutex_unlock (__objc_runtime_mutex); |
8a7d0ecc | 456 | } |
457 | ||
12e22ea5 | 458 | /* This function adds a class to the class hash table, and assigns the |
459 | class a number, unless it's already known. */ | |
8a7d0ecc | 460 | void |
61776355 | 461 | __objc_add_class_to_hash (Class class) |
8a7d0ecc | 462 | { |
463 | Class h_class; | |
464 | ||
61776355 | 465 | objc_mutex_lock (__objc_runtime_mutex); |
8a7d0ecc | 466 | |
12e22ea5 | 467 | /* Make sure the table is there. */ |
61776355 | 468 | assert (__class_table_lock); |
8a7d0ecc | 469 | |
12e22ea5 | 470 | /* Make sure it's not a meta class. */ |
61776355 | 471 | assert (CLS_ISCLASS (class)); |
8a7d0ecc | 472 | |
473 | /* Check to see if the class is already in the hash table. */ | |
12e22ea5 | 474 | h_class = class_table_get_safe (class->name); |
61776355 | 475 | if (! h_class) |
8a7d0ecc | 476 | { |
477 | /* The class isn't in the hash table. Add the class and assign a class | |
478 | number. */ | |
479 | static unsigned int class_number = 1; | |
480 | ||
61776355 | 481 | CLS_SETNUMBER (class, class_number); |
482 | CLS_SETNUMBER (class->class_pointer, class_number); | |
8a7d0ecc | 483 | |
484 | ++class_number; | |
12e22ea5 | 485 | class_table_insert (class->name, class); |
8a7d0ecc | 486 | } |
487 | ||
61776355 | 488 | objc_mutex_unlock (__objc_runtime_mutex); |
8a7d0ecc | 489 | } |
490 | ||
61776355 | 491 | Class |
86bde516 | 492 | objc_getClass (const char *name) |
8a7d0ecc | 493 | { |
494 | Class class; | |
495 | ||
86bde516 | 496 | if (name == NULL) |
497 | return Nil; | |
8a7d0ecc | 498 | |
86bde516 | 499 | class = class_table_get_safe (name); |
500 | ||
8a7d0ecc | 501 | if (class) |
502 | return class; | |
86bde516 | 503 | |
504 | if (__objc_get_unknown_class_handler) | |
505 | return (*__objc_get_unknown_class_handler) (name); | |
8a7d0ecc | 506 | |
507 | if (_objc_lookup_class) | |
61776355 | 508 | return (*_objc_lookup_class) (name); |
86bde516 | 509 | |
510 | return Nil; | |
511 | } | |
512 | ||
513 | Class | |
514 | objc_lookupClass (const char *name) | |
515 | { | |
516 | if (name == NULL) | |
517 | return Nil; | |
518 | else | |
519 | return class_table_get_safe (name); | |
520 | } | |
521 | ||
522 | Class | |
523 | objc_getMetaClass (const char *name) | |
524 | { | |
525 | Class class = objc_getClass (name); | |
526 | ||
527 | if (class) | |
528 | return class->class_pointer; | |
8a7d0ecc | 529 | else |
86bde516 | 530 | return Nil; |
8a7d0ecc | 531 | } |
532 | ||
86bde516 | 533 | Class |
534 | objc_getRequiredClass (const char *name) | |
535 | { | |
536 | Class class = objc_getClass (name); | |
537 | ||
538 | if (class) | |
539 | return class; | |
540 | else | |
541 | _objc_abort ("objc_getRequiredClass ('%s') failed: class not found\n", name); | |
542 | } | |
543 | ||
544 | int | |
545 | objc_getClassList (Class *returnValue, int maxNumberOfClassesToReturn) | |
546 | { | |
547 | /* Iterate over all entries in the table. */ | |
548 | int hash, count = 0; | |
549 | ||
86bde516 | 550 | for (hash = 0; hash < CLASS_TABLE_SIZE; hash++) |
551 | { | |
552 | class_node_ptr node = class_table_array[hash]; | |
553 | ||
554 | while (node != NULL) | |
555 | { | |
556 | if (returnValue) | |
557 | { | |
558 | if (count < maxNumberOfClassesToReturn) | |
559 | returnValue[count] = node->pointer; | |
560 | else | |
561 | { | |
86bde516 | 562 | return count; |
563 | } | |
564 | } | |
565 | count++; | |
566 | node = node->next; | |
567 | } | |
568 | } | |
569 | ||
86bde516 | 570 | return count; |
571 | } | |
572 | ||
573 | /* Traditional GNU Objective-C Runtime API. */ | |
574 | /* Get the class object for the class named NAME. If NAME does not | |
575 | identify a known class, the hook _objc_lookup_class is called. If | |
576 | this fails, nil is returned. */ | |
577 | Class | |
578 | objc_lookup_class (const char *name) | |
579 | { | |
580 | return objc_getClass (name); | |
581 | } | |
582 | ||
583 | /* Traditional GNU Objective-C Runtime API. Important: this method is | |
584 | called automatically by the compiler while messaging (if using the | |
585 | traditional ABI), so it is worth keeping it fast; don't make it | |
586 | just a wrapper around objc_getClass(). */ | |
f75aa158 | 587 | /* Note that this is roughly equivalent to objc_getRequiredClass(). */ |
8a7d0ecc | 588 | /* Get the class object for the class named NAME. If NAME does not |
589 | identify a known class, the hook _objc_lookup_class is called. If | |
12e22ea5 | 590 | this fails, an error message is issued and the system aborts. */ |
8a7d0ecc | 591 | Class |
592 | objc_get_class (const char *name) | |
593 | { | |
594 | Class class; | |
595 | ||
12e22ea5 | 596 | class = class_table_get_safe (name); |
8a7d0ecc | 597 | |
598 | if (class) | |
599 | return class; | |
600 | ||
86bde516 | 601 | if (__objc_get_unknown_class_handler) |
602 | class = (*__objc_get_unknown_class_handler) (name); | |
603 | ||
604 | if ((!class) && _objc_lookup_class) | |
61776355 | 605 | class = (*_objc_lookup_class) (name); |
8a7d0ecc | 606 | |
61776355 | 607 | if (class) |
8a7d0ecc | 608 | return class; |
609 | ||
c3a945cd | 610 | _objc_abort ("objc runtime: cannot find class %s\n", name); |
611 | ||
8a7d0ecc | 612 | return 0; |
613 | } | |
614 | ||
615 | MetaClass | |
61776355 | 616 | objc_get_meta_class (const char *name) |
8a7d0ecc | 617 | { |
61776355 | 618 | return objc_get_class (name)->class_pointer; |
8a7d0ecc | 619 | } |
620 | ||
621 | /* This function provides a way to enumerate all the classes in the | |
622 | executable. Pass *ENUM_STATE == NULL to start the enumeration. The | |
623 | function will return 0 when there are no more classes. | |
624 | For example: | |
625 | id class; | |
626 | void *es = NULL; | |
61776355 | 627 | while ((class = objc_next_class (&es))) |
8a7d0ecc | 628 | ... do something with class; |
629 | */ | |
630 | Class | |
61776355 | 631 | objc_next_class (void **enum_state) |
8a7d0ecc | 632 | { |
12e22ea5 | 633 | Class class; |
8a7d0ecc | 634 | |
61776355 | 635 | objc_mutex_lock (__objc_runtime_mutex); |
12e22ea5 | 636 | |
637 | /* Make sure the table is there. */ | |
61776355 | 638 | assert (__class_table_lock); |
8a7d0ecc | 639 | |
61776355 | 640 | class = class_table_next ((struct class_table_enumerator **) enum_state); |
8a7d0ecc | 641 | |
61776355 | 642 | objc_mutex_unlock (__objc_runtime_mutex); |
12e22ea5 | 643 | |
644 | return class; | |
8a7d0ecc | 645 | } |
646 | ||
cd9fd8f4 | 647 | /* This is used when the implementation of a method changes. It goes |
648 | through all classes, looking for the ones that have these methods | |
649 | (either method_a or method_b; method_b can be NULL), and reloads | |
650 | the implementation for these. You should call this with the | |
651 | runtime mutex already locked. */ | |
652 | void | |
653 | __objc_update_classes_with_methods (struct objc_method *method_a, struct objc_method *method_b) | |
654 | { | |
655 | int hash; | |
656 | ||
657 | /* Iterate over all classes. */ | |
658 | for (hash = 0; hash < CLASS_TABLE_SIZE; hash++) | |
659 | { | |
660 | class_node_ptr node = class_table_array[hash]; | |
661 | ||
662 | while (node != NULL) | |
663 | { | |
664 | /* Iterate over all methods in the class. */ | |
665 | Class class = node->pointer; | |
666 | struct objc_method_list * method_list = class->methods; | |
667 | ||
668 | while (method_list) | |
669 | { | |
670 | int i; | |
671 | ||
672 | for (i = 0; i < method_list->method_count; ++i) | |
673 | { | |
674 | struct objc_method *method = &method_list->method_list[i]; | |
675 | ||
676 | /* If the method is one of the ones we are looking | |
677 | for, update the implementation. */ | |
678 | if (method == method_a) | |
679 | { | |
680 | sarray_at_put_safe (class->dtable, | |
681 | (sidx) method_a->method_name->sel_id, | |
682 | method_a->method_imp); | |
683 | } | |
684 | ||
685 | if (method == method_b) | |
686 | { | |
687 | if (method_b != NULL) | |
688 | { | |
689 | sarray_at_put_safe (class->dtable, | |
690 | (sidx) method_b->method_name->sel_id, | |
691 | method_b->method_imp); | |
692 | } | |
693 | } | |
694 | } | |
695 | ||
696 | method_list = method_list->method_next; | |
697 | } | |
698 | node = node->next; | |
699 | } | |
700 | } | |
701 | } | |
702 | ||
12e22ea5 | 703 | /* Resolve super/subclass links for all classes. The only thing we |
704 | can be sure of is that the class_pointer for class objects point to | |
705 | the right meta class objects. */ | |
61776355 | 706 | void |
707 | __objc_resolve_class_links (void) | |
8a7d0ecc | 708 | { |
12e22ea5 | 709 | struct class_table_enumerator *es = NULL; |
8a7d0ecc | 710 | Class object_class = objc_get_class ("Object"); |
12e22ea5 | 711 | Class class1; |
8a7d0ecc | 712 | |
61776355 | 713 | assert (object_class); |
8a7d0ecc | 714 | |
61776355 | 715 | objc_mutex_lock (__objc_runtime_mutex); |
8a7d0ecc | 716 | |
12e22ea5 | 717 | /* Assign subclass links. */ |
718 | while ((class1 = class_table_next (&es))) | |
8a7d0ecc | 719 | { |
8a7d0ecc | 720 | /* Make sure we have what we think we have. */ |
61776355 | 721 | assert (CLS_ISCLASS (class1)); |
722 | assert (CLS_ISMETA (class1->class_pointer)); | |
8a7d0ecc | 723 | |
12e22ea5 | 724 | /* The class_pointer of all meta classes point to Object's meta |
725 | class. */ | |
8a7d0ecc | 726 | class1->class_pointer->class_pointer = object_class->class_pointer; |
727 | ||
61776355 | 728 | if (! CLS_ISRESOLV (class1)) |
8a7d0ecc | 729 | { |
61776355 | 730 | CLS_SETRESOLV (class1); |
731 | CLS_SETRESOLV (class1->class_pointer); | |
8a7d0ecc | 732 | |
61776355 | 733 | if (class1->super_class) |
8a7d0ecc | 734 | { |
735 | Class a_super_class | |
736 | = objc_get_class ((char *) class1->super_class); | |
737 | ||
738 | assert (a_super_class); | |
739 | ||
740 | DEBUG_PRINTF ("making class connections for: %s\n", | |
741 | class1->name); | |
742 | ||
12e22ea5 | 743 | /* Assign subclass links for superclass. */ |
8a7d0ecc | 744 | class1->sibling_class = a_super_class->subclass_list; |
745 | a_super_class->subclass_list = class1; | |
746 | ||
12e22ea5 | 747 | /* Assign subclass links for meta class of superclass. */ |
8a7d0ecc | 748 | if (a_super_class->class_pointer) |
749 | { | |
750 | class1->class_pointer->sibling_class | |
751 | = a_super_class->class_pointer->subclass_list; | |
752 | a_super_class->class_pointer->subclass_list | |
753 | = class1->class_pointer; | |
754 | } | |
755 | } | |
12e22ea5 | 756 | else /* A root class, make its meta object be a subclass of |
757 | Object. */ | |
8a7d0ecc | 758 | { |
759 | class1->class_pointer->sibling_class | |
760 | = object_class->subclass_list; | |
761 | object_class->subclass_list = class1->class_pointer; | |
762 | } | |
763 | } | |
764 | } | |
765 | ||
12e22ea5 | 766 | /* Assign superclass links. */ |
767 | es = NULL; | |
768 | while ((class1 = class_table_next (&es))) | |
8a7d0ecc | 769 | { |
8a7d0ecc | 770 | Class sub_class; |
771 | for (sub_class = class1->subclass_list; sub_class; | |
772 | sub_class = sub_class->sibling_class) | |
773 | { | |
774 | sub_class->super_class = class1; | |
61776355 | 775 | if (CLS_ISCLASS (sub_class)) |
8a7d0ecc | 776 | sub_class->class_pointer->super_class = class1->class_pointer; |
777 | } | |
778 | } | |
779 | ||
61776355 | 780 | objc_mutex_unlock (__objc_runtime_mutex); |
8a7d0ecc | 781 | } |
782 | ||
e983fc72 | 783 | const char * |
784 | class_getName (Class class_) | |
785 | { | |
786 | if (class_ == Nil) | |
787 | return "nil"; | |
8a7d0ecc | 788 | |
e983fc72 | 789 | return class_->name; |
790 | } | |
8a7d0ecc | 791 | |
f75aa158 | 792 | BOOL |
793 | class_isMetaClass (Class class_) | |
794 | { | |
795 | /* CLS_ISMETA includes the check for Nil class_. */ | |
796 | return CLS_ISMETA (class_); | |
797 | } | |
798 | ||
799 | Class | |
800 | class_getSuperclass (Class class_) | |
801 | { | |
802 | if (class_ == Nil) | |
803 | return Nil; | |
804 | ||
805 | return class_->super_class; | |
806 | } | |
807 | ||
808 | int | |
809 | class_getVersion (Class class_) | |
810 | { | |
811 | if (class_ == Nil) | |
812 | return 0; | |
813 | ||
814 | return (int)(class_->version); | |
815 | } | |
816 | ||
817 | void | |
818 | class_setVersion (Class class_, int version) | |
819 | { | |
820 | if (class_ == Nil) | |
821 | return; | |
822 | ||
823 | class_->version = version; | |
824 | } | |
825 | ||
826 | size_t | |
827 | class_getInstanceSize (Class class_) | |
828 | { | |
829 | if (class_ == Nil) | |
830 | return 0; | |
831 | ||
832 | return class_->instance_size; | |
833 | } | |
834 | ||
8a7d0ecc | 835 | #define CLASSOF(c) ((c)->class_pointer) |
836 | ||
837 | Class | |
838 | class_pose_as (Class impostor, Class super_class) | |
839 | { | |
61776355 | 840 | if (! CLS_ISRESOLV (impostor)) |
8a7d0ecc | 841 | __objc_resolve_class_links (); |
842 | ||
12e22ea5 | 843 | /* Preconditions */ |
8a7d0ecc | 844 | assert (impostor); |
845 | assert (super_class); | |
846 | assert (impostor->super_class == super_class); | |
847 | assert (CLS_ISCLASS (impostor)); | |
848 | assert (CLS_ISCLASS (super_class)); | |
849 | assert (impostor->instance_size == super_class->instance_size); | |
850 | ||
851 | { | |
852 | Class *subclass = &(super_class->subclass_list); | |
853 | ||
12e22ea5 | 854 | /* Move subclasses of super_class to impostor. */ |
8a7d0ecc | 855 | while (*subclass) |
856 | { | |
12e22ea5 | 857 | Class nextSub = (*subclass)->sibling_class; |
858 | ||
859 | if (*subclass != impostor) | |
860 | { | |
861 | Class sub = *subclass; | |
862 | ||
863 | /* Classes */ | |
864 | sub->sibling_class = impostor->subclass_list; | |
865 | sub->super_class = impostor; | |
866 | impostor->subclass_list = sub; | |
867 | ||
868 | /* It will happen that SUB is not a class object if it is | |
869 | the top of the meta class hierarchy chain (root | |
870 | meta-class objects inherit their class object). If | |
871 | that is the case... don't mess with the meta-meta | |
872 | class. */ | |
873 | if (CLS_ISCLASS (sub)) | |
874 | { | |
875 | /* Meta classes */ | |
876 | CLASSOF (sub)->sibling_class = | |
877 | CLASSOF (impostor)->subclass_list; | |
878 | CLASSOF (sub)->super_class = CLASSOF (impostor); | |
879 | CLASSOF (impostor)->subclass_list = CLASSOF (sub); | |
880 | } | |
881 | } | |
882 | ||
883 | *subclass = nextSub; | |
8a7d0ecc | 884 | } |
885 | ||
12e22ea5 | 886 | /* Set subclasses of superclass to be impostor only. */ |
8a7d0ecc | 887 | super_class->subclass_list = impostor; |
888 | CLASSOF (super_class)->subclass_list = CLASSOF (impostor); | |
889 | ||
12e22ea5 | 890 | /* Set impostor to have no sibling classes. */ |
8a7d0ecc | 891 | impostor->sibling_class = 0; |
892 | CLASSOF (impostor)->sibling_class = 0; | |
893 | } | |
894 | ||
12e22ea5 | 895 | /* Check relationship of impostor and super_class is kept. */ |
8a7d0ecc | 896 | assert (impostor->super_class == super_class); |
897 | assert (CLASSOF (impostor)->super_class == CLASSOF (super_class)); | |
898 | ||
12e22ea5 | 899 | /* This is how to update the lookup table. Regardless of what the |
900 | keys of the hashtable is, change all values that are superclass | |
901 | into impostor. */ | |
8a7d0ecc | 902 | |
61776355 | 903 | objc_mutex_lock (__objc_runtime_mutex); |
8a7d0ecc | 904 | |
12e22ea5 | 905 | class_table_replace (super_class, impostor); |
8a7d0ecc | 906 | |
61776355 | 907 | objc_mutex_unlock (__objc_runtime_mutex); |
8a7d0ecc | 908 | |
12e22ea5 | 909 | /* Next, we update the dispatch tables... */ |
8a7d0ecc | 910 | __objc_update_dispatch_table_for_class (CLASSOF (impostor)); |
911 | __objc_update_dispatch_table_for_class (impostor); | |
912 | ||
913 | return impostor; | |
914 | } |