/* GNU Objective C Runtime message lookup
- Copyright (C) 1993, 1995, 1996, 1997, 1998,
- 2001, 2002, 2004, 2009, 2010 Free Software Foundation, Inc.
+ Copyright (C) 1993-2021 Free Software Foundation, Inc.
Contributed by Kresten Krab Thorup
This file is part of GCC.
only while debugging the runtime. */
/* #define DEBUG 1 */
-/* FIXME: This file has no business including tm.h. */
/* FIXME: This should be using libffi instead of __builtin_apply
and friends. */
#include "objc-private/error.h"
#include "tconfig.h"
#include "coretypes.h"
-#include "tm.h"
#include "objc/runtime.h"
#include "objc/message.h" /* For objc_msg_lookup(), objc_msg_lookup_super(). */
#include "objc/thr.h"
#include <assert.h> /* For assert */
#include <string.h> /* For strlen */
-/* This is how we hack STRUCT_VALUE to be 1 or 0. */
-#define gen_rtx(args...) 1
-#define gen_rtx_MEM(args...) 1
-#define gen_rtx_REG(args...) 1
-/* Already defined in gcc/coretypes.h. So prevent double definition warning. */
-#undef rtx
-#define rtx int
-
-#if ! defined (STRUCT_VALUE) || STRUCT_VALUE == 0
#define INVISIBLE_STRUCT_RETURN 1
-#else
-#define INVISIBLE_STRUCT_RETURN 0
-#endif
-/* The uninstalled dispatch table. */
+/* 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
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
/* 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. */
+ 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);
- }
+ __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 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;
- }
+ res = 0;
+
objc_mutex_unlock (__objc_runtime_mutex);
- /* Call ourselves with the installed dispatch table and get
- the real method. */
+ /* Call ourselves with the installed dispatch table and get the
+ real method. */
if (!res)
res = get_implementation (receiver, class, sel);
}
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. */
+ /* 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. */
{
/* 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. */
+ 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);
res = __objc_resolve_instance_method (class, sel);
if (res == 0)
- {
- res = __objc_get_forward_imp (receiver, sel);
- }
+ 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)
/* 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. */
+ 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)
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 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);
{
objc_mutex_lock (__objc_runtime_mutex);
if (class_->dtable == __objc_uninstalled_dtable)
- {
- __objc_install_dtable_for_class (class_);
- }
+ __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. */
}
else
dtable = class_->dtable;
+
objc_mutex_unlock (__objc_runtime_mutex);
}
(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 */
+ /* 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;
return (IMP)nil_method;
}
-/* Temporarily defined here until objc_msg_sendv() goes away. */
-char *method_get_first_argument (struct objc_method *,
- arglist_t argframe,
- const char **type);
-char *method_get_next_argument (arglist_t argframe,
- const char **type);
-int method_get_sizeof_arguments (struct objc_method *);
-
-struct objc_method *
-class_get_instance_method (Class class, SEL op);
-
-retval_t
-objc_msg_sendv (id object, SEL op, arglist_t arg_frame)
-{
- struct objc_method *m = class_get_instance_method (object->class_pointer, op);
- const char *type;
- *((id *) method_get_first_argument (m, arg_frame, &type)) = object;
- *((SEL *) method_get_next_argument (arg_frame, &type)) = op;
- return __builtin_apply ((apply_t) m->method_imp,
- arg_frame,
- method_get_sizeof_arguments (m));
-}
-
void
__objc_init_dispatch_tables ()
{
/* TODO: It would be cool to register typed selectors here. */
selector_resolveClassMethod = sel_registerName ("resolveClassMethod:");
- selector_resolveInstanceMethod =sel_registerName ("resolveInstanceMethod:");
+ selector_resolveInstanceMethod = sel_registerName ("resolveInstanceMethod:");
}
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. */
+ /* 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);
{
SEL op = sel_registerName ("initialize");
- IMP imp = 0;
- struct objc_method_list * method_list = class->class_pointer->methods;
-
- while (method_list)
- {
- int i;
- struct objc_method * method;
-
- for (i = 0; i < method_list->method_count; i++)
- {
- method = &(method_list->method_list[i]);
- if (method->method_name
- && method->method_name->sel_id == op->sel_id)
- {
- imp = method->method_imp;
- break;
- }
- }
-
- if (imp)
- break;
-
- method_list = method_list->method_next;
- }
- if (imp)
+ struct objc_method *method = search_for_method_in_hierarchy (class->class_pointer,
+ op);
+
+ if (method)
{
DEBUG_PRINTF (" begin of [%s +initialize]\n", class->name);
- (*imp) ((id) class, op);
+ (*method->method_imp) ((id)class, op);
DEBUG_PRINTF (" end of [%s +initialize]\n", class->name);
}
#ifdef DEBUG
objc_mutex_lock (__objc_runtime_mutex);
- /* not yet installed -- skip it unless in +initialize */
+ /* 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. */
+ class ... we must re-do the table preparation. */
__objc_prepare_dtable_for_class (class);
}
objc_mutex_unlock (__objc_runtime_mutex);
__objc_update_dispatch_table_for_class (class);
}
-struct objc_method *
-class_get_instance_method (Class class, SEL op)
-{
- return search_for_method_in_hierarchy (class, op);
-}
-
-struct objc_method *
-class_get_class_method (MetaClass class, SEL op)
-{
- return search_for_method_in_hierarchy (class, op);
-}
-
struct objc_method *
class_getInstanceMethod (Class class_, SEL selector)
{
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. */
}
-/* This function is installed in the dispatch table for all methods
- which are not implemented. Thus, it is called when a selector is
- not recognized. */
+/* 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)
{
: "instance" ),
object->class_pointer->name, sel_getName (sel));
- /* TODO: support for error: is surely deprecated ? */
- err_sel = sel_get_any_uid ("error:");
- if (__objc_responds_to (object, err_sel))
- {
- imp = get_implementation (object, object->class_pointer, err_sel);
- return (*imp) (object, sel_get_any_uid ("error:"), msg);
- }
-
- /* The object doesn't respond to doesNotRecognize: or error:;
- Therefore, a default action is taken. */
+ /* The object doesn't respond to doesNotRecognize:. Therefore, a
+ default action is taken. */
_objc_abort ("%s\n", msg);
return 0;
objc_mutex_unlock (__objc_runtime_mutex);
}
-/* Returns the uninstalled dispatch table indicator. If a class'
- dispatch table points to __objc_uninstalled_dtable then that means
- it needs its dispatch table to be installed. */
-struct sarray *
-objc_get_uninstalled_dtable (void)
-{
- return __objc_uninstalled_dtable;
-}
-
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.
+/* 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.
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.
- */
+ 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 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.
- */
+ /* 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))
+ && 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. */
+ current class, in which case there is no more to do. */
if (cls->dtable != __objc_uninstalled_dtable)
- {
- return;
- }
+ 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. */
+ 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;
- }
+ return;
- /* We have this function cache the implementation pointers
- for _objc_get_prepared_imp but the dispatch table won't
- be initilized until __objc_send_initialize completes. */
+ /* We have this function cache the implementation pointers for
+ _objc_get_prepared_imp but the dispatch table won't be initilized
+ 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
+ /* 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. */
+ /* 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
{
- /* Retreive the class from the meta class. */
+ /* Retrieve the class from the meta class. */
Class c = objc_getClass (cls->name);
assert (CLS_ISMETA (cls));
assert (c);
__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. */
+/* 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 can not use the class name since
- the class maintains the instance methods and
- the meta class maintains the the class methods yet
- both share the same name.
- Classes should be unique in any program. */
+ /* 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 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 */
+ = 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. */
+ /* 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 (0 != dtable)
+ if (dtable != 0)
{
objc_hash_remove (prepared_dtable_table, cls);
sarray_free (dtable);
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. */
+ /* 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 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);
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. */
+/* 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)
{
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. */
+ /* 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.
- */
+ 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)
{
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. */
+ /* 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.
- */
+/* 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)
{