Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -15,10 +15,13 @@ LIBS="$LIBS -lobjc" AX_CHECK_COMPILER_FLAGS(-pipe, [ CFLAGS="$CFLAGS -pipe" OBJCFLAGS="$OBJCFLAGS -pipe"]) +AX_CHECK_COMPILER_FLAGS(-fno-common, [ + CFLAGS="$CFLAGS -fno-common" + OBJCFLAGS="$OBJCFLAGS -fno-common"]) AX_CHECK_COMPILER_FLAGS(-fno-constant-cfstrings, [OBJCFLAGS="$OBJCFLAGS -fno-constant-cfstrings"]) AC_DEFINE(OF_CONFIG_H, 1, [Define so that we know we got our config.h]) Index: src/OFConstString.h ================================================================== --- src/OFConstString.h +++ src/OFConstString.h @@ -10,17 +10,19 @@ */ #import "OFObject.h" #ifndef __objc_INCLUDE_GNU +#import + extern void *_OFConstStringClassReference; #endif /** * A class for storing static strings using the @"" literal. */ -@interface OFConstString: Object +@interface OFConstString: OFObject { char *string; size_t length; } Index: src/OFDictionary.h ================================================================== --- src/OFDictionary.h +++ src/OFDictionary.h @@ -55,21 +55,21 @@ * Sets a key to an object. A key can be any object. * * \param key The key to set * \param obj The object to set the key to */ -- set: (id )key - to: (id )obj; +- set: (OFObject*)key + to: (OFObject*)obj; /* * \param key The key whose object should be returned * \return The object for the given key */ -- get: (id )key; +- get: (OFObject*)key; /* * Remove the object with the given key from the dictionary. * * \param key The key whose object should be removed */ -- remove: (id )key; +- remove: (OFObject*)key; @end Index: src/OFDictionary.m ================================================================== --- src/OFDictionary.m +++ src/OFDictionary.m @@ -80,12 +80,12 @@ [data[i] release]; return [super free]; } -- set: (id )key - to: (id )obj +- set: (OFObject*)key + to: (OFObject*)obj { uint32_t hash = [key hash] & (size - 1); of_list_object_t *iter; if (data[hash] == nil) @@ -109,11 +109,11 @@ [data[hash] append: obj]; return self; } -- get: (id )key +- get: (OFObject*)key { uint32_t hash = [key hash] & (size - 1); of_list_object_t *iter; if (data[hash] == nil) @@ -124,11 +124,11 @@ return iter->next->object; return nil; } -- remove: (id )key +- remove: (OFObject*)key { uint32_t hash = [key hash] & (size - 1); of_list_object_t *iter; if (data[hash] == nil) Index: src/OFObject.h ================================================================== --- src/OFObject.h +++ src/OFObject.h @@ -7,37 +7,135 @@ * This file is part of libobjfw. It may be distributed under the terms of the * Q Public License 1.0, which can be found in the file LICENSE included in * the packaging of this file. */ -#import - +#include #include -@protocol OFRetainRelease -/** - * Increases the retain count. - */ -- retain; - -/** - * Decreases the retain cound and frees the object if it reaches 0. - */ -- (void)release; - -/** - * Adds the object to the autorelease pool that is on top of the thread's stack. - */ -- autorelease; - -/** - * \return The retain count - */ -- (size_t)retainCount; -@end - -@protocol OFHashable +#import +#ifndef __objc_INCLUDE_GNU +#import +#endif + +/** + * The OFObject class is the base class for all other classes inside ObjFW. + */ +@interface OFObject +{ + Class isa; +} + +/** + * This code is executed once when a method of the class is called for the first + * time. + * Derived classes can override this to execute their own code on + * initialization. + */ ++ initialize; + +/** + * Allocates memory for an instance of the class. + */ ++ alloc; + +/** + * Allocated memory for an instance of the class and initializes the instance. + */ ++ new; + +/** + * \return The class pointer + */ ++ (Class)class; + +/** + * \return The name of the class as a C string + */ ++ (const char*)name; + +/** + * Replace a method with a method from another class. + * + * \param selector The selector of the method to replace + * \param class The class from which the new method should be taken + * \return The old implementation + */ ++ (IMP)replaceMethod: (SEL)selector + withMethodFromClass: (Class)class; + +/** + * Initialize the already allocated object. + * Also sets up the memory pool for the object. + * + * \return An initialized object + */ +- init; + +/** + * \return A pointer to the class of the instance + */ +- (Class)class; + +/** + * \return The name of the instance's class as a C string + */ +- (const char*)name; + +/** + * \param class The class whose kind is checked + * + * \return A boolean whether the object is of the specified kind + */ +- (BOOL)isKindOf: (Class)class; + +/** + * \param selector The selector which should be checked + * + * \return A boolean whether the objects responds to the specified selector + */ +- (BOOL)respondsTo: (SEL)selector; + +/** + * \param selector The selector for which the method should be returned + * + * \return The implementation for the specified selector + */ +- (IMP)methodFor: (SEL)selector; + +/** + * This method is called when a method was called which isn't implemented. + * It's possible to override it so the method call cann be forwarded to another + * object. + * + * \param selector The selector which was called + * \param args The arguments with which the selector was called + * \return The return value of the call + */ +#ifdef __objc_INCLUDE_GNU +- (retval_t)forward: (SEL)selector + : (arglist_t)args; +#else +- (id)forward: (SEL)selector + : (marg_list)args; +#endif + +/** + * Perform the given selector with the given arguments. + * + * \param selector The selector to perform + * \param args The arguments with which the selector is performed + * \return The return value of the performed selector + */ +#ifdef __objc_INCLUDE_GNU +- (retval_t)performv: (SEL)selector + : (arglist_t)args; +#else +- performv: (SEL)selector + : (marg_list)args; +#endif + /** * Compare two objects. * Classes containing data (like strings, arrays, lists etc.) should reimplement * this! * @@ -52,34 +150,10 @@ * this! * * \return A 32 bit hash for the object */ - (uint32_t)hash; -@end - -/** - * The OFObject class is the base class for all other classes inside ObjFW. - */ -@interface OFObject: Object -{ - void **__memchunks; - size_t __memchunks_size; - size_t __retain_count; -} - -/** - * Initialize the already allocated object. - * Also sets up the memory pool for the object. - * - * \return An initialized object - */ -- init; - -/** - * Frees the object and also frees all memory allocated via its memory pool. - */ -- free; /** * Adds a pointer to the memory pool. * This is useful to add memory allocated by functions such as asprintf to the * pool so it gets freed automatically when the object is freed. @@ -135,6 +209,31 @@ * Frees allocated memory and removes it from the memory pool. * * \param ptr A pointer to the allocated memory */ - freeMem: (void*)ptr; + +/** + * Increases the retain count. + */ +- retain; + +/** + * Adds the object to the autorelease pool that is on top of the thread's stack. + */ +- autorelease; + +/** + * \return The retain count + */ +- (size_t)retainCount; + +/** + * Decreases the retain cound and frees the object if it reaches 0. + */ +- (void)release; + +/** + * Frees the object and also frees all memory allocated via its memory pool. + */ +- free; @end Index: src/OFObject.m ================================================================== --- src/OFObject.m +++ src/OFObject.m @@ -7,10 +7,16 @@ * This file is part of libobjfw. It may be distributed under the terms of the * Q Public License 1.0, which can be found in the file LICENSE included in * the packaging of this file. */ +/* + * Get rid of the stupid warnings until we have our own implementation for + * objc_msgSendv. + */ +#define OBJC2_UNAVAILABLE + #import "config.h" #include #include #include @@ -18,59 +24,214 @@ #import "OFObject.h" #import "OFAutoreleasePool.h" #import "OFExceptions.h" #import "OFMacros.h" -@implementation OFObject -- init -{ - if ((self = [super init]) != nil) { - __memchunks = NULL; - __memchunks_size = 0; - __retain_count = 1; - } - - return self; -} - -- free -{ - void **iter = __memchunks + __memchunks_size; - - while (iter-- > __memchunks) - free(*iter); - - if (__memchunks != NULL) - free(__memchunks); - - return [super free]; -} - -- retain -{ - __retain_count++; - - return self; -} - -- (void)release -{ - if (!--__retain_count) - [self free]; -} - -- autorelease -{ - [OFAutoreleasePool addToPool: self]; - - return self; -} - -- (size_t)retainCount -{ - return __retain_count; -} +#import +#ifndef __objc_INCLUDE_GNU +#import +#endif + +struct pre_ivar { + void **memchunks; + size_t memchunks_size; + size_t retain_count; +}; + +/* Hopefully no arch needs more than 16 bytes padding */ +#define PRE_IVAR_ALIGN ((sizeof(struct pre_ivar) + 15) & ~15) +#define PRE_IVAR ((struct pre_ivar*)((char*)self - PRE_IVAR_ALIGN)) + +@implementation OFObject +#ifndef __objc_INCLUDE_GNU ++ load +{ + return self; +} +#endif + ++ initialize +{ + return self; +} + ++ alloc +{ + OFObject *instance; +#ifdef __objc_INCLUDE_GNU + size_t isize = class_get_instance_size(self); +#else + size_t isize = class_getInstanceSize(self); +#endif + + if ((instance = malloc(isize + PRE_IVAR_ALIGN)) == NULL) + return nil; + + ((struct pre_ivar*)instance)->memchunks = NULL; + ((struct pre_ivar*)instance)->memchunks_size = 0; + ((struct pre_ivar*)instance)->retain_count = 1; + + instance = (OFObject*)((char*)instance + PRE_IVAR_ALIGN); + memset(instance, 0, isize); + instance->isa = self; + + return instance; +} + ++ new +{ + return [[self alloc] init]; +} + ++ (Class)class +{ + return self; +} + ++ (const char*)name +{ +#ifdef __objc_INCLUDE_GNU + return class_get_class_name(self); +#else + return class_getName(self); +#endif +} + ++ (IMP)replaceMethod: (SEL)selector + withMethodFromClass: (Class)class; +{ +#ifdef __objc_INCLUDE_GNU + Method_t method = class_get_instance_method(self, selector); + IMP oldimp, newimp; + + if (method == NULL) + @throw [OFInvalidArgumentException newWithClass: self + andSelector: _cmd]; + + oldimp = method_get_imp(method); + newimp = method_get_imp(class_get_instance_method(class, selector)); + + if (oldimp == (IMP)0 || newimp == (IMP)0) + @throw [OFInvalidArgumentException newWithClass: self + andSelector: _cmd]; + + method->method_imp = newimp; + return oldimp; +#else + IMP imp = class_getMethodImplementation(class, selector); + Method method = class_getInstanceMethod(self, selector); + + if (imp == NULL || method == NULL) + @throw [OFInvalidArgumentException newWithClass: self + andSelector: _cmd]; + + return method_setImplementation(method, imp); +#endif +} + +- init +{ + return self; +} + +- (Class)class +{ + return isa; +} + +- (const char*)name +{ +#ifdef __objc_INCLUDE_GNU + return object_get_class_name(self); +#else + return class_getName(isa); +#endif +} + +- (BOOL)isKindOf: (Class)class +{ + Class iter; + +#ifdef __objc_INCLUDE_GNU + for (iter = isa; iter != Nil; iter = class_get_super_class(iter)) +#else + for (iter = isa; iter != Nil; iter = class_getSuperclass(iter)) +#endif + if (iter == class) + return YES; + + return NO; +} + +- (BOOL)respondsTo: (SEL)selector +{ +#ifdef __objc_INCLUDE_GNU + if (object_is_instance(self)) + return class_get_instance_method(isa, selector) != METHOD_NULL; + else + return class_get_class_method(isa, selector) != METHOD_NULL; +#else + return class_respondsToSelector(isa, selector); +#endif +} + +- (IMP)methodFor: (SEL)selector +{ +#ifdef __objc_INCLUDE_GNU + if (object_is_instance(self)) + return method_get_imp(class_get_instance_method(isa, selector)); + else + return method_get_imp(class_get_class_method(isa, selector)); +#else + return class_getMethodImplementation(isa, selector); +#endif +} + +#ifdef __objc_INCLUDE_GNU +- (retval_t)forward: (SEL)selector + : (arglist_t)args +#else +- (id)forward: (SEL)selector + : (marg_list)args +#endif +{ + @throw [OFNotImplementedException newWithClass: [self class] + andSelector: _cmd]; + return self; +} + +#ifdef __objc_INCLUDE_GNU +- (retval_t)performv: (SEL)selector + : (arglist_t)args +{ + return objc_msg_sendv(self, selector, args); +} +#else +- performv: (SEL)selector + : (marg_list)args +{ +#if !__OBJC2__ + Method method; + unsigned size; + + if ((method = class_getInstanceMethod(isa, selector)) != NULL) + size = method_getSizeOfArguments(method); + else + size = 0; + + if (!size) + return [self forward: selector + : args]; + + return objc_msgSendv(self, selector, size, args); +#else +#warning ObjC2 removed objc_msgSendv and there is +#warning no own implementation for it yet! + return nil; +#endif +} +#endif - (BOOL)isEqual: (id)obj { /* Classes containing data should reimplement this! */ return (self == obj ? YES : NO); @@ -85,24 +246,24 @@ - addToMemoryPool: (void*)ptr { void **memchunks; size_t memchunks_size; - memchunks_size = __memchunks_size + 1; + memchunks_size = PRE_IVAR->memchunks_size + 1; - if (SIZE_MAX - __memchunks_size < 1 || + if (SIZE_MAX - PRE_IVAR->memchunks_size < 1 || memchunks_size > SIZE_MAX / sizeof(void*)) @throw [OFOutOfRangeException newWithClass: [self class]]; - if ((memchunks = realloc(__memchunks, + if ((memchunks = realloc(PRE_IVAR->memchunks, memchunks_size * sizeof(void*))) == NULL) @throw [OFNoMemException newWithClass: [self class] andSize: memchunks_size]; - __memchunks = memchunks; - __memchunks[__memchunks_size] = ptr; - __memchunks_size = memchunks_size; + PRE_IVAR->memchunks = memchunks; + PRE_IVAR->memchunks[PRE_IVAR->memchunks_size] = ptr; + PRE_IVAR->memchunks_size = memchunks_size; return self; } - (void*)getMemWithSize: (size_t)size @@ -111,30 +272,30 @@ size_t memchunks_size; if (size == 0) return NULL; - memchunks_size = __memchunks_size + 1; + memchunks_size = PRE_IVAR->memchunks_size + 1; - if (SIZE_MAX - __memchunks_size == 0 || + if (SIZE_MAX - PRE_IVAR->memchunks_size == 0 || memchunks_size > SIZE_MAX / sizeof(void*)) @throw [OFOutOfRangeException newWithClass: [self class]]; if ((ptr = malloc(size)) == NULL) @throw [OFNoMemException newWithClass: [self class] andSize: size]; - if ((memchunks = realloc(__memchunks, + if ((memchunks = realloc(PRE_IVAR->memchunks, memchunks_size * sizeof(void*))) == NULL) { free(ptr); @throw [OFNoMemException newWithClass: [self class] andSize: memchunks_size]; } - __memchunks = memchunks; - __memchunks[__memchunks_size] = ptr; - __memchunks_size = memchunks_size; + PRE_IVAR->memchunks = memchunks; + PRE_IVAR->memchunks[PRE_IVAR->memchunks_size] = ptr; + PRE_IVAR->memchunks_size = memchunks_size; return ptr; } - (void*)getMemForNItems: (size_t)nitems @@ -160,13 +321,13 @@ if (size == 0) { [self freeMem: ptr]; return NULL; } - iter = __memchunks + __memchunks_size; + iter = PRE_IVAR->memchunks + PRE_IVAR->memchunks_size; - while (iter-- > __memchunks) { + while (iter-- > PRE_IVAR->memchunks) { if (OF_UNLIKELY(*iter == ptr)) { if (OF_UNLIKELY((ptr = realloc(ptr, size)) == NULL)) @throw [OFNoMemException newWithClass: [self class] andSize: size]; @@ -207,50 +368,90 @@ - freeMem: (void*)ptr; { void **iter, *last, **memchunks; size_t i, memchunks_size; - iter = __memchunks + __memchunks_size; - i = __memchunks_size; + iter = PRE_IVAR->memchunks + PRE_IVAR->memchunks_size; + i = PRE_IVAR->memchunks_size; - while (iter-- > __memchunks) { + while (iter-- > PRE_IVAR->memchunks) { i--; if (OF_UNLIKELY(*iter == ptr)) { - memchunks_size = __memchunks_size - 1; - last = __memchunks[memchunks_size]; + memchunks_size = PRE_IVAR->memchunks_size - 1; + last = PRE_IVAR->memchunks[memchunks_size]; - if (OF_UNLIKELY(__memchunks_size == 0 || + if (OF_UNLIKELY(PRE_IVAR->memchunks_size == 0 || memchunks_size > SIZE_MAX / sizeof(void*))) @throw [OFOutOfRangeException newWithClass: [self class]]; if (OF_UNLIKELY(memchunks_size == 0)) { free(ptr); - free(__memchunks); + free(PRE_IVAR->memchunks); - __memchunks = NULL; - __memchunks_size = 0; + PRE_IVAR->memchunks = NULL; + PRE_IVAR->memchunks_size = 0; return self; } - if (OF_UNLIKELY((memchunks = realloc(__memchunks, - memchunks_size * sizeof(void*))) == NULL)) + if (OF_UNLIKELY((memchunks = realloc( + PRE_IVAR->memchunks, memchunks_size * + sizeof(void*))) == NULL)) @throw [OFNoMemException newWithClass: [self class] andSize: memchunks_size]; free(ptr); - __memchunks = memchunks; - __memchunks[i] = last; - __memchunks_size = memchunks_size; + PRE_IVAR->memchunks = memchunks; + PRE_IVAR->memchunks[i] = last; + PRE_IVAR->memchunks_size = memchunks_size; return self; } } @throw [OFMemNotPartOfObjException newWithClass: [self class] andPointer: ptr]; return self; /* never reached, but makes gcc happy */ } + +- retain +{ + PRE_IVAR->retain_count++; + + return self; +} + +- autorelease +{ + [OFAutoreleasePool addToPool: self]; + + return self; +} + +- (size_t)retainCount +{ + return PRE_IVAR->retain_count; +} + +- (void)release +{ + if (!--PRE_IVAR->retain_count) + [self free]; +} + +- free +{ + void **iter = PRE_IVAR->memchunks + PRE_IVAR->memchunks_size; + + while (iter-- > PRE_IVAR->memchunks) + free(*iter); + + if (PRE_IVAR->memchunks != NULL) + free(PRE_IVAR->memchunks); + + free((char*)self - PRE_IVAR_ALIGN); + return nil; +} @end Index: tests/OFAutoreleasePool/OFAutoreleasePool.m ================================================================== --- tests/OFAutoreleasePool/OFAutoreleasePool.m +++ tests/OFAutoreleasePool/OFAutoreleasePool.m @@ -19,10 +19,14 @@ #define ZD "%zd" #else #define ZD "%u" #endif +IMP init; +IMP retain; +IMP release; + @interface TestObject: OFObject - init; - retain; - (void)release; @end @@ -30,11 +34,11 @@ @implementation TestObject - init { id ret; - ret = [super init]; + ret = init(self, @selector(init)); printf("New %s with retain cnt " ZD "\n", [self name], [ret retainCount]); return ret; } @@ -41,28 +45,33 @@ - retain { id ret; - ret = [super retain]; + ret = retain(self, @selector(retain)); printf("Retaining %s to " ZD "\n", [self name], [ret retainCount]); return ret; } - (void)release { printf("Releasing %s to " ZD "\n", [self name], [self retainCount] - 1); - return [super release]; + release(self, @selector(release)); } @end int main() { - [TestObject poseAs: [OFObject class]]; + init = [OFObject replaceMethod: @selector(init) + withMethodFromClass: [TestObject class]]; + retain = [OFObject replaceMethod: @selector(retain) + withMethodFromClass: [TestObject class]]; + release = [OFObject replaceMethod: @selector(release) + withMethodFromClass: [TestObject class]]; OFObject *o1, *o2, *o3; OFAutoreleasePool *pool1, *pool2; o1 = [[OFObject new] autorelease];