/* GNU Objective C Runtime class related functions
- Copyright (C) 1993, 1995, 1996, 1997, 2001, 2002, 2009
+ Copyright (C) 1993, 1995, 1996, 1997, 2001, 2002, 2009, 2010
Free Software Foundation, Inc.
Contributed by Kresten Krab Thorup and Dennis Glatting.
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
<http://www.gnu.org/licenses/>. */
-/*
- The code in this file critically affects class method invocation
+/* The code in this file critically affects class method invocation
speed. This long preamble comment explains why, and the issues
- involved.
-
+ involved.
One of the traditional weaknesses of the GNU Objective-C runtime is
that class method invocations are slow. The reason is that when you
objc_get_class returns the class pointer corresponding to the string
`NSArray'; and because of the lookup, the operation is more
- complicated and slow than a simple instance method invocation.
+ complicated and slow than a simple instance method invocation.
Most high performance Objective-C code (using the GNU Objc runtime)
I had the opportunity to read (or write) work around this problem by
In this case, you always perform a class lookup (the first one), but
then all the [arrayClass new] methods run exactly as fast as an
instance method invocation. It helps if you have many class method
- invocations to the same class.
+ invocations to the same class.
The long-term solution to this problem would be to modify the
compiler to output tables of class pointers corresponding to all the
to perform precisely as fast as instance method invocations, because
no class lookup would be involved. I think the Apple Objective-C
runtime uses this technique. Doing this involves synchronized
- modifications in the runtime and in the compiler.
+ modifications in the runtime and in the compiler.
As a first medicine to the problem, I [NP] have redesigned and
rewritten the way the runtime is performing class lookup. This
doesn't give as much speed as the other (definitive) approach, but
at least a class method invocation now takes approximately 4.5 times
an instance method invocation on my machine (it would take approx 12
- times before the rewriting), which is a lot better.
+ times before the rewriting), which is a lot better.
One of the main reason the new class lookup is so faster is because
I implemented it in a way that can safely run multithreaded without
#include "objc-private/module-abi-8.h" /* For CLS_ISCLASS and similar. */
#include "objc-private/runtime.h" /* the kitchen sink */
#include "objc-private/sarray.h" /* For sarray_put_at_safe. */
+#include "objc-private/selector.h" /* For sarray_put_at_safe. */
#include <string.h> /* For memset */
/* We use a table which maps a class name to the corresponding class
- * pointer. The first part of this file defines this table, and
- * functions to do basic operations on the table. The second part of
- * the file implements some higher level Objective-C functionality for
- * classes by using the functions provided in the first part to manage
- * the table. */
+ pointer. The first part of this file defines this table, and
+ functions to do basic operations on the table. The second part of
+ the file implements some higher level Objective-C functionality for
+ classes by using the functions provided in the first part to manage
+ the table. */
/**
** Class Table Internals
static objc_mutex_t __class_table_lock = NULL;
/* CLASS_TABLE_HASH is how we compute the hash of a class name. It is
- a macro - *not* a function - arguments *are* modified directly.
+ a macro - *not* a function - arguments *are* modified directly.
INDEX should be a variable holding an int;
HASH should be a variable holding an int;
}
-/* Insert a class in the table (used when a new class is registered). */
+/* Insert a class in the table (used when a new class is
+ registered). */
static void
class_table_insert (const char *class_name, Class class_pointer)
{
objc_mutex_unlock (__class_table_lock);
}
-/* Replace a class in the table (used only by poseAs:). */
-static void
-class_table_replace (Class old_class_pointer, Class new_class_pointer)
-{
- int hash;
- class_node_ptr node;
-
- objc_mutex_lock (__class_table_lock);
-
- hash = 0;
- node = class_table_array[hash];
-
- while (hash < CLASS_TABLE_SIZE)
- {
- if (node == NULL)
- {
- hash++;
- if (hash < CLASS_TABLE_SIZE)
- {
- node = class_table_array[hash];
- }
- }
- else
- {
- Class class1 = node->pointer;
-
- if (class1 == old_class_pointer)
- {
- node->pointer = new_class_pointer;
- }
- node = node->next;
- }
- }
-
- objc_mutex_unlock (__class_table_lock);
-}
-
-
/* Get a class from the table. This does not need mutex protection.
Currently, this function is called each time you call a static
method, this is why it must be very fast. */
for (i = 0; i < length; i++)
{
if ((node->name)[i] != class_name[i])
- {
- break;
- }
+ break;
}
if (i == length)
next = class_table_array[enumerator->hash];
}
else
- {
- next = enumerator->node->next;
- }
+ next = enumerator->node->next;
if (next != NULL)
{
{
printf ("%4d:", i + 1);
for (j = 0; j < counter; j++)
- {
- printf ("X");
- }
+ printf ("X");
+
printf ("\n");
counter = 0;
}
}
printf ("%4d:", i + 1);
for (j = 0; j < counter; j++)
- {
- printf ("X");
- }
+ printf ("X");
+
printf ("\n");
}
#endif /* DEBUGGING FUNCTIONS */
should be via the class_table_* functions. */
/* This is a hook which is called by objc_get_class and
- objc_lookup_class if the runtime is not able to find the class.
+ objc_lookup_class if the runtime is not able to find the class.
This may e.g. try to load in the class using dynamic loading.
This hook was a public, global variable in the Traditional GNU
}
/* This function adds a class to the class hash table, and assigns the
- class a number, unless it's already known. */
-void
+ class a number, unless it's already known. Return 'YES' if the
+ class was added. Return 'NO' if the class was already known. */
+BOOL
__objc_add_class_to_hash (Class class)
{
- Class h_class;
+ Class existing_class;
objc_mutex_lock (__objc_runtime_mutex);
assert (CLS_ISCLASS (class));
/* Check to see if the class is already in the hash table. */
- h_class = class_table_get_safe (class->name);
- if (! h_class)
+ existing_class = class_table_get_safe (class->name);
+
+ if (existing_class)
+ {
+ objc_mutex_unlock (__objc_runtime_mutex);
+ return NO;
+ }
+ else
{
- /* The class isn't in the hash table. Add the class and assign a class
- number. */
+ /* The class isn't in the hash table. Add the class and assign
+ a class number. */
static unsigned int class_number = 1;
-
+
CLS_SETNUMBER (class, class_number);
CLS_SETNUMBER (class->class_pointer, class_number);
++class_number;
class_table_insert (class->name, class);
- }
- objc_mutex_unlock (__objc_runtime_mutex);
+ objc_mutex_unlock (__objc_runtime_mutex);
+ return YES;
+ }
}
Class
}
Class
-objc_lookupClass (const char *name)
+objc_lookUpClass (const char *name)
{
if (name == NULL)
return Nil;
if (count < maxNumberOfClassesToReturn)
returnValue[count] = node->pointer;
else
- {
- return count;
- }
+ return count;
}
count++;
node = node->next;
objc_free (class_);
}
-/* Traditional GNU Objective-C Runtime API. */
-/* Get the class object for the class named NAME. If NAME does not
- identify a known class, the hook _objc_lookup_class is called. If
- this fails, nil is returned. */
-Class
-objc_lookup_class (const char *name)
-{
- return objc_getClass (name);
-}
-
/* Traditional GNU Objective-C Runtime API. Important: this method is
called automatically by the compiler while messaging (if using the
traditional ABI), so it is worth keeping it fast; don't make it
return 0;
}
-MetaClass
+/* This is used by the compiler too. */
+Class
objc_get_meta_class (const char *name)
{
return objc_get_class (name)->class_pointer;
}
-/* This function provides a way to enumerate all the classes in the
- executable. Pass *ENUM_STATE == NULL to start the enumeration. The
- function will return 0 when there are no more classes.
- For example:
- id class;
- void *es = NULL;
- while ((class = objc_next_class (&es)))
- ... do something with class;
-*/
+/* This is not used by GCC, but the clang compiler seems to use it
+ when targetting the GNU runtime. That's wrong, but we have it to
+ be compatible. */
Class
-objc_next_class (void **enum_state)
+objc_lookup_class (const char *name)
{
- Class class;
-
- objc_mutex_lock (__objc_runtime_mutex);
-
- /* Make sure the table is there. */
- assert (__class_table_lock);
-
- class = class_table_next ((struct class_table_enumerator **) enum_state);
-
- objc_mutex_unlock (__objc_runtime_mutex);
-
- return class;
+ return objc_getClass (name);
}
/* This is used when the implementation of a method changes. It goes
while (node != NULL)
{
- /* Iterate over all methods in the class. */
- Class class = node->pointer;
- struct objc_method_list * method_list = class->methods;
-
- while (method_list)
+ /* We execute this loop twice: the first time, we iterate
+ over all methods in the class (instance methods), while
+ the second time we iterate over all methods in the meta
+ class (class methods). */
+ Class class = Nil;
+ BOOL done = NO;
+
+ while (done == NO)
{
- int i;
+ struct objc_method_list * method_list;
- for (i = 0; i < method_list->method_count; ++i)
+ if (class == Nil)
+ {
+ /* The first time, we work on the class. */
+ class = node->pointer;
+ }
+ else
{
- struct objc_method *method = &method_list->method_list[i];
+ /* The second time, we work on the meta class. */
+ class = class->class_pointer;
+ done = YES;
+ }
- /* If the method is one of the ones we are looking
- for, update the implementation. */
- if (method == method_a)
- {
- sarray_at_put_safe (class->dtable,
- (sidx) method_a->method_name->sel_id,
- method_a->method_imp);
- }
+ method_list = class->methods;
- if (method == method_b)
+ while (method_list)
+ {
+ int i;
+
+ for (i = 0; i < method_list->method_count; ++i)
{
- if (method_b != NULL)
+ struct objc_method *method = &method_list->method_list[i];
+
+ /* If the method is one of the ones we are
+ looking for, update the implementation. */
+ if (method == method_a)
+ sarray_at_put_safe (class->dtable,
+ (sidx) method_a->method_name->sel_id,
+ method_a->method_imp);
+
+ if (method == method_b)
{
- sarray_at_put_safe (class->dtable,
- (sidx) method_b->method_name->sel_id,
- method_b->method_imp);
+ if (method_b != NULL)
+ sarray_at_put_safe (class->dtable,
+ (sidx) method_b->method_name->sel_id,
+ method_b->method_imp);
}
}
+
+ method_list = method_list->method_next;
}
-
- method_list = method_list->method_next;
}
node = node->next;
}
if (class_ == Nil)
return Nil;
- /* Classes that are in construction are not resolved and can not be
- resolved! */
+ /* Classes that are in construction are not resolved, and still have
+ the class name (instead of a class pointer) in the
+ class_->super_class field. In that case we need to lookup the
+ superclass name to return the superclass. We can not resolve the
+ class until it is registered. */
if (CLS_IS_IN_CONSTRUCTION (class_))
- return Nil;
+ {
+ if (CLS_ISMETA (class_))
+ return object_getClass ((id)objc_lookUpClass ((const char *)(class_->super_class)));
+ else
+ return objc_lookUpClass ((const char *)(class_->super_class));
+ }
/* If the class is not resolved yet, super_class would point to a
string (the name of the super class) as opposed to the actual
return class_->instance_size;
}
-#define CLASSOF(c) ((c)->class_pointer)
-
-Class
-class_pose_as (Class impostor, Class super_class)
-{
- if (! CLS_ISRESOLV (impostor))
- __objc_resolve_class_links ();
-
- /* Preconditions */
- assert (impostor);
- assert (super_class);
- assert (impostor->super_class == super_class);
- assert (CLS_ISCLASS (impostor));
- assert (CLS_ISCLASS (super_class));
- assert (impostor->instance_size == super_class->instance_size);
-
- {
- Class *subclass = &(super_class->subclass_list);
-
- /* Move subclasses of super_class to impostor. */
- while (*subclass)
- {
- Class nextSub = (*subclass)->sibling_class;
-
- if (*subclass != impostor)
- {
- Class sub = *subclass;
-
- /* Classes */
- sub->sibling_class = impostor->subclass_list;
- sub->super_class = impostor;
- impostor->subclass_list = sub;
-
- /* It will happen that SUB is not a class object if it is
- the top of the meta class hierarchy chain (root
- meta-class objects inherit their class object). If
- that is the case... don't mess with the meta-meta
- class. */
- if (CLS_ISCLASS (sub))
- {
- /* Meta classes */
- CLASSOF (sub)->sibling_class =
- CLASSOF (impostor)->subclass_list;
- CLASSOF (sub)->super_class = CLASSOF (impostor);
- CLASSOF (impostor)->subclass_list = CLASSOF (sub);
- }
- }
-
- *subclass = nextSub;
- }
-
- /* Set subclasses of superclass to be impostor only. */
- super_class->subclass_list = impostor;
- CLASSOF (super_class)->subclass_list = CLASSOF (impostor);
-
- /* Set impostor to have no sibling classes. */
- impostor->sibling_class = 0;
- CLASSOF (impostor)->sibling_class = 0;
- }
-
- /* Check relationship of impostor and super_class is kept. */
- assert (impostor->super_class == super_class);
- assert (CLASSOF (impostor)->super_class == CLASSOF (super_class));
-
- /* This is how to update the lookup table. Regardless of what the
- keys of the hashtable is, change all values that are superclass
- into impostor. */
-
- objc_mutex_lock (__objc_runtime_mutex);
-
- class_table_replace (super_class, impostor);
-
- objc_mutex_unlock (__objc_runtime_mutex);
-
- /* Next, we update the dispatch tables... */
- __objc_update_dispatch_table_for_class (CLASSOF (impostor));
- __objc_update_dispatch_table_for_class (impostor);
-
- return impostor;
-}