[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[RFC] Method List Manipulation API
From: |
David Ayers |
Subject: |
[RFC] Method List Manipulation API |
Date: |
Fri, 04 Jun 2004 23:13:01 +0200 |
User-agent: |
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7) Gecko/20040514 |
Hello,
Here is proposed patch to provide a method list manipulation API:
I hope the documentation speaks for itself. If not I open for
suggestions. Here's an overview of the functions:
typedef struct objc_method *GSMethod;
GSMethodList
GSAllocMethodList (unsigned int count);
void
GSAppendMethodToList (GSMethodList list,
SEL sel,
const char *types,
IMP imp,
BOOL isFree);
BOOL
GSRemoveMethodFromList (GSMethodList list,
SEL sel,
BOOL isFree);
GSMethod
GSMethodFromList(GSMethodList list,
SEL sel,
BOOL isFree);
void
GSAddMethodList(Class class,
GSMethodList list,
BOOL toInstanceMethods);
void
GSRemoveMethodList(Class class,
GSMethodList list,
BOOL fromInstanceMethods);
Note that this patch merely introduces the new functions. I'll have a
follow up to use the new functions within the
GSObjCAddMethods, GSObjCAddClassBehavior and some of the internal methods.
Comments welcome.
Cheers,
David
Index: Headers/Additions/GNUstepBase/GSObjCRuntime.h
===================================================================
RCS file:
/cvsroot/gnustep/gnustep/core/base/Headers/Additions/GNUstepBase/GSObjCRuntime.h,v
retrieving revision 1.6
diff -u -r1.6 GSObjCRuntime.h
--- Headers/Additions/GNUstepBase/GSObjCRuntime.h 9 May 2004 19:29:14
-0000 1.6
+++ Headers/Additions/GNUstepBase/GSObjCRuntime.h 4 Jun 2004 17:40:15
-0000
@@ -357,12 +357,14 @@
/*
- * 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
@@ -475,6 +477,158 @@
*/
GS_EXPORT GSIVar
GSObjCGetInstanceVariableDefinition(Class class, NSString *name);
+
+/**
+ * <p>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. </p>
+ * <p> This function is intended for use in conjunction with
+ * GSAppendMethodToList() to fill the memory and GSAddMethodList()
+ * to activate the method list. </p>
+ * <p>After method list manipulation you should call
+ * GSFlushMethodCacheForClass() for the changes to take effect.</p>
+ * <p><em>WARNING:</em> Manipulating the runtime structures
+ * can be hazardous!</p>
+ * <p>This function should currently (June 2004) be considered WIP.
+ * Please follow potential changes (Name, parameters, ...) closely until
+ * it stabilizes.</p>
+ */
+GSMethodList
+GSAllocMethodList (unsigned int count);
+
+/**
+ * <p>Inserts the method described by sel, types amd 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.</p>
+ * <p>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.</p>
+ * <p>This function is intended for use in conjunction with
+ * GSAllocMethodList() to allocate the list and GSAddMethodList()
+ * to activate the method list. </p>
+ * <p>After method list manipulation you should call
+ * GSFlushMethodCacheForClass() for the changes to take effect.</p>
+ * <p><em>WARNING:</em> Manipulating the runtime structures
+ * can be hazardous!</p>
+ * <p>This function should currently (June 2004) be considered WIP.
+ * Please follow potential changes (Name, parameters, ...) closely until
+ * it stabilizes.</p>
+ */
+void
+GSAppendMethodToList (GSMethodList list,
+ SEL sel,
+ const char *types,
+ IMP imp,
+ BOOL isFree);
+
+/**
+ * <p>Removes the method identified by sel
+ * from the method list moving the following methods up in the list,
+ * leaving the last entry blank.</p>
+ * <p)Returns YES if the a matching method was found a removed,
+ * NO otherwise.</p>
+ * <p>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.</p>
+ * <p>After method list manipulation you should call
+ * GSFlushMethodCacheForClass() for the changes to take effect.</p>
+ * <p><em>WARNING:</em> Manipulating the runtime structures
+ * can be hazardous!</p>
+ * <p>This function should currently (June 2004) be considered WIP.
+ * Please follow potential changes (Name, parameters, ...) closely until
+ * it stabilizes.</p>
+ */
+BOOL
+GSRemoveMethodFromList (GSMethodList list,
+ SEL sel,
+ BOOL isFree);
+
+/**
+ * <p>Returns a method list of the class that contains the selector.
+ * Depeding 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 <code>void *</code>
+ * 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 selecetor 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.</p>
+ * <p>This function should currently (June 2004) be considered WIP.
+ * Please follow potential changes (Name, parameters, ...) closely until
+ * it stabilizes.</p>
+ */
+GSMethodList
+GSMethodListForSelector(Class class,
+ SEL selector,
+ void **iterator,
+ BOOL searchInstanceMethods);
+
+/**
+ * <p>Returns the (first) GSMethod contained in the supplied list
+ * that cooresponds to sel.
+ * Returns NULL if none is found.<p/>
+ * <p>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.</p>
+ */
+GSMethod
+GSMethodFromList(GSMethodList list,
+ SEL sel,
+ BOOL isFree);
+
+/**
+ * <p>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.</p>
+ * <p>After method list manipulation you should call
+ * GSFlushMethodCacheForClass() for the changes to take effect.</p>
+ * <p>This function should currently (June 2004) be considered WIP.
+ * Please follow potential changes (Name, parameters, ...) closely until
+ * it stabilizes.</p>
+ */
+void
+GSAddMethodList(Class class,
+ GSMethodList list,
+ BOOL toInstanceMethods);
+
+/**
+ * <p>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.</p>
+ * <p>After method list manipulation you should call
+ * GSFlushMethodCacheForClass() for the changes to take effect.</p>
+ * <p>This function should currently (June 2004) be considered WIP.
+ * Please follow potential changes (Name, parameters, ...) closely until
+ * it stabilizes.</p>
+ */
+void
+GSRemoveMethodList(Class class,
+ GSMethodList list,
+ BOOL fromInstanceMethods);
/**
Index: Source/Additions/GSObjCRuntime.m
===================================================================
RCS file: /cvsroot/gnustep/gnustep/core/base/Source/Additions/GSObjCRuntime.m,v
retrieving revision 1.37
diff -u -r1.37 GSObjCRuntime.m
--- Source/Additions/GSObjCRuntime.m 4 Jun 2004 13:58:36 -0000 1.37
+++ Source/Additions/GSObjCRuntime.m 4 Jun 2004 17:40:16 -0000
@@ -816,6 +816,256 @@
}
/* 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)
{
Index: Testing/GNUmakefile
===================================================================
RCS file: /cvsroot/gnustep/gnustep/core/base/Testing/GNUmakefile,v
retrieving revision 1.35
diff -u -r1.35 GNUmakefile
--- Testing/GNUmakefile 18 Mar 2004 06:56:12 -0000 1.35
+++ Testing/GNUmakefile 4 Jun 2004 17:40:16 -0000
@@ -37,6 +37,7 @@
containers \
exported-strings \
fref \
+ gsbehavior \
gslock \
nsarchiver \
nsarray \
@@ -86,6 +87,7 @@
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
Index: Testing/gsbehavior.m
===================================================================
RCS file: Testing/gsbehavior.m
diff -N Testing/gsbehavior.m
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Testing/gsbehavior.m 4 Jun 2004 17:40:16 -0000
@@ -0,0 +1,308 @@
+/** gsbehavior - Program to test GSObjCAddClassBehavior.
+ Copyright (C) 2003 Free Software Foundation, Inc.
+
+ Written by: David Ayers <d.ayers@inode.at>
+
+ 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 <Foundation/NSAutoreleasePool.h>
+#include <Foundation/NSNotification.h>
+#include <Foundation/NSException.h>
+
+#include <GNUstepBase/GSObjCRuntime.h>
+
+/*------------------------------------*/
+@interface MyClass : NSObject
+-(const char *)text;
+@end
+@implementation MyClass
+-(void)myClassMain {};
+-(const char *)text
+{
+ return "class_main";
+}
+@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;
+@end
+@implementation MyBehavior
+-(void)myBehaviorMain {};
+-(const char *)text
+{
+ return "behavior_main";
+}
+@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(strncmp([myObj text_main], "class_main", 10) == 0,
+ @"Add failed to add correct implementation!");
+ NSCAssert(strncmp([myObj text_1], "class_category_1", 16) == 0
+ || strncmp([myObj text_1], "class__category_2", 16),
+ @"Add failed to add correct implementation!");
+ NSCAssert(strncmp([myObj text_2], "class__category_2", 16) == 0
+ || strncmp([myObj text_2], "class__category_1", 16),
+ @"Add failed to add correct implementation!");
+
+}
+
+void
+test_reorder(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(strncmp([myObj text], "class_main", 14) == 0,
+ @"Add failed to add correct implementation!");
+
+ RELEASE(myClass);
+}
+
+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(strncmp([myTmplObj text], "template_main", 14) == 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();
+
+ 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);
+}
+
- [RFC] Method List Manipulation API,
David Ayers <=