Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -126,101 +126,10 @@ ], [ AC_MSG_RESULT(no) OBJCFLAGS="$old_OBJCFLAGS" ]) -AC_CHECK_HEADERS([objfw-rt.h objc/objc.h]) - -test x"$ac_cv_header_objfw_rt_h" = x"yes" && objc_runtime="ObjFW-RT" - -AC_MSG_CHECKING(which Objective C runtime we use) -AS_IF([test x"$ac_cv_header_objc_objc_h" = x"yes"], [ - dnl Only accept it if it's from gcc >= 4.7, as the one in 4.6 is buggy - dnl when using the new API. - AC_EGREP_CPP(yes, [ - #import - #if defined(__GNU_LIBOBJC__) && __GNU_LIBOBJC__ >= 20110608 - yes - #endif - ], [ - test x"$objc_runtime" = x"" && objc_runtime="GNU" - ], [ - dnl TODO: This is ugly. Let's think of a better check. - AC_EGREP_CPP(yes, [ - #import - #ifdef __objc_INCLUDE_GNU - yes - #endif - ], [ - test x"$objc_runtime" = x"" && objc_runtime="old GNU" - ], [ - objc_runtime="Apple" - ]) - ]) -]) -AC_MSG_RESULT($objc_runtime) - -case $objc_runtime in - ObjFW-RT) - AC_DEFINE(OF_OBJFW_RUNTIME, 1, - [Whether we use the ObjFW runtime]) - AC_SUBST(GNU_RUNTIME, "-fgnu-runtime") - OBJCFLAGS="$OBJCFLAGS -fgnu-runtime" - - AC_CHECK_LIB(objfw-rt, objc_msg_lookup, [ - LIBS="-lobjfw-rt $LIBS" - ], [ - AC_MSG_ERROR([libobjfw-rt not found!]) - ]) - ;; - Apple) - AC_DEFINE(OF_APPLE_RUNTIME, 1, - [Whether we use the Apple ObjC runtime]) - - AC_CHECK_LIB(objc, objc_msgSend, [ - LIBS="-lobjc $LIBS" - ], [ - AC_MSG_ERROR([libobjc not found!]) - ]) - ;; - GNU) - AC_DEFINE(OF_GNU_RUNTIME, 1, - [Whether we use the GNU ObjC runtime]) - - AC_CHECK_LIB(objc, objc_msg_lookup, [ - LIBS="-lobjc $LIBS" - ], [ - AC_MSG_ERROR([libobjc not found!]) - ]) - ;; - "old GNU") - AC_DEFINE(OF_OLD_GNU_RUNTIME, 1, - [Whether we use the old GNU ObjC runtime]) - - AC_CHECK_LIB(objc, objc_msg_lookup, [ - LIBS="-lobjc $LIBS" - ], [ - AC_MSG_ERROR([libobjc not found!]) - ]) - ;; - *) - AC_MSG_RESULT(none) - AC_MSG_ERROR(No ObjC runtime found! Please install ObjFW-RT!) - ;; -esac - -AC_CHECK_FUNC(objc_getProperty,, [ - AC_DEFINE(NEED_OBJC_PROPERTIES_INIT, 1, - [Whether objc_properties_init needs to be called]) - AC_SUBST(OBJC_PROPERTIES_M, "objc_properties.m") -]) - -AC_CHECK_FUNC(objc_enumerationMutation, [ - AC_DEFINE(HAVE_OBJC_ENUMERATIONMUTATION, 1, - [Whether we have objc_enumerationMutation]) -]) - AC_CHECK_TOOL(AR, ar) AC_PROG_RANLIB AC_ARG_ENABLE(shared, AS_HELP_STRING([--disable-shared], [do not build shared library])) AS_IF([test x"$enable_shared" != x"no"], [ @@ -242,10 +151,63 @@ AC_SUBST(OFPLUGINTESTS_M, "OFPluginTests.m") AC_SUBST(TESTPLUGIN, "plugin") AC_DEFINE(OF_PLUGINS, 1, [Whether we have plugin support]) AC_SUBST(OFPLUGINS_DEF, "-DOF_PLUGINS") ]) + +objc_runtime="ObjFW runtime" +AC_CHECK_HEADER(objc/objc.h) +AC_MSG_CHECKING(which Objective C runtime to use) +AS_IF([test x"$ac_cv_header_objc_objc_h" = x"yes"], [ + dnl TODO: This is ugly. Let's think of a better check. + AC_EGREP_CPP(yes, [ + #import + #ifdef __objc_INCLUDE_GNU + yes + #endif + ], [ + dnl We don't want the GNU runtime + ], [ + objc_runtime="Apple runtime" + ]) +]) +AC_MSG_RESULT($objc_runtime) + +case $objc_runtime in + "ObjFW runtime") + AC_DEFINE(OF_OBJFW_RUNTIME, 1, + [Whether we use the ObjFW runtime]) + AC_SUBST(GNU_RUNTIME, "-fgnu-runtime") + OBJCFLAGS="$OBJCFLAGS -fgnu-runtime" + + AC_SUBST(RUNTIME, "runtime") + if test x"$enable_shared" != x"no"; then + AC_SUBST(RUNTIME_LIB_A, "runtime.lib.a") + AC_SUBST(RUNTIME_RUNTIME_LIB_A, "runtime/runtime.lib.a") + fi + if test x"$enable_static" = x"yes" \ + -o x"$enable_shared" = x"no"; then + AC_SUBST(RUNTIME_A, "runtime.a") + AC_SUBST(RUNTIME_RUNTIME_A, "runtime/runtime.a") + fi + ;; + "Apple runtime") + AC_DEFINE(OF_APPLE_RUNTIME, 1, + [Whether we use the Apple ObjC runtime]) + + AC_CHECK_LIB(objc, objc_msgSend, [ + LIBS="-lobjc $LIBS" + ], [ + AC_MSG_ERROR([libobjc not found!]) + ]) + ;; +esac + +AC_CHECK_FUNC(objc_enumerationMutation, [ + AC_DEFINE(HAVE_OBJC_ENUMERATIONMUTATION, 1, + [Whether we have objc_enumerationMutation]) +]) case "$host_os" in darwin*) AC_SUBST(REEXPORT_LIBOBJC, ["-Wl,-reexport-lobjc"]) AC_SUBST(LDFLAGS_REEXPORT, ["-Wl,-reexport-lobjfw"]) @@ -407,16 +369,10 @@ AC_SUBST(OFTHREAD_M, "OFThread.m") AC_SUBST(OFTHREADTESTS_M, "OFThreadTests.m") AC_SUBST(OFHTTPREQUESTTESTS_M, "OFHTTPRequestTests.m") AC_SUBST(THREADING_H, "threading.h") - AC_CHECK_FUNC(objc_sync_enter,, [ - AC_SUBST(OBJC_SYNC_M, "objc_sync.m") - AC_DEFINE(NEED_OBJC_SYNC_INIT, 1, - [Whether objc_sync_init needs to be called]) - ]) - atomic_ops="none" AC_MSG_CHECKING(whether we have an atomic ops assembly implementation) AC_EGREP_CPP(yes, [ #if defined(__GNUC__) && (defined(__i386__) || \ @@ -576,11 +532,11 @@ ]) ], [ AC_MSG_RESULT(no) ]) -AS_IF([test x"$objc_runtime" = x"Apple"], [ +AS_IF([test x"$objc_runtime" = x"Apple runtime"], [ AC_CHECK_HEADER(Foundation/NSObject.h, [ AC_SUBST(FOUNDATION_COMPAT_M, "foundation-compat.m") ]) ]) Index: extra.mk.in ================================================================== --- extra.mk.in +++ extra.mk.in @@ -24,9 +24,14 @@ OFSTREAMOBSERVER_SELECT_M = @OFSTREAMOBSERVER_SELECT_M@ OFTHREAD_M = @OFTHREAD_M@ OFTHREADTESTS_M = @OFTHREADTESTS_M@ PROPERTIESTESTS_M = @PROPERTIESTESTS_M@ REEXPORT_LIBOBJC = @REEXPORT_LIBOBJC@ +RUNTIME = @RUNTIME@ +RUNTIME_A = @RUNTIME_A@ +RUNTIME_RUNTIME_A = @RUNTIME_RUNTIME_A@ +RUNTIME_RUNTIME_LIB_A = @RUNTIME_RUNTIME_LIB_A@ +RUNTIME_LIB_A = @RUNTIME_LIB_A@ TESTPLUGIN = @TESTPLUGIN@ TESTS = @TESTS@ TEST_LAUNCHER = @TEST_LAUNCHER@ THREADING_H = @THREADING_H@ Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -1,8 +1,8 @@ include ../extra.mk -SUBDIRS = exceptions +SUBDIRS = exceptions ${RUNTIME} SHARED_LIB = ${OBJFW_SHARED_LIB} STATIC_LIB = ${OBJFW_STATIC_LIB} LIB_MAJOR = ${OBJFW_LIB_MAJOR} LIB_MINOR = ${OBJFW_LIB_MINOR} @@ -94,17 +94,15 @@ OFString_UTF8.m \ OFTCPSocket+SOCKS5.m \ ${ASPRINTF_M} \ ${FOUNDATION_COMPAT_M} \ iso_8859_15.m \ - windows_1252.m \ - ${OBJC_PROPERTIES_M} \ - ${OBJC_SYNC_M} + windows_1252.m -OBJS_EXTRA = ${EXCEPTIONS_EXCEPTIONS_A} -LIB_OBJS_EXTRA = ${EXCEPTIONS_EXCEPTIONS_LIB_A} +OBJS_EXTRA = ${EXCEPTIONS_EXCEPTIONS_A} ${RUNTIME_RUNTIME_A} +LIB_OBJS_EXTRA = ${EXCEPTIONS_EXCEPTIONS_LIB_A} ${RUNTIME_RUNTIME_LIB_A} include ../buildsys.mk -CPPFLAGS += -I. -I.. -Iexceptions +CPPFLAGS += -I. -I.. -Iexceptions -Iruntime LD = ${OBJC} LDFLAGS += ${REEXPORT_LIBOBJC} ${MACH_ALIAS_LIST} Index: src/OFBlock.m ================================================================== --- src/OFBlock.m +++ src/OFBlock.m @@ -22,10 +22,12 @@ #include #if defined(OF_APPLE_RUNTIME) && !defined(__OBJC2__) # import +#elif defined(OF_OBJFW_RUNTIME) +# import "runtime-private.h" #endif #import "OFBlock.h" #import "OFAllocFailedException.h" @@ -71,65 +73,44 @@ @protocol RetainRelease - retain; - (void)release; @end -#if defined(OF_OBJFW_RUNTIME) || defined(OF_GNU_RUNTIME) || \ - defined(OF_OLD_GNU_RUNTIME) -struct objc_abi_class { - struct objc_abi_metaclass *metaclass; - const char *superclass, *name; - unsigned long version, info, instance_size; - void *ivars, *methodlist, *dtable, *subclass_list, *sibling_class; - void *protocols, *gc_object_type; - long abi_version; - void *ivar_offsets, *properties; -}; - -struct objc_abi_metaclass { - const char *metaclass, *superclass, *name; - unsigned long version, info, instance_size; - void *ivars, *methodlist, *dtable, *subclass_list, *sibling_class; - void *protocols, *gc_object_type; - long abi_version; - void *ivar_offsets, *properties; -}; - #ifndef OF_OBJFW_RUNTIME -/* ObjFW-RT already defines those */ enum objc_abi_class_info { OBJC_CLASS_INFO_CLASS = 0x01, OBJC_CLASS_INFO_METACLASS = 0x02 }; #endif -extern void __objc_exec_class(void*); +#if defined(OF_OBJFW_RUNTIME) +extern void __objc_exec_class(struct objc_abi_module*); /* Begin of ObjC module */ -static struct objc_abi_metaclass _NSConcreteStackBlock_metaclass = { - "OFBlock", "OFBlock", "OFStackBlock", 8, OBJC_CLASS_INFO_METACLASS, - sizeof(struct objc_abi_class), NULL, NULL +static struct objc_abi_class _NSConcreteStackBlock_metaclass = { + (struct objc_abi_class*)(void*)"OFBlock", "OFBlock", "OFStackBlock", 8, + OBJC_CLASS_INFO_METACLASS, sizeof(struct objc_abi_class), NULL, NULL }; struct objc_abi_class _NSConcreteStackBlock = { &_NSConcreteStackBlock_metaclass, "OFBlock", "OFStackBlock", 8, OBJC_CLASS_INFO_CLASS, sizeof(of_block_literal_t), NULL, NULL }; -static struct objc_abi_metaclass _NSConcreteGlobalBlock_metaclass = { - "OFBlock", "OFBlock", "OFGlobalBlock", 8, OBJC_CLASS_INFO_METACLASS, - sizeof(struct objc_abi_class), NULL, NULL +static struct objc_abi_class _NSConcreteGlobalBlock_metaclass = { + (struct objc_abi_class*)(void*)"OFBlock", "OFBlock", "OFGlobalBlock", 8, + OBJC_CLASS_INFO_METACLASS, sizeof(struct objc_abi_class), NULL, NULL }; struct objc_abi_class _NSConcreteGlobalBlock = { &_NSConcreteGlobalBlock_metaclass, "OFBlock", "OFGlobalBlock", 8, OBJC_CLASS_INFO_CLASS, sizeof(of_block_literal_t), NULL, NULL }; -static struct objc_abi_metaclass _NSConcreteMallocBlock_metaclass = { - "OFBlock", "OFBlock", "OFMallocBlock", 8, OBJC_CLASS_INFO_METACLASS, - sizeof(struct objc_abi_class), NULL, NULL +static struct objc_abi_class _NSConcreteMallocBlock_metaclass = { + (struct objc_abi_class*)(void*)"OFBlock", "OFBlock", "OFMallocBlock", 8, + OBJC_CLASS_INFO_METACLASS, sizeof(struct objc_abi_class), NULL, NULL }; struct objc_abi_class _NSConcreteMallocBlock = { &_NSConcreteMallocBlock_metaclass, "OFBlock", "OFMallocBlock", 8, OBJC_CLASS_INFO_CLASS, sizeof(of_block_literal_t), NULL, NULL @@ -138,20 +119,21 @@ static struct { unsigned int unknown; struct objc_abi_selector *sel_refs; uint16_t cls_def_cnt, cat_def_cnt; void *defs[4]; -} symtab = { 0, NULL, 3, 0, { - &_NSConcreteStackBlock, &_NSConcreteGlobalBlock, - &_NSConcreteMallocBlock, NULL -}}; - -static struct { - unsigned long version, size; - const char *name; - void *symtab; -} module = { 8, sizeof(module), NULL, &symtab }; +} symtab = { + 0, NULL, 3, 0, + { + &_NSConcreteStackBlock, &_NSConcreteGlobalBlock, + &_NSConcreteMallocBlock, NULL + } +}; + +static struct objc_abi_module module = { + 8, sizeof(module), NULL, (struct objc_abi_symtab*)&symtab +}; static void __attribute__((constructor)) constructor(void) { __objc_exec_class(&module); Index: src/OFIntrospection.m ================================================================== --- src/OFIntrospection.m +++ src/OFIntrospection.m @@ -16,14 +16,12 @@ #include "config.h" #include -#if defined(OF_APPLE_RUNTIME) || defined(OF_GNU_RUNTIME) +#if defined(OF_APPLE_RUNTIME) # import -#elif defined(OF_OLD_GNU_RUNTIME) -# import #endif #import "OFIntrospection.h" #import "OFString.h" #import "OFArray.h" @@ -30,11 +28,11 @@ #import "OFAutoreleasePool.h" #import "macros.h" @implementation OFMethod -#if defined(OF_APPLE_RUNTIME) || defined(OF_GNU_RUNTIME) +#if defined(OF_APPLE_RUNTIME) - _initWithMethod: (Method)method { self = [super init]; @try { @@ -46,28 +44,10 @@ } @catch (id e) { [self release]; @throw e; } - return self; -} -#elif defined(OF_OLD_GNU_RUNTIME) -- _initWithMethod: (Method_t)method -{ - self = [super init]; - - @try { - selector = method->method_name; - name = [[OFString alloc] - initWithCString: sel_get_name(selector) - encoding: OF_STRING_ENCODING_ASCII]; - typeEncoding = method->method_types; - } @catch (id e) { - [self release]; - @throw e; - } - return self; } #endif - (void)dealloc @@ -98,11 +78,11 @@ name, typeEncoding]; } @end @implementation OFInstanceVariable -#if defined(OF_APPLE_RUNTIME) || defined(OF_GNU_RUNTIME) +#if defined(OF_APPLE_RUNTIME) - _initWithIvar: (Ivar)ivar { self = [super init]; @try { @@ -114,28 +94,10 @@ } @catch (id e) { [self release]; @throw e; } - return self; -} -#elif defined(OF_OLD_GNU_RUNTIME) -- _initWithIvar: (Ivar_t)ivar -{ - self = [super init]; - - @try { - name = [[OFString alloc] - initWithCString: ivar->ivar_name - encoding: OF_STRING_ENCODING_ASCII]; - offset = ivar->ivar_offset; - typeEncoding = ivar->ivar_type; - } @catch (id e) { - [self release]; - @throw e; - } - return self; } #endif - (void)dealloc @@ -170,11 +132,11 @@ #ifdef OF_HAVE_PROPERTIES @implementation OFProperty @synthesize name, attributes; -#if defined(OF_APPLE_RUNTIME) || defined(OF_GNU_RUNTIME) +#if defined(OF_APPLE_RUNTIME) - _initWithProperty: (objc_property_t)property { self = [super init]; @try { @@ -220,29 +182,27 @@ { self = [super init]; @try { OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; -#if defined(OF_APPLE_RUNTIME) || defined(OF_GNU_RUNTIME) +#if defined(OF_APPLE_RUNTIME) Method *methodList; Ivar *ivarList; # ifdef OF_HAVE_PROPERTIES objc_property_t *propertyList; # endif unsigned i, count; -#elif defined(OF_OLD_GNU_RUNTIME) - MethodList_t methodList; #endif classMethods = [[OFMutableArray alloc] init]; instanceMethods = [[OFMutableArray alloc] init]; instanceVariables = [[OFMutableArray alloc] init]; #ifdef OF_HAVE_PROPERTIES properties = [[OFMutableArray alloc] init]; #endif -#if defined(OF_APPLE_RUNTIME) || defined(OF_GNU_RUNTIME) +#if defined(OF_APPLE_RUNTIME) methodList = class_copyMethodList(((OFObject*)class)->isa, &count); @try { for (i = 0; i < count; i++) { [classMethods addObject: [[[OFMethod alloc] @@ -288,46 +248,10 @@ [pool releaseObjects]; } } @finally { free(propertyList); } -#elif defined(OF_OLD_GNU_RUNTIME) - for (methodList = class->class_pointer->methods; - methodList != NULL; methodList = methodList->method_next) { - int i; - - for (i = 0; i < methodList->method_count; i++) { - [classMethods addObject: [[[OFMethod alloc] - _initWithMethod: - &methodList->method_list[i]] autorelease]]; - [pool releaseObjects]; - } - } - - for (methodList = class->methods; methodList != NULL; - methodList = methodList->method_next) { - int i; - - for (i = 0; i < methodList->method_count; i++) { - [instanceMethods addObject: [[[OFMethod alloc] - _initWithMethod: - &methodList->method_list[i]] autorelease]]; - [pool releaseObjects]; - } - } - - if (class->ivars != NULL) { - int i; - - for (i = 0; i < class->ivars->ivar_count; i++) { - [instanceVariables addObject: - [[[OFInstanceVariable alloc] - _initWithIvar: class->ivars->ivar_list + i] - autorelease]]; - [pool releaseObjects]; - } - } #endif [classMethods makeImmutable]; [instanceMethods makeImmutable]; [instanceVariables makeImmutable]; Index: src/OFObject.h ================================================================== --- src/OFObject.h +++ src/OFObject.h @@ -26,11 +26,11 @@ #include #include #include #ifdef OF_OBJFW_RUNTIME -# import +# import "runtime.h" #else # import #endif #define OF_RETAIN_COUNT_MAX UINT_MAX Index: src/OFObject.m ================================================================== --- src/OFObject.m +++ src/OFObject.m @@ -38,16 +38,14 @@ #import "OFOutOfMemoryException.h" #import "OFOutOfRangeException.h" #import "macros.h" -#if (defined(OF_APPLE_RUNTIME) && __OBJC2__) || defined(OF_GNU_RUNTIME) +#if defined(OF_APPLE_RUNTIME) && __OBJC2__ # import #elif defined(OF_OBJFW_RUNTIME) -# import -#elif defined(OF_OLD_GNU_RUNTIME) -# import +# import "runtime.h" #endif #ifdef _WIN32 # include #endif @@ -79,14 +77,10 @@ #define PRE_MEM_ALIGN ((sizeof(struct pre_mem) + \ (__BIGGEST_ALIGNMENT__ - 1)) & ~(__BIGGEST_ALIGNMENT__ - 1)) #define PRE_MEM(mem) ((struct pre_mem*)(void*)((char*)mem - PRE_MEM_ALIGN)) -#ifdef OF_OLD_GNU_RUNTIME -extern void __objc_update_dispatch_table_for_class(Class); -#endif - static struct { Class isa; } alloc_failed_exception; static Class autoreleasePool = Nil; @@ -94,19 +88,16 @@ static SEL cxx_destruct = NULL; size_t of_pagesize; size_t of_num_cpus; -#ifdef NEED_OBJC_SYNC_INIT +#ifdef OF_OBJFW_RUNTIME extern BOOL objc_sync_init(); -#endif - -#ifdef NEED_OBJC_PROPERTIES_INIT extern BOOL objc_properties_init(); #endif -#if (defined(OF_APPLE_RUNTIME) && __OBJC2__) || defined(OF_GNU_RUNTIME) +#if defined(OF_APPLE_RUNTIME) && __OBJC2__ static void uncaught_exception_handler(id exception) { fprintf(stderr, "\nUnhandled exception:\n%s\n", [[exception description] UTF8String]); @@ -127,14 +118,10 @@ { enumeration_mutation_handler(object); } #endif -#if defined(HAVE_OBJC_ENUMERATIONMUTATION) && defined(OF_OLD_GNU_RUNTIME) -extern void objc_setEnumerationMutationHandler(void(*handler)(id)); -#endif - id of_alloc_object(Class class, size_t extraSize, size_t extraAlignment, void **extra) { OFObject *instance; @@ -184,65 +171,33 @@ { return [[object description] cStringWithEncoding: OF_STRING_ENCODING_NATIVE]; } -#ifdef OF_OLD_GNU_RUNTIME -static BOOL -protocol_conformsToProtocol(Protocol *a, Protocol *b) -{ - /* - * This function is an ugly workaround for a bug that only happens with - * Clang 2.9 together with the libobjc from GCC 4.6. - * Since the instance variables of Protocol are @private, we have to - * cast them to a struct here in order to access them. - */ - struct objc_protocol { - Class isa; - const char *protocol_name; - struct objc_protocol_list *protocol_list; - } *pa = (struct objc_protocol*)a, *pb = (struct objc_protocol*)b; - struct objc_protocol_list *pl; - size_t i; - - if (!strcmp(pa->protocol_name, pb->protocol_name)) - return YES; - - for (pl = pa->protocol_list; pl != NULL; pl = pl->next) - for (i = 0; i < pl->count; i++) - if (protocol_conformsToProtocol(pl->list[i], b)) - return YES; - - return NO; -} -#endif - /* References for static linking */ void _references_to_categories_of_OFObject(void) { _OFObject_Serialization_reference = 1; } @implementation OFObject + (void)load { -#ifdef NEED_OBJC_SYNC_INIT +#ifdef OF_OBJFW_RUNTIME if (!objc_sync_init()) { fputs("Runtime error: objc_sync_init() failed!\n", stderr); abort(); } -#endif -#ifdef NEED_OBJC_PROPERTIES_INIT if (!objc_properties_init()) { fputs("Runtime error: objc_properties_init() failed!\n", stderr); abort(); } #endif -#if (defined(OF_APPLE_RUNTIME) && __OBJC2__) || defined(OF_GNU_RUNTIME) +#if defined(OF_APPLE_RUNTIME) && __OBJC2__ objc_setUncaughtExceptionHandler(uncaught_exception_handler); #endif #ifdef HAVE_OBJC_ENUMERATIONMUTATION objc_setEnumerationMutationHandler(enumeration_mutation_handler); @@ -314,52 +269,27 @@ return class_getSuperclass(self); } + (BOOL)instancesRespondToSelector: (SEL)selector { -#ifdef OF_OLD_GNU_RUNTIME - return class_get_instance_method(self, selector) != METHOD_NULL; -#else return class_respondsToSelector(self, selector); -#endif } + (BOOL)conformsToProtocol: (Protocol*)protocol { -#ifdef OF_OLD_GNU_RUNTIME - Class c; - struct objc_protocol_list *pl; - size_t i; - - for (c = self; c != Nil; c = class_get_super_class(c)) - for (pl = c->protocols; pl != NULL; pl = pl->next) - for (i = 0; i < pl->count; i++) - if (protocol_conformsToProtocol(pl->list[i], - protocol)) - return YES; - - return NO; -#else Class c; for (c = self; c != Nil; c = class_getSuperclass(c)) if (class_conformsToProtocol(c, protocol)) return YES; return NO; -#endif } + (IMP)instanceMethodForSelector: (SEL)selector { -#if defined(OF_OBJFW_RUNTIME) - return objc_get_instance_method(self, selector); -#elif defined(OF_OLD_GNU_RUNTIME) - return method_get_imp(class_get_instance_method(self, selector)); -#else return class_getMethodImplementation(self, selector); -#endif } + (const char*)typeEncodingForInstanceSelector: (SEL)selector { #if defined(OF_OBJFW_RUNTIME) @@ -368,19 +298,10 @@ if ((ret = objc_get_type_encoding(self, selector)) == NULL) @throw [OFNotImplementedException exceptionWithClass: self selector: selector]; return ret; -#elif defined(OF_OLD_GNU_RUNTIME) - Method_t m; - - if ((m = class_get_instance_method(self, selector)) == NULL || - m->method_types == NULL) - @throw [OFNotImplementedException exceptionWithClass: self - selector: selector]; - - return m->method_types; #else Method m; const char *ret; if ((m = class_getInstanceMethod(self, selector)) == NULL || @@ -427,121 +348,30 @@ + (IMP)replaceInstanceMethod: (SEL)selector withImplementation: (IMP)implementation typeEncoding: (const char*)typeEncoding { -#if defined(OF_APPLE_RUNTIME) || defined(OF_GNU_RUNTIME) return class_replaceMethod(self, selector, implementation, typeEncoding); -#elif defined(OF_OLD_GNU_RUNTIME) - MethodList_t methodList; - - for (methodList = ((Class)self)->methods; methodList != NULL; - methodList = methodList->method_next) { - int i; - - for (i = 0; i < methodList->method_count; i++) { - if (sel_eq(methodList->method_list[i].method_name, - selector)) { - IMP oldImp; - oldImp = methodList->method_list[i].method_imp; - - methodList->method_list[i].method_imp = - implementation; - - __objc_update_dispatch_table_for_class(self); - - return oldImp; - } - } - } - - if ((methodList = malloc(sizeof(*methodList))) == NULL) - @throw [OFOutOfMemoryException - exceptionWithClass: self - requestedSize: sizeof(*methodList)]; - - methodList->method_next = ((Class)self)->methods; - methodList->method_count = 1; - - methodList->method_list[0].method_name = selector; - methodList->method_list[0].method_types = typeEncoding; - methodList->method_list[0].method_imp = implementation; - - ((Class)self)->methods = methodList; - - __objc_update_dispatch_table_for_class(self); - - return (IMP)nil; -#else - @throw [OFNotImplementedException exceptionWithClass: self - selector: _cmd]; -#endif } + (IMP)replaceClassMethod: (SEL)selector withImplementation: (IMP)implementation typeEncoding: (const char*)typeEncoding { -#if defined(OF_APPLE_RUNTIME) || defined(OF_GNU_RUNTIME) return class_replaceMethod(((OFObject*)self)->isa, selector, implementation, typeEncoding); -#elif defined(OF_OLD_GNU_RUNTIME) - MethodList_t methodList; - - for (methodList = ((Class)self->class_pointer)->methods; - methodList != NULL; methodList = methodList->method_next) { - int i; - - for (i = 0; i < methodList->method_count; i++) { - if (sel_eq(methodList->method_list[i].method_name, - selector)) { - IMP oldImp; - oldImp = methodList->method_list[i].method_imp; - - methodList->method_list[i].method_imp = - implementation; - - __objc_update_dispatch_table_for_class( - (Class)self->class_pointer); - - return oldImp; - } - } - } - - if ((methodList = malloc(sizeof(*methodList))) == NULL) - @throw [OFOutOfMemoryException - exceptionWithClass: self - requestedSize: sizeof(*methodList)]; - - methodList->method_next = ((Class)self->class_pointer)->methods; - methodList->method_count = 1; - - methodList->method_list[0].method_name = selector; - methodList->method_list[0].method_types = typeEncoding; - methodList->method_list[0].method_imp = implementation; - - ((Class)self->class_pointer)->methods = methodList; - - __objc_update_dispatch_table_for_class((Class)self->class_pointer); - - return (IMP)nil; -#else - @throw [OFNotImplementedException exceptionWithClass: self - selector: _cmd]; -#endif } + (void)inheritMethodsFromClass: (Class)class { Class superclass = [self superclass]; if ([self isSubclassOfClass: class]) return; -#if defined(OF_APPLE_RUNTIME) || defined(OF_GNU_RUNTIME) +#if defined(OF_APPLE_RUNTIME) Method *methodList; unsigned i, count; methodList = class_copyMethodList(((OFObject*)class)->isa, &count); @try { @@ -578,19 +408,19 @@ withMethodFromClass: class]; } } @finally { free(methodList); } -#elif defined(OF_OLD_GNU_RUNTIME) - MethodList_t methodList; +#elif defined(OF_OBJFW_RUNTIME) + struct objc_method_list *methodlist; - for (methodList = class->class_pointer->methods; - methodList != NULL; methodList = methodList->method_next) { + for (methodlist = class->isa->methodlist; + methodlist != NULL; methodlist = methodlist->next) { int i; - for (i = 0; i < methodList->method_count; i++) { - SEL selector = methodList->method_list[i].method_name; + for (i = 0; i < methodlist->count; i++) { + SEL selector = (SEL)&methodlist->methods[i].sel; /* * Don't replace methods implemented in receiving class. */ if ([self methodForSelector: selector] != @@ -600,16 +430,16 @@ [self replaceClassMethod: selector withMethodFromClass: class]; } } - for (methodList = class->methods; methodList != NULL; - methodList = methodList->method_next) { + for (methodlist = class->methodlist; methodlist != NULL; + methodlist = methodlist->next) { int i; - for (i = 0; i < methodList->method_count; i++) { - SEL selector = methodList->method_list[i].method_name; + for (i = 0; i < methodlist->count; i++) { + SEL selector = (SEL)&methodlist->methods[i].sel; /* * Don't replace methods implemented in receiving class. */ if ([self instanceMethodForSelector: selector] != @@ -618,13 +448,10 @@ [self replaceInstanceMethod: selector withMethodFromClass: class]; } } -#else - @throw [OFNotImplementedException exceptionWithClass: self - selector: _cmd]; #endif [self inheritMethodsFromClass: [class superclass]]; } @@ -676,32 +503,21 @@ return (isa == class); } - (BOOL)respondsToSelector: (SEL)selector { -#ifdef OF_OLD_GNU_RUNTIME - 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 } - (BOOL)conformsToProtocol: (Protocol*)protocol { return [isa conformsToProtocol: protocol]; } - (IMP)methodForSelector: (SEL)selector { -#if defined(OF_OBJFW_RUNTIME) || defined(OF_OLD_GNU_RUNTIME) - return objc_msg_lookup(self, selector); -#else return class_getMethodImplementation(isa, selector); -#endif } - (id)performSelector: (SEL)selector { id (*imp)(id, SEL) = (id(*)(id, SEL))[self methodForSelector: selector]; @@ -736,19 +552,10 @@ if ((ret = objc_get_type_encoding(isa, selector)) == NULL) @throw [OFNotImplementedException exceptionWithClass: isa selector: selector]; return ret; -#elif defined(OF_OLD_GNU_RUNTIME) - Method_t m; - - if ((m = class_get_instance_method(isa, selector)) == NULL || - m->method_types == NULL) - @throw [OFNotImplementedException exceptionWithClass: isa - selector: selector]; - - return m->method_types; #else Method m; const char *ret; if ((m = class_getInstanceMethod(isa, selector)) == NULL || @@ -943,11 +750,11 @@ - autorelease { /* * Cache OFAutoreleasePool since class lookups are expensive with the - * GNU runtime. + * GNU ABI. */ if (autoreleasePool == Nil) autoreleasePool = [OFAutoreleasePool class]; [autoreleasePool addObject: self]; Index: src/OFThread.m ================================================================== --- src/OFThread.m +++ src/OFThread.m @@ -25,14 +25,10 @@ # include #else # include #endif -#if defined(OF_GNU_RUNTIME) || defined(OF_OLD_GNU_RUNTIME) -# import -#endif - #import "OFThread.h" #import "OFList.h" #import "OFDate.h" #import "OFAutoreleasePool.h" @@ -59,14 +55,10 @@ static id call_main(id object) { OFThread *thread = (OFThread*)object; -#if defined(OF_GNU_RUNTIME) || defined(OF_OLD_GNU_RUNTIME) - objc_thread_add(); -#endif - if (!of_tlskey_set(threadSelf, thread)) @throw [OFInitializationFailedException exceptionWithClass: [thread class]]; /* @@ -89,14 +81,10 @@ [OFTLSKey callAllDestructors]; [OFAutoreleasePool _releaseAll]; [thread release]; -#if defined(OF_GNU_RUNTIME) || defined(OF_OLD_GNU_RUNTIME) - objc_thread_remove(); -#endif - return 0; } @implementation OFThread #if defined(OF_HAVE_PROPERTIES) && defined(OF_HAVE_BLOCKS) @@ -225,14 +213,10 @@ [OFTLSKey callAllDestructors]; [OFAutoreleasePool _releaseAll]; [thread release]; -#if defined(OF_GNU_RUNTIME) || defined(OF_OLD_GNU_RUNTIME) - objc_thread_remove(); -#endif - of_thread_exit(); } - initWithObject: (id)object_ { Index: src/base64.h ================================================================== --- src/base64.h +++ src/base64.h @@ -22,11 +22,11 @@ #ifndef __STDC_CONSTANT_MACROS # define __STDC_CONSTANT_MACROS #endif #ifdef OF_OBJFW_RUNTIME -# import +# import "runtime.h" #else # import #endif @class OFString; Index: src/exceptions/Makefile ================================================================== --- src/exceptions/Makefile +++ src/exceptions/Makefile @@ -57,7 +57,7 @@ INCLUDES = ${SRCS:.m=.h} include ../../buildsys.mk -CPPFLAGS += -I. -I.. -I../.. +CPPFLAGS += -I. -I.. -I../.. -I../runtime LD = ${OBJC} Index: src/macros.h ================================================================== --- src/macros.h +++ src/macros.h @@ -17,14 +17,12 @@ #import "OFObject.h" #include #include -#if defined(OF_APPLE_RUNTIME) || defined(OF_GNU_RUNTIME) +#if defined(OF_APPLE_RUNTIME) # import -#elif defined(OF_OLD_GNU_RUNTIME) -# import #endif #ifdef _PSP # define INTMAX_MAX LONG_LONG_MAX #endif @@ -79,22 +77,14 @@ # elif defined(__arm__) || defined(__ARM__) # define OF_ARM_ASM # endif #endif -#if defined(OF_OLD_GNU_RUNTIME) || defined(OF_OBJFW_RUNTIME) +#ifdef OF_OBJFW_RUNTIME # define objc_lookUpClass objc_lookup_class #endif -#ifdef OF_OLD_GNU_RUNTIME -# define class_getInstanceSize class_get_instance_size -# define class_getName class_get_class_name -# define class_getSuperclass class_get_super_class -# define sel_getName sel_get_name -# define sel_registerName sel_get_uid -#endif - #ifndef _WIN32 # define OF_PATH_DELIMITER '/' #else # define OF_PATH_DELIMITER '\\' #endif DELETED src/objc_properties.m Index: src/objc_properties.m ================================================================== --- src/objc_properties.m +++ src/objc_properties.m @@ -1,122 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 2010, 2011, 2012 - * Jonathan Schleifer - * - * All rights reserved. - * - * This file is part of ObjFW. It may be distributed under the terms of the - * Q Public License 1.0, which can be found in the file LICENSE.QPL included in - * the packaging of this file. - * - * Alternatively, it may be distributed under the terms of the GNU General - * Public License, either version 2 or 3, which can be found in the file - * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this - * file. - */ - -#include "config.h" - -#include - -#import "OFObject.h" - -#ifdef OF_THREADS -# import "threading.h" -# define NUM_SPINLOCKS 8 /* needs to be a power of 2 */ -# define SPINLOCK_HASH(p) ((unsigned)((uintptr_t)p >> 4) & (NUM_SPINLOCKS - 1)) -static of_spinlock_t spinlocks[NUM_SPINLOCKS]; -#endif - -BOOL -objc_properties_init(void) -{ -#ifdef OF_THREADS - size_t i; - - for (i = 0; i < NUM_SPINLOCKS; i++) - if (!of_spinlock_new(&spinlocks[i])) - return NO; -#endif - - return YES; -} - -id -objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) -{ - if (atomic) { - id *ptr = (id*)(void*)((char*)self + offset); -#ifdef OF_THREADS - unsigned hash = SPINLOCK_HASH(ptr); - - assert(of_spinlock_lock(&spinlocks[hash])); - - @try { - return [[*ptr retain] autorelease]; - } @finally { - assert(of_spinlock_unlock(&spinlocks[hash])); - } -#else - return [[*ptr retain] autorelease]; -#endif - } - - return *(id*)(void*)((char*)self + offset); -} - -void -objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id value, BOOL atomic, - signed char copy) -{ - if (atomic) { - id *ptr = (id*)(void*)((char*)self + offset); -#ifdef OF_THREADS - unsigned hash = SPINLOCK_HASH(ptr); - - assert(of_spinlock_lock(&spinlocks[hash])); - - @try { -#endif - id old = *ptr; - - switch (copy) { - case 0: - *ptr = [value retain]; - break; - case 2: - *ptr = [value mutableCopy]; - break; - default: - *ptr = [value copy]; - } - - [old release]; -#ifdef OF_THREADS - } @finally { - assert(of_spinlock_unlock(&spinlocks[hash])); - } -#endif - - return; - } - - id *ptr = (id*)(void*)((char*)self + offset); - id old = *ptr; - - switch (copy) { - case 0: - *ptr = [value retain]; - break; - case 2: - /* - * Apple uses this to indicate that the copy should be mutable. - * Please hit them for abusing a poor BOOL! - */ - *ptr = [value mutableCopy]; - break; - default: - *ptr = [value copy]; - } - - [old release]; -} DELETED src/objc_sync.m Index: src/objc_sync.m ================================================================== --- src/objc_sync.m +++ src/objc_sync.m @@ -1,208 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 2010, 2011, 2012 - * Jonathan Schleifer - * - * All rights reserved. - * - * This file is part of ObjFW. It may be distributed under the terms of the - * Q Public License 1.0, which can be found in the file LICENSE.QPL included in - * the packaging of this file. - * - * Alternatively, it may be distributed under the terms of the GNU General - * Public License, either version 2 or 3, which can be found in the file - * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this - * file. - */ - -#include "config.h" - -#include -#include -#include - -#include - -#ifdef OF_OBJFW_RUNTIME -# import -#else -# import -#endif - -#import "threading.h" - -struct lock_s { - id object; - size_t count; - size_t recursion; - of_thread_t thread; - of_mutex_t mutex; -}; - -static of_mutex_t mutex; -static struct lock_s *locks = NULL; -static ssize_t numLocks = 0; - -#define SYNC_ERR(f) \ - { \ - fprintf(stderr, "WARNING: %s failed in line %d!\n" \ - "WARNING: This might result in a race " \ - "condition!\n", f, __LINE__); \ - return 1; \ - } - -BOOL -objc_sync_init(void) -{ - return of_mutex_new(&mutex); -} - -int -objc_sync_enter(id object) -{ - ssize_t i; - - if (object == nil) - return 0; - - if (!of_mutex_lock(&mutex)) - SYNC_ERR("of_mutex_lock(&mutex)"); - - for (i = numLocks - 1; i >= 0; i--) { - if (locks[i].object == object) { - if (of_thread_is_current(locks[i].thread)) - locks[i].recursion++; - else { - /* Make sure objc_sync_exit doesn't free it */ - locks[i].count++; - - /* Unlock so objc_sync_exit can return */ - if (!of_mutex_unlock(&mutex)) - SYNC_ERR("of_mutex_unlock(&mutex)"); - - if (!of_mutex_lock(&locks[i].mutex)) { - of_mutex_unlock(&mutex); - SYNC_ERR( - "of_mutex_lock(&locks[i].mutex"); - } - - if (!of_mutex_lock(&mutex)) - SYNC_ERR("of_mutex_lock(&mutex)"); - - assert(locks[i].recursion == 0); - - /* Update lock's active thread */ - locks[i].thread = of_thread_current(); - } - - if (!of_mutex_unlock(&mutex)) - SYNC_ERR("of_mutex_unlock(&mutex)"); - - return 0; - } - } - - if (locks == NULL) { - if ((locks = malloc(sizeof(struct lock_s))) == NULL) { - of_mutex_unlock(&mutex); - SYNC_ERR("malloc(...)"); - } - } else { - struct lock_s *new_locks; - - if ((new_locks = realloc(locks, (numLocks + 1) * - sizeof(struct lock_s))) == NULL) { - of_mutex_unlock(&mutex); - SYNC_ERR("realloc(...)"); - } - - locks = new_locks; - } - - locks[numLocks].object = object; - locks[numLocks].count = 1; - locks[numLocks].recursion = 0; - locks[numLocks].thread = of_thread_current(); - - if (!of_mutex_new(&locks[numLocks].mutex)) { - of_mutex_unlock(&mutex); - SYNC_ERR("of_mutex_new(&locks[numLocks].mutex"); - } - - if (!of_mutex_lock(&locks[numLocks].mutex)) { - of_mutex_unlock(&mutex); - SYNC_ERR("of_mutex_lock(&locks[numLocks].mutex"); - } - - numLocks++; - - if (!of_mutex_unlock(&mutex)) - SYNC_ERR("of_mutex_unlock(&mutex)"); - - return 0; -} - -int -objc_sync_exit(id object) -{ - ssize_t i; - - if (object == nil) - return 0; - - if (!of_mutex_lock(&mutex)) - SYNC_ERR("of_mutex_lock(&mutex)"); - - for (i = numLocks - 1; i >= 0; i--) { - if (locks[i].object == object) { - if (locks[i].recursion > 0 && - of_thread_is_current(locks[i].thread)) { - locks[i].recursion--; - - if (!of_mutex_unlock(&mutex)) - SYNC_ERR("of_mutex_unlock(&mutex)"); - - return 0; - } - - if (!of_mutex_unlock(&locks[i].mutex)) { - of_mutex_unlock(&mutex); - SYNC_ERR("of_mutex_unlock(&locks[i].mutex)"); - } - - locks[i].count--; - - if (locks[i].count == 0) { - struct lock_s *new_locks = NULL; - - if (!of_mutex_free(&locks[i].mutex)) { - of_mutex_unlock(&mutex); - SYNC_ERR( - "of_mutex_free(&locks[i].mutex"); - } - - numLocks--; - locks[i] = locks[numLocks]; - - if (numLocks == 0) { - free(locks); - new_locks = NULL; - } else if ((new_locks = realloc(locks, - numLocks * sizeof(struct lock_s))) == - NULL) { - of_mutex_unlock(&mutex); - SYNC_ERR("realloc(...)"); - } - - locks = new_locks; - } - - if (!of_mutex_unlock(&mutex)) - SYNC_ERR("of_mutex_unlock(&mutex)"); - - return 0; - } - } - - of_mutex_unlock(&mutex); - SYNC_ERR("objc_sync_exit()"); -} ADDED src/runtime/Makefile Index: src/runtime/Makefile ================================================================== --- src/runtime/Makefile +++ src/runtime/Makefile @@ -0,0 +1,27 @@ +include ../../extra.mk + +STATIC_PIC_LIB_NOINST = ${RUNTIME_LIB_A} +STATIC_LIB_NOINST = ${RUNTIME_A} + +SRCS = category.m \ + class.m \ + hashtable.m \ + init.m \ + lookup.m \ + lookup-amd64-elf.S \ + lookup-x86-elf.S \ + property.m \ + protocol.m \ + selector.m \ + sparsearray.m \ + static-instances.m \ + synchronized.m \ + threading.m +INCLUDES = runtime.h + +include ../../buildsys.mk + +CPPFLAGS += -I. -I.. -I../.. +AS = ${OBJC} +ASFLAGS = ${CPPFLAGS} +LD = ${OBJC} ADDED src/runtime/category.m Index: src/runtime/category.m ================================================================== --- src/runtime/category.m +++ src/runtime/category.m @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include +#include +#include + +#import "runtime.h" +#import "runtime-private.h" + +static struct objc_hashtable *categories = NULL; + +static void +register_selectors(struct objc_abi_category *cat) +{ + struct objc_abi_method_list *ml; + unsigned int i; + + for (ml = cat->instance_methods; ml != NULL; ml = ml->next) + for (i = 0; i < ml->count; i++) + objc_register_selector( + (struct objc_abi_selector*)&ml->methods[i]); + + for (ml = cat->class_methods; ml != NULL; ml = ml->next) + for (i = 0; i < ml->count; i++) + objc_register_selector( + (struct objc_abi_selector*)&ml->methods[i]); +} + +static void +register_category(struct objc_abi_category *cat) +{ + struct objc_abi_category **cats; + Class cls = objc_classname_to_class(cat->class_name); + + if (categories == NULL) + categories = objc_hashtable_alloc(2); + + cats = (struct objc_abi_category**)objc_hashtable_get(categories, + cat->class_name); + + if (cats != NULL) { + struct objc_abi_category **ncats; + size_t i; + + for (i = 0; cats[i] != NULL; i++); + + if ((ncats = realloc(cats, + (i + 2) * sizeof(struct objc_abi_category*))) == NULL) + ERROR("Not enough memory for category %s of class %s!", + cat->category_name, cat->class_name); + + ncats[i] = cat; + ncats[i + 1] = NULL; + objc_hashtable_set(categories, cat->class_name, ncats); + + if (cls != Nil && cls->info & OBJC_CLASS_INFO_SETUP) { + objc_update_dtable(cls); + objc_update_dtable(cls->isa); + } + + return; + } + + if ((cats = malloc(2 * sizeof(struct objc_abi_category*))) == NULL) + ERROR("Not enough memory for category %s of class %s!\n", + cat->category_name, cat->class_name); + + cats[0] = cat; + cats[1] = NULL; + objc_hashtable_set(categories, cat->class_name, cats); + + if (cls != Nil && cls->info & OBJC_CLASS_INFO_SETUP) { + objc_update_dtable(cls); + objc_update_dtable(cls->isa); + } +} + +void +objc_register_all_categories(struct objc_abi_symtab *symtab) +{ + struct objc_abi_category **cats; + size_t i; + + cats = (struct objc_abi_category**)symtab->defs + symtab->cls_def_cnt; + + for (i = 0; i < symtab->cat_def_cnt; i++) { + register_selectors(cats[i]); + register_category(cats[i]); + } +} + +struct objc_category** +objc_categories_for_class(Class cls) +{ + if (categories == NULL) + return NULL; + + return (struct objc_category**)objc_hashtable_get(categories, + cls->name); +} + +void +objc_free_all_categories(void) +{ + uint32_t i; + + if (categories == NULL) + return; + + for (i = 0; i <= categories->last_idx; i++) + if (categories->data[i] != NULL) + free((void*)categories->data[i]->obj); + + objc_hashtable_free(categories); + categories = NULL; +} ADDED src/runtime/class.m Index: src/runtime/class.m ================================================================== --- src/runtime/class.m +++ src/runtime/class.m @@ -0,0 +1,538 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include +#include +#include + +#include + +#import "runtime.h" +#import "runtime-private.h" + +static struct objc_hashtable *classes = NULL; +static Class *load_queue = NULL; +static size_t load_queue_cnt = 0; + +static void +register_class(Class cls) +{ + if (classes == NULL) + classes = objc_hashtable_alloc(2); + + objc_hashtable_set(classes, cls->name, cls); +} + +static void +register_selectors(struct objc_abi_class *cls) +{ + struct objc_abi_method_list *ml; + unsigned int i; + + for (ml = cls->methodlist; ml != NULL; ml = ml->next) + for (i = 0; i < ml->count; i++) + objc_register_selector( + (struct objc_abi_selector*)&ml->methods[i]); +} + +static void +call_method(Class cls, const char *method) +{ + struct objc_method_list *ml; + SEL selector; + unsigned int i; + + selector = sel_registerName(method); + + for (ml = cls->isa->methodlist; ml != NULL; ml = ml->next) + for (i = 0; i < ml->count; i++) + if (sel_isEqual((SEL)&ml->methods[i].sel, selector)) + ((void(*)(id, SEL))ml->methods[i].imp)(cls, + selector); +} + +static BOOL +has_load(struct objc_abi_class *cls) +{ + struct objc_abi_method_list *ml; + unsigned int i; + + for (ml = cls->methodlist; ml != NULL; ml = ml->next) + for (i = 0; i < ml->count; i++) + if (!strcmp(ml->methods[i].name, "load")) + return YES; + + return NO; +} + +static void +call_load(Class cls) +{ + if (cls->info & OBJC_CLASS_INFO_LOADED) + return; + + if (cls->superclass != Nil) + call_load(cls->superclass); + + call_method(cls, "load"); + + cls->info |= OBJC_CLASS_INFO_LOADED; +} + +void +objc_update_dtable(Class cls) +{ + struct objc_method_list *ml; + struct objc_category **cats; + struct objc_sparsearray *dtable; + unsigned int i; + + if (cls->superclass != Nil) + dtable = objc_sparsearray_copy(cls->superclass->dtable); + else + dtable = objc_sparsearray_new(); + + for (ml = cls->methodlist; ml != NULL; ml = ml->next) + for (i = 0; i < ml->count; i++) + objc_sparsearray_set(dtable, + (uint32_t)ml->methods[i].sel.uid, + ml->methods[i].imp); + + if ((cats = objc_categories_for_class(cls)) != NULL) { + for (i = 0; cats[i] != NULL; i++) { + unsigned int j; + + ml = (cls->info & OBJC_CLASS_INFO_CLASS ? + cats[i]->instance_methods : cats[i]->class_methods); + + for (; ml != NULL; ml = ml->next) + for (j = 0; j < ml->count; j++) + objc_sparsearray_set(dtable, + (uint32_t)ml->methods[j].sel.uid, + ml->methods[j].imp); + } + } + + if (cls->dtable != NULL) + objc_sparsearray_free_when_singlethreaded(cls->dtable); + + cls->dtable = dtable; + + if (cls->subclass_list != NULL) + for (i = 0; cls->subclass_list[i] != NULL; i++) + objc_update_dtable(cls->subclass_list[i]); +} + +static void +add_subclass(Class cls) +{ + size_t i; + + if (cls->superclass->subclass_list == NULL) { + if ((cls->superclass->subclass_list = + malloc(2 * sizeof(Class))) == NULL) + ERROR("Not enough memory for subclass list of " + "class %s!", cls->superclass->name); + + cls->superclass->subclass_list[0] = cls; + cls->superclass->subclass_list[1] = Nil; + + return; + } + + for (i = 0; cls->superclass->subclass_list[i] != Nil; i++); + + cls->superclass->subclass_list = + realloc(cls->superclass->subclass_list, (i + 2) * sizeof(Class)); + + if (cls->superclass->subclass_list == NULL) + ERROR("Not enough memory for subclass list of class %s\n", + cls->superclass->name); + + cls->superclass->subclass_list[i] = cls; + cls->superclass->subclass_list[i + 1] = Nil; +} + +static void +setup_class(Class cls) +{ + const char *superclass; + + if (cls->info & OBJC_CLASS_INFO_SETUP) + return; + + if ((superclass = ((struct objc_abi_class*)cls)->superclass) != NULL) { + Class super = objc_classname_to_class(superclass); + + if (super == nil) + return; + + setup_class(super); + + if (!(super->info & OBJC_CLASS_INFO_SETUP)) + return; + + cls->superclass = super; + cls->isa->superclass = super->isa; + + add_subclass(cls); + add_subclass(cls->isa); + } else + cls->isa->superclass = cls; + + objc_update_dtable(cls); + objc_update_dtable(cls->isa); + + cls->info |= OBJC_CLASS_INFO_SETUP; + cls->isa->info |= OBJC_CLASS_INFO_SETUP; +} + +static void +initialize_class(Class cls) +{ + if (cls->info & OBJC_CLASS_INFO_INITIALIZED) + return; + + if (cls->superclass) + initialize_class(cls->superclass); + + /* + * Set it first to prevent calling it recursively due to message sends + * in the initialize method + */ + cls->info |= OBJC_CLASS_INFO_INITIALIZED; + cls->isa->info |= OBJC_CLASS_INFO_INITIALIZED; + + call_method(cls, "initialize"); +} + +void +objc_register_all_classes(struct objc_abi_symtab *symtab) +{ + size_t i; + + for (i = 0; i < symtab->cls_def_cnt; i++) + register_class((Class)symtab->defs[i]); + + for (i = 0; i < symtab->cls_def_cnt; i++) { + struct objc_abi_class *cls; + BOOL load; + + cls = (struct objc_abi_class*)symtab->defs[i]; + load = has_load(cls->metaclass); + + register_selectors(cls); + register_selectors(cls->metaclass); + + if (load) { + Class cls_ = (Class)cls; + + setup_class(cls_); + + if (cls_->info & OBJC_CLASS_INFO_SETUP) + call_load(cls_); + else { + if (load_queue == NULL) + load_queue = malloc(sizeof(Class)); + else + load_queue = realloc(load_queue, + sizeof(Class) * + (load_queue_cnt + 1)); + + if (load_queue == NULL) + ERROR("Not enough memory for load " + "queue!"); + + load_queue[load_queue_cnt++] = cls_; + } + } + } + + /* Process load queue */ + for (i = 0; i < load_queue_cnt; i++) { + setup_class(load_queue[i]); + + if (load_queue[i]->info & OBJC_CLASS_INFO_LOADED) { + call_load(load_queue[i]); + + load_queue_cnt--; + + if (load_queue_cnt == 0) { + free(load_queue); + load_queue = NULL; + continue; + } + + load_queue[i] = load_queue[load_queue_cnt]; + + load_queue = realloc(load_queue, + sizeof(Class) * load_queue_cnt); + + if (load_queue == NULL) + ERROR("Not enough memory for load queue!"); + } + } +} + +inline Class +objc_classname_to_class(const char *name) +{ + Class c; + + if (classes == NULL) + return Nil; + + objc_global_mutex_lock(); + c = (Class)objc_hashtable_get(classes, name); + objc_global_mutex_unlock(); + + return c; +} + +inline Class +objc_lookup_class(const char *name) +{ + Class cls = objc_classname_to_class(name); + + if (cls == NULL) + return Nil; + + if (cls->info & OBJC_CLASS_INFO_INITIALIZED) + return cls; + + objc_global_mutex_lock(); + + /* + * It's possible that two threads try to get a class at the same time. + * Make sure that the thread which held the lock did not already + * initialize it. + */ + if (cls->info & OBJC_CLASS_INFO_INITIALIZED) { + objc_global_mutex_unlock(); + return cls; + } + + setup_class(cls); + + if (!(cls->info & OBJC_CLASS_INFO_SETUP)) { + objc_global_mutex_unlock(); + return Nil; + } + + initialize_class(cls); + + objc_global_mutex_unlock(); + + return cls; +} + +Class +objc_get_class(const char *name) +{ + Class cls; + + if ((cls = objc_lookup_class(name)) == Nil) + ERROR("Class %s not found!", name); + + return cls; +} + +const char* +class_getName(Class cls) +{ + return cls->name; +} + +Class +class_getSuperclass(Class cls) +{ + return cls->superclass; +} + +BOOL +class_isKindOfClass(Class cls1, Class cls2) +{ + Class iter; + + for (iter = cls1; iter != Nil; iter = iter->superclass) + if (iter == cls2) + return YES; + + return NO; +} + +unsigned long +class_getInstanceSize(Class cls) +{ + return cls->instance_size; +} + +IMP +class_getMethodImplementation(Class cls, SEL sel) +{ + return objc_sparsearray_get(cls->dtable, (uint32_t)sel->uid); +} + +const char* +objc_get_type_encoding(Class cls, SEL sel) +{ + struct objc_method_list *ml; + struct objc_category **cats; + unsigned int i; + + objc_global_mutex_lock(); + + for (ml = cls->methodlist; ml != NULL; ml = ml->next) { + for (i = 0; i < ml->count; i++) { + if (ml->methods[i].sel.uid == sel->uid) { + const char *ret = ml->methods[i].sel.types; + objc_global_mutex_unlock(); + return ret; + } + } + } + + if ((cats = objc_categories_for_class(cls)) != NULL) { + for (; *cats != NULL; cats++) { + for (ml = (*cats)->instance_methods; ml != NULL; + ml = ml->next) { + for (i = 0; i < ml->count; i++) { + if (ml->methods[i].sel.uid == + sel->uid) { + const char *ret = + ml->methods[i].sel.types; + objc_global_mutex_unlock(); + return ret; + } + } + } + } + } + + objc_global_mutex_unlock(); + + return NULL; +} + +IMP +class_replaceMethod(Class cls, SEL sel, IMP newimp, const char *types) +{ + struct objc_method_list *ml; + struct objc_category **cats; + unsigned int i; + IMP oldimp; + + objc_global_mutex_lock(); + + for (ml = cls->methodlist; ml != NULL; ml = ml->next) { + for (i = 0; i < ml->count; i++) { + if (ml->methods[i].sel.uid == sel->uid) { + oldimp = ml->methods[i].imp; + + ml->methods[i].imp = newimp; + objc_update_dtable(cls); + + objc_global_mutex_unlock(); + + return oldimp; + } + } + } + + if ((cats = objc_categories_for_class(cls)) != NULL) { + for (; *cats != NULL; cats++) { + if (cls->info & OBJC_CLASS_INFO_METACLASS) + ml = (*cats)->class_methods; + else + ml = (*cats)->instance_methods; + + for (; ml != NULL; ml = ml->next) { + for (i = 0; i < ml->count; i++) { + if (ml->methods[i].sel.uid == + sel->uid) { + oldimp = ml->methods[i].imp; + + ml->methods[i].imp = newimp; + objc_update_dtable(cls); + + objc_global_mutex_unlock(); + + return oldimp; + } + } + } + } + } + + /* FIXME: We need a way to free this at objc_exit() */ + if ((ml = malloc(sizeof(struct objc_method_list))) == NULL) + ERROR("Not enough memory to replace method!"); + + ml->next = cls->methodlist; + ml->count = 1; + ml->methods[0].sel.uid = sel->uid; + ml->methods[0].sel.types = types; + ml->methods[0].imp = newimp; + + cls->methodlist = ml; + + objc_update_dtable(cls); + + objc_global_mutex_unlock(); + + return (IMP)nil; +} + +static void +free_class(Class rcls) +{ + struct objc_abi_class *cls = (struct objc_abi_class*)rcls; + + if (!(rcls->info & OBJC_CLASS_INFO_INITIALIZED)) + return; + + if (rcls->subclass_list != NULL) { + free(rcls->subclass_list); + rcls->subclass_list = NULL; + } + + objc_sparsearray_free(rcls->dtable); + rcls->dtable = NULL; + + if (rcls->superclass != Nil) + cls->superclass = rcls->superclass->name; +} + +void +objc_free_all_classes(void) +{ + uint32_t i; + + if (classes == NULL) + return; + + for (i = 0; i <= classes->last_idx; i++) { + if (classes->data[i] != NULL) { + free_class((Class)classes->data[i]->obj); + free_class(((Class)classes->data[i]->obj)->isa); + } + } + + objc_hashtable_free(classes); + classes = NULL; +} ADDED src/runtime/hashtable.m Index: src/runtime/hashtable.m ================================================================== --- src/runtime/hashtable.m +++ src/runtime/hashtable.m @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include +#include +#include +#include + +#include + +#import "runtime.h" +#import "runtime-private.h" + +uint32_t +objc_hash_string(const char *str) +{ + uint32_t hash = 0; + + while (*str != 0) { + hash += *str; + hash += (hash << 10); + hash ^= (hash >> 6); + str++; + } + + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + + return hash; +} + +struct objc_hashtable* +objc_hashtable_alloc(uint32_t size) +{ + struct objc_hashtable *h; + uint32_t i; + + if ((h = malloc(sizeof(struct objc_hashtable))) == NULL) + ERROR("Not enough memory to allocate hash table!"); + + h->count = 0; + h->last_idx = size - 1; + h->data = malloc(size * sizeof(struct objc_hashtable_bucket*)); + + if (h->data == NULL) + ERROR("Not enough memory to allocate hash table!"); + + for (i = 0; i < size; i++) + h->data[i] = NULL; + + return h; +} + +static void +insert(struct objc_hashtable *h, const char *key, const void *obj) +{ + uint32_t i, hash, last; + struct objc_hashtable_bucket *bucket; + + hash = objc_hash_string(key); + assert(h->count + 1 <= UINT32_MAX / 4); + + if ((h->count + 1) * 4 / (h->last_idx + 1) >= 3) { + struct objc_hashtable_bucket **ndata; + uint32_t nsize = (h->last_idx + 1) << 1; + + assert(nsize > 0); + + ndata = malloc(nsize * sizeof(struct objc_hashtable_bucket*)); + if (ndata == NULL) + ERROR("Not enough memory to insert into hash table!"); + + for (i = 0; i < nsize; i++) + ndata[i] = NULL; + + for (i = 0; i <= h->last_idx; i++) { + if (h->data[i] != NULL) { + uint32_t j; + + last = nsize; + + for (j = h->data[i]->hash & (nsize - 1); + j < last && ndata[j] != NULL; j++); + + if (j >= last) { + last = h->data[i]->hash & (nsize - 1); + + for (j = 0; j < last && + ndata[j] != NULL; j++); + } + + if (j >= last) + ERROR("No free bucket!"); + + ndata[j] = h->data[i]; + } + } + + free(h->data); + h->data = ndata; + h->last_idx = nsize - 1; + } + + last = h->last_idx + 1; + + for (i = hash & h->last_idx; i < last && h->data[i] != NULL; i++); + + if (i >= last) { + last = hash & h->last_idx; + + for (i = 0; i < last && h->data[i] != NULL; i++); + } + + if (i >= last) + ERROR("No free bucket!"); + + if ((bucket = malloc(sizeof(struct objc_hashtable_bucket))) == NULL) + ERROR("Not enough memory to allocate hash table bucket!"); + + bucket->key = key; + bucket->hash = hash; + bucket->obj = obj; + + h->data[i] = bucket; + h->count++; +} + +static inline int64_t +index_for_key(struct objc_hashtable *h, const char *key) +{ + uint32_t i, hash; + + hash = objc_hash_string(key) & h->last_idx; + + for (i = hash; i <= h->last_idx && h->data[i] != NULL; i++) + if (!strcmp(h->data[i]->key, key)) + return i; + + if (i <= h->last_idx) + return -1; + + for (i = 0; i < hash && h->data[i] != NULL; i++) + if (!strcmp(h->data[i]->key, key)) + return i; + + return -1; +} + +void +objc_hashtable_set(struct objc_hashtable *h, const char *key, const void *obj) +{ + int64_t idx = index_for_key(h, key); + + if (idx < 0) { + insert(h, key, obj); + return; + } + + h->data[idx]->obj = obj; +} + +const void* +objc_hashtable_get(struct objc_hashtable *h, const char *key) +{ + int64_t idx = index_for_key(h, key); + + if (idx < 0) + return NULL; + + return h->data[idx]->obj; +} + +void +objc_hashtable_free(struct objc_hashtable *h) +{ + uint32_t i; + + for (i = 0; i <= h->last_idx; i++) + if (h->data[i] != NULL) + free(h->data[i]); + + free(h->data); + free(h); +} ADDED src/runtime/init.m Index: src/runtime/init.m ================================================================== --- src/runtime/init.m +++ src/runtime/init.m @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "runtime.h" +#import "runtime-private.h" + +void +__objc_exec_class(struct objc_abi_module *module) +{ + objc_global_mutex_lock(); + + objc_register_all_selectors(module->symtab); + objc_register_all_classes(module->symtab); + objc_register_all_categories(module->symtab); + objc_init_static_instances(module->symtab); + + objc_global_mutex_unlock(); +} + +void +objc_exit(void) +{ + objc_global_mutex_lock(); + + objc_free_all_categories(); + objc_free_all_classes(); + objc_free_all_selectors(); + objc_sparsearray_cleanup(); + + objc_global_mutex_unlock(); + objc_global_mutex_free(); +} ADDED src/runtime/lookup-amd64-elf.S Index: src/runtime/lookup-amd64-elf.S ================================================================== --- src/runtime/lookup-amd64-elf.S +++ src/runtime/lookup-amd64-elf.S @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#if defined(__x86_64__) && defined(__ELF__) + +.globl objc_msg_lookup +.globl objc_msg_lookup_super + +.section .text +objc_msg_lookup: + testq %rdi, %rdi + jz ret_nil + + movq (%rdi), %r8 + movq 64(%r8), %r8 + +lookup: + movq (%rsi), %rax + movzbl %ah, %ecx + movzbl %al, %edx + shrl $16, %eax + + movq (%r8,%rax,8), %r8 + movq (%r8,%rcx,8), %r8 + movq (%r8,%rdx,8), %rax + + testq %rax, %rax + jz forward + + ret + +forward: + mov objc_forward_handler@GOTPCREL(%rip), %rax + jmp *(%rax) + +objc_msg_lookup_super: + movq (%rdi), %rax + testq %rax, %rax + jz ret_nil + + movq 8(%rdi), %r8 + movq 64(%r8), %r8 + movq %rdi, %rax + jmp lookup + +ret_nil: + leaq nil_method(%rip), %rax + ret + +nil_method: + movq %rdi, %rax + ret + +.type objc_msg_lookup, @function +.type objc_msg_lookup_super, @function +.size objc_msg_lookup, forward-objc_msg_lookup +.size objc_msg_lookup_super, ret_nil-objc_msg_lookup_super + +#endif ADDED src/runtime/lookup-x86-elf.S Index: src/runtime/lookup-x86-elf.S ================================================================== --- src/runtime/lookup-x86-elf.S +++ src/runtime/lookup-x86-elf.S @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#if defined(__i386__) && defined(__ELF__) + +.globl objc_msg_lookup +.globl objc_msg_lookup_super + +.section .text +objc_msg_lookup: + movl 4(%esp), %edx + testl %edx, %edx + jz ret_nil + + movl (%edx), %edx + movl 32(%edx), %edx + +lookup: + movl 8(%esp), %eax + + movzbl 2(%eax), %ecx + movl (%edx,%ecx,4), %edx + movzbl 1(%eax), %ecx + movl (%edx,%ecx,4), %edx + movzbl (%eax), %ecx + movl (%edx,%ecx,4), %eax + + testl %eax, %eax + jz forward + + ret + +forward: + call get_eip +.L1: + addl $objc_forward_handler-.L1, %eax + jmp *(%eax) + +objc_msg_lookup_super: + movl 4(%esp), %edx + cmpl $0, (%edx) + je ret_nil + + movl 4(%edx), %edx + movl 32(%edx), %edx + jmp lookup + +ret_nil: + call get_eip +.L2: + addl $nil_method-.L2, %eax + ret + +nil_method: + movl 4(%esp), %eax + ret + +get_eip: + movl (%esp), %eax + ret + +.type objc_msg_lookup, @function +.type objc_msg_lookup_super, @function +.size objc_msg_lookup, forward-objc_msg_lookup +.size objc_msg_lookup_super, ret_nil-objc_msg_lookup_super + +#endif ADDED src/runtime/lookup.m Index: src/runtime/lookup.m ================================================================== --- src/runtime/lookup.m +++ src/runtime/lookup.m @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include +#include + +#import "runtime.h" +#import "runtime-private.h" +#import "macros.h" + +static IMP not_found_handler(id, SEL); +IMP (*objc_forward_handler)(id, SEL) = not_found_handler; + +static IMP +not_found_handler(id obj, SEL sel) +{ + ERROR("Selector %s is not implemented for class %s!", + sel_getName(sel), obj->isa->name); +} + +BOOL +class_respondsToSelector(Class cls, SEL sel) +{ + if (cls == Nil) + return NO; + + return (objc_sparsearray_get(cls->dtable, sel->uid) != NULL ? YES : NO); +} + +#if !defined(__ELF__) || (!defined(OF_X86_ASM) && !defined(OF_AMD64_ASM)) +static id +nil_method(id self, SEL _cmd) +{ + return nil; +} + +IMP +objc_msg_lookup(id obj, SEL sel) +{ + IMP imp; + + if (obj == nil) + return (IMP)nil_method; + + if ((imp = objc_sparsearray_get(obj->isa->dtable, sel->uid)) == NULL) + return objc_forward_handler(obj, sel); + + return imp; +} + +IMP +objc_msg_lookup_super(struct objc_super *super, SEL sel) +{ + IMP imp; + + if (super->self == nil) + return (IMP)nil_method; + + imp = objc_sparsearray_get(super->class->dtable, sel->uid); + + if (imp == NULL) + return objc_forward_handler(super->self, sel); + + return imp; +} +#endif ADDED src/runtime/property.m Index: src/runtime/property.m ================================================================== --- src/runtime/property.m +++ src/runtime/property.m @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include + +#include + +#import "OFObject.h" + +#ifdef OF_THREADS +# import "threading.h" +# define NUM_SPINLOCKS 8 /* needs to be a power of 2 */ +# define SPINLOCK_HASH(p) ((unsigned)((uintptr_t)p >> 4) & (NUM_SPINLOCKS - 1)) +static of_spinlock_t spinlocks[NUM_SPINLOCKS]; +#endif + +BOOL +objc_properties_init(void) +{ +#ifdef OF_THREADS + size_t i; + + for (i = 0; i < NUM_SPINLOCKS; i++) + if (!of_spinlock_new(&spinlocks[i])) + return NO; +#endif + + return YES; +} + +id +objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) +{ + if (atomic) { + id *ptr = (id*)(void*)((char*)self + offset); +#ifdef OF_THREADS + unsigned hash = SPINLOCK_HASH(ptr); + + assert(of_spinlock_lock(&spinlocks[hash])); + + @try { + return [[*ptr retain] autorelease]; + } @finally { + assert(of_spinlock_unlock(&spinlocks[hash])); + } +#else + return [[*ptr retain] autorelease]; +#endif + } + + return *(id*)(void*)((char*)self + offset); +} + +void +objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id value, BOOL atomic, + signed char copy) +{ + if (atomic) { + id *ptr = (id*)(void*)((char*)self + offset); +#ifdef OF_THREADS + unsigned hash = SPINLOCK_HASH(ptr); + + assert(of_spinlock_lock(&spinlocks[hash])); + + @try { +#endif + id old = *ptr; + + switch (copy) { + case 0: + *ptr = [value retain]; + break; + case 2: + *ptr = [value mutableCopy]; + break; + default: + *ptr = [value copy]; + } + + [old release]; +#ifdef OF_THREADS + } @finally { + assert(of_spinlock_unlock(&spinlocks[hash])); + } +#endif + + return; + } + + id *ptr = (id*)(void*)((char*)self + offset); + id old = *ptr; + + switch (copy) { + case 0: + *ptr = [value retain]; + break; + case 2: + /* + * Apple uses this to indicate that the copy should be mutable. + * Please hit them for abusing a poor BOOL! + */ + *ptr = [value mutableCopy]; + break; + default: + *ptr = [value copy]; + } + + [old release]; +} + +/* The following methods are only required for GCC >= 4.6 */ +void +objc_getPropertyStruct(void *dest, const void *src, ptrdiff_t size, BOOL atomic, + BOOL strong) +{ + if (atomic) { +#ifdef OF_THREADS + unsigned hash = SPINLOCK_HASH(src); + + assert(of_spinlock_lock(&spinlocks[hash])); +#endif + + memcpy(dest, src, size); + +#ifdef OF_THREADS + assert(of_spinlock_unlock(&spinlocks[hash])); +#endif + + return; + } + + memcpy(dest, src, size); +} + +void +objc_setPropertyStruct(void *dest, const void *src, ptrdiff_t size, BOOL atomic, + BOOL strong) +{ + if (atomic) { +#ifdef OF_THREADS + unsigned hash = SPINLOCK_HASH(src); + + assert(of_spinlock_lock(&spinlocks[hash])); +#endif + + memcpy(dest, src, size); + +#ifdef OF_THREADS + assert(of_spinlock_unlock(&spinlocks[hash])); +#endif + + return; + } + + memcpy(dest, src, size); +} ADDED src/runtime/protocol.m Index: src/runtime/protocol.m ================================================================== --- src/runtime/protocol.m +++ src/runtime/protocol.m @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include + +#import "runtime.h" +#import "runtime-private.h" + +@implementation Protocol +@end + +inline const char* +protocol_getName(Protocol *p) +{ + return p->name; +} + +inline BOOL +protocol_isEqual(Protocol *a, Protocol *b) +{ + return !strcmp(protocol_getName(a), protocol_getName(b)); +} + +BOOL +protocol_conformsToProtocol(Protocol *a, Protocol *b) +{ + struct objc_protocol_list *pl; + size_t i; + + if (protocol_isEqual(a, b)) + return YES; + + for (pl = a->protocol_list; pl != NULL; pl = pl->next) + for (i = 0; i < pl->count; i++) + if (protocol_conformsToProtocol(pl->list[i], b)) + return YES; + + return NO; +} + +BOOL +class_conformsToProtocol(Class cls, Protocol *p) +{ + struct objc_protocol_list *pl; + struct objc_category **cats; + long i, j; + + for (pl = cls->protocols; pl != NULL; pl = pl->next) + for (i = 0; i < pl->count; i++) + if (protocol_conformsToProtocol(pl->list[i], p)) + return YES; + + objc_global_mutex_lock(); + + if ((cats = objc_categories_for_class(cls)) == NULL) { + objc_global_mutex_unlock(); + return NO; + } + + for (i = 0; cats[i] != NULL; i++) { + for (pl = cats[i]->protocols; pl != NULL; pl = pl->next) { + for (j = 0; j < pl->count; j++) { + if (protocol_conformsToProtocol( + pl->list[j], p)) { + objc_global_mutex_unlock(); + return YES; + } + } + } + } + + objc_global_mutex_unlock(); + + return NO; +} ADDED src/runtime/runtime-private.h Index: src/runtime/runtime-private.h ================================================================== --- src/runtime/runtime-private.h +++ src/runtime/runtime-private.h @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#import "threading.h" + +struct objc_abi_class { + struct objc_abi_class *metaclass; + const char *superclass; + const char *name; + unsigned long version; + unsigned long info; + unsigned long instance_size; + void *ivars; + struct objc_abi_method_list *methodlist; + void *dtable; + void *subclass_list; + void *sibling_class; + void *protocols; + void *gc_object_type; + long abi_version; + void *ivar_offsets; + void *properties; +}; + +struct objc_abi_method { + const char *name; + const char *types; + IMP imp; +}; + +struct objc_abi_method_list { + struct objc_abi_method_list *next; + unsigned int count; + struct objc_abi_method methods[1]; +}; + +struct objc_abi_selector { + const char *name; + const char *types; +}; + +struct objc_abi_category { + const char *category_name; + const char *class_name; + struct objc_abi_method_list *instance_methods; + struct objc_abi_method_list *class_methods; + struct objc_protocol_list *protocols; +}; + +struct objc_abi_method_description { + const char *name; + const char *types; +}; + +struct objc_abi_method_description_list { + int count; + struct objc_abi_method_description list[1]; +}; + +struct objc_abi_static_instances { + const char *class_name; + id instances[1]; +}; + +struct objc_abi_symtab { + unsigned long unknown; + struct objc_abi_selector *sel_refs; + uint16_t cls_def_cnt; + uint16_t cat_def_cnt; + void *defs[1]; +}; + +struct objc_abi_module { + unsigned long version; /* 9 = non-fragile */ + unsigned long size; + const char *name; + struct objc_abi_symtab *symtab; +}; + +struct objc_hashtable_bucket { + const char *key; + const void *obj; + uint32_t hash; +}; + +struct objc_hashtable { + uint32_t count; + uint32_t last_idx; + struct objc_hashtable_bucket **data; +}; + +struct objc_sparsearray { + struct objc_sparsearray_level2 *buckets[256]; +}; + +struct objc_sparsearray_level2 { + struct objc_sparsearray_level3 *buckets[256]; + BOOL empty; +}; + +struct objc_sparsearray_level3 { + const void *buckets[256]; + BOOL empty; +}; + +enum objc_abi_class_info { + OBJC_CLASS_INFO_CLASS = 0x01, + OBJC_CLASS_INFO_METACLASS = 0x02, + OBJC_CLASS_INFO_SETUP = 0x04, + OBJC_CLASS_INFO_LOADED = 0x08, + OBJC_CLASS_INFO_INITIALIZED = 0x10 +}; + +typedef struct { + of_mutex_t mutex; + of_thread_t owner; + int count; +} objc_mutex_t; + +extern void objc_register_all_categories(struct objc_abi_symtab*); +extern struct objc_category** objc_categories_for_class(Class); +extern void objc_free_all_categories(void); +extern void objc_update_dtable(Class); +extern void objc_register_all_classes(struct objc_abi_symtab*); +extern Class objc_classname_to_class(const char*); +extern void objc_free_all_classes(void); +extern uint32_t objc_hash_string(const char*); +extern struct objc_hashtable* objc_hashtable_alloc(uint32_t); +extern void objc_hashtable_set(struct objc_hashtable*, const char*, + const void*); +extern const void* objc_hashtable_get(struct objc_hashtable*, const char*); +extern void objc_hashtable_free(struct objc_hashtable *h); +extern BOOL objc_hashtable_warn_on_collision; +extern void objc_register_selector(struct objc_abi_selector*); +extern void objc_register_all_selectors(struct objc_abi_symtab*); +extern void objc_free_all_selectors(void); +extern struct objc_sparsearray* objc_sparsearray_new(void); +extern struct objc_sparsearray* objc_sparsearray_copy(struct objc_sparsearray*); +extern void objc_sparsearray_set(struct objc_sparsearray*, uint32_t, + const void*); +extern void objc_sparsearray_free(struct objc_sparsearray*); +extern void objc_sparsearray_free_when_singlethreaded(struct objc_sparsearray*); +extern void objc_sparsearray_cleanup(void); +extern void objc_init_static_instances(struct objc_abi_symtab*); +extern void __objc_exec_class(struct objc_abi_module*); +extern BOOL objc_mutex_new(objc_mutex_t*); +extern BOOL objc_mutex_lock(objc_mutex_t*); +extern BOOL objc_mutex_unlock(objc_mutex_t*); +extern BOOL objc_mutex_free(objc_mutex_t*); +extern void objc_global_mutex_lock(void); +extern void objc_global_mutex_unlock(void); +extern void objc_global_mutex_free(void); +extern void objc_free_when_singlethreaded(void*); + +static inline const void* +objc_sparsearray_get(const struct objc_sparsearray *s, uint32_t idx) +{ + uint8_t i = idx >> 16; + uint8_t j = idx >> 8; + uint8_t k = idx; + + return s->buckets[i]->buckets[j]->buckets[k]; +} + +#define ERROR(...) \ + { \ + fprintf(stderr, "[objc @ " __FILE__ ":%d] ", __LINE__); \ + fprintf(stderr, __VA_ARGS__); \ + fputs("\n", stderr); \ + abort(); \ + } ADDED src/runtime/runtime.h Index: src/runtime/runtime.h ================================================================== --- src/runtime/runtime.h +++ src/runtime/runtime.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#ifndef __OBJFW_RUNTIME_H__ +#define __OBJFW_RUNTIME_H__ +#include + +typedef struct objc_class *Class; +typedef struct objc_object *id; +typedef const struct objc_selector *SEL; +typedef signed char BOOL; +typedef id (*IMP)(id, SEL, ...); + +struct objc_class { + Class isa; + Class superclass; + const char *name; + unsigned long version; + unsigned long info; + unsigned long instance_size; + void *ivars; + struct objc_method_list *methodlist; + struct objc_sparsearray *dtable; + Class *subclass_list; + void *sibling_class; + struct objc_protocol_list *protocols; + void *gc_object_type; + unsigned long abi_version; + void *ivar_offsets; + void *properties; +}; + +struct objc_object { + Class isa; +}; + +struct objc_selector { + uintptr_t uid; + const char *types; +}; + +struct objc_super { + id self; + Class class; +}; + +struct objc_method { + struct objc_selector sel; + IMP imp; +}; + +struct objc_method_list { + struct objc_method_list *next; + unsigned int count; + struct objc_method methods[1]; +}; + +struct objc_category { + const char *category_name; + const char *class_name; + struct objc_method_list *instance_methods; + struct objc_method_list *class_methods; + struct objc_protocol_list *protocols; +}; + +#ifdef __OBJC__ +@interface Protocol +{ +@public +#else +typedef struct { +#endif + Class isa; + const char *name; + struct objc_protocol_list *protocol_list; + struct objc_abi_method_description_list *instance_methods; + struct objc_abi_method_description_list *class_methods; +#ifdef __OBJC__ +} +@end +#else +} Protocol; +#endif + +struct objc_protocol_list { + struct objc_protocol_list *next; + long count; + Protocol *list[1]; +}; + +#define Nil (Class)0 +#define nil (id)0 +#define YES (BOOL)1 +#define NO (BOOL)0 + +extern SEL sel_registerName(const char*); +extern const char* sel_getName(SEL); +extern BOOL sel_isEqual(SEL, SEL); +extern Class objc_get_class(const char*); +extern Class objc_lookup_class(const char*); +extern const char* class_getName(Class); +extern Class class_getSuperclass(Class); +extern BOOL class_isKindOfClass(Class, Class); +extern unsigned long class_getInstanceSize(Class); +extern BOOL class_respondsToSelector(Class, SEL); +extern BOOL class_conformsToProtocol(Class, Protocol*); +extern IMP class_getMethodImplementation(Class, SEL); +extern IMP class_replaceMethod(Class, SEL, IMP, const char*); +extern const char* objc_get_type_encoding(Class, SEL); +extern IMP objc_msg_lookup(id, SEL); +extern IMP objc_msg_lookup_super(struct objc_super*, SEL); +extern const char* protocol_getName(Protocol*); +extern BOOL protocol_isEqual(Protocol*, Protocol*); +extern BOOL protocol_conformsToProtocol(Protocol*, Protocol*); +extern void objc_thread_add(void); +extern void objc_thread_remove(void); +extern void objc_exit(void); +#endif ADDED src/runtime/selector.m Index: src/runtime/selector.m ================================================================== --- src/runtime/selector.m +++ src/runtime/selector.m @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include +#include +#include + +#import "runtime.h" +#import "runtime-private.h" + +static struct objc_sparsearray *selectors = NULL; + +void +objc_register_selector(struct objc_abi_selector *sel) +{ + uint32_t hash, last; + struct objc_selector *rsel = (struct objc_selector*)sel; + const char *name; + + if (selectors == NULL) + selectors = objc_sparsearray_new(); + + hash = objc_hash_string(sel->name) >> 8; + + while (hash <= 0xFFFFFF && + (name = objc_sparsearray_get(selectors, hash)) != NULL) { + if (!strcmp(name, sel->name)) { + rsel->uid = hash; + return; + } + + hash++; + } + + if (hash > 0xFFFFFF) { + last = hash; + hash = 0; + + while (hash < last && + (name = objc_sparsearray_get(selectors, hash)) != NULL) { + if (!strcmp(name, sel->name)) { + rsel->uid = hash; + return; + } + + hash++; + } + } + + objc_sparsearray_set(selectors, hash, (void*)sel->name); + rsel->uid = hash; +} + +SEL +sel_registerName(const char *name) +{ + struct objc_abi_selector *sel; + + /* FIXME: Free on objc_exit() */ + if ((sel = malloc(sizeof(struct objc_abi_selector))) == NULL) + ERROR("Not enough memory to allocate selector!"); + + if ((sel->name = strdup(name)) == NULL) + ERROR("Not enough memory to allocate selector!"); + + sel->types = NULL; + + objc_global_mutex_lock(); + objc_register_selector(sel); + objc_global_mutex_unlock(); + + return (SEL)sel; +} + +void +objc_register_all_selectors(struct objc_abi_symtab *symtab) +{ + struct objc_abi_selector *sel; + + if (symtab->sel_refs == NULL) + return; + + for (sel = symtab->sel_refs; sel->name != NULL; sel++) + objc_register_selector(sel); +} + +const char* +sel_getName(SEL sel) +{ + const char *ret; + + objc_global_mutex_lock(); + ret = objc_sparsearray_get(selectors, sel->uid); + objc_global_mutex_unlock(); + + return ret; +} + +BOOL +sel_isEqual(SEL sel1, SEL sel2) +{ + return sel1->uid == sel2->uid; +} + +void +objc_free_all_selectors(void) +{ + objc_sparsearray_free(selectors); + selectors = NULL; +} ADDED src/runtime/sparsearray.m Index: src/runtime/sparsearray.m ================================================================== --- src/runtime/sparsearray.m +++ src/runtime/sparsearray.m @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include +#include + +#import "runtime.h" +#import "runtime-private.h" + +static struct objc_sparsearray_level2 *empty_level2 = NULL; +static struct objc_sparsearray_level3 *empty_level3 = NULL; + +static void +init(void) +{ + size_t i; + + empty_level2 = malloc(sizeof(struct objc_sparsearray_level2)); + empty_level3 = malloc(sizeof(struct objc_sparsearray_level3)); + + if (empty_level2 == NULL || empty_level3 == NULL) + ERROR("Not enough memory to allocate sparse array!"); + + empty_level2->empty = YES; + empty_level3->empty = YES; + + for (i = 0; i < 256; i++) { + empty_level2->buckets[i] = empty_level3; + empty_level3->buckets[i] = NULL; + } +} + +struct objc_sparsearray* +objc_sparsearray_new(void) +{ + struct objc_sparsearray *s; + size_t i; + + if (empty_level2 == NULL || empty_level3 == NULL) + init(); + + if ((s = malloc(sizeof(struct objc_sparsearray))) == NULL) + ERROR("Not enough memory to allocate sparse array!"); + + for (i = 0; i < 256; i++) + s->buckets[i] = empty_level2; + + return s; +} + +struct objc_sparsearray* +objc_sparsearray_copy(struct objc_sparsearray *src) +{ + struct objc_sparsearray *dst; + size_t i, j, k; + uint32_t idx; + + dst = objc_sparsearray_new(); + + for (i = 0; i < 256; i++) { + if (src->buckets[i]->empty) + continue; + + for (j = 0; j < 256; j++) { + if (src->buckets[i]->buckets[j]->empty) + continue; + + for (k = 0; k < 256; k++) { + const void *obj; + + obj = src->buckets[i]->buckets[j]->buckets[k]; + + if (obj == NULL) + continue; + + idx = (i << 16) | (j << 8) | k; + objc_sparsearray_set(dst, idx, obj); + } + } + } + + return dst; +} + +void +objc_sparsearray_set(struct objc_sparsearray *s, uint32_t idx, const void *obj) +{ + uint8_t i = idx >> 16; + uint8_t j = idx >> 8; + uint8_t k = idx; + + if (s->buckets[i]->empty) { + struct objc_sparsearray_level2 *t; + size_t l; + + t = malloc(sizeof(struct objc_sparsearray_level2)); + + if (t == NULL) + ERROR("Not enough memory to insert into sparse array!"); + + t->empty = NO; + + for (l = 0; l < 256; l++) + t->buckets[l] = empty_level3; + + s->buckets[i] = t; + } + + if (s->buckets[i]->buckets[j]->empty) { + struct objc_sparsearray_level3 *t; + size_t l; + + t = malloc(sizeof(struct objc_sparsearray_level3)); + + if (t == NULL) + ERROR("Not enough memory to insert into sparse array!"); + + t->empty = NO; + + for (l = 0; l < 256; l++) + t->buckets[l] = NULL; + + s->buckets[i]->buckets[j] = t; + } + + s->buckets[i]->buckets[j]->buckets[k] = obj; +} + +void +objc_sparsearray_free(struct objc_sparsearray *s) +{ + size_t i, j; + + for (i = 0; i < 256; i++) { + if (s->buckets[i]->empty) + continue; + + for (j = 0; j < 256; j++) + if (!s->buckets[i]->buckets[j]->empty) + free(s->buckets[i]->buckets[j]); + + free(s->buckets[i]); + } + + free(s); +} + +void +objc_sparsearray_free_when_singlethreaded(struct objc_sparsearray *s) +{ + size_t i, j; + + for (i = 0; i < 256; i++) { + if (s->buckets[i]->empty) + continue; + + for (j = 0; j < 256; j++) + if (!s->buckets[i]->buckets[j]->empty) + objc_free_when_singlethreaded( + s->buckets[i]->buckets[j]); + + objc_free_when_singlethreaded(s->buckets[i]); + } + + objc_free_when_singlethreaded(s); +} + +void +objc_sparsearray_cleanup(void) +{ + if (empty_level2 != NULL) + free(empty_level2); + if (empty_level3 != NULL) + free(empty_level3); + + empty_level2 = NULL; + empty_level3 = NULL; +} ADDED src/runtime/static-instances.m Index: src/runtime/static-instances.m ================================================================== --- src/runtime/static-instances.m +++ src/runtime/static-instances.m @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include +#include + +#import "runtime.h" +#import "runtime-private.h" + +static struct objc_abi_static_instances **static_instances = NULL; +static size_t static_instances_cnt = 0; + +void +objc_init_static_instances(struct objc_abi_symtab *symtab) +{ + struct objc_abi_static_instances **si; + size_t i; + + /* Check if the class for a static instance became available */ + for (i = 0; i < static_instances_cnt; i++) { + Class cls = objc_lookup_class(static_instances[i]->class_name); + + if (cls != Nil) { + id *instances; + + for (instances = static_instances[i]->instances; + *instances != nil; instances++) + (*instances)->isa = cls; + + static_instances_cnt--; + + if (static_instances_cnt == 0) { + free(static_instances); + static_instances = NULL; + continue; + } + + static_instances[i] = + static_instances[static_instances_cnt]; + + static_instances = realloc(static_instances, + sizeof(struct objc_abi_static_instances*) * + static_instances_cnt); + + if (static_instances == NULL) + ERROR("Not enough memory for list of static " + "instances!"); + } + } + + si = (struct objc_abi_static_instances**) + symtab->defs[symtab->cls_def_cnt + symtab->cat_def_cnt]; + + if (si == NULL) + return; + + for (; *si != NULL; si++) { + Class cls = objc_lookup_class((*si)->class_name); + + if (cls != Nil) { + id *instances; + + for (instances = (*si)->instances; *instances != nil; + instances++) + (*instances)->isa = cls; + } else { + if (static_instances == NULL) + static_instances = malloc(sizeof( + struct objc_abi_static_instances*)); + else + static_instances = realloc(static_instances, + sizeof(struct objc_abi_static_instances*) * + (static_instances_cnt + 1)); + + if (static_instances == NULL) + ERROR("Not enough memory for list of static " + "instances!"); + + static_instances[static_instances_cnt++] = *si; + } + } +} ADDED src/runtime/synchronized.m Index: src/runtime/synchronized.m ================================================================== --- src/runtime/synchronized.m +++ src/runtime/synchronized.m @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include +#include +#include + +#include + +#import "runtime.h" +#import "threading.h" + +struct lock_s { + id object; + size_t count; + size_t recursion; + of_thread_t thread; + of_mutex_t mutex; +}; + +static of_mutex_t mutex; +static struct lock_s *locks = NULL; +static ssize_t numLocks = 0; + +#define SYNC_ERR(f) \ + { \ + fprintf(stderr, "WARNING: %s failed in line %d!\n" \ + "WARNING: This might result in a race " \ + "condition!\n", f, __LINE__); \ + return 1; \ + } + +BOOL +objc_sync_init(void) +{ + return of_mutex_new(&mutex); +} + +int +objc_sync_enter(id object) +{ + ssize_t i; + + if (object == nil) + return 0; + + if (!of_mutex_lock(&mutex)) + SYNC_ERR("of_mutex_lock(&mutex)"); + + for (i = numLocks - 1; i >= 0; i--) { + if (locks[i].object == object) { + if (of_thread_is_current(locks[i].thread)) + locks[i].recursion++; + else { + /* Make sure objc_sync_exit doesn't free it */ + locks[i].count++; + + /* Unlock so objc_sync_exit can return */ + if (!of_mutex_unlock(&mutex)) + SYNC_ERR("of_mutex_unlock(&mutex)"); + + if (!of_mutex_lock(&locks[i].mutex)) { + of_mutex_unlock(&mutex); + SYNC_ERR( + "of_mutex_lock(&locks[i].mutex"); + } + + if (!of_mutex_lock(&mutex)) + SYNC_ERR("of_mutex_lock(&mutex)"); + + assert(locks[i].recursion == 0); + + /* Update lock's active thread */ + locks[i].thread = of_thread_current(); + } + + if (!of_mutex_unlock(&mutex)) + SYNC_ERR("of_mutex_unlock(&mutex)"); + + return 0; + } + } + + if (locks == NULL) { + if ((locks = malloc(sizeof(struct lock_s))) == NULL) { + of_mutex_unlock(&mutex); + SYNC_ERR("malloc(...)"); + } + } else { + struct lock_s *new_locks; + + if ((new_locks = realloc(locks, (numLocks + 1) * + sizeof(struct lock_s))) == NULL) { + of_mutex_unlock(&mutex); + SYNC_ERR("realloc(...)"); + } + + locks = new_locks; + } + + locks[numLocks].object = object; + locks[numLocks].count = 1; + locks[numLocks].recursion = 0; + locks[numLocks].thread = of_thread_current(); + + if (!of_mutex_new(&locks[numLocks].mutex)) { + of_mutex_unlock(&mutex); + SYNC_ERR("of_mutex_new(&locks[numLocks].mutex"); + } + + if (!of_mutex_lock(&locks[numLocks].mutex)) { + of_mutex_unlock(&mutex); + SYNC_ERR("of_mutex_lock(&locks[numLocks].mutex"); + } + + numLocks++; + + if (!of_mutex_unlock(&mutex)) + SYNC_ERR("of_mutex_unlock(&mutex)"); + + return 0; +} + +int +objc_sync_exit(id object) +{ + ssize_t i; + + if (object == nil) + return 0; + + if (!of_mutex_lock(&mutex)) + SYNC_ERR("of_mutex_lock(&mutex)"); + + for (i = numLocks - 1; i >= 0; i--) { + if (locks[i].object == object) { + if (locks[i].recursion > 0 && + of_thread_is_current(locks[i].thread)) { + locks[i].recursion--; + + if (!of_mutex_unlock(&mutex)) + SYNC_ERR("of_mutex_unlock(&mutex)"); + + return 0; + } + + if (!of_mutex_unlock(&locks[i].mutex)) { + of_mutex_unlock(&mutex); + SYNC_ERR("of_mutex_unlock(&locks[i].mutex)"); + } + + locks[i].count--; + + if (locks[i].count == 0) { + struct lock_s *new_locks = NULL; + + if (!of_mutex_free(&locks[i].mutex)) { + of_mutex_unlock(&mutex); + SYNC_ERR( + "of_mutex_free(&locks[i].mutex"); + } + + numLocks--; + locks[i] = locks[numLocks]; + + if (numLocks == 0) { + free(locks); + new_locks = NULL; + } else if ((new_locks = realloc(locks, + numLocks * sizeof(struct lock_s))) == + NULL) { + of_mutex_unlock(&mutex); + SYNC_ERR("realloc(...)"); + } + + locks = new_locks; + } + + if (!of_mutex_unlock(&mutex)) + SYNC_ERR("of_mutex_unlock(&mutex)"); + + return 0; + } + } + + of_mutex_unlock(&mutex); + SYNC_ERR("objc_sync_exit()"); +} ADDED src/runtime/threading.m Index: src/runtime/threading.m ================================================================== --- src/runtime/threading.m +++ src/runtime/threading.m @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include +#include + +#import "runtime.h" +#import "runtime-private.h" + +static objc_mutex_t global_mutex; +static int num_threads = 1; +static void **free_queue = NULL; +static size_t free_queue_cnt = 0; + +BOOL +objc_mutex_new(objc_mutex_t *mutex) +{ + if (!of_mutex_new(&mutex->mutex )) + return NO; + + mutex->count = 0; + + return YES; +} + +BOOL +objc_mutex_lock(objc_mutex_t *mutex) +{ + if (mutex->count > 0 && of_thread_is_current(mutex->owner)) { + mutex->count++; + return YES; + } + + if (!of_mutex_lock(&mutex->mutex)) + return NO; + + mutex->owner = of_thread_current(); + mutex->count++; + + return YES; +} + +BOOL +objc_mutex_unlock(objc_mutex_t *mutex) +{ + if (--mutex->count == 0) + return of_mutex_unlock(&mutex->mutex); + + return YES; +} + +BOOL +objc_mutex_free(objc_mutex_t *mutex) +{ + return of_mutex_free(&mutex->mutex); +} + +static void __attribute__((constructor)) +objc_global_mutex_new(void) +{ + if (!objc_mutex_new(&global_mutex)) + ERROR("Failed to create global mutex!"); +} + +void +objc_global_mutex_lock(void) +{ + if (!objc_mutex_lock(&global_mutex)) + ERROR("Failed to lock global mutex!"); +} + +void +objc_global_mutex_unlock(void) +{ + if (!objc_mutex_unlock(&global_mutex)) + ERROR("Failed to unlock global mutex!"); +} + +void +objc_global_mutex_free(void) +{ + if (!objc_mutex_free(&global_mutex)) + ERROR("Failed to free global mutex!"); +} + +void +objc_thread_add(void) +{ + /* + * If some class is being initialized, we want to wait for it, thus + * we use the global lock instead of atomic operations. + */ + objc_global_mutex_lock(); + num_threads++; + objc_global_mutex_unlock(); +} + +void +objc_thread_remove(void) +{ + size_t i; + + objc_global_mutex_lock(); + + if (free_queue != NULL) { + for (i = 0; i < free_queue_cnt; i++) + free(free_queue[i]); + + free(free_queue); + + free_queue = NULL; + free_queue_cnt = 0; + } + + num_threads--; + objc_global_mutex_unlock(); +} + +void +objc_free_when_singlethreaded(void *ptr) +{ + if (num_threads == 1) { + free(ptr); + return; + } + + if (free_queue == NULL) + free_queue = malloc(sizeof(void*)); + else + free_queue = realloc(free_queue, sizeof(void*) * + (free_queue_cnt + 1)); + + if (free_queue == NULL) + ERROR("Not enough memory for queue of pointers to free!"); + + free_queue[free_queue_cnt++] = ptr; +} Index: tests/Makefile ================================================================== --- tests/Makefile +++ tests/Makefile @@ -85,8 +85,8 @@ psp-strip ${PROG_NOINST} pack-pbp $@ PARAM.SFO NULL NULL NULL NULL NULL ${PROG_NOINST} NULL include ../buildsys.mk -CPPFLAGS += -I../src -I../src/exceptions -I.. -DSTDOUT +CPPFLAGS += -I../src -I../src/exceptions -I../src/runtime -I.. -DSTDOUT LIBS := -L../src -lobjfw ${LIBS} LD = ${OBJC} Index: tests/OFBlockTests.m ================================================================== --- tests/OFBlockTests.m +++ tests/OFBlockTests.m @@ -19,17 +19,15 @@ #import "OFBlock.h" #import "OFString.h" #import "OFAutoreleasePool.h" #if defined(OF_OBJFW_RUNTIME) -# include -#elif defined(OF_APPLE_RUNTIME) || defined(OF_GNU_RUNTIME) +# include "runtime.h" +#elif defined(OF_APPLE_RUNTIME) # include -#elif defined(OF_OLD_GNU_RUNTIME) -# include #endif -#if defined(OF_OLD_GNU_RUNTIME) || defined(OF_OBJFW_RUNTIME) +#ifdef OF_OBJFW_RUNTIME # define objc_getClass objc_get_class #endif #import "TestsAppDelegate.h" Index: tests/plugin/Makefile ================================================================== --- tests/plugin/Makefile +++ tests/plugin/Makefile @@ -2,8 +2,8 @@ SRCS = TestPlugin.m include ../../buildsys.mk include ../../extra.mk -CPPFLAGS += -I../.. -I../../src +CPPFLAGS += -I../.. -I../../src -I../../src/runtime LIBS := -L../../src -lobjfw ${LIBS} LD = ${OBJC}