+/* 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);
+