mirror of
https://github.com/gnustep/libs-base.git
synced 2025-05-11 00:30:49 +00:00
* 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
This commit is contained in:
parent
ffa9b78a57
commit
5ec2ddbd0f
5 changed files with 803 additions and 6 deletions
13
ChangeLog
13
ChangeLog
|
@ -1,3 +1,14 @@
|
||||||
|
2004-06-05 David Ayers <d.ayers@inode.at>
|
||||||
|
|
||||||
|
* 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 <rfm@gnu.org>
|
2004-06-05 Richard Frith-Macdonald <rfm@gnu.org>
|
||||||
|
|
||||||
* Source/GSDictionary.m: Make exceptions a little more informative.
|
* Source/GSDictionary.m: Make exceptions a little more informative.
|
||||||
|
@ -7,7 +18,7 @@
|
||||||
* Source/GSString.m: New function to externalize a string.
|
* Source/GSString.m: New function to externalize a string.
|
||||||
implementations of init with format for concrete classes.
|
implementations of init with format for concrete classes.
|
||||||
|
|
||||||
2004-06-03 David Ayers <d.ayers@inode.at>
|
2004-06-04 David Ayers <d.ayers@inode.at>
|
||||||
|
|
||||||
* Source/Additions/GSObjCRuntime.m (BDBGPrintf) New macro.
|
* Source/Additions/GSObjCRuntime.m (BDBGPrintf) New macro.
|
||||||
(GSObjCAddClassBehavior, GSObjCAddMethods): Use new macro.
|
(GSObjCAddClassBehavior, GSObjCAddMethods): Use new macro.
|
||||||
|
|
|
@ -357,12 +357,14 @@ GSRegisterProtocol(Protocol *proto);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Unfortunately the definition of the symbol 'Method' "IVar(_t)"
|
* Unfortunately the definition of the symbols
|
||||||
* is incompatible between the GNU and NeXT/Apple runtimes.
|
* 'Method(_t)', 'MethodList(_t)' and 'IVar(_t)'
|
||||||
* We introduce GSMethod and GSIVar to allow portability.
|
* 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_method *GSMethod;
|
||||||
typedef struct objc_ivar *GSIVar;
|
typedef struct objc_method_list *GSMethodList;
|
||||||
|
typedef struct objc_ivar *GSIVar;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the pointer to the instance method structure
|
* Returns the pointer to the instance method structure
|
||||||
|
@ -476,6 +478,161 @@ GSCGetInstanceVariableDefinition(Class class, const char *name);
|
||||||
GS_EXPORT GSIVar
|
GS_EXPORT GSIVar
|
||||||
GSObjCGetInstanceVariableDefinition(Class class, NSString *name);
|
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 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.</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. 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.</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.
|
||||||
|
* 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 <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 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.</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 corresponds 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);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the version number of this.
|
* Returns the version number of this.
|
||||||
|
|
|
@ -815,6 +815,256 @@ GSGetClassMethodNotInhertited (Class class, SEL sel)
|
||||||
return search_for_method_in_class (class->class_pointer, 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. */
|
/* See header for documentation. */
|
||||||
GSIVar
|
GSIVar
|
||||||
GSCGetInstanceVariableDefinition(Class class, const char *name)
|
GSCGetInstanceVariableDefinition(Class class, const char *name)
|
||||||
|
|
|
@ -37,6 +37,7 @@ CHECKABLE_TOOLS = \
|
||||||
containers \
|
containers \
|
||||||
exported-strings \
|
exported-strings \
|
||||||
fref \
|
fref \
|
||||||
|
gsbehavior \
|
||||||
gslock \
|
gslock \
|
||||||
nsarchiver \
|
nsarchiver \
|
||||||
nsarray \
|
nsarray \
|
||||||
|
@ -86,6 +87,7 @@ containers_OBJC_FILES = containers.m
|
||||||
diningPhilosophers_OBJC_FILES = diningPhilosophers.m
|
diningPhilosophers_OBJC_FILES = diningPhilosophers.m
|
||||||
exported-strings_OBJC_FILES = exported-strings.m
|
exported-strings_OBJC_FILES = exported-strings.m
|
||||||
fref_OBJC_FILES = fref.m
|
fref_OBJC_FILES = fref.m
|
||||||
|
gsbehavior_OBJC_FILES = gsbehavior.m
|
||||||
gslock_OBJC_FILES = gslock.m
|
gslock_OBJC_FILES = gslock.m
|
||||||
gstcpport-client_OBJC_FILES = gstcpport-client.m
|
gstcpport-client_OBJC_FILES = gstcpport-client.m
|
||||||
gstcpport-server_OBJC_FILES = gstcpport-server.m
|
gstcpport-server_OBJC_FILES = gstcpport-server.m
|
||||||
|
|
377
Testing/gsbehavior.m
Normal file
377
Testing/gsbehavior.m
Normal file
|
@ -0,0 +1,377 @@
|
||||||
|
/** 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 <Foundation/NSDebug.h>
|
||||||
|
|
||||||
|
#include <GNUstepBase/GSObjCRuntime.h>
|
||||||
|
|
||||||
|
/*------------------------------------*/
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue