summaryrefslogtreecommitdiff
path: root/libobjc
diff options
context:
space:
mode:
authornicola <nicola@138bc75d-0d04-0410-961f-82ee72b054a4>2010-12-11 19:43:21 +0000
committernicola <nicola@138bc75d-0d04-0410-961f-82ee72b054a4>2010-12-11 19:43:21 +0000
commit101978acaa779540c14c366687226ade41c471f7 (patch)
treec50ecdee614fbdc518483d8012362f18adfbe922 /libobjc
parent609b9937816df81c4bf275b5d470fa1bc7fb75e5 (diff)
downloadgcc-101978acaa779540c14c366687226ade41c471f7.tar.gz
In libobjc/:
2010-12-11 Nicola Pero <nicola.pero@meta-innovation.com> * sendmsg.c (selector_resolveClassMethod): New. (selector_resolveInstanceMethod): New. (__objc_resolve_class_method): New. (__objc_resolve_instance_method): New. (get_imp): Call __objc_resolve_class_method or __objc_resolve_instance_method at the appropriate time. (objc_msg_lookup): Same. (class_getClassMethod): Same. (class_getInstanceMethod): Same. (__objc_init_dispatch_tables): Initialize selector_resolveClassMethod and selector_resolveInstanceMethod. * objc/runtime.h: Updated documentation of class_getClassMethod, class_getInstanceMethod and class_getMethodImplementation. In gcc/testsuite/: 2010-12-11 Nicola Pero <nicola.pero@meta-innovation.com> * objc.dg/gnu-api-2-resolve-method.m: New. * obj-c++.dg/gnu-api-2-resolve-method.mm: New. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@167712 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libobjc')
-rw-r--r--libobjc/ChangeLog16
-rw-r--r--libobjc/objc/runtime.h18
-rw-r--r--libobjc/sendmsg.c183
3 files changed, 200 insertions, 17 deletions
diff --git a/libobjc/ChangeLog b/libobjc/ChangeLog
index bc522133feb..fa609a58f86 100644
--- a/libobjc/ChangeLog
+++ b/libobjc/ChangeLog
@@ -1,5 +1,21 @@
2010-12-11 Nicola Pero <nicola.pero@meta-innovation.com>
+ * sendmsg.c (selector_resolveClassMethod): New.
+ (selector_resolveInstanceMethod): New.
+ (__objc_resolve_class_method): New.
+ (__objc_resolve_instance_method): New.
+ (get_imp): Call __objc_resolve_class_method or
+ __objc_resolve_instance_method at the appropriate time.
+ (objc_msg_lookup): Same.
+ (class_getClassMethod): Same.
+ (class_getInstanceMethod): Same.
+ (__objc_init_dispatch_tables): Initialize
+ selector_resolveClassMethod and selector_resolveInstanceMethod.
+ * objc/runtime.h: Updated documentation of class_getClassMethod,
+ class_getInstanceMethod and class_getMethodImplementation.
+
+2010-12-11 Nicola Pero <nicola.pero@meta-innovation.com>
+
* objc-private/module-abi-8.h (struct objc_symtab): Updated
description of sel_ref_cnt and refs.
* objc/deprecated/struct_objc_symtab.h (objc_symtab): Same change.
diff --git a/libobjc/objc/runtime.h b/libobjc/objc/runtime.h
index b37eee2125b..f5df691fe52 100644
--- a/libobjc/objc/runtime.h
+++ b/libobjc/objc/runtime.h
@@ -590,13 +590,17 @@ objc_disposeClassPair (Class class_);
/* Return the instance method with selector 'selector' of class
'class_', or NULL if the class (or one of its superclasses) does
not implement the method. Return NULL if class_ is Nil or selector
- is NULL. */
+ is NULL. Calling this function may trigger a call to
+ +resolveInstanceMethod:, but does not return a forwarding
+ function. */
objc_EXPORT Method class_getInstanceMethod (Class class_, SEL selector);
/* Return the class method with selector 'selector' of class 'class_',
or NULL if the class (or one of its superclasses) does not
implement the method. Return NULL if class_ is Nil or selector is
- NULL. */
+ NULL. Calling this function may trigger a call to
+ +resolveClassMethod:, but does not return a forwarding
+ function. */
objc_EXPORT Method class_getClassMethod (Class class_, SEL selector);
/* Return the IMP (pointer to the function implementing a method) for
@@ -607,7 +611,15 @@ objc_EXPORT Method class_getClassMethod (Class class_, SEL selector);
arguments and return type before calling it. To get a class
method, you can pass the meta-class as the class_ argument (ie, use
class_getMethodImplementation (object_getClass (class_),
- selector)). Return NULL if class_ is Nil or selector is NULL. */
+ selector)). Return NULL if class_ is Nil or selector is NULL.
+ This function first looks for an existing method; if it is not
+ found, it calls +resolveClassMethod: or +resolveInstanceMethod:
+ (depending on whether a class or instance method is being looked
+ up) if it is implemented. If the method returns YES, then it tries
+ the look up again (the assumption being that +resolveClassMethod:
+ or resolveInstanceMethod: will add the method using
+ class_addMethod()). If it is still not found, it returns a
+ forwarding function. */
objc_EXPORT IMP class_getMethodImplementation (Class class_, SEL selector);
/* Compatibility Note: the Apple/NeXT runtime has the function
diff --git a/libobjc/sendmsg.c b/libobjc/sendmsg.c
index 7f7024c275a..4748695938d 100644
--- a/libobjc/sendmsg.c
+++ b/libobjc/sendmsg.c
@@ -138,6 +138,102 @@ __objc_get_forward_imp (id rcv, SEL sel)
}
}
+/* 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_dispatch_table_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;
+}
+
+/* Temporary definition until we include objc/runtime.h. */
+objc_EXPORT Class objc_lookupClass (const char *name);
+
/* Given a class and selector, return the selector's implementation. */
inline
IMP
@@ -188,14 +284,35 @@ get_imp (Class class, SEL sel)
{
/* 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. Return the forwarding
- implementation. We don't know the receiver (only its
- class), so we have to pass 'nil' as the first
- argument. Passing the class as first argument is
- wrong because the class is not the receiver; it can
- result in us calling a class method when we want an
- instance method of the same name. */
- res = __objc_get_forward_imp (nil, sel);
+ 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)
+ {
+ /* If that fails, then return the forwarding
+ implementation. We don't know the receiver (only its
+ class), so we have to pass 'nil' as the first
+ argument. Passing the class as first argument is
+ wrong because the class is not the receiver; it can
+ result in us calling a class method when we want an
+ instance method of the same name. */
+ res = __objc_get_forward_imp (nil, sel);
+ }
}
}
}
@@ -304,9 +421,20 @@ objc_msg_lookup (id receiver, SEL op)
(sidx)op->sel_id);
if (result == 0)
{
- /* If the method still just doesn't exist for the
- class, attempt to forward the method. */
- result = __objc_get_forward_imp (receiver, op);
+ /* Try going through the +resolveClassMethod: or
+ +resolveInstanceMethod: process. */
+ if (CLS_ISMETA (receiver->class_pointer))
+ result = __objc_resolve_class_method ((Class)receiver, op);
+ else
+ result = __objc_resolve_instance_method (receiver->class_pointer,
+ op);
+
+ if (result == 0)
+ {
+ /* If the method still just doesn't exist for
+ the class, attempt to forward the method. */
+ result = __objc_get_forward_imp (receiver, op);
+ }
}
}
}
@@ -343,6 +471,10 @@ 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_register_name ("resolveClassMethod:");
+ selector_resolveInstanceMethod =sel_register_name ("resolveInstanceMethod:");
}
/* This function is called by objc_msg_lookup when the
@@ -566,20 +698,43 @@ class_get_class_method (MetaClass class, SEL op)
struct objc_method *
class_getInstanceMethod (Class class_, SEL selector)
{
+ struct objc_method *m;
+
if (class_ == Nil || selector == NULL)
return NULL;
- return search_for_method_in_hierarchy (class_, selector);
+ 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;
- return search_for_method_in_hierarchy (class_->class_pointer,
- selector);
+ 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