Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -35,21 +35,12 @@ AX_CHECK_COMPILER_FLAGS(-fno-common, [OBJCFLAGS="$OBJCFLAGS -fno-common"]) AX_CHECK_COMPILER_FLAGS(-fno-constant-cfstrings, [ OBJCFLAGS="$OBJCFLAGS -fno-constant-cfstrings" AC_SUBST(NO_CONST_CFSTRINGS, "-fno-constant-cfstrings") ]) -AX_CHECK_COMPILER_FLAGS(-Wshorten-64-to-32, [ - old_OBJCFLAGS="$OBJCFLAGS" - OBJCFLAGS="$OBJCFLAGS -Wshorten-64-to-32 -Werror" - AC_TRY_COMPILE([ - #import - ], [], [ - OBJCFLAGS="$old_OBJCFLAGS -Wshorten-64-to-32" - ], [ - OBJCFLAGS="$old_OBJCFLAGS" - ]) -]) +AX_CHECK_COMPILER_FLAGS(-Wshorten-64-to-32, + [OBJCFLAGS="$OBJCFLAGS -Wshorten-64-to-32"]) AX_CHECK_COMPILER_FLAGS(-Wsemicolon-before-method-body, [OBJCFLAGS="$OBJCFLAGS -Wsemicolon-before-method-body"]) AC_MSG_CHECKING(whether Objective C compiler supports fast enumeration) AC_TRY_COMPILE([ @@ -126,101 +117,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,17 +142,126 @@ 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) +AC_ARG_ENABLE(runtime, + AS_HELP_STRING([--enable-runtime], [use the included runtime])) +AC_ARG_ENABLE(seluid16, + AS_HELP_STRING([--enable-seluid16], + [use only 16 bit for selectors UIDs])) +AS_IF([test x"$enable_runtime" != x"yes"], [ + 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]) + GNU_RUNTIME="-fgnu-runtime" + OBJCFLAGS="$OBJCFLAGS -fgnu-runtime" + RUNTIME_FLAGS="-fgnu-runtime" + + AX_CHECK_COMPILER_FLAGS(-fno-objc-nonfragile-abi, [ + OBJCFLAGS="$OBJCFLAGS -fno-objc-nonfragile-abi" + GNU_RUNTIME="$GNU_RUNTIME -fno-objc-nonfragile-abi" + ]) + AX_CHECK_COMPILER_FLAGS(-Wno-deprecated-objc-isa-usage, + [OBJCFLAGS="$OBJCFLAGS -Wno-deprecated-objc-isa-usage"]) + + AC_SUBST(GNU_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 + + AC_EGREP_CPP(yes, [ + #if defined(__amd64__) || defined(__x86_64__) + # ifdef __ELF__ + yes + # endif + #endif + ], [ + AC_SUBST(LOOKUP_S, lookup-amd64-elf.S) + AC_DEFINE(OF_ASM_LOOKUP, 1, + [Whether to use lookup in assembly]) + ], [ + AC_EGREP_CPP(yes, [ + #if defined(__i386__) && defined(__ELF__) + yes + #endif + ], [ + AC_SUBST(LOOKUP_S, lookup-x86-elf.S) + AC_DEFINE(OF_ASM_LOOKUP, 1, + [Whether to use lookup in assembly]) + ]) + ]) + + AC_EGREP_CPP(yes, [ + #if defined(__amd64__) && defined(__MACH__) + yes + #endif + ], [ + AC_SUBST(LOOKUP_S, lookup-amd64-macho.S) + AC_DEFINE(OF_ASM_LOOKUP, 1, + [Whether to use lookup in assembly]) + ]) + + AS_IF([test x"$enable_seluid16" = x"yes"], [ + AC_DEFINE(OF_SELUID16, 1, + [Whether to use 16 bit selector UIDs]) + ]) + ;; + "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"]) - AC_SUBST(MACH_ALIAS_LIST, - ["-Xarch_x86_64 -Wl,-alias_list,mach_alias_list"]) + AS_IF([test x"$objc_runtime" = x"Apple runtime"], [ + AC_SUBST(REEXPORT_LIBOBJC, ["-Wl,-reexport-lobjc"]) + tmp="-Xarch_x86_64 -Wl,-alias_list,mach_alias_list" + AC_SUBST(MACH_ALIAS_LIST, $tmp) + ]) ;; esac AC_C_BIGENDIAN([ AC_DEFINE(OF_BIG_ENDIAN, 1, [Whether we are big endian]) @@ -407,16 +416,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 +579,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 @@ -10,10 +10,11 @@ EXCEPTIONS_A = @EXCEPTIONS_A@ EXCEPTIONS_EXCEPTIONS_A = @EXCEPTIONS_EXCEPTIONS_A@ EXCEPTIONS_EXCEPTIONS_LIB_A = @EXCEPTIONS_EXCEPTIONS_LIB_A@ EXCEPTIONS_LIB_A = @EXCEPTIONS_LIB_A@ FOUNDATION_COMPAT_M = @FOUNDATION_COMPAT_M@ +LOOKUP_S = @LOOKUP_S@ MACH_ALIAS_LIST = @MACH_ALIAS_LIST@ OFBLOCKTESTS_M = @OFBLOCKTESTS_M@ OBJC_PROPERTIES_M = @OBJC_PROPERTIES_M@ OBJC_SYNC_M = @OBJC_SYNC_M@ OFHTTPREQUESTTESTS_M = @OFHTTPREQUESTTESTS_M@ @@ -24,9 +25,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 #ifdef __has_feature 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) || defined(__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) || defined(__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/OFString.m ================================================================== --- src/OFString.m +++ src/OFString.m @@ -55,10 +55,15 @@ _OFString_Serialization_reference = 1; _OFString_URLEncoding_reference = 1; _OFString_XMLEscaping_reference = 1; _OFString_XMLUnescaping_reference = 1; } + +void _reference_to_OFConstantString(void) +{ + [OFConstantString class]; +} int of_string_check_utf8(const char *cString, size_t cStringLength, size_t *length) { size_t i, tmpLength = cStringLength; 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 @@ -58,7 +58,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 \ + exception.m \ + hashtable.m \ + init.m \ + lookup.m \ + ${LOOKUP_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_new(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,571 @@ +/* + * 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 struct objc_sparsearray *empty_dtable = NULL; + +static void +register_class(struct objc_abi_class *cls) +{ + if (classes == NULL) + classes = objc_hashtable_new(2); + + objc_hashtable_set(classes, cls->name, cls); + + if (empty_dtable == NULL) + empty_dtable = objc_sparsearray_new(); + + cls->dtable = empty_dtable; + cls->metaclass->dtable = empty_dtable; +} + +BOOL +class_registerAlias_np(Class cls, const char *name) +{ + if (classes == NULL) + return NO; + + objc_hashtable_set(classes, name, cls); + + return YES; +} + +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]); +} + +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; +} + +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(Class cls) +{ + struct objc_method_list *ml; + SEL selector; + unsigned int i; + + selector = sel_registerName("load"); + + 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)) + 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; + unsigned int i; + + if (!(cls->info & OBJC_CLASS_INFO_INITIALIZED)) + return; + + if (cls->dtable == empty_dtable) + cls->dtable = objc_sparsearray_new(); + + if (cls->superclass != Nil) + objc_sparsearray_copy(cls->dtable, cls->superclass->dtable); + + for (ml = cls->methodlist; ml != NULL; ml = ml->next) + for (i = 0; i < ml->count; i++) + objc_sparsearray_set(cls->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(cls->dtable, + (uint32_t)ml->methods[j].sel.uid, + ml->methods[j].imp); + } + } + + 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; + + 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; + + objc_update_dtable(cls); + objc_update_dtable(cls->isa); + + call_method(cls, "initialize"); +} + +void +objc_initialize_class(Class cls) +{ + if (cls->info & OBJC_CLASS_INFO_INITIALIZED) + return; + + objc_global_mutex_lock(); + + /* + * It's possible that two threads try to initialize 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; + } + + setup_class(cls); + + if (!(cls->info & OBJC_CLASS_INFO_SETUP)) { + objc_global_mutex_unlock(); + return; + } + + initialize_class(cls); + + objc_global_mutex_unlock(); +} + +void +objc_register_all_classes(struct objc_abi_symtab *symtab) +{ + uint_fast32_t i; + + for (i = 0; i < symtab->cls_def_cnt; i++) { + struct objc_abi_class *cls = + (struct objc_abi_class*)symtab->defs[i]; + + register_class(cls); + register_selectors(cls); + register_selectors(cls->metaclass); + } + + for (i = 0; i < symtab->cls_def_cnt; i++) { + Class cls = (Class)symtab->defs[i]; + + if (has_load(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; + } + } else + cls->info |= OBJC_CLASS_INFO_LOADED; + } + + /* Process load queue */ + for (i = 0; i < load_queue_cnt; i++) { + setup_class(load_queue[i]); + + if (load_queue[i]->info & OBJC_CLASS_INFO_SETUP) { + 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_lookup_class(const char *name) +{ + Class cls = objc_classname_to_class(name); + + if (cls == NULL) + return Nil; + + if (cls->info & OBJC_CLASS_INFO_SETUP) + return cls; + + objc_global_mutex_lock(); + + setup_class(cls); + + objc_global_mutex_unlock(); + + if (!(cls->info & OBJC_CLASS_INFO_SETUP)) + return Nil; + + 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/exception.m Index: src/runtime/exception.m ================================================================== --- src/runtime/exception.m +++ src/runtime/exception.m @@ -0,0 +1,487 @@ +/* + * 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" + +static const uint64_t objc_exception_class = 0x474E55434F424A43; /* GNUCOBJC */ + +#define _UA_SEARCH_PHASE 0x01 +#define _UA_CLEANUP_PHASE 0x02 +#define _UA_HANDLER_FRAME 0x04 +#define _UA_FORCE_UNWIND 0x08 + +#define DW_EH_PE_absptr 0x00 + +#define DW_EH_PE_uleb128 0x01 +#define DW_EH_PE_udata2 0x02 +#define DW_EH_PE_udata4 0x03 +#define DW_EH_PE_udata8 0x04 + +#define DW_EH_PE_signed 0x08 +#define DW_EH_PE_sleb128 (DW_EH_PE_signed | DW_EH_PE_uleb128) +#define DW_EH_PE_sdata2 (DW_EH_PE_signed | DW_EH_PE_udata2) +#define DW_EH_PE_sdata4 (DW_EH_PE_signed | DW_EH_PE_udata4) +#define DW_EH_PE_sdata8 (DW_EH_PE_signed | DW_EH_PE_udata8) + +#define DW_EH_PE_pcrel 0x10 +#define DW_EH_PE_textrel 0x20 +#define DW_EH_PE_datarel 0x30 +#define DW_EH_PE_funcrel 0x40 +#define DW_EH_PE_aligned 0x50 + +#define DW_EH_PE_indirect 0x80 + +#define DW_EH_PE_omit 0xFF + +#define CLEANUP_FOUND 0x01 +#define HANDLER_FOUND 0x02 + +struct _Unwind_Context; + +typedef enum +{ + _URC_FATAL_PHASE1_ERROR = 3, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8 +} _Unwind_Reason_Code; + +struct objc_exception { + struct _Unwind_Exception { + uint64_t class; + void (*cleanup)(_Unwind_Reason_Code, struct _Unwind_Exception*); + /* + * The Itanium Exception ABI says to have those and never touch + * them. + */ + uintptr_t private1, private2; + } exception; + id object; + uintptr_t landingpad; + intptr_t filter; +}; + +struct lsda { + uintptr_t region_start, landingpads_start; + uint8_t typestable_enc; + const uint8_t *typestable; + uintptr_t typestable_base; + uint8_t callsites_enc; + const uint8_t *callsites, *actiontable; +}; + +extern _Unwind_Reason_Code _Unwind_RaiseException(struct _Unwind_Exception*); +extern void* _Unwind_GetLanguageSpecificData(struct _Unwind_Context*); +extern uint64_t _Unwind_GetRegionStart(struct _Unwind_Context*); +extern uint64_t _Unwind_GetDataRelBase(struct _Unwind_Context*); +extern uint64_t _Unwind_GetTextRelBase(struct _Unwind_Context*); +extern uint64_t _Unwind_GetIP(struct _Unwind_Context*); +extern void _Unwind_SetIP(struct _Unwind_Context*, uint64_t); +extern void _Unwind_SetGR(struct _Unwind_Context*, int, uint64_t); +extern void _Unwind_DeleteException(struct _Unwind_Exception*); + +static objc_uncaught_exception_handler uncaught_exception_handler; + +static inline uint64_t +read_leb128(const uint8_t **ptr, uint8_t *bits) +{ + uint64_t value = 0; + uint8_t shift = 0; + + do { + value |= (**ptr & 0x7F) << shift; + (*ptr)++; + shift += 7; + } while (*(*ptr - 1) & 0x80); + + if (bits != NULL) + *bits = shift; + + return value; +} + +static uint64_t +read_uleb128(const uint8_t **ptr) +{ + return read_leb128(ptr, NULL); +} + +static int64_t +read_sleb128(const uint8_t **ptr) +{ + uint8_t bits; + int64_t value; + + value = read_leb128(ptr, &bits); + + if (bits < 64 && value & (1 << (bits - 1))) + value |= -(1 << bits); + + return value; +} + +static uintptr_t +get_base(struct _Unwind_Context *ctx, uint8_t enc) +{ + if (enc == DW_EH_PE_omit) + return 0; + + switch (enc & 0x70) { + case DW_EH_PE_absptr: + case DW_EH_PE_pcrel: + case DW_EH_PE_aligned: + return 0; + case DW_EH_PE_funcrel: + return _Unwind_GetRegionStart(ctx); + case DW_EH_PE_datarel: + return _Unwind_GetDataRelBase(ctx); + case DW_EH_PE_textrel: + return _Unwind_GetTextRelBase(ctx); + } + + abort(); +} + +static size_t +size_for_encoding(uint8_t enc) +{ + if (enc == DW_EH_PE_omit) + return 0; + + switch (enc & 0x07) { + case DW_EH_PE_absptr: + return sizeof(void*); + case DW_EH_PE_udata2: + return 2; + case DW_EH_PE_udata4: + return 4; + case DW_EH_PE_udata8: + return 8; + } + + abort(); +} + +static uint64_t +read_value(uint8_t enc, const uint8_t **ptr) +{ + uint64_t value; + + if (enc == DW_EH_PE_aligned) + /* Not implemented */ + abort(); + +#define READ_TYPE(type, size) \ + { \ + value = *(type*)(void*)*ptr; \ + *ptr += size; \ + break; \ + } + + switch (enc & 0x0F) { + case DW_EH_PE_absptr: + READ_TYPE(uintptr_t, sizeof(void*)) + case DW_EH_PE_uleb128: + value = read_uleb128(ptr); + break; + case DW_EH_PE_udata2: + READ_TYPE(uint16_t, 2) + case DW_EH_PE_udata4: + READ_TYPE(uint32_t, 4) + case DW_EH_PE_udata8: + READ_TYPE(uint64_t, 8) + case DW_EH_PE_sleb128: + value = read_sleb128(ptr); + break; + case DW_EH_PE_sdata2: + READ_TYPE(int16_t, 2) + case DW_EH_PE_sdata4: + READ_TYPE(int32_t, 4) + case DW_EH_PE_sdata8: + READ_TYPE(int64_t, 8) + default: + abort(); + } + +#undef READ_TYPE + + return value; +} + +static uint64_t +resolve_value(uint64_t value, uint8_t enc, const uint8_t *start, uint64_t base) +{ + if (value == 0) + return 0; + + value += ((enc & 0x70) == DW_EH_PE_pcrel ? (uintptr_t)start : base); + + if (enc & DW_EH_PE_indirect) + value = *(uint64_t*)value; + + return value; +} + +static void +read_lsda(struct _Unwind_Context *ctx, const uint8_t *ptr, struct lsda *lsda) +{ + uint8_t landingpads_start_enc; + uintptr_t callsites_size; + + lsda->region_start = _Unwind_GetRegionStart(ctx); + lsda->landingpads_start = lsda->region_start; + lsda->typestable = 0; + + if ((landingpads_start_enc = *ptr++) != DW_EH_PE_omit) + lsda->landingpads_start = + read_value(landingpads_start_enc, &ptr); + + if ((lsda->typestable_enc = *ptr++) != DW_EH_PE_omit) { + uintptr_t tmp = read_uleb128(&ptr); + lsda->typestable = ptr + tmp; + } + + lsda->typestable_base = get_base(ctx, lsda->typestable_enc); + + lsda->callsites_enc = *ptr++; + callsites_size = read_uleb128(&ptr); + lsda->callsites = ptr; + + lsda->actiontable = lsda->callsites + callsites_size; +} + +static BOOL +find_callsite(struct _Unwind_Context *ctx, struct lsda *lsda, + uintptr_t *landingpad, const uint8_t **actionrecords) +{ + uintptr_t ip = _Unwind_GetIP(ctx); + const uint8_t *ptr; + + *landingpad = 0; + *actionrecords = NULL; + + ptr = lsda->callsites; + while (ptr < lsda->actiontable) { + uintptr_t callsite_start, callsite_len, callsite_landingpad; + uintptr_t callsite_action; + + callsite_start = lsda->region_start + + read_value(lsda->callsites_enc, &ptr); + callsite_len = read_value(lsda->callsites_enc, &ptr); + callsite_landingpad = read_value(lsda->callsites_enc, &ptr); + callsite_action = read_uleb128(&ptr); + + /* We can stop if we passed IP, as the table is sorted */ + if (callsite_start >= ip) + break; + + if (callsite_start + callsite_len >= ip) { + if (callsite_landingpad != 0) + *landingpad = lsda->landingpads_start + + callsite_landingpad; + if (callsite_action != 0) + *actionrecords = lsda->actiontable + + callsite_action - 1; + + return YES; + } + } + + return NO; +} + +static BOOL +class_matches(Class class, id object) +{ + Class iter; + + if (class == Nil) + return YES; + + if (object == nil) + return NO; + + for (iter = object->isa; iter != Nil; iter = class_getSuperclass(iter)) + if (iter == class) + return YES; + + return NO; +} + +static uint8_t +find_actionrecord(const uint8_t *actionrecords, struct lsda *lsda, int actions, + BOOL foreign, struct objc_exception *e, intptr_t *filtervalue) +{ + uint8_t found = 0; + const uint8_t *ptr; + intptr_t filter, displacement; + + do { + ptr = actionrecords; + filter = read_sleb128(&ptr); + + /* + * Get the next action record. Since read_sleb128 modifies ptr, + * we first set the actionrecord to the current ptr and then + * add the displacement. + */ + actionrecords = ptr; + displacement = read_sleb128(&ptr); + actionrecords += displacement; + + if (filter > 0 && !(actions & _UA_FORCE_UNWIND) && !foreign) { + Class class; + uintptr_t i, c; + const uint8_t *tmp; + + i = filter * size_for_encoding(lsda->typestable_enc); + tmp = lsda->typestable - i; + c = read_value(lsda->typestable_enc, &tmp); + c = resolve_value(c, lsda->typestable_enc, + lsda->typestable - i, lsda->typestable_base); + + class = (c != 0 ? objc_get_class((const char*)c) : Nil); + + if (class_matches(class, e->object)) { + *filtervalue = filter; + return (found | HANDLER_FOUND); + } + } else if (filter == 0) + found |= CLEANUP_FOUND; + else + abort(); + } while (displacement != 0); + + return found; +} + +_Unwind_Reason_Code +__gnu_objc_personality_v0(int version, int actions, uint64_t ex_class, + struct _Unwind_Exception *ex, struct _Unwind_Context *ctx) +{ + struct objc_exception *e = (struct objc_exception*)ex; + BOOL foreign = (ex_class != objc_exception_class); + const uint8_t *lsda_addr, *actionrecords; + struct lsda lsda; + uintptr_t landingpad = 0; + uint8_t found = 0; + intptr_t filter = 0; + + if (version != 1) + return _URC_FATAL_PHASE1_ERROR; + + if (ctx == NULL) + abort(); + + /* + * We already cached everything we found in phase 1, so we only need + * to install the context in phase 2. + */ + if (actions & _UA_HANDLER_FRAME && !foreign) { + /* + * For handlers, reg #0 must be the exception's object and reg + * #1 the filter. + */ + _Unwind_SetGR(ctx, __builtin_eh_return_data_regno(0), + __builtin_extend_pointer(e->object)); + _Unwind_SetGR(ctx, __builtin_eh_return_data_regno(1), + e->filter); + _Unwind_SetIP(ctx, e->landingpad); + + return _URC_INSTALL_CONTEXT; + } + + /* No LSDA -> nothing to handle */ + if ((lsda_addr = _Unwind_GetLanguageSpecificData(ctx)) == NULL) + return _URC_CONTINUE_UNWIND; + + read_lsda(ctx, lsda_addr, &lsda); + + if (!find_callsite(ctx, &lsda, &landingpad, &actionrecords)) + return _URC_CONTINUE_UNWIND; + + if (landingpad != 0 && actionrecords == NULL) + found = CLEANUP_FOUND; + else if (landingpad != 0) + found = find_actionrecord(actionrecords, &lsda, actions, + foreign, e, &filter); + + if (!found) + return _URC_CONTINUE_UNWIND; + + if (actions & _UA_SEARCH_PHASE) { + if (!(found & HANDLER_FOUND)) + return _URC_CONTINUE_UNWIND; + + /* Cache it so we don't have to search it again in phase 2 */ + if (!foreign) { + e->landingpad = landingpad; + e->filter = filter; + } + + return _URC_HANDLER_FOUND; + } else if (actions & _UA_CLEANUP_PHASE) { + /* For cleanup, reg #0 must be the exception and reg #1 zero */ + _Unwind_SetGR(ctx, __builtin_eh_return_data_regno(0), + __builtin_extend_pointer(e)); + _Unwind_SetGR(ctx, __builtin_eh_return_data_regno(1), 0); + _Unwind_SetIP(ctx, landingpad); + + return _URC_INSTALL_CONTEXT; + } + + abort(); +} + +static void +cleanup(_Unwind_Reason_Code reason, struct _Unwind_Exception *ex) +{ + free(ex); +} + +void +objc_exception_throw(id object) +{ + struct objc_exception *e; + + if ((e = malloc(sizeof(*e))) == NULL) + abort(); + + e->exception.class = objc_exception_class; + e->exception.cleanup = cleanup; + e->exception.private1 = e->exception.private2 = 0; + e->object = object; + + if (_Unwind_RaiseException(&e->exception) == _URC_END_OF_STACK && + uncaught_exception_handler != NULL) + uncaught_exception_handler(object); + + abort(); +} + +objc_uncaught_exception_handler +objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler handler) +{ + objc_uncaught_exception_handler old = uncaught_exception_handler; + uncaught_exception_handler = handler; + + return old; +} 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_new(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; +} + +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 (void*)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. + */ + +#include "config.h" + +.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 +#ifndef OF_SELUID16 + shrl $16, %eax + + movq (%r8,%rax,8), %r8 +#endif + movq (%r8,%rcx,8), %r8 + movq (%r8,%rdx,8), %rax + + testq %rax, %rax + jz forward + + ret + +forward: + movq objc_not_found_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 ADDED src/runtime/lookup-amd64-macho.S Index: src/runtime/lookup-amd64-macho.S ================================================================== --- src/runtime/lookup-amd64-macho.S +++ src/runtime/lookup-amd64-macho.S @@ -0,0 +1,66 @@ +/* + * 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" + +.globl _objc_msg_lookup +.globl _objc_msg_lookup_super + +.section __TEXT, __text, regular, pure_instructions +_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 +#ifndef OF_SELUID16 + shrl $16, %eax + + movq (%r8,%rax,8), %r8 +#endif + movq (%r8,%rcx,8), %r8 + movq (%r8,%rdx,8), %rax + + testq %rax, %rax + jz forward + + ret + +forward: + jmp _objc_not_found_handler + +_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 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. + */ + +#include "config.h" + +.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 + +#ifndef OF_SELUID16 + movzbl 2(%eax), %ecx + movl (%edx,%ecx,4), %edx +#endif + 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_not_found_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 ADDED src/runtime/lookup.m Index: src/runtime/lookup.m ================================================================== --- src/runtime/lookup.m +++ src/runtime/lookup.m @@ -0,0 +1,109 @@ +/* + * 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" + +IMP (*objc_forward_handler)(id, SEL) = NULL; + +IMP +objc_not_found_handler(id obj, SEL sel) +{ + if (!(obj->isa->info & OBJC_CLASS_INFO_INITIALIZED)) { + BOOL is_class = obj->isa->info & OBJC_CLASS_INFO_METACLASS; + Class cls = (is_class ? (Class)obj : obj->isa); + + objc_initialize_class(cls); + + if (!(cls->info & OBJC_CLASS_INFO_SETUP)) { + if (is_class) + return objc_msg_lookup(nil, sel); + else + ERROR("Could not dispatch message for " + "incomplete class %s!", cls->name); + } + + /* + * We don't need to handle the case that super was called. + * The reason for this is that a call to super is not possible + * before a message to the class has been sent and it thus has + * been initialized together with its superclasses. + */ + return objc_msg_lookup(obj, sel); + } + + if (objc_forward_handler != NULL) + return objc_forward_handler(obj, 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, (uint32_t)sel->uid) != NULL + ? YES : NO); +} + +#ifndef OF_ASM_LOOKUP +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; + + imp = objc_sparsearray_get(obj->isa->dtable, (uint32_t)sel->uid); + + if (imp == NULL) + return objc_not_found_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, (uint32_t)sel->uid); + + if (imp == NULL) + return objc_not_found_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,201 @@ +/* + * 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]; +}; + +#ifndef OF_SELUID16 +struct objc_sparsearray_level2 { + struct objc_sparsearray_level3 *buckets[256]; + BOOL empty; +}; + +struct objc_sparsearray_level3 { + const void *buckets[256]; + BOOL empty; +}; +#else +struct objc_sparsearray_level2 { + const void *buckets[256]; + BOOL empty; +}; +#endif + + +enum objc_abi_class_info { + OBJC_CLASS_INFO_CLASS = 0x001, + OBJC_CLASS_INFO_METACLASS = 0x002, + OBJC_CLASS_INFO_SETUP = 0x100, + OBJC_CLASS_INFO_LOADED = 0x200, + OBJC_CLASS_INFO_INITIALIZED = 0x400 +}; + +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_initialize_class(Class); +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_new(uint32_t); +extern void objc_hashtable_set(struct objc_hashtable*, const char*, + const void*); +extern 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 void objc_sparsearray_copy(struct objc_sparsearray*, + 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 void* +objc_sparsearray_get(const struct objc_sparsearray *s, uint32_t idx) +{ +#ifndef OF_SELUID16 + uint8_t i = idx >> 16; + uint8_t j = idx >> 8; + uint8_t k = idx; + + return (void*)s->buckets[i]->buckets[j]->buckets[k]; +#else + uint8_t i = idx >> 8; + uint8_t j = idx; + + return (void*)s->buckets[i]->buckets[j]; +#endif +} + +#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,136 @@ +/* + * 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 + +typedef void (*objc_uncaught_exception_handler)(id); + +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); +extern objc_uncaught_exception_handler objc_setUncaughtExceptionHandler( + objc_uncaught_exception_handler); + +#endif ADDED src/runtime/selector.m Index: src/runtime/selector.m ================================================================== --- src/runtime/selector.m +++ src/runtime/selector.m @@ -0,0 +1,127 @@ +/* + * 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" + +#ifndef OF_SELUID16 +# define SEL_MAX 0xFFFFFF +#else +# define SEL_MAX 0xFFFF +#endif + +static struct objc_hashtable *selectors = NULL; +static uint32_t selectors_cnt = 0; +static struct objc_sparsearray *selector_names = NULL; + +void +objc_register_selector(struct objc_abi_selector *sel) +{ + struct objc_selector *rsel; + const char *name; + + if (selectors == NULL) + selectors = objc_hashtable_new(2); + else if ((rsel = objc_hashtable_get(selectors, sel->name)) != NULL) { + ((struct objc_selector*)sel)->uid = rsel->uid; + return; + } + + if (selector_names == NULL) + selector_names = objc_sparsearray_new(); + + name = sel->name; + rsel = (struct objc_selector*)sel; + rsel->uid = selectors_cnt++; + + if (selectors_cnt > SEL_MAX) + ERROR("Out of selector slots!"); + + objc_hashtable_set(selectors, name, rsel); + objc_sparsearray_set(selector_names, (uint32_t)rsel->uid, name); +} + +SEL +sel_registerName(const char *name) +{ + const struct objc_abi_selector *rsel; + struct objc_abi_selector *sel; + + if ((rsel = objc_hashtable_get(selectors, name)) != NULL) + return (SEL)rsel; + + /* 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(selector_names, (uint32_t)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_hashtable_free(selectors); + objc_sparsearray_free(selector_names); + + selectors = NULL; + selectors_cnt = 0; + selector_names = NULL; +} ADDED src/runtime/sparsearray.m Index: src/runtime/sparsearray.m ================================================================== --- src/runtime/sparsearray.m +++ src/runtime/sparsearray.m @@ -0,0 +1,225 @@ +/* + * 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; +#ifndef OF_SELUID16 +static struct objc_sparsearray_level3 *empty_level3 = NULL; +#endif + +static void +init(void) +{ + uint_fast16_t i; + + empty_level2 = malloc(sizeof(struct objc_sparsearray_level2)); + if (empty_level2 == NULL) + ERROR("Not enough memory to allocate sparse array!"); + + empty_level2->empty = YES; + +#ifndef OF_SELUID16 + empty_level3 = malloc(sizeof(struct objc_sparsearray_level3)); + if (empty_level3 == NULL) + ERROR("Not enough memory to allocate sparse array!"); + + empty_level3->empty = YES; +#endif + +#ifndef OF_SELUID16 + for (i = 0; i < 256; i++) { + empty_level2->buckets[i] = empty_level3; + empty_level3->buckets[i] = NULL; + } +#else + for (i = 0; i < 256; i++) + empty_level2->buckets[i] = NULL; +#endif +} + +struct objc_sparsearray* +objc_sparsearray_new(void) +{ + struct objc_sparsearray *s; + uint_fast16_t i; + +#ifndef OF_SELUID16 + if (empty_level2 == NULL || empty_level3 == NULL) + init(); +#else + if (empty_level2 == NULL) + init(); +#endif + + 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; +} + +void +objc_sparsearray_copy(struct objc_sparsearray *dst, + struct objc_sparsearray *src) +{ + uint_fast16_t i, j; +#ifndef OF_SELUID16 + uint_fast16_t k; +#endif + uint32_t idx; + + for (i = 0; i < 256; i++) { + if (src->buckets[i]->empty) + continue; + +#ifndef OF_SELUID16 + 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 = (uint32_t)((i << 16) | (j << 8) | k); + objc_sparsearray_set(dst, idx, obj); + } + } +#else + for (j = 0; j < 256; j++) { + const void *obj; + + obj = src->buckets[i]->buckets[j]; + + if (obj == NULL) + continue; + + idx = (i << 8) | j; + objc_sparsearray_set(dst, idx, obj); + } +#endif + } +} + +void +objc_sparsearray_set(struct objc_sparsearray *s, uint32_t idx, const void *obj) +{ +#ifndef OF_SELUID16 + uint8_t i = idx >> 16; + uint8_t j = idx >> 8; + uint8_t k = idx; +#else + uint8_t i = idx >> 8; + uint8_t j = idx; +#endif + + if (s->buckets[i]->empty) { + struct objc_sparsearray_level2 *t; + uint_fast16_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++) +#ifndef OF_SELUID16 + t->buckets[l] = empty_level3; +#else + t->buckets[l] = NULL; +#endif + + s->buckets[i] = t; + } + +#ifndef OF_SELUID16 + if (s->buckets[i]->buckets[j]->empty) { + struct objc_sparsearray_level3 *t; + uint_fast16_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; +#else + s->buckets[i]->buckets[j] = obj; +#endif +} + +void +objc_sparsearray_free(struct objc_sparsearray *s) +{ + uint_fast16_t i; +#ifndef OF_SELUID16 + uint_fast16_t j; +#endif + + for (i = 0; i < 256; i++) { +#ifndef OF_SELUID16 + 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]); +#endif + + free(s->buckets[i]); + } + + free(s); +} + +void +objc_sparsearray_cleanup(void) +{ + if (empty_level2 != NULL) + free(empty_level2); +#ifndef OF_SELUID16 + if (empty_level3 != NULL) + free(empty_level3); +#endif + + empty_level2 = NULL; +#ifndef OF_SELUID16 + empty_level3 = NULL; +#endif +} 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,102 @@ +/* + * 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 BOOL global_mutex_init = NO; + +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 +objc_global_mutex_new(void) +{ + if (!objc_mutex_new(&global_mutex)) + ERROR("Failed to create global mutex!"); + + global_mutex_init = YES; +} + +void +objc_global_mutex_lock(void) +{ + if (!global_mutex_init) + objc_global_mutex_new(); + + 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!"); +} 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}