/* GNU Objective C Runtime class related functions
- Copyright (C) 1993, 1995, 1996, 1997, 2001, 2002, 2009
- Free Software Foundation, Inc.
+ Copyright (C) 1993-2014 Free Software Foundation, Inc.
Contributed by Kresten Krab Thorup and Dennis Glatting.
Lock-free class table code designed and written from scratch by
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/common.h"
#include "objc-private/error.h"
-#include "objc/objc-api.h"
+#include "objc/runtime.h"
#include "objc/thr.h"
-#include "objc-private/runtime.h" /* the kitchen sink */
-#include <string.h> /* For memset */
+#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
*/
Class (*_objc_lookup_class) (const char *name) = 0; /* !T:SAFE */
-/* Temporarily while we still include objc/objc-api.h instead of objc/runtime.h. */
-#ifndef __objc_runtime_INCLUDE_GNU
-typedef Class (*objc_get_unknown_class_handler)(const char *class_name);
-#endif
-
/* The handler currently in use. PS: if both
__obj_get_unknown_class_handler and _objc_lookup_class are defined,
__objc_get_unknown_class_handler is called first. */
}
/* 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)
{
- /* The class isn't in the hash table. Add the class and assign a class
- number. */
+ 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. */
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
if (class)
return class;
-
+
if (__objc_get_unknown_class_handler)
return (*__objc_get_unknown_class_handler) (name);
}
Class
-objc_lookupClass (const char *name)
+objc_lookUpClass (const char *name)
{
if (name == NULL)
return Nil;
/* Iterate over all entries in the table. */
int hash, count = 0;
- objc_mutex_lock (__class_table_lock);
-
for (hash = 0; hash < CLASS_TABLE_SIZE; hash++)
{
class_node_ptr node = class_table_array[hash];
if (count < maxNumberOfClassesToReturn)
returnValue[count] = node->pointer;
else
- {
- objc_mutex_unlock (__class_table_lock);
- return count;
- }
+ return count;
}
count++;
node = node->next;
}
}
- objc_mutex_unlock (__class_table_lock);
return count;
}
-/* 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)
+objc_allocateClassPair (Class super_class, const char *class_name, size_t extraBytes)
{
- return objc_getClass (name);
+ Class new_class;
+ Class new_meta_class;
+
+ if (class_name == NULL)
+ return Nil;
+
+ if (objc_getClass (class_name))
+ return Nil;
+
+ if (super_class)
+ {
+ /* If you want to build a hierarchy of classes, you need to
+ build and register them one at a time. The risk is that you
+ are able to cause confusion by registering a subclass before
+ the superclass or similar. */
+ if (CLS_IS_IN_CONSTRUCTION (super_class))
+ return Nil;
+ }
+
+ /* Technically, we should create the metaclass first, then use
+ class_createInstance() to create the class. That complication
+ would be relevant if we had class variables, but we don't, so we
+ just ignore it and create everything directly and assume all
+ classes have the same size. */
+ new_class = objc_calloc (1, sizeof (struct objc_class) + extraBytes);
+ new_meta_class = objc_calloc (1, sizeof (struct objc_class) + extraBytes);
+
+ /* We create an unresolved class, similar to one generated by the
+ compiler. It will be resolved later when we register it.
+
+ Note how the metaclass details are not that important; when the
+ class is resolved, the ones that matter will be fixed up. */
+ new_class->class_pointer = new_meta_class;
+ new_meta_class->class_pointer = 0;
+
+ if (super_class)
+ {
+ /* Force the name of the superclass in place of the link to the
+ actual superclass, which will be put there when the class is
+ resolved. */
+ const char *super_class_name = class_getName (super_class);
+ new_class->super_class = (void *)super_class_name;
+ new_meta_class->super_class = (void *)super_class_name;
+ }
+ else
+ {
+ new_class->super_class = (void *)0;
+ new_meta_class->super_class = (void *)0;
+ }
+
+ new_class->name = objc_malloc (strlen (class_name) + 1);
+ strcpy ((char*)new_class->name, class_name);
+ new_meta_class->name = new_class->name;
+
+ new_class->version = 0;
+ new_meta_class->version = 0;
+
+ new_class->info = _CLS_CLASS | _CLS_IN_CONSTRUCTION;
+ new_meta_class->info = _CLS_META | _CLS_IN_CONSTRUCTION;
+
+ if (super_class)
+ new_class->instance_size = super_class->instance_size;
+ else
+ new_class->instance_size = 0;
+ new_meta_class->instance_size = sizeof (struct objc_class);
+
+ return new_class;
+}
+
+void
+objc_registerClassPair (Class class_)
+{
+ if (class_ == Nil)
+ return;
+
+ if ((! CLS_ISCLASS (class_)) || (! CLS_IS_IN_CONSTRUCTION (class_)))
+ return;
+
+ if ((! CLS_ISMETA (class_->class_pointer)) || (! CLS_IS_IN_CONSTRUCTION (class_->class_pointer)))
+ return;
+
+ objc_mutex_lock (__objc_runtime_mutex);
+
+ if (objc_getClass (class_->name))
+ {
+ objc_mutex_unlock (__objc_runtime_mutex);
+ return;
+ }
+
+ CLS_SET_NOT_IN_CONSTRUCTION (class_);
+ CLS_SET_NOT_IN_CONSTRUCTION (class_->class_pointer);
+
+ __objc_init_class (class_);
+
+ /* Resolve class links immediately. No point in waiting. */
+ __objc_resolve_class_links ();
+
+ objc_mutex_unlock (__objc_runtime_mutex);
+}
+
+void
+objc_disposeClassPair (Class class_)
+{
+ if (class_ == Nil)
+ return;
+
+ if ((! CLS_ISCLASS (class_)) || (! CLS_IS_IN_CONSTRUCTION (class_)))
+ return;
+
+ if ((! CLS_ISMETA (class_->class_pointer)) || (! CLS_IS_IN_CONSTRUCTION (class_->class_pointer)))
+ return;
+
+ /* Undo any class_addIvar(). */
+ if (class_->ivars)
+ {
+ int i;
+ for (i = 0; i < class_->ivars->ivar_count; i++)
+ {
+ struct objc_ivar *ivar = &(class_->ivars->ivar_list[i]);
+
+ objc_free ((char *)ivar->ivar_name);
+ objc_free ((char *)ivar->ivar_type);
+ }
+
+ objc_free (class_->ivars);
+ }
+
+ /* Undo any class_addMethod(). */
+ if (class_->methods)
+ {
+ struct objc_method_list *list = class_->methods;
+ while (list)
+ {
+ int i;
+ struct objc_method_list *next = list->method_next;
+
+ for (i = 0; i < list->method_count; i++)
+ {
+ struct objc_method *method = &(list->method_list[i]);
+
+ objc_free ((char *)method->method_name);
+ objc_free ((char *)method->method_types);
+ }
+
+ objc_free (list);
+ list = next;
+ }
+ }
+
+ /* Undo any class_addProtocol(). */
+ if (class_->protocols)
+ {
+ struct objc_protocol_list *list = class_->protocols;
+ while (list)
+ {
+ struct objc_protocol_list *next = list->next;
+
+ objc_free (list);
+ list = next;
+ }
+ }
+
+ /* Undo any class_addMethod() on the meta-class. */
+ if (class_->class_pointer->methods)
+ {
+ struct objc_method_list *list = class_->class_pointer->methods;
+ while (list)
+ {
+ int i;
+ struct objc_method_list *next = list->method_next;
+
+ for (i = 0; i < list->method_count; i++)
+ {
+ struct objc_method *method = &(list->method_list[i]);
+
+ objc_free ((char *)method->method_name);
+ objc_free ((char *)method->method_types);
+ }
+
+ objc_free (list);
+ list = next;
+ }
+ }
+
+ /* Undo objc_allocateClassPair(). */
+ objc_free ((char *)(class_->name));
+ objc_free (class_->class_pointer);
+ objc_free (class_);
}
/* 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
just a wrapper around objc_getClass(). */
+/* Note that this is roughly equivalent to objc_getRequiredClass(). */
/* 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, an error message is issued and the system aborts. */
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 targeting 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;
+ return objc_getClass (name);
+}
- objc_mutex_lock (__objc_runtime_mutex);
-
- /* Make sure the table is there. */
- assert (__class_table_lock);
+/* This is used when the implementation of a method changes. It goes
+ through all classes, looking for the ones that have these methods
+ (either method_a or method_b; method_b can be NULL), and reloads
+ the implementation for these. You should call this with the
+ runtime mutex already locked. */
+void
+__objc_update_classes_with_methods (struct objc_method *method_a, struct objc_method *method_b)
+{
+ int hash;
- class = class_table_next ((struct class_table_enumerator **) enum_state);
+ /* Iterate over all classes. */
+ for (hash = 0; hash < CLASS_TABLE_SIZE; hash++)
+ {
+ class_node_ptr node = class_table_array[hash];
+
+ while (node != NULL)
+ {
+ /* 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)
+ {
+ struct objc_method_list * method_list;
- objc_mutex_unlock (__objc_runtime_mutex);
-
- return class;
+ if (class == Nil)
+ {
+ /* The first time, we work on the class. */
+ class = node->pointer;
+ }
+ else
+ {
+ /* The second time, we work on the meta class. */
+ class = class->class_pointer;
+ done = YES;
+ }
+
+ method_list = class->methods;
+
+ while (method_list)
+ {
+ int i;
+
+ for (i = 0; i < method_list->method_count; ++i)
+ {
+ 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)
+ {
+ 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;
+ }
+ }
+ node = node->next;
+ }
+ }
}
/* Resolve super/subclass links for all classes. The only thing we
return class_->name;
}
-#define CLASSOF(c) ((c)->class_pointer)
+BOOL
+class_isMetaClass (Class class_)
+{
+ /* CLS_ISMETA includes the check for Nil class_. */
+ return CLS_ISMETA (class_);
+}
+/* Even inside libobjc it may be worth using class_getSuperclass
+ instead of accessing class_->super_class directly because it
+ resolves the class links if needed. If you access
+ class_->super_class directly, make sure to deal with the situation
+ where the class is not resolved yet! */
Class
-class_pose_as (Class impostor, Class super_class)
+class_getSuperclass (Class class_)
{
- if (! CLS_ISRESOLV (impostor))
- __objc_resolve_class_links ();
+ if (class_ == Nil)
+ return Nil;
- /* 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;
- }
+ /* 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_))
+ {
+ 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
+ super class. In that case, we need to resolve the class links
+ before we can return super_class. */
+ if (! CLS_ISRESOLV (class_))
+ __objc_resolve_class_links ();
- /* Check relationship of impostor and super_class is kept. */
- assert (impostor->super_class == super_class);
- assert (CLASSOF (impostor)->super_class == CLASSOF (super_class));
+ return class_->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. */
+int
+class_getVersion (Class class_)
+{
+ if (class_ == Nil)
+ return 0;
- objc_mutex_lock (__objc_runtime_mutex);
+ return (int)(class_->version);
+}
- class_table_replace (super_class, impostor);
+void
+class_setVersion (Class class_, int version)
+{
+ if (class_ == Nil)
+ return;
- objc_mutex_unlock (__objc_runtime_mutex);
+ class_->version = version;
+}
- /* Next, we update the dispatch tables... */
- __objc_update_dispatch_table_for_class (CLASSOF (impostor));
- __objc_update_dispatch_table_for_class (impostor);
+size_t
+class_getInstanceSize (Class class_)
+{
+ if (class_ == Nil)
+ return 0;
- return impostor;
+ return class_->instance_size;
}
+