|  | /* GNU Objective C Runtime message lookup | 
|  | Copyright (C) 1993-2025 Free Software Foundation, Inc. | 
|  | Contributed by Kresten Krab Thorup | 
|  |  | 
|  | This file is part of GCC. | 
|  |  | 
|  | GCC is free software; you can redistribute it and/or modify it under the | 
|  | terms of the GNU General Public License as published by the Free Software | 
|  | Foundation; either version 3, or (at your option) any later version. | 
|  |  | 
|  | GCC is distributed in the hope that it will be useful, but WITHOUT ANY | 
|  | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | 
|  | FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more | 
|  | details. | 
|  |  | 
|  | Under Section 7 of GPL version 3, you are granted additional | 
|  | permissions described in the GCC Runtime Library Exception, version | 
|  | 3.1, as published by the Free Software Foundation. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License and | 
|  | a copy of the GCC Runtime Library Exception along with this program; | 
|  | see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see | 
|  | <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | /* Uncommented the following line to enable debug logging.  Use this | 
|  | only while debugging the runtime.  */ | 
|  | /* #define DEBUG 1 */ | 
|  |  | 
|  | /* FIXME: This should be using libffi instead of __builtin_apply | 
|  | and friends.  */ | 
|  |  | 
|  | #include "objc-private/common.h" | 
|  | #include "objc-private/error.h" | 
|  | #include "tconfig.h" | 
|  | #include "coretypes.h" | 
|  | #include "objc/runtime.h" | 
|  | #include "objc/message.h"          /* For objc_msg_lookup(), objc_msg_lookup_super().  */ | 
|  | #include "objc/thr.h" | 
|  | #include "objc-private/module-abi-8.h" | 
|  | #include "objc-private/runtime.h" | 
|  | #include "objc-private/hash.h" | 
|  | #include "objc-private/sarray.h" | 
|  | #include "objc-private/selector.h" /* For sel_is_mapped() */ | 
|  | #include "runtime-info.h" | 
|  | #include <assert.h> /* For assert */ | 
|  | #include <string.h> /* For strlen */ | 
|  |  | 
|  | #define INVISIBLE_STRUCT_RETURN 1 | 
|  |  | 
|  | /* The uninstalled dispatch table.  If a class' dispatch table points | 
|  | to __objc_uninstalled_dtable then that means it needs its dispatch | 
|  | table to be installed.  */ | 
|  | struct sarray *__objc_uninstalled_dtable = 0;   /* !T:MUTEX */ | 
|  |  | 
|  | /* Two hooks for method forwarding. If either is set, it is invoked to | 
|  | * return a function that performs the real forwarding.  If both are | 
|  | * set, the result of __objc_msg_forward2 will be preferred over that | 
|  | * of __objc_msg_forward.  If both return NULL or are unset, the | 
|  | * libgcc based functions (__builtin_apply and friends) are used.  */ | 
|  | IMP (*__objc_msg_forward) (SEL) = NULL; | 
|  | IMP (*__objc_msg_forward2) (id, SEL) = NULL; | 
|  |  | 
|  | /* Send +initialize to class.  */ | 
|  | static void __objc_send_initialize (Class); | 
|  |  | 
|  | /* Forward declare some functions */ | 
|  | static void __objc_install_dtable_for_class (Class cls); | 
|  | static void __objc_prepare_dtable_for_class (Class cls); | 
|  | static void __objc_install_prepared_dtable_for_class (Class cls); | 
|  |  | 
|  | static struct sarray *__objc_prepared_dtable_for_class (Class cls); | 
|  | static IMP __objc_get_prepared_imp (Class cls,SEL sel); | 
|  |  | 
|  |  | 
|  | /* Various forwarding functions that are used based upon the | 
|  | return type for the selector. | 
|  | __objc_block_forward for structures. | 
|  | __objc_double_forward for floats/doubles. | 
|  | __objc_word_forward for pointers or types that fit in registers.  */ | 
|  | static double __objc_double_forward (id, SEL, ...); | 
|  | static id __objc_word_forward (id, SEL, ...); | 
|  | typedef struct { id many[8]; } __big; | 
|  | #if INVISIBLE_STRUCT_RETURN | 
|  | static __big | 
|  | #else | 
|  | static id | 
|  | #endif | 
|  | __objc_block_forward (id, SEL, ...); | 
|  | static struct objc_method * search_for_method_in_hierarchy (Class class, SEL sel); | 
|  | struct objc_method * search_for_method_in_list (struct objc_method_list * list, SEL op); | 
|  | id nil_method (id, SEL); | 
|  |  | 
|  | /* Make sure this inline function is exported regardless of GNU89 or C99 | 
|  | inlining semantics as it is part of the libobjc ABI.  */ | 
|  | extern IMP __objc_get_forward_imp (id, SEL); | 
|  |  | 
|  | /* Given a selector, return the proper forwarding implementation.  */ | 
|  | inline | 
|  | IMP | 
|  | __objc_get_forward_imp (id rcv, SEL sel) | 
|  | { | 
|  | /* If a custom forwarding hook was registered, try getting a | 
|  | forwarding function from it. There are two forward routine hooks, | 
|  | one that takes the receiver as an argument and one that does | 
|  | not.  */ | 
|  | if (__objc_msg_forward2) | 
|  | { | 
|  | IMP result; | 
|  | if ((result = __objc_msg_forward2 (rcv, sel)) != NULL) | 
|  | return result; | 
|  | } | 
|  | if (__objc_msg_forward) | 
|  | { | 
|  | IMP result; | 
|  | if ((result = __objc_msg_forward (sel)) != NULL) | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /* In all other cases, use the default forwarding functions built | 
|  | using __builtin_apply and friends.  */ | 
|  | { | 
|  | const char *t = sel->sel_types; | 
|  |  | 
|  | if (t && (*t == '[' || *t == '(' || *t == '{') | 
|  | #ifdef OBJC_MAX_STRUCT_BY_VALUE | 
|  | && objc_sizeof_type (t) > OBJC_MAX_STRUCT_BY_VALUE | 
|  | #endif | 
|  | ) | 
|  | return (IMP)(void*)__objc_block_forward; | 
|  | else if (t && (*t == 'f' || *t == 'd')) | 
|  | return (IMP)(void*)__objc_double_forward; | 
|  | else | 
|  | return (IMP)(void*)__objc_word_forward; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Selectors for +resolveClassMethod: and +resolveInstanceMethod:. | 
|  | These are set up at startup.  */ | 
|  | static SEL selector_resolveClassMethod = NULL; | 
|  | static SEL selector_resolveInstanceMethod = NULL; | 
|  |  | 
|  | /* Internal routines use to resolve a class method using | 
|  | +resolveClassMethod:.  'class' is always a non-Nil class (*not* a | 
|  | meta-class), and 'sel' is the selector that we are trying to | 
|  | resolve.  This must be called when class is not Nil, and the | 
|  | dispatch table for class methods has already been installed. | 
|  |  | 
|  | This routine tries to call +resolveClassMethod: to give an | 
|  | opportunity to resolve the method.  If +resolveClassMethod: returns | 
|  | YES, it tries looking up the method again, and if found, it returns | 
|  | it.  Else, it returns NULL.  */ | 
|  | static inline | 
|  | IMP | 
|  | __objc_resolve_class_method (Class class, SEL sel) | 
|  | { | 
|  | /* We need to lookup +resolveClassMethod:.  */ | 
|  | BOOL (*resolveMethodIMP) (id, SEL, SEL); | 
|  |  | 
|  | /* The dispatch table for class methods is already installed and we | 
|  | don't want any forwarding to happen when looking up this method, | 
|  | so we just look it up directly.  Note that if 'sel' is precisely | 
|  | +resolveClassMethod:, this would look it up yet again and find | 
|  | nothing.  That's no problem and there's no recursion.  */ | 
|  | resolveMethodIMP = (BOOL (*) (id, SEL, SEL))sarray_get_safe | 
|  | (class->class_pointer->dtable, (size_t) selector_resolveClassMethod->sel_id); | 
|  |  | 
|  | if (resolveMethodIMP && resolveMethodIMP ((id)class, selector_resolveClassMethod, sel)) | 
|  | { | 
|  | /* +resolveClassMethod: returned YES.  Look the method up again. | 
|  | We already know the dtable is installed.  */ | 
|  |  | 
|  | /* TODO: There is the case where +resolveClassMethod: is buggy | 
|  | and returned YES without actually adding the method.  We | 
|  | could maybe print an error message.  */ | 
|  | return sarray_get_safe (class->class_pointer->dtable, (size_t) sel->sel_id); | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* Internal routines use to resolve a instance method using | 
|  | +resolveInstanceMethod:.  'class' is always a non-Nil class, and | 
|  | 'sel' is the selector that we are trying to resolve.  This must be | 
|  | called when class is not Nil, and the dispatch table for instance | 
|  | methods has already been installed. | 
|  |  | 
|  | This routine tries to call +resolveInstanceMethod: to give an | 
|  | opportunity to resolve the method.  If +resolveInstanceMethod: | 
|  | returns YES, it tries looking up the method again, and if found, it | 
|  | returns it.  Else, it returns NULL.  */ | 
|  | static inline | 
|  | IMP | 
|  | __objc_resolve_instance_method (Class class, SEL sel) | 
|  | { | 
|  | /* We need to lookup +resolveInstanceMethod:.  */ | 
|  | BOOL (*resolveMethodIMP) (id, SEL, SEL); | 
|  |  | 
|  | /* The dispatch table for class methods may not be already installed | 
|  | so we have to install it if needed.  */ | 
|  | resolveMethodIMP = sarray_get_safe (class->class_pointer->dtable, | 
|  | (size_t) selector_resolveInstanceMethod->sel_id); | 
|  | if (resolveMethodIMP == 0) | 
|  | { | 
|  | /* Try again after installing the dtable.  */ | 
|  | if (class->class_pointer->dtable == __objc_uninstalled_dtable) | 
|  | { | 
|  | objc_mutex_lock (__objc_runtime_mutex); | 
|  | if (class->class_pointer->dtable == __objc_uninstalled_dtable) | 
|  | __objc_install_dtable_for_class (class->class_pointer); | 
|  | objc_mutex_unlock (__objc_runtime_mutex); | 
|  | } | 
|  | resolveMethodIMP = sarray_get_safe (class->class_pointer->dtable, | 
|  | (size_t) selector_resolveInstanceMethod->sel_id); | 
|  | } | 
|  |  | 
|  | if (resolveMethodIMP && resolveMethodIMP ((id)class, selector_resolveInstanceMethod, sel)) | 
|  | { | 
|  | /* +resolveInstanceMethod: returned YES.  Look the method up | 
|  | again.  We already know the dtable is installed.  */ | 
|  |  | 
|  | /* TODO: There is the case where +resolveInstanceMethod: is | 
|  | buggy and returned YES without actually adding the method. | 
|  | We could maybe print an error message.  */ | 
|  | return sarray_get_safe (class->dtable, (size_t) sel->sel_id); | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* Given a CLASS and selector, return the implementation corresponding | 
|  | to the method of the selector. | 
|  |  | 
|  | If CLASS is a class, the instance method is returned. | 
|  | If CLASS is a meta class, the class method is returned. | 
|  |  | 
|  | Since this requires the dispatch table to be installed, this function | 
|  | will implicitly invoke +initialize for CLASS if it hasn't been | 
|  | invoked yet.  This also insures that +initialize has been invoked | 
|  | when the returned implementation is called directly. | 
|  |  | 
|  | The forwarding hooks require the receiver as an argument (if they are to | 
|  | perform dynamic lookup in proxy objects etc), so this function has a | 
|  | receiver argument to be used with those hooks.  */ | 
|  | static inline | 
|  | IMP | 
|  | get_implementation (id receiver, Class class, SEL sel) | 
|  | { | 
|  | void *res; | 
|  |  | 
|  | if (class->dtable == __objc_uninstalled_dtable) | 
|  | { | 
|  | /* The dispatch table needs to be installed.  */ | 
|  | objc_mutex_lock (__objc_runtime_mutex); | 
|  |  | 
|  | /* Double-checked locking pattern: Check | 
|  | __objc_uninstalled_dtable again in case another thread | 
|  | installed the dtable while we were waiting for the lock to be | 
|  | released.  */ | 
|  | if (class->dtable == __objc_uninstalled_dtable) | 
|  | __objc_install_dtable_for_class (class); | 
|  |  | 
|  | /* If the dispatch table is not yet installed, we are still in | 
|  | the process of executing +initialize.  But the implementation | 
|  | pointer should be available in the prepared ispatch table if | 
|  | it exists at all.  */ | 
|  | if (class->dtable == __objc_uninstalled_dtable) | 
|  | { | 
|  | assert (__objc_prepared_dtable_for_class (class) != 0); | 
|  | res = __objc_get_prepared_imp (class, sel); | 
|  | } | 
|  | else | 
|  | res = 0; | 
|  |  | 
|  | objc_mutex_unlock (__objc_runtime_mutex); | 
|  | /* Call ourselves with the installed dispatch table and get the | 
|  | real method.  */ | 
|  | if (!res) | 
|  | res = get_implementation (receiver, class, sel); | 
|  | } | 
|  | else | 
|  | { | 
|  | /* The dispatch table has been installed.  */ | 
|  | res = sarray_get_safe (class->dtable, (size_t) sel->sel_id); | 
|  | if (res == 0) | 
|  | { | 
|  | /* The dispatch table has been installed, and the method is | 
|  | not in the dispatch table.  So the method just doesn't | 
|  | exist for the class.  */ | 
|  |  | 
|  | /* Try going through the +resolveClassMethod: or | 
|  | +resolveInstanceMethod: process.  */ | 
|  | if (CLS_ISMETA (class)) | 
|  | { | 
|  | /* We have the meta class, but we need to invoke the | 
|  | +resolveClassMethod: method on the class.  So, we | 
|  | need to obtain the class from the meta class, which | 
|  | we do using the fact that both the class and the | 
|  | meta-class have the same name.  */ | 
|  | Class realClass = objc_lookUpClass (class->name); | 
|  | if (realClass) | 
|  | res = __objc_resolve_class_method (realClass, sel); | 
|  | } | 
|  | else | 
|  | res = __objc_resolve_instance_method (class, sel); | 
|  |  | 
|  | if (res == 0) | 
|  | res = __objc_get_forward_imp (receiver, sel); | 
|  | } | 
|  | } | 
|  | return res; | 
|  | } | 
|  |  | 
|  | /* Make sure this inline function is exported regardless of GNU89 or C99 | 
|  | inlining semantics as it is part of the libobjc ABI.  */ | 
|  | extern IMP get_imp (Class, SEL); | 
|  |  | 
|  | inline | 
|  | IMP | 
|  | get_imp (Class class, SEL sel) | 
|  | { | 
|  | /* In a vanilla implementation we would first check if the dispatch | 
|  | table is installed.  Here instead, to get more speed in the | 
|  | standard case (that the dispatch table is installed) we first try | 
|  | to get the imp using brute force.  Only if that fails, we do what | 
|  | we should have been doing from the very beginning, that is, check | 
|  | if the dispatch table needs to be installed, install it if it's | 
|  | not installed, and retrieve the imp from the table if it's | 
|  | installed.  */ | 
|  | void *res = sarray_get_safe (class->dtable, (size_t) sel->sel_id); | 
|  | if (res == 0) | 
|  | { | 
|  | res = get_implementation(nil, class, sel); | 
|  | } | 
|  | return res; | 
|  | } | 
|  |  | 
|  | /* The new name of get_imp().  */ | 
|  | IMP | 
|  | class_getMethodImplementation (Class class_, SEL selector) | 
|  | { | 
|  | if (class_ == Nil  ||  selector == NULL) | 
|  | return NULL; | 
|  |  | 
|  | /* get_imp is inlined, so we're good.  */ | 
|  | return get_imp (class_, selector); | 
|  | } | 
|  |  | 
|  | /* Given a method, return its implementation.  This has been replaced | 
|  | by method_getImplementation() in the modern API.  */ | 
|  | IMP | 
|  | method_get_imp (struct objc_method * method) | 
|  | { | 
|  | return (method != (struct objc_method *)0) ? method->method_imp : (IMP)0; | 
|  | } | 
|  |  | 
|  | /* Query if an object can respond to a selector, returns YES if the | 
|  | object implements the selector otherwise NO.  Does not check if the | 
|  | method can be forwarded.  Since this requires the dispatch table to | 
|  | installed, this function will implicitly invoke +initialize for the | 
|  | class of OBJECT if it hasn't been invoked yet.  */ | 
|  | inline | 
|  | BOOL | 
|  | __objc_responds_to (id object, SEL sel) | 
|  | { | 
|  | void *res; | 
|  | struct sarray *dtable; | 
|  |  | 
|  | /* Install dispatch table if need be */ | 
|  | dtable = object->class_pointer->dtable; | 
|  | if (dtable == __objc_uninstalled_dtable) | 
|  | { | 
|  | objc_mutex_lock (__objc_runtime_mutex); | 
|  | if (object->class_pointer->dtable == __objc_uninstalled_dtable) | 
|  | __objc_install_dtable_for_class (object->class_pointer); | 
|  |  | 
|  | /* If the dispatch table is not yet installed, we are still in | 
|  | the process of executing +initialize.  Yet the dispatch table | 
|  | should be available.  */ | 
|  | if (object->class_pointer->dtable == __objc_uninstalled_dtable) | 
|  | { | 
|  | dtable = __objc_prepared_dtable_for_class (object->class_pointer); | 
|  | assert (dtable); | 
|  | } | 
|  | else | 
|  | dtable = object->class_pointer->dtable; | 
|  |  | 
|  | objc_mutex_unlock (__objc_runtime_mutex); | 
|  | } | 
|  |  | 
|  | /* Get the method from the dispatch table.  */ | 
|  | res = sarray_get_safe (dtable, (size_t) sel->sel_id); | 
|  | return (res != 0) ? YES : NO; | 
|  | } | 
|  |  | 
|  | BOOL | 
|  | class_respondsToSelector (Class class_, SEL selector) | 
|  | { | 
|  | struct sarray *dtable; | 
|  | void *res; | 
|  |  | 
|  | if (class_ == Nil  ||  selector == NULL) | 
|  | return NO; | 
|  |  | 
|  | /* Install dispatch table if need be.  */ | 
|  | dtable = class_->dtable; | 
|  | if (dtable == __objc_uninstalled_dtable) | 
|  | { | 
|  | objc_mutex_lock (__objc_runtime_mutex); | 
|  | if (class_->dtable == __objc_uninstalled_dtable) | 
|  | __objc_install_dtable_for_class (class_); | 
|  |  | 
|  | /* If the dispatch table is not yet installed, | 
|  | we are still in the process of executing +initialize. | 
|  | Yet the dispatch table should be available.  */ | 
|  | if (class_->dtable == __objc_uninstalled_dtable) | 
|  | { | 
|  | dtable = __objc_prepared_dtable_for_class (class_); | 
|  | assert (dtable); | 
|  | } | 
|  | else | 
|  | dtable = class_->dtable; | 
|  |  | 
|  | objc_mutex_unlock (__objc_runtime_mutex); | 
|  | } | 
|  |  | 
|  | /* Get the method from the dispatch table.  */ | 
|  | res = sarray_get_safe (dtable, (size_t) selector->sel_id); | 
|  | return (res != 0) ? YES : NO; | 
|  | } | 
|  |  | 
|  | /* This is the lookup function.  All entries in the table are either a | 
|  | valid method *or* zero.  If zero then either the dispatch table | 
|  | needs to be installed or it doesn't exist and forwarding is | 
|  | attempted.  */ | 
|  | IMP | 
|  | objc_msg_lookup (id receiver, SEL op) | 
|  | { | 
|  | IMP result; | 
|  | if (receiver) | 
|  | { | 
|  | /* First try a quick lookup assuming the dispatch table exists.  */ | 
|  | result = sarray_get_safe (receiver->class_pointer->dtable, | 
|  | (sidx)op->sel_id); | 
|  | if (result == 0) | 
|  | { | 
|  | /* Not found ... call get_implementation () to install the | 
|  | dispatch table and call +initialize as required, | 
|  | providing the method implementation or a forwarding | 
|  | function.  */ | 
|  | result = get_implementation (receiver, receiver->class_pointer, op); | 
|  | } | 
|  | return result; | 
|  | } | 
|  | else | 
|  | return (IMP)nil_method; | 
|  | } | 
|  |  | 
|  | IMP | 
|  | objc_msg_lookup_super (struct objc_super *super, SEL sel) | 
|  | { | 
|  | if (super->self) | 
|  | return get_imp (super->super_class, sel); | 
|  | else | 
|  | return (IMP)nil_method; | 
|  | } | 
|  |  | 
|  | void | 
|  | __objc_init_dispatch_tables () | 
|  | { | 
|  | __objc_uninstalled_dtable = sarray_new (200, 0); | 
|  |  | 
|  | /* TODO: It would be cool to register typed selectors here.  */ | 
|  | selector_resolveClassMethod = sel_registerName ("resolveClassMethod:"); | 
|  | selector_resolveInstanceMethod = sel_registerName ("resolveInstanceMethod:"); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Install dummy table for class which causes the first message to | 
|  | that class (or instances hereof) to be initialized properly.  */ | 
|  | void | 
|  | __objc_install_premature_dtable (Class class) | 
|  | { | 
|  | assert (__objc_uninstalled_dtable); | 
|  | class->dtable = __objc_uninstalled_dtable; | 
|  | } | 
|  |  | 
|  | /* Send +initialize to class if not already done.  */ | 
|  | static void | 
|  | __objc_send_initialize (Class class) | 
|  | { | 
|  | /* This *must* be a class object.  */ | 
|  | assert (CLS_ISCLASS (class)); | 
|  | assert (! CLS_ISMETA (class)); | 
|  |  | 
|  | /* class_add_method_list/__objc_update_dispatch_table_for_class may | 
|  | have reset the dispatch table.  The canonical way to insure that | 
|  | we send +initialize just once, is this flag.  */ | 
|  | if (! CLS_ISINITIALIZED (class)) | 
|  | { | 
|  | DEBUG_PRINTF ("+initialize: need to initialize class '%s'\n", class->name); | 
|  | CLS_SETINITIALIZED (class); | 
|  | CLS_SETINITIALIZED (class->class_pointer); | 
|  |  | 
|  | /* Create the garbage collector type memory description.  */ | 
|  | __objc_generate_gc_type_description (class); | 
|  |  | 
|  | if (class->super_class) | 
|  | __objc_send_initialize (class->super_class); | 
|  |  | 
|  | { | 
|  | SEL op = sel_registerName ("initialize"); | 
|  | struct objc_method *method = search_for_method_in_hierarchy (class->class_pointer, | 
|  | op); | 
|  |  | 
|  | if (method) | 
|  | { | 
|  | DEBUG_PRINTF (" begin of [%s +initialize]\n", class->name); | 
|  | (*method->method_imp) ((id)class, op); | 
|  | DEBUG_PRINTF (" end of [%s +initialize]\n", class->name); | 
|  | } | 
|  | #ifdef DEBUG | 
|  | else | 
|  | { | 
|  | DEBUG_PRINTF (" class '%s' has no +initialize method\n", class->name); | 
|  | } | 
|  | #endif | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Walk on the methods list of class and install the methods in the | 
|  | reverse order of the lists.  Since methods added by categories are | 
|  | before the methods of class in the methods list, this allows | 
|  | categories to substitute methods declared in class.  However if | 
|  | more than one category replaces the same method nothing is | 
|  | guaranteed about what method will be used.  Assumes that | 
|  | __objc_runtime_mutex is locked down.  */ | 
|  | static void | 
|  | __objc_install_methods_in_dtable (struct sarray *dtable, struct objc_method_list * method_list) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | if (! method_list) | 
|  | return; | 
|  |  | 
|  | if (method_list->method_next) | 
|  | __objc_install_methods_in_dtable (dtable, method_list->method_next); | 
|  |  | 
|  | for (i = 0; i < method_list->method_count; i++) | 
|  | { | 
|  | struct objc_method * method = &(method_list->method_list[i]); | 
|  | sarray_at_put_safe (dtable, | 
|  | (sidx) method->method_name->sel_id, | 
|  | method->method_imp); | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | __objc_update_dispatch_table_for_class (Class class) | 
|  | { | 
|  | Class next; | 
|  | struct sarray *arr; | 
|  |  | 
|  | DEBUG_PRINTF (" _objc_update_dtable_for_class (%s)\n", class->name); | 
|  |  | 
|  | objc_mutex_lock (__objc_runtime_mutex); | 
|  |  | 
|  | /* Not yet installed -- skip it unless in +initialize.  */ | 
|  | if (class->dtable == __objc_uninstalled_dtable) | 
|  | { | 
|  | if (__objc_prepared_dtable_for_class (class)) | 
|  | { | 
|  | /* There is a prepared table so we must be initialising this | 
|  | class ... we must re-do the table preparation.  */ | 
|  | __objc_prepare_dtable_for_class (class); | 
|  | } | 
|  | objc_mutex_unlock (__objc_runtime_mutex); | 
|  | return; | 
|  | } | 
|  |  | 
|  | arr = class->dtable; | 
|  | __objc_install_premature_dtable (class); /* someone might require it... */ | 
|  | sarray_free (arr);			   /* release memory */ | 
|  |  | 
|  | /* Could have been lazy...  */ | 
|  | __objc_install_dtable_for_class (class); | 
|  |  | 
|  | if (class->subclass_list)	/* Traverse subclasses.  */ | 
|  | for (next = class->subclass_list; next; next = next->sibling_class) | 
|  | __objc_update_dispatch_table_for_class (next); | 
|  |  | 
|  | objc_mutex_unlock (__objc_runtime_mutex); | 
|  | } | 
|  |  | 
|  | /* This function adds a method list to a class.  This function is | 
|  | typically called by another function specific to the run-time.  As | 
|  | such this function does not worry about thread safe issues. | 
|  |  | 
|  | This one is only called for categories. Class objects have their | 
|  | methods installed right away, and their selectors are made into | 
|  | SEL's by the function __objc_register_selectors_from_class.  */ | 
|  | void | 
|  | class_add_method_list (Class class, struct objc_method_list * list) | 
|  | { | 
|  | /* Passing of a linked list is not allowed.  Do multiple calls.  */ | 
|  | assert (! list->method_next); | 
|  |  | 
|  | __objc_register_selectors_from_list(list); | 
|  |  | 
|  | /* Add the methods to the class's method list.  */ | 
|  | list->method_next = class->methods; | 
|  | class->methods = list; | 
|  |  | 
|  | /* Update the dispatch table of class.  */ | 
|  | __objc_update_dispatch_table_for_class (class); | 
|  | } | 
|  |  | 
|  | struct objc_method * | 
|  | class_getInstanceMethod (Class class_, SEL selector) | 
|  | { | 
|  | struct objc_method *m; | 
|  |  | 
|  | if (class_ == Nil  ||  selector == NULL) | 
|  | return NULL; | 
|  |  | 
|  | m = search_for_method_in_hierarchy (class_, selector); | 
|  | if (m) | 
|  | return m; | 
|  |  | 
|  | /* Try going through +resolveInstanceMethod:, and do the search | 
|  | again if successful.  */ | 
|  | if (__objc_resolve_instance_method (class_, selector)) | 
|  | return search_for_method_in_hierarchy (class_, selector); | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | struct objc_method * | 
|  | class_getClassMethod (Class class_, SEL selector) | 
|  | { | 
|  | struct objc_method *m; | 
|  |  | 
|  | if (class_ == Nil  ||  selector == NULL) | 
|  | return NULL; | 
|  |  | 
|  | m = search_for_method_in_hierarchy (class_->class_pointer, | 
|  | selector); | 
|  | if (m) | 
|  | return m; | 
|  |  | 
|  | /* Try going through +resolveClassMethod:, and do the search again | 
|  | if successful.  */ | 
|  | if (__objc_resolve_class_method (class_, selector)) | 
|  | return search_for_method_in_hierarchy (class_->class_pointer, | 
|  | selector); | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | BOOL | 
|  | class_addMethod (Class class_, SEL selector, IMP implementation, | 
|  | const char *method_types) | 
|  | { | 
|  | struct objc_method_list *method_list; | 
|  | struct objc_method *method; | 
|  | const char *method_name; | 
|  |  | 
|  | if (class_ == Nil  ||  selector == NULL  ||  implementation == NULL | 
|  | || method_types == NULL  || (strcmp (method_types, "") == 0)) | 
|  | return NO; | 
|  |  | 
|  | method_name = sel_getName (selector); | 
|  | if (method_name == NULL) | 
|  | return NO; | 
|  |  | 
|  | /* If the method already exists in the class, return NO.  It is fine | 
|  | if the method already exists in the superclass; in that case, we | 
|  | are overriding it.  */ | 
|  | if (CLS_IS_IN_CONSTRUCTION (class_)) | 
|  | { | 
|  | /* The class only contains a list of methods; they have not been | 
|  | registered yet, ie, the method_name of each of them is still | 
|  | a string, not a selector.  Iterate manually over them to | 
|  | check if we have already added the method.  */ | 
|  | struct objc_method_list * method_list = class_->methods; | 
|  | while (method_list) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | /* Search the method list.  */ | 
|  | for (i = 0; i < method_list->method_count; ++i) | 
|  | { | 
|  | struct objc_method * method = &method_list->method_list[i]; | 
|  |  | 
|  | if (method->method_name | 
|  | && strcmp ((char *)method->method_name, method_name) == 0) | 
|  | return NO; | 
|  | } | 
|  |  | 
|  | /* The method wasn't found.  Follow the link to the next list of | 
|  | methods.  */ | 
|  | method_list = method_list->method_next; | 
|  | } | 
|  | /* The method wasn't found.  It's a new one.  Go ahead and add | 
|  | it.  */ | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Do the standard lookup.  This assumes the selectors are | 
|  | mapped.  */ | 
|  | if (search_for_method_in_list (class_->methods, selector)) | 
|  | return NO; | 
|  | } | 
|  |  | 
|  | method_list = (struct objc_method_list *)objc_calloc (1, sizeof (struct objc_method_list)); | 
|  | method_list->method_count = 1; | 
|  |  | 
|  | method = &(method_list->method_list[0]); | 
|  | method->method_name = objc_malloc (strlen (method_name) + 1); | 
|  | strcpy ((char *)method->method_name, method_name); | 
|  |  | 
|  | method->method_types = objc_malloc (strlen (method_types) + 1); | 
|  | strcpy ((char *)method->method_types, method_types); | 
|  |  | 
|  | method->method_imp = implementation; | 
|  |  | 
|  | if (CLS_IS_IN_CONSTRUCTION (class_)) | 
|  | { | 
|  | /* We only need to add the method to the list.  It will be | 
|  | registered with the runtime when the class pair is registered | 
|  | (if ever).  */ | 
|  | method_list->method_next = class_->methods; | 
|  | class_->methods = method_list; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Add the method to a live class.  */ | 
|  | objc_mutex_lock (__objc_runtime_mutex); | 
|  | class_add_method_list (class_, method_list); | 
|  | objc_mutex_unlock (__objc_runtime_mutex); | 
|  | } | 
|  |  | 
|  | return YES; | 
|  | } | 
|  |  | 
|  | IMP | 
|  | class_replaceMethod (Class class_, SEL selector, IMP implementation, | 
|  | const char *method_types) | 
|  | { | 
|  | struct objc_method * method; | 
|  |  | 
|  | if (class_ == Nil  ||  selector == NULL  ||  implementation == NULL | 
|  | || method_types == NULL) | 
|  | return NULL; | 
|  |  | 
|  | method = search_for_method_in_hierarchy (class_, selector); | 
|  |  | 
|  | if (method) | 
|  | { | 
|  | return method_setImplementation (method, implementation); | 
|  | } | 
|  | else | 
|  | { | 
|  | class_addMethod (class_, selector, implementation, method_types); | 
|  | return NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Search for a method starting from the current class up its | 
|  | hierarchy.  Return a pointer to the method's method structure if | 
|  | found.  NULL otherwise.  */ | 
|  | static struct objc_method * | 
|  | search_for_method_in_hierarchy (Class cls, SEL sel) | 
|  | { | 
|  | struct objc_method * method = NULL; | 
|  | Class class; | 
|  |  | 
|  | if (! sel_is_mapped (sel)) | 
|  | return NULL; | 
|  |  | 
|  | /* Scan the method list of the class.  If the method isn't found in | 
|  | the list then step to its super class.  */ | 
|  | for (class = cls; ((! method) && class); class = class->super_class) | 
|  | method = search_for_method_in_list (class->methods, sel); | 
|  |  | 
|  | return method; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | /* Given a linked list of method and a method's name.  Search for the | 
|  | named method's method structure.  Return a pointer to the method's | 
|  | method structure if found.  NULL otherwise.  */ | 
|  | struct objc_method * | 
|  | search_for_method_in_list (struct objc_method_list * list, SEL op) | 
|  | { | 
|  | struct objc_method_list * method_list = list; | 
|  |  | 
|  | if (! sel_is_mapped (op)) | 
|  | return NULL; | 
|  |  | 
|  | /* If not found then we'll search the list.  */ | 
|  | while (method_list) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | /* Search the method list.  */ | 
|  | for (i = 0; i < method_list->method_count; ++i) | 
|  | { | 
|  | struct objc_method * method = &method_list->method_list[i]; | 
|  |  | 
|  | if (method->method_name) | 
|  | if (method->method_name->sel_id == op->sel_id) | 
|  | return method; | 
|  | } | 
|  |  | 
|  | /* The method wasn't found.  Follow the link to the next list of | 
|  | methods.  */ | 
|  | method_list = method_list->method_next; | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | typedef void * retval_t; | 
|  | typedef void * arglist_t; | 
|  |  | 
|  | static retval_t __objc_forward (id object, SEL sel, arglist_t args); | 
|  |  | 
|  | /* Forwarding pointers/integers through the normal registers.  */ | 
|  | static id | 
|  | __objc_word_forward (id rcv, SEL op, ...) | 
|  | { | 
|  | void *args, *res; | 
|  |  | 
|  | args = __builtin_apply_args (); | 
|  | res = __objc_forward (rcv, op, args); | 
|  | if (res) | 
|  | __builtin_return (res); | 
|  | else | 
|  | return res; | 
|  | } | 
|  |  | 
|  | /* Specific routine for forwarding floats/double because of | 
|  | architectural differences on some processors.  i386s for example | 
|  | which uses a floating point stack versus general registers for | 
|  | floating point numbers.  This forward routine makes sure that GCC | 
|  | restores the proper return values.  */ | 
|  | static double | 
|  | __objc_double_forward (id rcv, SEL op, ...) | 
|  | { | 
|  | void *args, *res; | 
|  |  | 
|  | args = __builtin_apply_args (); | 
|  | res = __objc_forward (rcv, op, args); | 
|  | __builtin_return (res); | 
|  | } | 
|  |  | 
|  | #if INVISIBLE_STRUCT_RETURN | 
|  | static __big | 
|  | #else | 
|  | static id | 
|  | #endif | 
|  | __objc_block_forward (id rcv, SEL op, ...) | 
|  | { | 
|  | void *args, *res; | 
|  |  | 
|  | args = __builtin_apply_args (); | 
|  | res = __objc_forward (rcv, op, args); | 
|  | if (res) | 
|  | __builtin_return (res); | 
|  | else | 
|  | #if INVISIBLE_STRUCT_RETURN | 
|  | return (__big) {{0, 0, 0, 0, 0, 0, 0, 0}}; | 
|  | #else | 
|  | return nil; | 
|  | #endif | 
|  | } | 
|  |  | 
|  |  | 
|  | /* This function is called for methods which are not implemented, | 
|  | unless a custom forwarding routine has been installed.  Please note | 
|  | that most serious users of libobjc (eg, GNUstep base) do install | 
|  | their own forwarding routines, and hence this is never actually | 
|  | used.  But, if no custom forwarding routine is installed, this is | 
|  | called when a selector is not recognized.  */ | 
|  | static retval_t | 
|  | __objc_forward (id object, SEL sel, arglist_t args) | 
|  | { | 
|  | IMP imp; | 
|  | static SEL frwd_sel = 0;                      /* !T:SAFE2 */ | 
|  | SEL err_sel; | 
|  |  | 
|  | /* First try if the object understands forward::.  */ | 
|  | if (! frwd_sel) | 
|  | frwd_sel = sel_get_any_uid ("forward::"); | 
|  |  | 
|  | if (__objc_responds_to (object, frwd_sel)) | 
|  | { | 
|  | imp = get_implementation (object, object->class_pointer, frwd_sel); | 
|  | return (*imp) (object, frwd_sel, sel, args); | 
|  | } | 
|  |  | 
|  | /* If the object recognizes the doesNotRecognize: method then we're | 
|  | going to send it.  */ | 
|  | err_sel = sel_get_any_uid ("doesNotRecognize:"); | 
|  | if (__objc_responds_to (object, err_sel)) | 
|  | { | 
|  | imp = get_implementation (object, object->class_pointer, err_sel); | 
|  | return (*imp) (object, err_sel, sel); | 
|  | } | 
|  |  | 
|  | /* The object doesn't recognize the method.  Check for responding to | 
|  | error:.  If it does then sent it.  */ | 
|  | { | 
|  | char msg[256 + strlen ((const char *) sel_getName (sel)) | 
|  | + strlen ((const char *) object->class_pointer->name)]; | 
|  |  | 
|  | sprintf (msg, "(%s) %s does not recognize %s", | 
|  | (CLS_ISMETA (object->class_pointer) | 
|  | ? "class" | 
|  | : "instance" ), | 
|  | object->class_pointer->name, sel_getName (sel)); | 
|  |  | 
|  | /* The object doesn't respond to doesNotRecognize:.  Therefore, a | 
|  | default action is taken.  */ | 
|  | _objc_abort ("%s\n", msg); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | __objc_print_dtable_stats (void) | 
|  | { | 
|  | int total = 0; | 
|  |  | 
|  | objc_mutex_lock (__objc_runtime_mutex); | 
|  |  | 
|  | #ifdef OBJC_SPARSE2 | 
|  | printf ("memory usage: (%s)\n", "2-level sparse arrays"); | 
|  | #else | 
|  | printf ("memory usage: (%s)\n", "3-level sparse arrays"); | 
|  | #endif | 
|  |  | 
|  | printf ("arrays: %d = %ld bytes\n", narrays, | 
|  | (long) ((size_t) narrays * sizeof (struct sarray))); | 
|  | total += narrays * sizeof (struct sarray); | 
|  | printf ("buckets: %d = %ld bytes\n", nbuckets, | 
|  | (long) ((size_t) nbuckets * sizeof (struct sbucket))); | 
|  | total += nbuckets * sizeof (struct sbucket); | 
|  |  | 
|  | printf ("idxtables: %d = %ld bytes\n", | 
|  | idxsize, (long) ((size_t) idxsize * sizeof (void *))); | 
|  | total += idxsize * sizeof (void *); | 
|  | printf ("-----------------------------------\n"); | 
|  | printf ("total: %d bytes\n", total); | 
|  | printf ("===================================\n"); | 
|  |  | 
|  | objc_mutex_unlock (__objc_runtime_mutex); | 
|  | } | 
|  |  | 
|  | static cache_ptr prepared_dtable_table = 0; | 
|  |  | 
|  | /* This function is called by: objc_msg_lookup, get_imp and | 
|  | __objc_responds_to (and the dispatch table installation functions | 
|  | themselves) to install a dispatch table for a class. | 
|  |  | 
|  | If CLS is a class, it installs instance methods. | 
|  | If CLS is a meta class, it installs class methods. | 
|  |  | 
|  | In either case +initialize is invoked for the corresponding class. | 
|  |  | 
|  | The implementation must insure that the dispatch table is not | 
|  | installed until +initialize completes.  Otherwise it opens a | 
|  | potential race since the installation of the dispatch table is used | 
|  | as gate in regular method dispatch and we need to guarantee that | 
|  | +initialize is the first method invoked an that no other thread my | 
|  | dispatch messages to the class before +initialize completes.  */ | 
|  | static void | 
|  | __objc_install_dtable_for_class (Class cls) | 
|  | { | 
|  | /* If the class has not yet had its class links resolved, we must | 
|  | re-compute all class links.  */ | 
|  | if (! CLS_ISRESOLV (cls)) | 
|  | __objc_resolve_class_links (); | 
|  |  | 
|  | /* Make sure the super class has its dispatch table installed or is | 
|  | at least preparing.  We do not need to send initialize for the | 
|  | super class since __objc_send_initialize will insure that.  */ | 
|  | if (cls->super_class | 
|  | && cls->super_class->dtable == __objc_uninstalled_dtable | 
|  | && !__objc_prepared_dtable_for_class (cls->super_class)) | 
|  | { | 
|  | __objc_install_dtable_for_class (cls->super_class); | 
|  | /* The superclass initialisation may have also initialised the | 
|  | current class, in which case there is no more to do.  */ | 
|  | if (cls->dtable != __objc_uninstalled_dtable) | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* We have already been prepared but +initialize hasn't completed. | 
|  | The +initialize implementation is probably sending 'self' | 
|  | messages.  We rely on _objc_get_prepared_imp to retrieve the | 
|  | implementation pointers.  */ | 
|  | if (__objc_prepared_dtable_for_class (cls)) | 
|  | return; | 
|  |  | 
|  | /* We have this function cache the implementation pointers for | 
|  | _objc_get_prepared_imp but the dispatch table won't be initialized | 
|  | until __objc_send_initialize completes.  */ | 
|  | __objc_prepare_dtable_for_class (cls); | 
|  |  | 
|  | /* We may have already invoked +initialize but | 
|  | __objc_update_dispatch_table_for_class invoked by | 
|  | class_add_method_list may have reset dispatch table.  */ | 
|  |  | 
|  | /* Call +initialize.  If we are a real class, we are installing | 
|  | instance methods.  If we are a meta class, we are installing | 
|  | class methods.  The __objc_send_initialize itself will insure | 
|  | that the message is called only once per class.  */ | 
|  | if (CLS_ISCLASS (cls)) | 
|  | __objc_send_initialize (cls); | 
|  | else | 
|  | { | 
|  | /* Retrieve the class from the meta class.  */ | 
|  | Class c = objc_getClass (cls->name); | 
|  | assert (CLS_ISMETA (cls)); | 
|  | assert (c); | 
|  | __objc_send_initialize (c); | 
|  | } | 
|  |  | 
|  | /* We install the dispatch table correctly when +initialize completed.  */ | 
|  | __objc_install_prepared_dtable_for_class (cls); | 
|  | } | 
|  |  | 
|  | /* Builds the dispatch table for the class CLS and stores it in a | 
|  | place where it can be retrieved by __objc_get_prepared_imp until | 
|  | __objc_install_prepared_dtable_for_class installs it into the | 
|  | class.  The dispatch table should not be installed into the class | 
|  | until +initialize has completed.  */ | 
|  | static void | 
|  | __objc_prepare_dtable_for_class (Class cls) | 
|  | { | 
|  | struct sarray *dtable; | 
|  | struct sarray *super_dtable; | 
|  |  | 
|  | /* This table could be initialized in init.c.  We cannot use the | 
|  | class name since the class maintains the instance methods and the | 
|  | meta class maintains the class methods yet both share the | 
|  | same name.  Classes should be unique in any program.  */ | 
|  | if (! prepared_dtable_table) | 
|  | prepared_dtable_table | 
|  | = objc_hash_new (32, | 
|  | (hash_func_type) objc_hash_ptr, | 
|  | (compare_func_type) objc_compare_ptrs); | 
|  |  | 
|  | /* If the class has not yet had its class links resolved, we must | 
|  | re-compute all class links.  */ | 
|  | if (! CLS_ISRESOLV (cls)) | 
|  | __objc_resolve_class_links (); | 
|  |  | 
|  | assert (cls); | 
|  | assert (cls->dtable == __objc_uninstalled_dtable); | 
|  |  | 
|  | /* If there is already a prepared dtable for this class, we must | 
|  | replace it with a new version (since there must have been methods | 
|  | added to or otherwise modified in the class while executing | 
|  | +initialize, and the table needs to be recomputed.  */ | 
|  | dtable = __objc_prepared_dtable_for_class (cls); | 
|  | if (dtable != 0) | 
|  | { | 
|  | objc_hash_remove (prepared_dtable_table, cls); | 
|  | sarray_free (dtable); | 
|  | } | 
|  |  | 
|  | /* Now prepare the dtable for population.  */ | 
|  | assert (cls != cls->super_class); | 
|  | if (cls->super_class) | 
|  | { | 
|  | /* Inherit the method list from the super class.  Yet the super | 
|  | class may still be initializing in the case when a class | 
|  | cluster sub class initializes its super classes.  */ | 
|  | if (cls->super_class->dtable == __objc_uninstalled_dtable) | 
|  | __objc_install_dtable_for_class (cls->super_class); | 
|  |  | 
|  | super_dtable = cls->super_class->dtable; | 
|  | /* If the dispatch table is not yet installed, we are still in | 
|  | the process of executing +initialize.  Yet the dispatch table | 
|  | should be available.  */ | 
|  | if (super_dtable == __objc_uninstalled_dtable) | 
|  | super_dtable = __objc_prepared_dtable_for_class (cls->super_class); | 
|  |  | 
|  | assert (super_dtable); | 
|  | dtable = sarray_lazy_copy (super_dtable); | 
|  | } | 
|  | else | 
|  | dtable = sarray_new (__objc_selector_max_index, 0); | 
|  |  | 
|  | __objc_install_methods_in_dtable (dtable, cls->methods); | 
|  |  | 
|  | objc_hash_add (&prepared_dtable_table, | 
|  | cls, | 
|  | dtable); | 
|  | } | 
|  |  | 
|  | /* This wrapper only exists to allow an easy replacement of the lookup | 
|  | implementation and it is expected that the compiler will optimize | 
|  | it away.  */ | 
|  | static struct sarray * | 
|  | __objc_prepared_dtable_for_class (Class cls) | 
|  | { | 
|  | struct sarray *dtable = 0; | 
|  | assert (cls); | 
|  | if (prepared_dtable_table) | 
|  | dtable = objc_hash_value_for_key (prepared_dtable_table, cls); | 
|  | /* dtable my be nil, since we call this to check whether we are | 
|  | currently preparing before we start preparing.  */ | 
|  | return dtable; | 
|  | } | 
|  |  | 
|  | /* Helper function for messages sent to CLS or implementation pointers | 
|  | retrieved from CLS during +initialize before the dtable is | 
|  | installed.  When a class implicitly initializes another class which | 
|  | in turn implicitly invokes methods in this class, before the | 
|  | implementation of +initialize of CLS completes, this returns the | 
|  | expected implementation.  Forwarding remains the responsibility of | 
|  | objc_msg_lookup.  This function should only be called under the | 
|  | global lock.  */ | 
|  | static IMP | 
|  | __objc_get_prepared_imp (Class cls,SEL sel) | 
|  | { | 
|  | struct sarray *dtable; | 
|  | IMP imp; | 
|  |  | 
|  | assert (cls); | 
|  | assert (sel); | 
|  | assert (cls->dtable == __objc_uninstalled_dtable); | 
|  | dtable = __objc_prepared_dtable_for_class (cls); | 
|  |  | 
|  | assert (dtable); | 
|  | assert (dtable != __objc_uninstalled_dtable); | 
|  | imp = sarray_get_safe (dtable, (size_t) sel->sel_id); | 
|  |  | 
|  | /* imp may be Nil if the method does not exist and we may fallback | 
|  | to the forwarding implementation later.  */ | 
|  | return imp; | 
|  | } | 
|  |  | 
|  | /* When this function is called +initialize should be completed.  So | 
|  | now we are safe to install the dispatch table for the class so that | 
|  | they become available for other threads that may be waiting in the | 
|  | lock.  */ | 
|  | static void | 
|  | __objc_install_prepared_dtable_for_class (Class cls) | 
|  | { | 
|  | assert (cls); | 
|  | assert (cls->dtable == __objc_uninstalled_dtable); | 
|  | cls->dtable = __objc_prepared_dtable_for_class (cls); | 
|  |  | 
|  | assert (cls->dtable); | 
|  | assert (cls->dtable != __objc_uninstalled_dtable); | 
|  | objc_hash_remove (prepared_dtable_table, cls); | 
|  | } |