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(OBJFW_RUNTIME) +# import "runtime-private.h" #endif #import "OFBlock.h" #import "OFAllocFailedException.h" Index: src/runtime/Makefile ================================================================== --- src/runtime/Makefile +++ src/runtime/Makefile @@ -1,9 +1,24 @@ include ../../extra.mk STATIC_PIC_LIB_NOINST = ${RUNTIME_LIB_A} STATIC_LIB_NOINST = ${RUNTIME_A} +SRCS = category.m \ + class.m \ + hashtable.m \ + init.m \ + lookup.m \ + protocol.m \ + selector.m \ + sparsearray.m \ + static-instances.m \ + threading.m \ + asm/amd64-elf/lookup.S \ + asm/x86-elf/lookup.S + include ../../buildsys.mk CPPFLAGS += -I. -I.. -I../.. +AS = ${OBJC} +ASFLAGS = ${CPPFLAGS} LD = ${OBJC} ADDED src/runtime/asm/amd64-elf/lookup.S Index: src/runtime/asm/amd64-elf/lookup.S ================================================================== --- src/runtime/asm/amd64-elf/lookup.S +++ src/runtime/asm/amd64-elf/lookup.S @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#if defined(__x86_64__) && defined(__ELF__) + +.intel_syntax noprefix +.globl objc_msg_lookup +.globl objc_msg_lookup_super + +.section .text +objc_msg_lookup: + test rdi, rdi + jz ret_nil + + mov r8, [rdi] + mov r8, [r8+64] + +lookup: + mov rax, [rsi] + movzx ecx, ah + movzx edx, al + shr eax, 16 + + mov r8, [r8+rax*8] + mov r8, [r8+rcx*8] + mov rax, [r8+rdx*8] + + test rax, rax + jz forward + + ret + +forward: + mov rax, qword ptr objc_forward_handler@GOTPCREL[rip] + jmp [rax] + +objc_msg_lookup_super: + mov rax, [rdi] + test rax, rax + jz ret_nil + + mov r8, [rdi+8] + mov r8, [r8+64] + mov rdi, rax + jmp lookup + +ret_nil: + lea rax, nil_method[rip] + ret + +nil_method: + mov rax, rdi + ret + +.type objc_msg_lookup, @function +.type objc_msg_lookup_super, @function +.size objc_msg_lookup, forward-objc_msg_lookup +.size objc_msg_lookup_super, ret_nil-objc_msg_lookup_super + +#endif ADDED src/runtime/asm/x86-elf/lookup.S Index: src/runtime/asm/x86-elf/lookup.S ================================================================== --- src/runtime/asm/x86-elf/lookup.S +++ src/runtime/asm/x86-elf/lookup.S @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#if defined(__i386__) && defined(__ELF__) + +.intel_syntax noprefix +.globl objc_msg_lookup +.globl objc_msg_lookup_super + +.section .text +objc_msg_lookup: + mov edx, [esp+4] + test edx, edx + jz ret_nil + + mov edx, [edx] + mov edx, [edx+32] + +lookup: + mov eax, [esp+8] + + movzx ecx, byte ptr [eax+2] + mov edx, [edx+ecx*4] + movzx ecx, byte ptr [eax+1] + mov edx, [edx+ecx*4] + movzx ecx, byte ptr [eax] + mov eax, [edx+ecx*4] + + test eax, eax + jz forward + + ret + +forward: + call get_eip +.L1: + add eax, offset objc_forward_handler - offset .L1 + jmp [eax] + +objc_msg_lookup_super: + mov edx, [esp+4] + cmp dword ptr [edx], 0 + je ret_nil + + mov edx, [edx+4] + mov edx, [edx+32] + jmp lookup + +ret_nil: + call get_eip +.L2: + add eax, offset nil_method - offset .L2 + ret + +nil_method: + mov eax, [esp+4] + ret + +get_eip: + mov eax, dword ptr [esp] + ret + +.type objc_msg_lookup, @function +.type objc_msg_lookup_super, @function +.size objc_msg_lookup, forward-objc_msg_lookup +.size objc_msg_lookup_super, ret_nil-objc_msg_lookup_super + +#endif ADDED src/runtime/category.m Index: src/runtime/category.m ================================================================== --- src/runtime/category.m +++ src/runtime/category.m @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include +#include +#include + +#import "runtime.h" +#import "runtime-private.h" + +static struct objc_hashtable *categories = NULL; + +static void +register_selectors(struct objc_abi_category *cat) +{ + struct objc_abi_method_list *ml; + unsigned int i; + + for (ml = cat->instance_methods; ml != NULL; ml = ml->next) + for (i = 0; i < ml->count; i++) + objc_register_selector( + (struct objc_abi_selector*)&ml->methods[i]); + + for (ml = cat->class_methods; ml != NULL; ml = ml->next) + for (i = 0; i < ml->count; i++) + objc_register_selector( + (struct objc_abi_selector*)&ml->methods[i]); +} + +static void +register_category(struct objc_abi_category *cat) +{ + struct objc_abi_category **cats; + Class cls = objc_classname_to_class(cat->class_name); + + if (categories == NULL) + categories = objc_hashtable_alloc(2); + + cats = (struct objc_abi_category**)objc_hashtable_get(categories, + cat->class_name); + + if (cats != NULL) { + struct objc_abi_category **ncats; + size_t i; + + for (i = 0; cats[i] != NULL; i++); + + if ((ncats = realloc(cats, + (i + 2) * sizeof(struct objc_abi_category*))) == NULL) + ERROR("Not enough memory for category %s of class %s!", + cat->category_name, cat->class_name); + + ncats[i] = cat; + ncats[i + 1] = NULL; + objc_hashtable_set(categories, cat->class_name, ncats); + + if (cls != Nil && cls->info & OBJC_CLASS_INFO_INITIALIZED) { + 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_INITIALIZED) { + 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_abi_category** +objc_categories_for_class(Class cls) +{ + if (categories == NULL) + return NULL; + + return (struct objc_abi_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,493 @@ +/* + * 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" + +@protocol BasicClass ++ (void)load; ++ (void)initialize; +@end + +static struct objc_hashtable *classes = NULL; + +static void +register_class(Class cls) +{ + if (classes == NULL) + classes = objc_hashtable_alloc(2); + + objc_hashtable_set(classes, cls->name, cls); +} + +static void +register_selectors(struct objc_abi_class *cls) +{ + struct objc_abi_method_list *ml; + unsigned int i; + + for (ml = cls->methodlist; ml != NULL; ml = ml->next) + for (i = 0; i < ml->count; i++) + objc_register_selector( + (struct objc_abi_selector*)&ml->methods[i]); +} + +static BOOL +has_load(struct objc_abi_class *cls) +{ + struct objc_abi_method_list *ml; + unsigned int i; + + for (ml = cls->methodlist; ml != NULL; ml = ml->next) + for (i = 0; i < ml->count; i++) + if (!strcmp(ml->methods[i].name, "load")) + return YES; + + return NO; +} + +void +objc_update_dtable(Class cls) +{ + struct objc_abi_method_list *ml; + struct objc_abi_category **cats; + struct objc_sparsearray *dtable; + unsigned int i; + + if (cls->superclass != Nil) + dtable = objc_sparsearray_copy(cls->superclass->dtable); + else + dtable = objc_sparsearray_new(); + + for (ml = cls->methodlist; ml != NULL; ml = ml->next) + for (i = 0; i < ml->count; i++) + objc_sparsearray_set(dtable, + (uintptr_t)ml->methods[i].name, ml->methods[i].imp); + + if ((cats = objc_categories_for_class(cls)) != NULL) { + for (i = 0; cats[i] != NULL; i++) { + unsigned int j; + + ml = (cls->info & OBJC_CLASS_INFO_CLASS ? + cats[i]->instance_methods : cats[i]->class_methods); + + for (; ml != NULL; ml = ml->next) + for (j = 0; j < ml->count; j++) + objc_sparsearray_set(dtable, + (uintptr_t)ml->methods[j].name, + ml->methods[j].imp); + } + } + + if (cls->dtable != NULL) + objc_sparsearray_free_when_singlethreaded(cls->dtable); + + cls->dtable = dtable; + + if (cls->subclass_list != NULL) + for (i = 0; cls->subclass_list[i] != NULL; i++) + objc_update_dtable(cls->subclass_list[i]); +} + +void +objc_register_all_classes(struct objc_abi_symtab *symtab) +{ + size_t i; + + for (i = 0; i < symtab->cls_def_cnt; i++) + register_class((Class)symtab->defs[i]); + + for (i = 0; i < symtab->cls_def_cnt; i++) { + struct objc_abi_class *cls; + BOOL load; + + cls = (struct objc_abi_class*)symtab->defs[i]; + load = has_load(cls->metaclass); + + register_selectors(cls); + register_selectors(cls->metaclass); + + if (load) { + /* Sets up the dtable */ + assert(objc_get_class(cls->name) == (Class)cls); + + [(Class)cls load]; + } + } +} + +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; +} + +inline Class +objc_classname_to_class(const char *name) +{ + Class c; + + if (classes == NULL) + return Nil; + + objc_global_mutex_lock(); + c = (Class)objc_hashtable_get(classes, name); + objc_global_mutex_unlock(); + + return c; +} + +inline Class +objc_lookup_class(const char *name) +{ + Class cls = objc_classname_to_class(name); + const char *superclass; + + if (cls == NULL) + return Nil; + + if (cls->info & OBJC_CLASS_INFO_INITIALIZED) + return cls; + + objc_global_mutex_lock(); + + /* + * It's possible that two threads try to get a class at the same time. + * Make sure that the thread which held the lock did not already + * initialize it. + */ + if (cls->info & OBJC_CLASS_INFO_INITIALIZED) { + objc_global_mutex_unlock(); + return cls; + } + + if ((superclass = ((struct objc_abi_class*)cls)->superclass) != NULL) { + if ((cls->superclass = objc_lookup_class(superclass)) == Nil) + ERROR("Class %s not found, which is the superclass for " + "class %s!", superclass, cls->name); + + cls->isa->superclass = cls->superclass->isa; + + add_subclass(cls); + add_subclass(cls->isa); + } else if ((superclass = ((struct objc_abi_class*)cls->isa)->superclass) + != NULL) { + if (strcmp(superclass, name)) + abort(); + + cls->isa->superclass = cls; + } + + objc_update_dtable(cls); + objc_update_dtable(cls->isa); + + cls->info |= OBJC_CLASS_INFO_INITIALIZED; + cls->isa->info |= OBJC_CLASS_INFO_INITIALIZED; + + if (class_respondsToSelector(cls->isa, @selector(initialize))) + [cls initialize]; + + objc_global_mutex_unlock(); + + return cls; +} + +Class +objc_get_class(const char *name) +{ + Class cls; + + if ((cls = objc_lookup_class(name)) == Nil) + ERROR("Class %s not found!", name); + + return cls; +} + +const char* +class_getName(Class cls) +{ + return cls->name; +} + +Class +class_getSuperclass(Class cls) +{ + return cls->superclass; +} + +BOOL +class_isKindOfClass(Class cls1, Class cls2) +{ + Class iter; + + for (iter = cls1; iter != Nil; iter = iter->superclass) + if (iter == cls2) + return YES; + + return NO; +} + +unsigned long +class_getInstanceSize(Class cls) +{ + return cls->instance_size; +} + +IMP +objc_get_class_method(Class cls, SEL sel) +{ + return objc_sparsearray_get(cls->isa->dtable, sel->uid); +} + +IMP +objc_get_instance_method(Class cls, SEL sel) +{ + return objc_sparsearray_get(cls->dtable, sel->uid); +} + +const char* +objc_get_type_encoding(Class cls, SEL sel) +{ + struct objc_abi_method_list *ml; + struct objc_abi_category **cats; + unsigned int i; + + objc_global_mutex_lock(); + + for (ml = cls->isa->methodlist; ml != NULL; ml = ml->next) { + for (i = 0; i < ml->count; i++) { + if ((uintptr_t)ml->methods[i].name == sel->uid) { + const char *ret = ml->methods[i].types; + objc_global_mutex_unlock(); + return ret; + } + } + } + + if ((cats = objc_categories_for_class(cls)) != NULL) { + for (; *cats != NULL; cats++) { + for (ml = (*cats)->class_methods; ml != NULL; + ml = ml->next) { + for (i = 0; i < ml->count; i++) { + if ((uintptr_t)ml->methods[i].name == + sel->uid) { + const char *ret = + ml->methods[i].types; + objc_global_mutex_unlock(); + return ret; + } + } + } + } + } + + objc_global_mutex_unlock(); + + return NULL; +} + +IMP +objc_replace_class_method(Class cls, SEL sel, IMP newimp) +{ + struct objc_abi_method_list *ml; + struct objc_abi_category **cats; + unsigned int i; + BOOL replaced = NO; + IMP oldimp = NULL; + + objc_global_mutex_lock(); + + for (ml = cls->isa->methodlist; ml != NULL; ml = ml->next) { + for (i = 0; i < ml->count; i++) { + if ((uintptr_t)ml->methods[i].name == sel->uid) { + oldimp = ml->methods[i].imp; + ml->methods[i].imp = newimp; + replaced = YES; + break; + } + } + } + + if ((cats = objc_categories_for_class(cls)) != NULL) { + for (; *cats != NULL; cats++) { + for (ml = (*cats)->class_methods; ml != NULL; + ml = ml->next) { + for (i = 0; i < ml->count; i++) { + if ((uintptr_t)ml->methods[i].name == + sel->uid) { + oldimp = ml->methods[i].imp; + ml->methods[i].imp = newimp; + replaced = YES; + break; + } + } + } + } + } + + if (!replaced) { + /* FIXME: We need a way to free this at objc_exit() */ + if ((ml = malloc(sizeof(struct objc_abi_method_list))) == NULL) + ERROR("Not enough memory to replace method!"); + + ml->next = cls->isa->methodlist; + ml->count = 1; + ml->methods[0].name = (const char*)sel->uid; + /* FIXME: We need to get the type from a superclass */ + ml->methods[0].types = sel->types; + ml->methods[0].imp = newimp; + + cls->isa->methodlist = ml; + } + + objc_update_dtable(cls->isa); + + objc_global_mutex_unlock(); + + return oldimp; +} + +IMP +objc_replace_instance_method(Class cls, SEL sel, IMP newimp) +{ + struct objc_abi_method_list *ml; + struct objc_abi_category **cats; + unsigned int i; + BOOL replaced = NO; + IMP oldimp = NULL; + + objc_global_mutex_lock(); + + for (ml = cls->methodlist; ml != NULL; ml = ml->next) { + for (i = 0; i < ml->count; i++) { + if ((uintptr_t)ml->methods[i].name == sel->uid) { + oldimp = ml->methods[i].imp; + ml->methods[i].imp = newimp; + replaced = YES; + break; + } + } + } + + 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 ((uintptr_t)ml->methods[i].name == + sel->uid) { + oldimp = ml->methods[i].imp; + ml->methods[i].imp = newimp; + replaced = YES; + break; + } + } + } + } + } + + if (!replaced) { + /* FIXME: We need a way to free this at objc_exit() */ + if ((ml = malloc(sizeof(struct objc_abi_method_list))) == NULL) + ERROR("Not enough memory to replace method!"); + + ml->next = cls->methodlist; + ml->count = 1; + ml->methods[0].name = (const char*)sel->uid; + /* FIXME: We need to get the type from a superclass */ + ml->methods[0].types = sel->types; + ml->methods[0].imp = newimp; + + cls->methodlist = ml; + } + + objc_update_dtable(cls); + + objc_global_mutex_unlock(); + + return oldimp; +} + +static void +free_class(Class rcls) +{ + struct objc_abi_class *cls = (struct objc_abi_class*)rcls; + + if (!(rcls->info & OBJC_CLASS_INFO_INITIALIZED)) + return; + + if (rcls->subclass_list != NULL) { + free(rcls->subclass_list); + rcls->subclass_list = NULL; + } + + objc_sparsearray_free(rcls->dtable); + rcls->dtable = NULL; + + if (rcls->superclass != Nil) + cls->superclass = rcls->superclass->name; +} + +void +objc_free_all_classes(void) +{ + uint32_t i; + + if (classes == NULL) + return; + + for (i = 0; i <= classes->last_idx; i++) { + if (classes->data[i] != NULL) { + free_class((Class)classes->data[i]->obj); + free_class(((Class)classes->data[i]->obj)->isa); + } + } + + objc_hashtable_free(classes); + classes = NULL; +} ADDED src/runtime/hashtable.m Index: src/runtime/hashtable.m ================================================================== --- src/runtime/hashtable.m +++ src/runtime/hashtable.m @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include +#include +#include +#include + +#include + +#import "runtime.h" +#import "runtime-private.h" + +uint32_t +objc_hash_string(const char *str) +{ + uint32_t hash = 0; + + while (*str != 0) { + hash += *str; + hash += (hash << 10); + hash ^= (hash >> 6); + str++; + } + + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + + return hash; +} + +struct objc_hashtable* +objc_hashtable_alloc(uint32_t size) +{ + struct objc_hashtable *h; + uint32_t i; + + if ((h = malloc(sizeof(struct objc_hashtable))) == NULL) + ERROR("Not enough memory to allocate hash table!"); + + h->count = 0; + h->last_idx = size - 1; + h->data = malloc(size * sizeof(struct objc_hashtable_bucket*)); + + if (h->data == NULL) + ERROR("Not enough memory to allocate hash table!"); + + for (i = 0; i < size; i++) + h->data[i] = NULL; + + return h; +} + +static void +insert(struct objc_hashtable *h, const char *key, const void *obj) +{ + uint32_t i, hash, last; + struct objc_hashtable_bucket *bucket; + + hash = objc_hash_string(key); + assert(h->count + 1 <= UINT32_MAX / 4); + + if ((h->count + 1) * 4 / (h->last_idx + 1) >= 3) { + struct objc_hashtable_bucket **ndata; + uint32_t nsize = (h->last_idx + 1) << 1; + + assert(nsize > 0); + + ndata = malloc(nsize * sizeof(struct objc_hashtable_bucket*)); + if (ndata == NULL) + ERROR("Not enough memory to insert into hash table!"); + + for (i = 0; i < nsize; i++) + ndata[i] = NULL; + + for (i = 0; i <= h->last_idx; i++) { + if (h->data[i] != NULL) { + uint32_t j; + + last = nsize; + + for (j = h->data[i]->hash & (nsize - 1); + j < last && ndata[j] != NULL; j++); + + if (j >= last) { + last = h->data[i]->hash & (nsize - 1); + + for (j = 0; j < last && + ndata[j] != NULL; j++); + } + + if (j >= last) + ERROR("No free bucket!"); + + ndata[j] = h->data[i]; + } + } + + free(h->data); + h->data = ndata; + h->last_idx = nsize - 1; + } + + last = h->last_idx + 1; + + for (i = hash & h->last_idx; i < last && h->data[i] != NULL; i++); + + if (i >= last) { + last = hash & h->last_idx; + + for (i = 0; i < last && h->data[i] != NULL; i++); + } + + if (i >= last) + ERROR("No free bucket!"); + + if ((bucket = malloc(sizeof(struct objc_hashtable_bucket))) == NULL) + ERROR("Not enough memory to allocate hash table bucket!"); + + bucket->key = key; + bucket->hash = hash; + bucket->obj = obj; + + h->data[i] = bucket; + h->count++; +} + +static inline int64_t +index_for_key(struct objc_hashtable *h, const char *key) +{ + uint32_t i, hash; + + hash = objc_hash_string(key) & h->last_idx; + + for (i = hash; i <= h->last_idx && h->data[i] != NULL; i++) + if (!strcmp(h->data[i]->key, key)) + return i; + + if (i <= h->last_idx) + return -1; + + for (i = 0; i < hash && h->data[i] != NULL; i++) + if (!strcmp(h->data[i]->key, key)) + return i; + + return -1; +} + +void +objc_hashtable_set(struct objc_hashtable *h, const char *key, const void *obj) +{ + int64_t idx = index_for_key(h, key); + + if (idx < 0) { + insert(h, key, obj); + return; + } + + h->data[idx]->obj = obj; +} + +const void* +objc_hashtable_get(struct objc_hashtable *h, const char *key) +{ + int64_t idx = index_for_key(h, key); + + if (idx < 0) + return NULL; + + return h->data[idx]->obj; +} + +void +objc_hashtable_free(struct objc_hashtable *h) +{ + uint32_t i; + + for (i = 0; i <= h->last_idx; i++) + if (h->data[i] != NULL) + free(h->data[i]); + + free(h->data); + free(h); +} ADDED src/runtime/init.m Index: src/runtime/init.m ================================================================== --- src/runtime/init.m +++ src/runtime/init.m @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "runtime.h" +#import "runtime-private.h" + +void +__objc_exec_class(struct objc_abi_module *module) +{ + objc_global_mutex_lock(); + + objc_register_all_selectors(module->symtab); + objc_register_all_classes(module->symtab); + objc_register_all_categories(module->symtab); + objc_init_static_instances(module->symtab); + + objc_global_mutex_unlock(); +} + +void +objc_exit(void) +{ + objc_global_mutex_lock(); + + objc_free_all_categories(); + objc_free_all_classes(); + objc_free_all_selectors(); + objc_sparsearray_cleanup(); + + objc_global_mutex_unlock(); + objc_global_mutex_free(); +} ADDED src/runtime/lookup.m Index: src/runtime/lookup.m ================================================================== --- src/runtime/lookup.m +++ src/runtime/lookup.m @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include +#include + +#import "runtime.h" +#import "runtime-private.h" +#import "macros.h" + +static IMP not_found_handler(id, SEL); +IMP (*objc_forward_handler)(id, SEL) = not_found_handler; + +static IMP +not_found_handler(id obj, SEL sel) +{ + ERROR("Selector %s is not implemented for class %s!", + sel_getName(sel), obj->isa->name); +} + +BOOL +class_respondsToSelector(Class cls, SEL sel) +{ + if (cls == Nil) + return NO; + + return (objc_sparsearray_get(cls->dtable, sel->uid) != NULL ? YES : NO); +} + +#if !defined(__ELF__) || (!defined(OF_X86_ASM) && !defined(OF_AMD64_ASM)) +static id +nil_method(id self, SEL _cmd) +{ + return nil; +} + +IMP +objc_msg_lookup(id obj, SEL sel) +{ + IMP imp; + + if (obj == nil) + return (IMP)nil_method; + + if ((imp = objc_sparsearray_get(obj->isa->dtable, sel->uid)) == NULL) + return objc_forward_handler(obj, sel); + + return imp; +} + +IMP +objc_msg_lookup_super(struct objc_abi_super *super, SEL sel) +{ + IMP imp; + + if (super->self == nil) + return (IMP)nil_method; + + imp = objc_sparsearray_get(super->class->dtable, sel->uid); + + if (imp == NULL) + return objc_forward_handler(super->self, sel); + + return imp; +} +#endif ADDED src/runtime/protocol.m Index: src/runtime/protocol.m ================================================================== --- src/runtime/protocol.m +++ src/runtime/protocol.m @@ -0,0 +1,68 @@ +/* + * 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 +- (BOOL)_isImplementedByClass: (Class)cls +{ + struct objc_abi_protocol_list *pl; + struct objc_abi_category **cats; + long i, j; + + objc_global_mutex_lock(); + + for (pl = cls->protocols; pl != NULL; pl = pl->next) { + for (i = 0; i < pl->count; i++) { + if (!strcmp(pl->list[i]->name, name)) { + objc_global_mutex_unlock(); + return YES; + } + } + } + + 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 (!strcmp(pl->list[j]->name, name)) { + objc_global_mutex_unlock(); + return YES; + } + } + } + } + + objc_global_mutex_unlock(); + + return NO; +} +@end + +BOOL +class_conformsToProtocl(Class cls, Protocol *p) +{ + return [p _isImplementedByClass: cls]; +} 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,187 @@ +/* + * 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_abi_protocol_list *protocols; +}; + +struct objc_abi_super { + id self; + Class class; +}; + +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_protocol_list { + struct objc_abi_protocol_list *next; + long count; + Protocol *list[1]; +}; + +struct objc_abi_static_instances { + const char *class_name; + id instances[1]; +}; + +struct objc_abi_symtab { + unsigned long unknown; + struct objc_abi_selector *sel_refs; + uint16_t cls_def_cnt; + uint16_t cat_def_cnt; + void *defs[1]; +}; + +struct objc_abi_module { + unsigned long version; /* 9 = non-fragile */ + unsigned long size; + const char *name; + struct objc_abi_symtab *symtab; +}; + +struct objc_hashtable_bucket { + const char *key; + const void *obj; + uint32_t hash; +}; + +struct objc_hashtable { + uint32_t count; + uint32_t last_idx; + struct objc_hashtable_bucket **data; +}; + +struct objc_sparsearray { + struct objc_sparsearray_level2 *buckets[256]; +}; + +struct objc_sparsearray_level2 { + struct objc_sparsearray_level3 *buckets[256]; + BOOL empty; +}; + +struct objc_sparsearray_level3 { + const void *buckets[256]; + BOOL empty; +}; + +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_abi_category** objc_categories_for_class(Class); +extern void objc_free_all_categories(void); +extern void objc_update_dtable(Class); +extern void objc_register_all_classes(struct objc_abi_symtab*); +extern Class objc_classname_to_class(const char*); +extern void objc_free_all_classes(void); +extern uint32_t objc_hash_string(const char*); +extern struct objc_hashtable* objc_hashtable_alloc(uint32_t); +extern void objc_hashtable_set(struct objc_hashtable*, const char*, + const void*); +extern const void* objc_hashtable_get(struct objc_hashtable*, const char*); +extern void objc_hashtable_free(struct objc_hashtable *h); +extern BOOL objc_hashtable_warn_on_collision; +extern void objc_register_selector(struct objc_abi_selector*); +extern void objc_register_all_selectors(struct objc_abi_symtab*); +extern void objc_free_all_selectors(void); +extern struct objc_sparsearray* objc_sparsearray_new(void); +extern struct objc_sparsearray* objc_sparsearray_copy(struct objc_sparsearray*); +extern void objc_sparsearray_set(struct objc_sparsearray*, uint32_t, + const void*); +extern void objc_sparsearray_free(struct objc_sparsearray*); +extern void objc_sparsearray_free_when_singlethreaded(struct objc_sparsearray*); +extern void objc_sparsearray_cleanup(void); +extern void objc_init_static_instances(struct objc_abi_symtab*); +extern void __objc_exec_class(struct objc_abi_module*); +extern BOOL objc_mutex_new(objc_mutex_t*); +extern BOOL objc_mutex_lock(objc_mutex_t*); +extern BOOL objc_mutex_unlock(objc_mutex_t*); +extern BOOL objc_mutex_free(objc_mutex_t*); +extern void objc_global_mutex_lock(void); +extern void objc_global_mutex_unlock(void); +extern void objc_global_mutex_free(void); +extern void objc_free_when_singlethreaded(void*); + +static inline const void* +objc_sparsearray_get(const struct objc_sparsearray *s, uint32_t idx) +{ + uint8_t i = idx >> 16; + uint8_t j = idx >> 8; + uint8_t k = idx; + + return s->buckets[i]->buckets[j]->buckets[k]; +} + +#define ERROR(...) \ + { \ + fprintf(stderr, "[objc @ " __FILE__ ":%d] ", __LINE__); \ + fprintf(stderr, __VA_ARGS__); \ + fputs("\n", stderr); \ + abort(); \ + } ADDED src/runtime/runtime.h Index: src/runtime/runtime.h ================================================================== --- src/runtime/runtime.h +++ src/runtime/runtime.h @@ -0,0 +1,100 @@ +/* + * 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, ...); + +#ifdef __OBJC__ +@interface Protocol +{ +@private + Class isa; + const char *name; + struct objc_abi_protocol_list *protocol_list; + struct objc_abi_method_description_list *instance_methods; + struct objc_abi_method_description_list *class_methods; +} +@end +#else +typedef const void Protocol; +#endif + +struct objc_class { + Class isa; + Class superclass; + const char *name; + unsigned long version; + unsigned long info; + unsigned long instance_size; + void *ivars; + struct objc_abi_method_list *methodlist; + struct objc_sparsearray *dtable; + Class *subclass_list; + void *sibling_class; + struct objc_abi_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; +}; + +enum objc_abi_class_info { + OBJC_CLASS_INFO_CLASS = 0x01, + OBJC_CLASS_INFO_METACLASS = 0x02, + OBJC_CLASS_INFO_INITIALIZED = 0x04 +}; + +#define Nil (Class)0 +#define nil (id)0 +#define YES (BOOL)1 +#define NO (BOOL)0 + +extern SEL sel_registerName(const char*); +extern const char* sel_getName(SEL); +extern 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 objc_get_class_method(Class, SEL); +extern IMP objc_get_instance_method(Class, SEL); +extern IMP objc_replace_class_method(Class, SEL, IMP); +extern IMP objc_replace_instance_method(Class, SEL, IMP); +extern const char* objc_get_type_encoding(Class, SEL); +extern IMP objc_msg_lookup(id, SEL); +extern void objc_thread_add(void); +extern void objc_thread_remove(void); +extern void objc_exit(void); +#endif ADDED src/runtime/selector.m Index: src/runtime/selector.m ================================================================== --- src/runtime/selector.m +++ src/runtime/selector.m @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include +#include +#include + +#import "runtime.h" +#import "runtime-private.h" + +static struct objc_sparsearray *selectors = NULL; + +void +objc_register_selector(struct objc_abi_selector *sel) +{ + uint32_t hash, last; + struct objc_selector *rsel = (struct objc_selector*)sel; + const char *name; + + if (selectors == NULL) + selectors = objc_sparsearray_new(); + + hash = objc_hash_string(sel->name) >> 8; + + while (hash <= 0xFFFFFF && + (name = objc_sparsearray_get(selectors, hash)) != NULL) { + if (!strcmp(name, sel->name)) { + rsel->uid = hash; + return; + } + + hash++; + } + + if (hash > 0xFFFFFF) { + last = hash; + hash = 0; + + while (hash < last && + (name = objc_sparsearray_get(selectors, hash)) != NULL) { + if (!strcmp(name, sel->name)) { + rsel->uid = hash; + return; + } + + hash++; + } + } + + objc_sparsearray_set(selectors, hash, (void*)sel->name); + rsel->uid = hash; +} + +SEL +sel_registerName(const char *name) +{ + struct objc_abi_selector *sel; + + /* FIXME: Free on objc_exit() */ + if ((sel = malloc(sizeof(struct objc_abi_selector))) == NULL) + ERROR("Not enough memory to allocate selector!"); + + if ((sel->name = strdup(name)) == NULL) + ERROR("Not enough memory to allocate selector!"); + + sel->types = NULL; + + objc_global_mutex_lock(); + objc_register_selector(sel); + objc_global_mutex_unlock(); + + return (SEL)sel; +} + +void +objc_register_all_selectors(struct objc_abi_symtab *symtab) +{ + struct objc_abi_selector *sel; + + if (symtab->sel_refs == NULL) + return; + + for (sel = symtab->sel_refs; sel->name != NULL; sel++) + objc_register_selector(sel); +} + +const char* +sel_getName(SEL sel) +{ + const char *ret; + + objc_global_mutex_lock(); + ret = objc_sparsearray_get(selectors, sel->uid); + objc_global_mutex_unlock(); + + return ret; +} + +void +objc_free_all_selectors(void) +{ + objc_sparsearray_free(selectors); + selectors = NULL; +} ADDED src/runtime/sparsearray.m Index: src/runtime/sparsearray.m ================================================================== --- src/runtime/sparsearray.m +++ src/runtime/sparsearray.m @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include +#include + +#import "runtime.h" +#import "runtime-private.h" + +static struct objc_sparsearray_level2 *empty_level2 = NULL; +static struct objc_sparsearray_level3 *empty_level3 = NULL; + +static void +init(void) +{ + size_t i; + + empty_level2 = malloc(sizeof(struct objc_sparsearray_level2)); + empty_level3 = malloc(sizeof(struct objc_sparsearray_level3)); + + if (empty_level2 == NULL || empty_level3 == NULL) + ERROR("Not enough memory to allocate sparse array!"); + + empty_level2->empty = YES; + empty_level3->empty = YES; + + for (i = 0; i < 256; i++) { + empty_level2->buckets[i] = empty_level3; + empty_level3->buckets[i] = NULL; + } +} + +struct objc_sparsearray* +objc_sparsearray_new(void) +{ + struct objc_sparsearray *s; + size_t i; + + if (empty_level2 == NULL || empty_level3 == NULL) + init(); + + if ((s = malloc(sizeof(struct objc_sparsearray))) == NULL) + ERROR("Not enough memory to allocate sparse array!"); + + for (i = 0; i < 256; i++) + s->buckets[i] = empty_level2; + + return s; +} + +struct objc_sparsearray* +objc_sparsearray_copy(struct objc_sparsearray *src) +{ + struct objc_sparsearray *dst; + size_t i, j, k; + uint32_t idx; + + dst = objc_sparsearray_new(); + + for (i = 0; i < 256; i++) { + if (src->buckets[i]->empty) + continue; + + for (j = 0; j < 256; j++) { + if (src->buckets[i]->buckets[j]->empty) + continue; + + for (k = 0; k < 256; k++) { + const void *obj; + + obj = src->buckets[i]->buckets[j]->buckets[k]; + + if (obj == NULL) + continue; + + idx = (i << 16) | (j << 8) | k; + objc_sparsearray_set(dst, idx, obj); + } + } + } + + return dst; +} + +void +objc_sparsearray_set(struct objc_sparsearray *s, uint32_t idx, const void *obj) +{ + uint8_t i = idx >> 16; + uint8_t j = idx >> 8; + uint8_t k = idx; + + if (s->buckets[i]->empty) { + struct objc_sparsearray_level2 *t; + size_t l; + + t = malloc(sizeof(struct objc_sparsearray_level2)); + + if (t == NULL) + ERROR("Not enough memory to insert into sparse array!"); + + t->empty = NO; + + for (l = 0; l < 256; l++) + t->buckets[l] = empty_level3; + + s->buckets[i] = t; + } + + if (s->buckets[i]->buckets[j]->empty) { + struct objc_sparsearray_level3 *t; + size_t l; + + t = malloc(sizeof(struct objc_sparsearray_level3)); + + if (t == NULL) + ERROR("Not enough memory to insert into sparse array!"); + + t->empty = NO; + + for (l = 0; l < 256; l++) + t->buckets[l] = NULL; + + s->buckets[i]->buckets[j] = t; + } + + s->buckets[i]->buckets[j]->buckets[k] = obj; +} + +void +objc_sparsearray_free(struct objc_sparsearray *s) +{ + size_t i, j; + + for (i = 0; i < 256; i++) { + if (s->buckets[i]->empty) + continue; + + for (j = 0; j < 256; j++) + if (!s->buckets[i]->buckets[j]->empty) + free(s->buckets[i]->buckets[j]); + + free(s->buckets[i]); + } + + free(s); +} + +void +objc_sparsearray_free_when_singlethreaded(struct objc_sparsearray *s) +{ + size_t i, j; + + for (i = 0; i < 256; i++) { + if (s->buckets[i]->empty) + continue; + + for (j = 0; j < 256; j++) + if (!s->buckets[i]->buckets[j]->empty) + objc_free_when_singlethreaded( + s->buckets[i]->buckets[j]); + + objc_free_when_singlethreaded(s->buckets[i]); + } + + objc_free_when_singlethreaded(s); +} + +void +objc_sparsearray_cleanup(void) +{ + if (empty_level2 != NULL) + free(empty_level2); + if (empty_level3 != NULL) + free(empty_level3); + + empty_level2 = NULL; + empty_level3 = NULL; +} ADDED src/runtime/static-instances.m Index: src/runtime/static-instances.m ================================================================== --- src/runtime/static-instances.m +++ src/runtime/static-instances.m @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include +#include + +#import "runtime.h" +#import "runtime-private.h" + +static struct objc_abi_static_instances **static_instances = NULL; +static size_t static_instances_cnt = 0; + +void +objc_init_static_instances(struct objc_abi_symtab *symtab) +{ + struct objc_abi_static_instances **si; + size_t i; + + /* Check if the class for a static instance became available */ + for (i = 0; i < static_instances_cnt; i++) { + Class cls = objc_lookup_class(static_instances[i]->class_name); + + if (cls != Nil) { + id *instances; + + for (instances = static_instances[i]->instances; + *instances != nil; instances++) + (*instances)->isa = cls; + + static_instances_cnt--; + + if (static_instances_cnt == 0) { + free(static_instances); + static_instances = NULL; + continue; + } + + static_instances[i] = + static_instances[static_instances_cnt]; + + static_instances = realloc(static_instances, + sizeof(struct objc_abi_static_instances*) * + static_instances_cnt); + + if (static_instances == NULL) + ERROR("Not enough memory for list of static " + "instances!"); + } + } + + si = (struct objc_abi_static_instances**) + symtab->defs[symtab->cls_def_cnt + symtab->cat_def_cnt]; + + if (si == NULL) + return; + + for (; *si != NULL; si++) { + Class cls = objc_lookup_class((*si)->class_name); + + if (cls != Nil) { + id *instances; + + for (instances = (*si)->instances; *instances != nil; + instances++) + (*instances)->isa = cls; + } else { + if (static_instances == NULL) + static_instances = malloc(sizeof( + struct objc_abi_static_instances*)); + else + static_instances = realloc(static_instances, + sizeof(struct objc_abi_static_instances*) * + (static_instances_cnt + 1)); + + if (static_instances == NULL) + ERROR("Not enough memory for list of static " + "instances!"); + + static_instances[static_instances_cnt++] = *si; + } + } +} ADDED src/runtime/threading.m Index: src/runtime/threading.m ================================================================== --- src/runtime/threading.m +++ src/runtime/threading.m @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include +#include + +#import "runtime.h" +#import "runtime-private.h" + +static objc_mutex_t global_mutex; +static int num_threads = 1; +static void **free_queue = NULL; +static size_t free_queue_cnt = 0; + +BOOL +objc_mutex_new(objc_mutex_t *mutex) +{ + if (of_mutex_new(&mutex->mutex )) + return NO; + + mutex->count = 0; + + return YES; +} + +BOOL +objc_mutex_lock(objc_mutex_t *mutex) +{ + if (mutex->count > 0 && of_thread_is_current(mutex->owner)) { + mutex->count++; + return YES; + } + + if (!of_mutex_lock(&mutex->mutex)) + return NO; + + mutex->owner = of_thread_current(); + mutex->count++; + + return YES; +} + +BOOL +objc_mutex_unlock(objc_mutex_t *mutex) +{ + if (--mutex->count == 0) + return of_mutex_unlock(&mutex->mutex); + + return YES; +} + +BOOL +objc_mutex_free(objc_mutex_t *mutex) +{ + return of_mutex_free(&mutex->mutex); +} + +static void __attribute__((constructor)) +objc_global_mutex_new(void) +{ + if (!objc_mutex_new(&global_mutex)) + ERROR("Failed to create global mutex!"); +} + +void +objc_global_mutex_lock(void) +{ + if (!objc_mutex_lock(&global_mutex)) + ERROR("Failed to lock global mutex!"); +} + +void +objc_global_mutex_unlock(void) +{ + if (!objc_mutex_unlock(&global_mutex)) + ERROR("Failed to unlock global mutex!"); +} + +void +objc_global_mutex_free(void) +{ + if (!objc_mutex_free(&global_mutex)) + ERROR("Failed to free global mutex!"); +} + +void +objc_thread_add(void) +{ + /* + * If some class is being initialized, we want to wait for it, thus + * we use the global lock instead of atomic operations. + */ + objc_global_mutex_lock(); + num_threads++; + objc_global_mutex_unlock(); +} + +void +objc_thread_remove(void) +{ + size_t i; + + objc_global_mutex_lock(); + + if (free_queue != NULL) { + for (i = 0; i < free_queue_cnt; i++) + free(free_queue[i]); + + free(free_queue); + + free_queue = NULL; + free_queue_cnt = 0; + } + + num_threads--; + objc_global_mutex_unlock(); +} + +void +objc_free_when_singlethreaded(void *ptr) +{ + if (num_threads == 1) { + free(ptr); + return; + } + + if (free_queue == NULL) + free_queue = malloc(sizeof(void*)); + else + free_queue = realloc(free_queue, sizeof(void*) * + (free_queue_cnt + 1)); + + if (free_queue == NULL) + ERROR("Not enough memory for queue of pointers to free!"); + + free_queue[free_queue_cnt++] = ptr; +}