From cd9fd8f49ed8d1933887d048b692de11a2247e88 Mon Sep 17 00:00:00 2001 From: nicola Date: Fri, 15 Oct 2010 22:27:39 +0000 Subject: In libobjc/: 2010-10-15 Nicola Pero * objc-private/runtime.h (__objc_update_classes_with_methods): New. * class.c (__objc_update_classes_with_methods): New. (objc_getClassList): Do not lock the class lock. * methods.c (method_exchangeImplementations): New. (method_setImplementation): New. * objc/runtime.h (method_setImplementation): New. (method_exchangeImplementations): New. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@165525 138bc75d-0d04-0410-961f-82ee72b054a4 --- libobjc/ChangeLog | 10 +++++++ libobjc/class.c | 61 +++++++++++++++++++++++++++++++++++++++--- libobjc/methods.c | 51 +++++++++++++++++++++++++++++++++++ libobjc/objc-private/runtime.h | 3 +++ libobjc/objc/runtime.h | 21 ++++++++++++++- 5 files changed, 141 insertions(+), 5 deletions(-) diff --git a/libobjc/ChangeLog b/libobjc/ChangeLog index 9ae38383ffe..0561f88d15e 100644 --- a/libobjc/ChangeLog +++ b/libobjc/ChangeLog @@ -1,3 +1,13 @@ +2010-10-15 Nicola Pero + + * objc-private/runtime.h (__objc_update_classes_with_methods): New. + * class.c (__objc_update_classes_with_methods): New. + (objc_getClassList): Do not lock the class lock. + * methods.c (method_exchangeImplementations): New. + (method_setImplementation): New. + * objc/runtime.h (method_setImplementation): New. + (method_exchangeImplementations): New. + 2010-10-15 Nicola Pero * Protocol.m: Include objc/runtime.h and diff --git a/libobjc/class.c b/libobjc/class.c index 4eb86761ee8..8c6f989a8e9 100644 --- a/libobjc/class.c +++ b/libobjc/class.c @@ -93,6 +93,7 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see #include "objc/thr.h" #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 /* For memset */ /* We use a table which maps a class name to the corresponding class @@ -546,8 +547,6 @@ objc_getClassList (Class *returnValue, int maxNumberOfClassesToReturn) /* 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]; @@ -560,7 +559,6 @@ objc_getClassList (Class *returnValue, int maxNumberOfClassesToReturn) returnValue[count] = node->pointer; else { - objc_mutex_unlock (__class_table_lock); return count; } } @@ -569,7 +567,6 @@ objc_getClassList (Class *returnValue, int maxNumberOfClassesToReturn) } } - objc_mutex_unlock (__class_table_lock); return count; } @@ -647,6 +644,62 @@ objc_next_class (void **enum_state) return class; } +/* 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; + + /* Iterate over all classes. */ + for (hash = 0; hash < CLASS_TABLE_SIZE; hash++) + { + class_node_ptr node = class_table_array[hash]; + + 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) + { + 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 can be sure of is that the class_pointer for class objects point to the right meta class objects. */ diff --git a/libobjc/methods.c b/libobjc/methods.c index 65939a6d5cd..b4faee533c2 100644 --- a/libobjc/methods.c +++ b/libobjc/methods.c @@ -124,3 +124,54 @@ class_copyMethodList (Class class_, unsigned int *numberOfReturnedMethods) return returnValue; } + +IMP +method_setImplementation (struct objc_method * method, IMP implementation) +{ + IMP old_implementation; + + if (method == NULL || implementation == NULL) + return NULL; + + /* We lock the runtime mutex so that concurrent calls to change the + same method won't conflict with each other. */ + objc_mutex_lock (__objc_runtime_mutex); + + old_implementation = method->method_imp; + method->method_imp = implementation; + + /* That was easy :-). But now we need to find all classes that use + this method, and update the IMP in the dispatch tables. */ + __objc_update_classes_with_methods (method, NULL); + + objc_mutex_unlock (__objc_runtime_mutex); + + return old_implementation; +} + +void +method_exchangeImplementations (struct objc_method * method_a, struct objc_method * method_b) +{ + IMP old_implementation_a; + IMP old_implementation_b; + + if (method_a == NULL || method_b == NULL) + return; + + /* We lock the runtime mutex so that concurrent calls to exchange + similar methods won't conflict with each other. Each of them + should be atomic. */ + objc_mutex_lock (__objc_runtime_mutex); + + old_implementation_a = method_a->method_imp; + old_implementation_b = method_b->method_imp; + + method_a->method_imp = old_implementation_b; + method_b->method_imp = old_implementation_a; + + /* That was easy :-). But now we need to find all classes that use + these methods, and update the IMP in the dispatch tables. */ + __objc_update_classes_with_methods (method_a, method_b); + + objc_mutex_unlock (__objc_runtime_mutex); +} diff --git a/libobjc/objc-private/runtime.h b/libobjc/objc-private/runtime.h index 6794d181513..ff924becd93 100644 --- a/libobjc/objc-private/runtime.h +++ b/libobjc/objc-private/runtime.h @@ -74,6 +74,9 @@ extern void class_add_method_list(Class, struct objc_method_list *); extern void __objc_register_instance_methods_to_class(Class); extern struct objc_method * search_for_method_in_list(struct objc_method_list * list, SEL op); +extern void +__objc_update_classes_with_methods (struct objc_method *method_a, struct objc_method *method_b); /* class.c */ + /* True when class links has been resolved */ extern BOOL __objc_class_links_resolved; diff --git a/libobjc/objc/runtime.h b/libobjc/objc/runtime.h index 938033148ae..bb241acd5b3 100644 --- a/libobjc/objc/runtime.h +++ b/libobjc/objc/runtime.h @@ -545,7 +545,26 @@ objc_EXPORT void method_getReturnType (Method method, char *returnValue, objc_EXPORT void method_getArgumentType (Method method, unsigned int argumentNumber, char *returnValue, size_t returnValueSize); - +/* Change the implementation of the method. It also searches all + classes for any class implementing the method, and replaces the + existing implementation with the new one. For that to work, + 'method' must be a method returned by class_getInstanceMethod() or + class_getClassMethod() as the matching is done by comparing the + pointers; in that case, only the implementation in the class is + modified. Return the previous implementation that has been + replaced. If method or implementation is NULL, do nothing and + return NULL. */ +objc_EXPORT IMP +method_setImplementation (Method method, IMP implementation); + +/* Swap the implementation of two methods in a single, atomic + operation. This is equivalent to getting the implementation of + each method and then calling method_setImplementation() on the + other one. For this to work, the two methods must have been + returned by class_getInstanceMethod() or class_getClassMethod(). + If 'method_a' or 'method_b' is NULL, do nothing. */ +objc_EXPORT void +method_exchangeImplementations (Method method_a, Method method_b); /** Implementation: the following functions are in protocols.c. */ -- cgit v1.2.3