From 5ec2ddbd0ffaeb0cea0502c1c556a0ca5a8a1d86 Mon Sep 17 00:00:00 2001 From: David Ayers Date: Sat, 5 Jun 2004 13:54:26 +0000 Subject: [PATCH] * Headers/Additions/GNUstepBase/GSObjCRuntime.h * Source/Additions/GSObjCRuntime.m (GSMethodList): Define new type. (GSAllocMethodList, GSAppendMethodToList, * GSRemoveMethodFromList) (GSMethodListForSelector, GSMethodFromList) (GSAddMethodList, GSRemoveMethodList): New functions. * Testing/GNUmakefile * Testing/gsbehavior.m: New tests. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@19464 72102866-910b-0410-8b05-ffd578937521 --- ChangeLog | 13 +- Headers/Additions/GNUstepBase/GSObjCRuntime.h | 167 +++++++- Source/Additions/GSObjCRuntime.m | 250 ++++++++++++ Testing/GNUmakefile | 2 + Testing/gsbehavior.m | 377 ++++++++++++++++++ 5 files changed, 803 insertions(+), 6 deletions(-) create mode 100644 Testing/gsbehavior.m diff --git a/ChangeLog b/ChangeLog index 59f36b552..06062ab5a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2004-06-05 David Ayers + + * Headers/Additions/GNUstepBase/GSObjCRuntime.h + * Source/Additions/GSObjCRuntime.m + (GSMethodList): Define new type. + (GSAllocMethodList, GSAppendMethodToList, GSRemoveMethodFromList) + (GSMethodListForSelector, GSMethodFromList) + (GSAddMethodList, GSRemoveMethodList): New functions. + * Testing/GNUmakefile + * Testing/gsbehavior.m: New tests. + 2004-06-05 Richard Frith-Macdonald * Source/GSDictionary.m: Make exceptions a little more informative. @@ -7,7 +18,7 @@ * Source/GSString.m: New function to externalize a string. implementations of init with format for concrete classes. -2004-06-03 David Ayers +2004-06-04 David Ayers * Source/Additions/GSObjCRuntime.m (BDBGPrintf) New macro. (GSObjCAddClassBehavior, GSObjCAddMethods): Use new macro. diff --git a/Headers/Additions/GNUstepBase/GSObjCRuntime.h b/Headers/Additions/GNUstepBase/GSObjCRuntime.h index afec7e791..4bab3c38e 100644 --- a/Headers/Additions/GNUstepBase/GSObjCRuntime.h +++ b/Headers/Additions/GNUstepBase/GSObjCRuntime.h @@ -357,12 +357,14 @@ GSRegisterProtocol(Protocol *proto); /* - * Unfortunately the definition of the symbol 'Method' "IVar(_t)" - * is incompatible between the GNU and NeXT/Apple runtimes. - * We introduce GSMethod and GSIVar to allow portability. + * Unfortunately the definition of the symbols + * 'Method(_t)', 'MethodList(_t)' and 'IVar(_t)' + * are incompatible between the GNU and NeXT/Apple runtimes. + * We introduce GSMethod, GSMethodList and GSIVar to allow portability. */ -typedef struct objc_method *GSMethod; -typedef struct objc_ivar *GSIVar; +typedef struct objc_method *GSMethod; +typedef struct objc_method_list *GSMethodList; +typedef struct objc_ivar *GSIVar; /** * Returns the pointer to the instance method structure @@ -476,6 +478,161 @@ GSCGetInstanceVariableDefinition(Class class, const char *name); GS_EXPORT GSIVar GSObjCGetInstanceVariableDefinition(Class class, NSString *name); +/** + *

Returns a pointer to objc_malloc'ed memory large enough + * to hold a struct objc_method_list with 'count' number of + * struct objc_method entries. The memory returned is + * initialized with 0, including the method count and + * next method list fields.

+ *

This function is intended for use in conjunction with + * GSAppendMethodToList() to fill the memory and GSAddMethodList() + * to activate the method list.

+ *

After method list manipulation you should call + * GSFlushMethodCacheForClass() for the changes to take effect.

+ *

WARNING: Manipulating the runtime structures + * can be hazardous!

+ *

This function should currently (June 2004) be considered WIP. + * Please follow potential changes (Name, parameters, ...) closely until + * it stabilizes.

+ */ +GSMethodList +GSAllocMethodList (unsigned int count); + +/** + *

Inserts the method described by sel, types and imp + * into the slot of the list's method_count incremented by 1. + * This function does not and cannot check whether + * the list provided has the necessary capacity.

+ *

The GNU runtime makes a difference between method lists + * that are "free standing" and those that "attached" to classes. + * For "free standing" method lists (e.g. created with GSAllocMethodList() + * that have not been added to a class or those which have been removed + * via GSRemoveMethodList()) isFree must be passed YES. + * When manipulating "attached" method lists, specify NO.

+ *

This function is intended for use in conjunction with + * GSAllocMethodList() to allocate the list and GSAddMethodList() + * to activate the method list.

+ *

After method list manipulation you should call + * GSFlushMethodCacheForClass() for the changes to take effect.

+ *

WARNING: Manipulating the runtime structures + * can be hazardous!

+ *

This function should currently (June 2004) be considered WIP. + * Please follow potential changes (Name, parameters, ...) closely until + * it stabilizes.

+ */ +void +GSAppendMethodToList (GSMethodList list, + SEL sel, + const char *types, + IMP imp, + BOOL isFree); + +/** + *

Removes the method identified by sel + * from the method list moving the following methods up in the list, + * leaving the last entry blank. After this call, all references + * of previous GSMethodFromList() calls with this list should be + * considered invalid. If the values they referenced are needed, they + * must be copied to external buffers before this function is called.

+ * + *

The GNU runtime makes a difference between method lists + * that are "free standing" and those that "attached" to classes. + * For "free standing" method lists (e.g. created with GSAllocMethodList() + * that have not been added to a class or those which have been removed + * via GSRemoveMethodList()) isFree must be passed YES. + * When manipulating "attached" method lists, specify NO.

+ *

After method list manipulation you should call + * GSFlushMethodCacheForClass() for the changes to take effect.

+ *

WARNING: Manipulating the runtime structures + * can be hazardous!

+ *

This function should currently (June 2004) be considered WIP. + * Please follow potential changes (Name, parameters, ...) closely until + * it stabilizes.

+ */ +BOOL +GSRemoveMethodFromList (GSMethodList list, + SEL sel, + BOOL isFree); + +/** + *

Returns a method list of the class that contains the selector. + * Depending on searchInstanceMethods either instance or class methods + * are searched. + * Returns NULL if none are found. + * This function does not search the superclasses method lists. + * Call this method with the address of a void * + * pointing to NULL to obtain the first (active) method list + * containing the selector. + * Subsequent calls will return further method lists which contain the + * selector. If none are found, it returns NULL. + * You may instead pass NULL as the iterator in which case the first + * method list containing the selector will be returned. + * Do not call it with an uninitialized iterator. + * If either class or selector are NULL the function returns NULL. + * If subsequent calls to this function with the same non-NULL iterator yet + * different searchInstanceMethods value are called, the behavior + * is undefined.

+ *

This function should currently (June 2004) be considered WIP. + * Please follow potential changes (Name, parameters, ...) closely until + * it stabilizes.

+ */ +GSMethodList +GSMethodListForSelector(Class class, + SEL selector, + void **iterator, + BOOL searchInstanceMethods); + +/** + *

Returns the (first) GSMethod contained in the supplied list + * that corresponds to sel. + * Returns NULL if none is found.

+ *

The GNU runtime makes a difference between method lists + * that are "free standing" and those that "attached" to classes. + * For "free standing" method lists (e.g. created with GSAllocMethodList() + * that have not been added to a class or those which have been removed + * via GSRemoveMethodList()) isFree must be passed YES. + * When manipulating "attached" method lists, specify NO.

+ */ +GSMethod +GSMethodFromList(GSMethodList list, + SEL sel, + BOOL isFree); + +/** + *

Add the method list to the class as the first list to be + * searched during method invocation for the given class. + * Depending on toInstanceMethods, this list will be added as + * an instance or a class method list. + * If the list is in use by another class, behavior is undefined. + * Create a new list with GSAllocMethodList() or use GSRemoveMethodList() + * to remove a list before inserting it in a class.

+ *

After method list manipulation you should call + * GSFlushMethodCacheForClass() for the changes to take effect.

+ *

This function should currently (June 2004) be considered WIP. + * Please follow potential changes (Name, parameters, ...) closely until + * it stabilizes.

+ */ +void +GSAddMethodList(Class class, + GSMethodList list, + BOOL toInstanceMethods); + +/** + *

Removes the method list from the classes instance or class method + * lists depending on fromInstanceMethods. + * If the list is not part of the class, behavior is undefined.

+ *

After method list manipulation you should call + * GSFlushMethodCacheForClass() for the changes to take effect.

+ *

This function should currently (June 2004) be considered WIP. + * Please follow potential changes (Name, parameters, ...) closely until + * it stabilizes.

+ */ +void +GSRemoveMethodList(Class class, + GSMethodList list, + BOOL fromInstanceMethods); + /** * Returns the version number of this. diff --git a/Source/Additions/GSObjCRuntime.m b/Source/Additions/GSObjCRuntime.m index 8f38c3f91..052419117 100644 --- a/Source/Additions/GSObjCRuntime.m +++ b/Source/Additions/GSObjCRuntime.m @@ -815,6 +815,256 @@ GSGetClassMethodNotInhertited (Class class, SEL sel) return search_for_method_in_class (class->class_pointer, sel); } +/* See header for documentation. */ +GSMethodList +GSAllocMethodList (unsigned int count) +{ + GSMethodList list; + size_t size; + + size = (sizeof (struct objc_method_list) + + sizeof (struct objc_method[count])); + list = objc_malloc (size); + memset(list, 0, size); + + return list; +} + +/* See header for documentation. */ +void +GSAppendMethodToList (GSMethodList list, + SEL sel, + const char *types, + IMP imp, + BOOL isFree) +{ + unsigned int num; + + num = (list->method_count)++; + +#ifdef GNU_RUNTIME + /* + Deal with typed selectors: No matter what kind of selector we get + convert it into a c-string. Cache that c-string incase the + selector isn't found, then search for cooresponding typed selector. + If none is found use the cached name to register an new selector + with the cooresponding types. + */ + sel = (SEL)GSNameFromSelector (sel); + + if (isFree == NO) + { + const char *sel_save = (const char *)sel; + + sel = sel_get_typed_uid (sel_save, types); + if (sel == 0) + { + sel = sel_register_typed_name (sel_save, types); + } + } +#endif + + list->method_list[num].method_name = sel; + list->method_list[num].method_types = types; + list->method_list[num].method_imp = imp; +} + +/* See header for documentation. */ +BOOL +GSRemoveMethodFromList (GSMethodList list, + SEL sel, + BOOL isFree) +{ + int i; + +#ifdef GNU_RUNTIME + if (isFree == YES) + { + sel = (SEL)GSNameFromSelector (sel); + } +#else + /* Insure that we always use sel_eq on non GNU Runtimes. */ + isFree = NO; +#endif + + for (i = 0; i < list->method_count; i++) + { + SEL method_name = list->method_list[i].method_name; + + /* For the GNU runtime we have use strcmp instead of sel_eq + for free standing method lists. */ + if ((isFree == YES && strcmp((char *)method_name, (char *)sel) == 0) + || (isFree == NO && sel_eq(method_name, sel))) + { + /* Found the list. Now fill up the gap. */ + for ((list->method_count)--; i < list->method_count; i++) + { + list->method_list[i].method_name + = list->method_list[i+1].method_name; + list->method_list[i].method_types + = list->method_list[i+1].method_types; + list->method_list[i].method_imp + = list->method_list[i+1].method_imp; + } + + /* Clear the last entry. */ + list->method_list[i].method_name = 0; + list->method_list[i].method_types = 0; + list->method_list[i].method_imp = 0; + + return YES; + } + } + return NO; +} + +/* See header for documentation. */ +GSMethodList +GSMethodListForSelector(Class class, + SEL selector, + void **iterator, + BOOL searchInstanceMethods) +{ + void *local_iterator = 0; + + if (class == 0 || selector == 0) + { + return 0; + } + + if (searchInstanceMethods == NO) + { + class = class->class_pointer; + } + + if(sel_is_mapped(selector)) + { + void **iterator_pointer; + GSMethodList method_list; + + iterator_pointer = (iterator == 0 ? &local_iterator : iterator); + while((method_list = class_nextMethodList(class, iterator_pointer))) + { + /* Search the method in the current list. */ + if (GSMethodFromList(method_list, selector, NO) != 0) + { + return method_list; + } + } + } + + return 0; +} + +/* See header for documentation. */ +GSMethod +GSMethodFromList(GSMethodList list, + SEL sel, + BOOL isFree) +{ + unsigned i; + +#ifdef GNU_RUNTIME + if (isFree) + { + sel = (SEL)GSNameFromSelector (sel); + } +#else + isFree = NO; +#endif + + for(i = 0; i < list->method_count; ++i) + { + GSMethod method = &list->method_list[i]; + SEL method_name = method->method_name; + + /* For the GNU runtime we have use strcmp instead of sel_eq + for free standing method lists. */ + if ((isFree == YES && strcmp((char *)method_name, (char *)sel) == 0) + || (isFree == NO && sel_eq(method_name, sel))) + { + return method; + } + } + return 0; +} + +/* See header for documentation. */ +void +GSAddMethodList(Class class, + GSMethodList list, + BOOL toInstanceMethods) +{ + if (class == 0 || list == 0) + { + return; + } + + if (toInstanceMethods == NO) + { + class = class->class_pointer; + } + + class_add_method_list(class, list); +} + +/* See header for documentation. */ +void +GSRemoveMethodList(Class class, + GSMethodList list, + BOOL fromInstanceMethods) +{ + if (class == 0 || list == 0) + { + return; + } + + if (fromInstanceMethods == NO) + { + class = class->class_pointer; + } + +#ifdef NeXT_RUNTIME + class_removeMethods(class, list); +#else + if (list == class->methods) + { + class->methods = list->method_next; + list->method_next = 0; + } + else + { + GSMethodList current_list; + for (current_list = class->methods; + current_list != 0; + current_list = current_list->method_next) + { + if (current_list->method_next == list) + { + int i; + current_list->method_next = list->method_next; + list->method_next = 0; + + /* + The list has become "free standing". + Replace all selector references with selector names + so the runtime can convert them again + it the list gets reinserted. + */ + for (i = 0; i < list->method_count; i++) + { + const char *name; + + name = GSNameFromSelector(list->method_list[i].method_name); + list->method_list[i].method_name = (SEL)name; + } + } + } + } +#endif /* NeXT_RUNTIME */ +} + + /* See header for documentation. */ GSIVar GSCGetInstanceVariableDefinition(Class class, const char *name) diff --git a/Testing/GNUmakefile b/Testing/GNUmakefile index da751e771..50298ad4a 100644 --- a/Testing/GNUmakefile +++ b/Testing/GNUmakefile @@ -37,6 +37,7 @@ CHECKABLE_TOOLS = \ containers \ exported-strings \ fref \ + gsbehavior \ gslock \ nsarchiver \ nsarray \ @@ -86,6 +87,7 @@ containers_OBJC_FILES = containers.m diningPhilosophers_OBJC_FILES = diningPhilosophers.m exported-strings_OBJC_FILES = exported-strings.m fref_OBJC_FILES = fref.m +gsbehavior_OBJC_FILES = gsbehavior.m gslock_OBJC_FILES = gslock.m gstcpport-client_OBJC_FILES = gstcpport-client.m gstcpport-server_OBJC_FILES = gstcpport-server.m diff --git a/Testing/gsbehavior.m b/Testing/gsbehavior.m new file mode 100644 index 000000000..21de802b6 --- /dev/null +++ b/Testing/gsbehavior.m @@ -0,0 +1,377 @@ +/** gsbehavior - Program to test GSObjCAddClassBehavior. + Copyright (C) 2003 Free Software Foundation, Inc. + + Written by: David Ayers + + This file is part of the GNUstep Base Library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. +*/ + + +#include +#include +#include +#include + +#include + +/*------------------------------------*/ +@interface MyClass : NSObject +-(const char *)text; +-(const char *)textBase; +@end +@implementation MyClass +-(void)myClassMain {}; +-(const char *)text +{ + return "class_main"; +} +-(const char *)textBase +{ + return "class_main_base"; +} +@end + +@interface MyClass (Category1) +-(void)myClassCategory1; +@end +@implementation MyClass (Category1) +-(void)myClassCategory1 {}; +-(const char *)text +{ + return "class_category_1"; +} +@end + +@interface MyClass (Category2) +-(void)myClassCategory2; +@end +@implementation MyClass (Category2) +-(void)myClassCategory2 {}; +-(const char *)text +{ + return "class_category_2"; +} +@end + +/*------------------------------------*/ + +@interface MyTemplate1 : NSObject +@end +@implementation MyTemplate1 +@end + +/*------------------------------------*/ +/*------------------------------------*/ + +@interface MyTemplate2 : NSObject +-(const char *)text; +@end +@implementation MyTemplate2 +-(const char *)text +{ + return "template_main"; +} +@end + +/*------------------------------------*/ + +@interface MyBehavior : NSObject +-(const char *)text; +-(const char *)textBase; +@end +@implementation MyBehavior +-(void)myBehaviorMain {}; +-(const char *)text +{ + return "behavior_main"; +} +-(const char *)textBase +{ + return "behavior_main_base"; +} +@end +@interface MyBehavior (Category1) +-(void)myBehaviorCategory1; +@end +@implementation MyBehavior (Category1) +-(void)myBehaviorCategory1 {}; +-(const char *)text +{ + return "behavior_category_1"; +} +@end + +@interface MyBehavior (Category2) +-(void)myBehaviorCategory2; +@end +@implementation MyBehavior (Category2) +-(void)myBehaviorCategory2 {}; +-(const char *)text +{ + return "behavior_category_2"; +} +@end + +/*------------------------------------*/ + +void +test_basic(void) +{ + id myClass; + id myBehavior; + + myClass = [MyClass new]; + myBehavior = [MyBehavior new]; + + NSCAssert(strncmp([myClass text], "class_category", 14) == 0, + @"Default implementation isn't Category!"); + NSCAssert(strncmp([myBehavior text], "behavior_category", 17) == 0, + @"Default implementation isn't Category!"); + + RELEASE(myClass); + RELEASE(myBehavior); +} + +void +test_create_list(void) +{ + GSMethodList myList; + GSMethod myMethod; + Class myClass; + void *it; + IMP imp_main; + IMP imp_1; + IMP imp_2; + const char *types; + id myObj; + + it = 0; + myClass = [MyClass class]; + myObj = [myClass new]; + myList = GSMethodListForSelector(myClass, @selector(text), &it, YES); + NSCAssert(myList,@"List is NULL!"); + myMethod = GSMethodFromList(myList, @selector(text), NO); + NSCAssert(myMethod,@"Method is NULL!"); + imp_1 = myMethod->method_imp; + + myList = GSMethodListForSelector(myClass, @selector(text), &it, YES); + NSCAssert(myList,@"List is NULL!"); + myMethod = GSMethodFromList(myList, @selector(text), NO); + NSCAssert(myMethod,@"Method is NULL!"); + imp_2 = myMethod->method_imp; + + myList = GSMethodListForSelector(myClass, @selector(text), &it, YES); + NSCAssert(myList,@"List is NULL!"); + myMethod = GSMethodFromList(myList, @selector(text), NO); + NSCAssert(myMethod,@"Method is NULL!"); + imp_main = myMethod->method_imp; + + types = myMethod->method_types; + + myList = GSAllocMethodList(3); + GSAppendMethodToList(myList, @selector(text_main), types, imp_main, YES); + GSAppendMethodToList(myList, @selector(text_1), types, imp_1, YES); + GSAppendMethodToList(myList, @selector(text_2), types, imp_2, YES); + + GSAddMethodList(myClass, myList, YES); + GSFlushMethodCacheForClass(myClass); + NSCAssert([myObj respondsToSelector:@selector(text_main)] == YES, + @"Add failed."); + NSCAssert([myObj respondsToSelector:@selector(text_1)] == YES, + @"Add failed."); + NSCAssert([myObj respondsToSelector:@selector(text_2)] == YES, + @"Add failed."); + NSCAssert(strcmp([myObj text_main], "class_main") == 0, + @"Add failed to add correct implementation!"); + NSCAssert(strncmp([myObj text_1], "class_category", 14) == 0, + @"Add failed to add correct implementation!"); + NSCAssert(strncmp([myObj text_2], "class_category", 14) == 0, + @"Add failed to add correct implementation!"); + +} + +void +test_reorder_list(void) +{ + Class myClass; + id myObj; + GSMethodList list; + + myClass = [MyClass class]; + myObj = [MyClass new]; + + list = GSMethodListForSelector(myClass, @selector(myClassMain), 0, YES); + + /* Remove */ + GSRemoveMethodList(myClass, list, YES); + GSFlushMethodCacheForClass(myClass); + NSCAssert([myObj respondsToSelector:@selector(myClassMain)] == NO, + @"Remove failed."); + + /* Add */ + GSAddMethodList(myClass, list, YES); + GSFlushMethodCacheForClass(myClass); + + NSCAssert([myObj respondsToSelector:@selector(myClassMain)] == YES, + @"Add failed."); + NSCAssert(strcmp([myObj text], "class_main") == 0, + @"Add failed to add correct implementation!"); + + RELEASE(myClass); +} + +void +test_exchange_method(void) +{ + Class myClass; + Class myBehavior; + id myClsObj; + id myBhvObj; + GSMethodList myListC; + GSMethodList myListB; + GSMethod myMethodC; + GSMethod myMethodB; + struct objc_method myMethodStructC; + struct objc_method myMethodStructB; + + myClass = [MyClass class]; + myBehavior = [MyBehavior class]; + + myClsObj = [myClass new]; + myBhvObj = [myBehavior new]; + + NSCAssert(strcmp([myClsObj textBase], "class_main_base") == 0, + @"Wrong precondition!"); + NSCAssert(strcmp([myBhvObj textBase], "behavior_main_base") == 0, + @"Wrong precondition!"); + + myListC = GSMethodListForSelector(myClass, @selector(textBase), 0, YES); + myListB = GSMethodListForSelector(myBehavior, @selector(textBase), 0, YES); + + myMethodC = GSMethodFromList(myListC, @selector(textBase), NO); + myMethodStructC = *myMethodC; + myMethodC = &myMethodStructC; + myMethodB = GSMethodFromList(myListB, @selector(textBase), NO); + myMethodStructB = *myMethodB; + myMethodB = &myMethodStructB; + + GSRemoveMethodFromList(myListC, @selector(textBase), NO); + GSRemoveMethodFromList(myListB, @selector(textBase), NO); + + GSAppendMethodToList(myListC, + myMethodB->method_name, + myMethodB->method_types, + myMethodB->method_imp, + NO); + GSAppendMethodToList(myListB, + myMethodC->method_name, + myMethodC->method_types, + myMethodC->method_imp, + NO); + + GSFlushMethodCacheForClass(myClass); + GSFlushMethodCacheForClass(myBehavior); + + NSCAssert(strcmp([myClsObj textBase], "behavior_main_base") == 0, + @"Couldn't replace implementation!"); + NSCAssert(strcmp([myBhvObj textBase], "class_main_base") == 0, + @"Couldn't replace implementation!"); + +} + +void +test_behavior1(void) +{ + Class myTmplClass; + id myTmplObj; + + myTmplClass = [MyTemplate1 class]; + myTmplObj = [MyTemplate1 new]; + + NSCAssert([myTmplObj respondsToSelector:@selector(text)] == NO, + @"Initial state invalid"); + GSObjCAddClassBehavior(myTmplClass, [MyClass class]); + NSCAssert([myTmplObj respondsToSelector:@selector(text)] == YES, + @"Behavior failed"); + +} + + +void +test_behavior2(void) +{ + Class myTmplClass; + id myTmplObj; + + myTmplClass = [MyTemplate2 class]; + myTmplObj = [MyTemplate2 new]; + + NSCAssert([myTmplObj respondsToSelector:@selector(myClassCategory1)] == NO, + @"Initial state invalid"); + GSObjCAddClassBehavior(myTmplClass, [MyClass class]); + NSCAssert([myTmplObj respondsToSelector:@selector(myClassCategory1)] == YES, + @"Behavior failed"); + + NSCAssert(strcmp([myTmplObj text], "template_main") == 0, + @"Overwritten existing implementation!"); +} + +void +test_methodnames(void) +{ + id obj = [NSNotificationCenter defaultCenter]; + NSArray *names; + + names = GSObjCMethodNames(obj); + NSDebugLog(@"obj:%@", names); + names = GSObjCMethodNames([obj class]); + NSDebugLog(@"class:%@", names); +} + +int +main(int argc, char *argv[]) +{ + NSAutoreleasePool *pool; + // [NSAutoreleasePool enableDoubleReleaseCheck:YES]; + pool = [[NSAutoreleasePool alloc] init]; + + NS_DURING + { + test_methodnames(); + test_basic(); + test_create_list(); + test_reorder_list(); + test_exchange_method(); + + NSLog(@"Behavior Test Succeeded."); + } + NS_HANDLER + { + NSLog(@"Behavior Test Failed:"); + NSLog(@"%@ %@ %@", + [localException name], + [localException reason], + [localException userInfo]); + [localException raise]; + } + NS_ENDHANDLER + + [pool release]; + + exit(0); +} +