diff --git a/ChangeLog b/ChangeLog index af6ebaf4e..9e8844e54 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,10 +1,22 @@ +2004-05-09 David Ayers + + * Headers/Additions/GNUstepBase/GSObjCRuntime.h + * Source/Additions//GSObjCRuntime.m + (GSProtocolFromName): New function. + (GSRegisterProtocol): Ditto. + (gs_string_hash): New internal function. + (gs_find_protocol_named_in_protocol_list): Ditto. + (gs_find_protocol_named): Ditto. + (gs_init_protocol_lock): Ditto. + 2004-05-07 23:48 Alexander Malmberg * Resources/Languages/Hungarian: Add a missing ". 2004-05-06 David Ayers - * Headers/Additions/GNUstepBase/GSObjCRuntime.h/m: + * Headers/Additions/GNUstepBase/GSObjCRuntime.h: + * Source/Additions//GSObjCRuntime.m (GSClassList): New function. 2004-05-07 Richard Frith-Macdonald @@ -14,7 +26,8 @@ 2004-05-06 David Ayers - * Headers/Additions/GNUstepBase/GSObjCRuntime.h/m + * Headers/Additions/GNUstepBase/GSObjCRuntime.h + * Source/Additions//GSObjCRuntime.m (GSAllocateMutexAt): New function. (_GSObjCRuntimeInitializer): Define local support class. diff --git a/Headers/Additions/GNUstepBase/GSObjCRuntime.h b/Headers/Additions/GNUstepBase/GSObjCRuntime.h index 3bcea9067..afec7e791 100644 --- a/Headers/Additions/GNUstepBase/GSObjCRuntime.h +++ b/Headers/Additions/GNUstepBase/GSObjCRuntime.h @@ -337,6 +337,24 @@ GSTypesFromSelector(SEL this) return sel_get_type(this); } +/** + * Returns a protocol object with the corresponding name. + * This function searches the registered classes for any protocol + * with the supplied name. If one is found, it is cached in + * for future requests. If efficiency is a factor then use + * GSRegisterProtocol() to insert a protocol explicitly into the cache + * used by this function. If no protocol is found this function returns + * nil. + */ +GS_EXPORT Protocol * +GSProtocolFromName(const char *name); + +/** + * Registers proto in the cache used by GSProtocolFromName(). + */ +GS_EXPORT void +GSRegisterProtocol(Protocol *proto); + /* * Unfortunately the definition of the symbol 'Method' "IVar(_t)" diff --git a/Source/Additions/GSObjCRuntime.m b/Source/Additions/GSObjCRuntime.m index 804fe90d8..e985346c4 100644 --- a/Source/Additions/GSObjCRuntime.m +++ b/Source/Additions/GSObjCRuntime.m @@ -49,6 +49,9 @@ #include "GNUstepBase/GSObjCRuntime.h" #include "GNUstepBase/GNUstep.h" #include "GNUstepBase/GSCategories.h" + +#include + #include @class NSNull; @@ -848,6 +851,186 @@ GSObjCGetInstanceVariableDefinition(Class class, NSString *name) return GSCGetInstanceVariableDefinition(class, [name cString]); } +typedef struct { + @defs(Protocol) +} *pcl; + +GS_STATIC_INLINE unsigned int +gs_string_hash(const char *s) +{ + unsigned int val = 0; + while (*s != 0) + { + val = (val << 5) + val + *s++; + } + return val; +} + +GS_STATIC_INLINE pcl +gs_find_protocol_named_in_protocol_list(const char *name, + struct objc_protocol_list *pcllist) +{ + pcl p = NULL; + size_t i; + + while (pcllist != NULL) + { + for (i=0; icount; i++) + { + p = (pcl)pcllist->list[i]; + if (strcmp(p->protocol_name, name) == 0) + { + return p; + } + } + pcllist = pcllist->next; + } + return NULL; +} + +GS_STATIC_INLINE pcl +gs_find_protocol_named(const char *name) +{ + pcl p = NULL; + Class cls; +#ifdef NeXT_RUNTIME + Class *clsList, *clsListStart; + unsigned int num; + + /* Setting the clearCache flag is a noop for the Apple runtime. */ + num = GSClassList(NULL, 0, NO); + clsList = objc_malloc(sizeof(Class) * (num + 1)); + GSClassList(clsList, num, NO); + + clsListStart = clsList; + + while(p == NULL && (cls = *clsList++)) + { + p = gs_find_protocol_named_in_protocol_list(name, cls->protocols); + } + + objc_free(clsListStart); + +#else + void *iterator = NULL; + + while(p == NULL && (cls = objc_next_class(&iterator))) + { + p = gs_find_protocol_named_in_protocol_list(name, cls->protocols); + } + +#endif + return p; +} + +#define GSI_MAP_HAS_VALUE 1 +#define GSI_MAP_RETAIN_KEY(M, X) +#define GSI_MAP_RETAIN_VAL(M, X) +#define GSI_MAP_RELEASE_KEY(M, X) +#define GSI_MAP_RELEASE_VAL(M, X) +#define GSI_MAP_HASH(M, X) (gs_string_hash(X.ptr)) +#define GSI_MAP_EQUAL(M, X,Y) (strcmp(X.ptr, Y.ptr) == 0) +#define GSI_MAP_NOCLEAN 1 + +#define GSI_MAP_KTYPES GSUNION_PTR +#define GSI_MAP_VTYPES GSUNION_PTR + +#include "GNUstepBase/GSIMap.h" + +static GSIMapTable_t protocol_by_name; +static BOOL protocol_by_name_init = NO; +static volatile objc_mutex_t protocol_by_name_lock = NULL; + +/* Not sure about the semantics of inlining + functions with static variables. */ +static void +gs_init_protocol_lock(void) +{ + if (protocol_by_name_lock == NULL) + { + GSAllocateMutexAt((void *)&protocol_by_name_lock); + objc_mutex_lock(protocol_by_name_lock); + if (protocol_by_name_init == NO) + { + GSIMapInitWithZoneAndCapacity (&protocol_by_name, + NSDefaultMallocZone(), + 128); + protocol_by_name_init = YES; + } + objc_mutex_unlock(protocol_by_name_lock); + } +} + +void +GSRegisterProtocol(Protocol *proto) +{ + if (protocol_by_name_init == NO) + { + gs_init_protocol_lock(); + } + + if (proto != nil) + { + GSIMapNode node; + pcl p; + + p = (pcl)proto; + objc_mutex_lock(protocol_by_name_lock); + node = GSIMapNodeForKey(&protocol_by_name, + (GSIMapKey) p->protocol_name); + if (node == 0) + { + GSIMapAddPairNoRetain(&protocol_by_name, + (GSIMapKey) (void *) p->protocol_name, + (GSIMapVal) (void *) p); + } + objc_mutex_unlock(protocol_by_name_lock); + } +} + +Protocol * +GSProtocolFromName(const char *name) +{ + GSIMapNode node; + pcl p; + + if (protocol_by_name_init == NO) + { + gs_init_protocol_lock(); + } + + node = GSIMapNodeForKey(&protocol_by_name, (GSIMapKey) name); + if (node) + { + p = node->value.ptr; + } + else + { + objc_mutex_lock(protocol_by_name_lock); + node = GSIMapNodeForKey(&protocol_by_name, (GSIMapKey) name); + + if (node) + { + p = node->value.ptr; + } + else + { + p = gs_find_protocol_named(name); + if (p) + { + /* Use the protocol's name to save us from allocating + a copy of the parameter 'name'. */ + GSIMapAddPairNoRetain(&protocol_by_name, + (GSIMapKey) (void *) p->protocol_name, + (GSIMapVal) (void *) p); + } + } + objc_mutex_unlock(protocol_by_name_lock); + + } + + return (Protocol *)p; +} /**