diff --git a/Source/behavior.c b/Source/behavior.c index 0ffad9e2f..9c031a124 100644 --- a/Source/behavior.c +++ b/Source/behavior.c @@ -38,6 +38,11 @@ If you add several behaviors to a class, be aware that the order of the additions is significant. + McCallum talking to himself: + "Yipes. Be careful with [super ...] calls. + BEHAVIOR methods running in CLASS will now have a different super class. + No; wrong. See objc-api.h; typedef struct objc_super." + */ #include @@ -56,7 +61,12 @@ static void __objc_class_add_protocols (Class class, struct objc_protocol_list* protos); #endif static BOOL class_is_kind_of(Class self, Class class); -void class_add_methods_if_not_there(Class class, MethodList_t mlist); +static void check_class_methods(Class class); + +void class_add_methods_if_not_there(Class class, Class behavior); + +/* The uninstalled dispatch table, declared in gcc/objc/sendmsg.c. */ +extern struct sarray* __objc_uninstalled_dtable; /* xxx consider using sendmsg.c:__objc_update_dispatch_table_for_class, but, I think it will be slower than the current method. */ @@ -89,12 +99,21 @@ class_add_behavior (Class class, Class behavior) #endif /* Add instance methods */ - class_add_methods_if_not_there(class, - behavior->methods); + if (behavior_debug) + { + fprintf(stderr, "Adding instance methods from %s\n", + behavior->name); + } + class_add_methods_if_not_there(class, behavior); /* Add class methods */ + if (behavior_debug) + { + fprintf(stderr, "Adding class methods from %s\n", + behavior->class_pointer->name); + } class_add_methods_if_not_there(class->class_pointer, - behavior->class_pointer->methods); + behavior->class_pointer); /* Add behavior's superclass, if not already there. */ { @@ -106,15 +125,16 @@ class_add_behavior (Class class, Class behavior) } void -class_add_methods_if_not_there(Class class, MethodList_t mlist) +class_add_methods_if_not_there(Class class, Class behavior) { static SEL initialize_sel = 0; + MethodList_t mlist; if (!initialize_sel) initialize_sel = sel_register_name ("initialize"); /* Add methods to class->dtable and class->methods */ - for ( ; mlist; mlist = mlist->method_next) + for (mlist = behavior->methods; mlist; mlist = mlist->method_next) { int counter; MethodList_t new_list; @@ -125,7 +145,7 @@ class_add_methods_if_not_there(Class class, MethodList_t mlist) all methods will go in here. */ new_list = (MethodList_t) (*objc_malloc) - (sizeof(MethodList) + sizeof(struct objc_method[counter])); + (sizeof(MethodList) + sizeof(struct objc_method[counter+1])); new_list->method_count = 0; while (counter >= 0) @@ -133,7 +153,7 @@ class_add_methods_if_not_there(Class class, MethodList_t mlist) Method_t method = &(mlist->method_list[counter]); if (behavior_debug) - fprintf(stderr, "processing method %s\n", + fprintf(stderr, " processing method [%s]\n", sel_get_name(method->method_name)); if (!search_for_method_in_list(class->methods, method->method_name) @@ -142,19 +162,38 @@ class_add_methods_if_not_there(Class class, MethodList_t mlist) /* As long as the method isn't defined in the CLASS, put the BEHAVIOR method in there. Thus, behavior methods override the superclasses' methods. */ - sarray_at_put_safe (class->dtable, - (sidx) method->method_name->sel_id, - method->method_imp); + + /* If dtable is already installed, go ahead and put it in + the dtable sarray, but if it isn't, let + __objc_install_dispatch_table_for_class do it. */ + + if (class->dtable != __objc_uninstalled_dtable) + { + sarray_at_put_safe (class->dtable, + (sidx) method->method_name->sel_id, + method->method_imp); + if (behavior_debug) + fprintf(stderr, "\tinstalled method\n"); + } + else + { + if (behavior_debug) + fprintf(stderr, "\tappended method\n"); + } new_list->method_list[new_list->method_count] = *method; (new_list->method_count)++; - if (behavior_debug) - fprintf(stderr, "added method %s\n", - sel_get_name(method->method_name)); } counter -= 1; } - new_list->method_next = class->methods; - class->methods = new_list; + if (new_list->method_count) + { + new_list->method_next = class->methods; + class->methods = new_list; + } + else + { + OBJC_FREE(new_list); + } } } @@ -354,3 +393,34 @@ static BOOL class_is_kind_of(Class self, Class aClassObject) return YES; return NO; } + +void +check_class_methods(Class class) +{ + int counter; + MethodList_t mlist; + + if (class->dtable == __objc_uninstalled_dtable) + return; + + for (mlist = class->methods; mlist; mlist = mlist->method_next) + { + counter = mlist->method_count - 1; + while (counter >= 0) + { + Method_t method = &(mlist->method_list[counter]); + IMP imp = sarray_get(class->dtable, + (size_t)method->method_name->sel_id); + assert((imp == method->method_imp)); + sarray_at_put_safe (class->dtable, + (sidx) method->method_name->sel_id, + method->method_imp); + counter -= 1; + } + } + if (class->super_class) + check_class_methods(class->super_class); + + (void) &check_class_methods; /* to prevent compiler warning about unused */ +} + diff --git a/Source/behavior.m b/Source/behavior.m index 0ffad9e2f..9c031a124 100644 --- a/Source/behavior.m +++ b/Source/behavior.m @@ -38,6 +38,11 @@ If you add several behaviors to a class, be aware that the order of the additions is significant. + McCallum talking to himself: + "Yipes. Be careful with [super ...] calls. + BEHAVIOR methods running in CLASS will now have a different super class. + No; wrong. See objc-api.h; typedef struct objc_super." + */ #include @@ -56,7 +61,12 @@ static void __objc_class_add_protocols (Class class, struct objc_protocol_list* protos); #endif static BOOL class_is_kind_of(Class self, Class class); -void class_add_methods_if_not_there(Class class, MethodList_t mlist); +static void check_class_methods(Class class); + +void class_add_methods_if_not_there(Class class, Class behavior); + +/* The uninstalled dispatch table, declared in gcc/objc/sendmsg.c. */ +extern struct sarray* __objc_uninstalled_dtable; /* xxx consider using sendmsg.c:__objc_update_dispatch_table_for_class, but, I think it will be slower than the current method. */ @@ -89,12 +99,21 @@ class_add_behavior (Class class, Class behavior) #endif /* Add instance methods */ - class_add_methods_if_not_there(class, - behavior->methods); + if (behavior_debug) + { + fprintf(stderr, "Adding instance methods from %s\n", + behavior->name); + } + class_add_methods_if_not_there(class, behavior); /* Add class methods */ + if (behavior_debug) + { + fprintf(stderr, "Adding class methods from %s\n", + behavior->class_pointer->name); + } class_add_methods_if_not_there(class->class_pointer, - behavior->class_pointer->methods); + behavior->class_pointer); /* Add behavior's superclass, if not already there. */ { @@ -106,15 +125,16 @@ class_add_behavior (Class class, Class behavior) } void -class_add_methods_if_not_there(Class class, MethodList_t mlist) +class_add_methods_if_not_there(Class class, Class behavior) { static SEL initialize_sel = 0; + MethodList_t mlist; if (!initialize_sel) initialize_sel = sel_register_name ("initialize"); /* Add methods to class->dtable and class->methods */ - for ( ; mlist; mlist = mlist->method_next) + for (mlist = behavior->methods; mlist; mlist = mlist->method_next) { int counter; MethodList_t new_list; @@ -125,7 +145,7 @@ class_add_methods_if_not_there(Class class, MethodList_t mlist) all methods will go in here. */ new_list = (MethodList_t) (*objc_malloc) - (sizeof(MethodList) + sizeof(struct objc_method[counter])); + (sizeof(MethodList) + sizeof(struct objc_method[counter+1])); new_list->method_count = 0; while (counter >= 0) @@ -133,7 +153,7 @@ class_add_methods_if_not_there(Class class, MethodList_t mlist) Method_t method = &(mlist->method_list[counter]); if (behavior_debug) - fprintf(stderr, "processing method %s\n", + fprintf(stderr, " processing method [%s]\n", sel_get_name(method->method_name)); if (!search_for_method_in_list(class->methods, method->method_name) @@ -142,19 +162,38 @@ class_add_methods_if_not_there(Class class, MethodList_t mlist) /* As long as the method isn't defined in the CLASS, put the BEHAVIOR method in there. Thus, behavior methods override the superclasses' methods. */ - sarray_at_put_safe (class->dtable, - (sidx) method->method_name->sel_id, - method->method_imp); + + /* If dtable is already installed, go ahead and put it in + the dtable sarray, but if it isn't, let + __objc_install_dispatch_table_for_class do it. */ + + if (class->dtable != __objc_uninstalled_dtable) + { + sarray_at_put_safe (class->dtable, + (sidx) method->method_name->sel_id, + method->method_imp); + if (behavior_debug) + fprintf(stderr, "\tinstalled method\n"); + } + else + { + if (behavior_debug) + fprintf(stderr, "\tappended method\n"); + } new_list->method_list[new_list->method_count] = *method; (new_list->method_count)++; - if (behavior_debug) - fprintf(stderr, "added method %s\n", - sel_get_name(method->method_name)); } counter -= 1; } - new_list->method_next = class->methods; - class->methods = new_list; + if (new_list->method_count) + { + new_list->method_next = class->methods; + class->methods = new_list; + } + else + { + OBJC_FREE(new_list); + } } } @@ -354,3 +393,34 @@ static BOOL class_is_kind_of(Class self, Class aClassObject) return YES; return NO; } + +void +check_class_methods(Class class) +{ + int counter; + MethodList_t mlist; + + if (class->dtable == __objc_uninstalled_dtable) + return; + + for (mlist = class->methods; mlist; mlist = mlist->method_next) + { + counter = mlist->method_count - 1; + while (counter >= 0) + { + Method_t method = &(mlist->method_list[counter]); + IMP imp = sarray_get(class->dtable, + (size_t)method->method_name->sel_id); + assert((imp == method->method_imp)); + sarray_at_put_safe (class->dtable, + (sidx) method->method_name->sel_id, + method->method_imp); + counter -= 1; + } + } + if (class->super_class) + check_class_methods(class->super_class); + + (void) &check_class_methods; /* to prevent compiler warning about unused */ +} +