From e589fcc8044f45248f7946a839a64afb4b20824d Mon Sep 17 00:00:00 2001 From: theraven Date: Sun, 14 Feb 2010 15:29:20 +0000 Subject: [PATCH] Imported ObjectiveC2 framework. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@29625 72102866-910b-0410-8b05-ffd578937521 --- Source/ObjectiveC2/COPYING | 20 + Source/ObjectiveC2/GNUmakefile | 41 ++ Source/ObjectiveC2/README | 3 + Source/ObjectiveC2/blocks_runtime.h | 17 + Source/ObjectiveC2/blocks_runtime.m | 247 ++++++++ Source/ObjectiveC2/properties.m | 47 ++ Source/ObjectiveC2/runtime.c | 898 ++++++++++++++++++++++++++++ Source/ObjectiveC2/runtime.h | 270 +++++++++ Source/ObjectiveC2/sync.m | 107 ++++ 9 files changed, 1650 insertions(+) create mode 100644 Source/ObjectiveC2/COPYING create mode 100644 Source/ObjectiveC2/GNUmakefile create mode 100644 Source/ObjectiveC2/README create mode 100644 Source/ObjectiveC2/blocks_runtime.h create mode 100644 Source/ObjectiveC2/blocks_runtime.m create mode 100644 Source/ObjectiveC2/properties.m create mode 100644 Source/ObjectiveC2/runtime.c create mode 100644 Source/ObjectiveC2/runtime.h create mode 100644 Source/ObjectiveC2/sync.m diff --git a/Source/ObjectiveC2/COPYING b/Source/ObjectiveC2/COPYING new file mode 100644 index 000000000..8647a56b7 --- /dev/null +++ b/Source/ObjectiveC2/COPYING @@ -0,0 +1,20 @@ +Copyright (c) 2009 David Chisnall + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/Source/ObjectiveC2/GNUmakefile b/Source/ObjectiveC2/GNUmakefile new file mode 100644 index 000000000..613152e7f --- /dev/null +++ b/Source/ObjectiveC2/GNUmakefile @@ -0,0 +1,41 @@ +include $(GNUSTEP_MAKEFILES)/common.make + +# +# Application +# +VERSION = 0.1 +FRAMEWORK_NAME = ObjectiveC2 + +# +# Resource files +# +ObjectiveC2_LANGUAGES = English + +ObjectiveC2_OBJC_FILES = \ + blocks_runtime.m\ + properties.m\ + sync.m + + +ObjectiveC2_C_FILES = \ + runtime.c + +ObjectiveC2_HEADER_FILES = \ + Availability.h\ + blocks_runtime.h\ + runtime.h + +ADDITIONAL_CFLAGS = -std=c99 -fexceptions +ADDITIONAL_OBJCFLAGS = -fobjc-exceptions -fexceptions + +ifeq ($(CC), clang) + ADDITIONAL_OBJCFLAGS = -fblocks +endif +ifeq ($(GNUSTEP_TARGET_CPU), ix86) + ADDITIONAL_OBJCFLAGS += -march=i686 +endif + +ObjectiveC2_OBJCFLAGS += -std=c99 + +include $(GNUSTEP_MAKEFILES)/framework.make +-include ../../etoile.make diff --git a/Source/ObjectiveC2/README b/Source/ObjectiveC2/README new file mode 100644 index 000000000..8166a2310 --- /dev/null +++ b/Source/ObjectiveC2/README @@ -0,0 +1,3 @@ +ObjectiveC2.framework implements the new Apple runtime APIs, introduced with OS X 10.5 on top of the GCC Objective-C runtime. Its use is now discouraged. This code has been merged into the GNUstep Objective-C runtime, which can act as a drop-in replacement for GCC libobjc. You can find the code here: + +svn://svn.gna.org/svn/gnustep/libs/libobjc2/trunk diff --git a/Source/ObjectiveC2/blocks_runtime.h b/Source/ObjectiveC2/blocks_runtime.h new file mode 100644 index 000000000..2def0a495 --- /dev/null +++ b/Source/ObjectiveC2/blocks_runtime.h @@ -0,0 +1,17 @@ +/* + * Blocks Runtime + */ + +#include "Availability.h" +#ifdef __cplusplus +#define BLOCKS_EXPORT extern "C" +#else +#define BLOCKS_EXPORT extern +#endif + +BLOCKS_EXPORT void *_Block_copy(void *); +BLOCKS_EXPORT void _Block_release(void *); +BLOCKS_EXPORT const char *_Block_get_types(void*) OBJC_NONPORTABLE; + +#define Block_copy(x) ((__typeof(x))_Block_copy((void *)(x))) +#define Block_release(x) _Block_release((void *)(x)) diff --git a/Source/ObjectiveC2/blocks_runtime.m b/Source/ObjectiveC2/blocks_runtime.m new file mode 100644 index 000000000..6e5aa5a4b --- /dev/null +++ b/Source/ObjectiveC2/blocks_runtime.m @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2009 Remy Demarest + * Portions Copyright (c) 2009 David Chisnall + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#import "objc/blocks_runtime.h" +#import "objc/runtime.h" +#include +#include +#include +#include + +/* Makes the compiler happy even without Foundation */ +@interface Dummy +- (id)retain; +- (void)release; +@end + +// Descriptor attributes +enum { + BLOCK_HAS_COPY_DISPOSE = (1 << 25), + BLOCK_HAS_CTOR = (1 << 26), // helpers have C++ code + BLOCK_IS_GLOBAL = (1 << 28), + BLOCK_HAS_DESCRIPTOR = (1 << 29), // interim until complete world build is accomplished +}; + +// _Block_object_assign() and _Block_object_dispose() flag helpers. +enum { + BLOCK_FIELD_IS_OBJECT = 3, // id, NSObject, __attribute__((NSObject)), block, ... + BLOCK_FIELD_IS_BLOCK = 7, // a block variable + BLOCK_FIELD_IS_BYREF = 8, // the on stack structure holding the __block variable + + BLOCK_FIELD_IS_WEAK = 16, // declared __weak + + BLOCK_BYREF_CALLER = 128, // called from byref copy/dispose helpers +}; + +// Helper structure +struct psy_block_literal { + void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock + int flags; + int reserved; + void (*invoke)(void *, ...); + struct { + unsigned long int reserved; // NULL + unsigned long int size; // sizeof(struct Block_literal_1) + // optional helper functions + void (*copy_helper)(void *dst, void *src); + void (*dispose_helper)(void *src); + } *descriptor; + const char *types; +}; + +// Helper structure +struct psy_block_byref_obj { + void *isa; // uninitialized + struct psy_block_byref_obj *forwarding; + int flags; //refcount; + int size; + void (*byref_keep)(struct psy_block_byref_obj *dst, struct psy_block_byref_obj *src); + void (*byref_dispose)(struct psy_block_byref_obj *); +}; + +/* Certain field types require runtime assistance when being copied to the + * heap. The following function is used to copy fields of types: blocks, + * pointers to byref structures, and objects (including + * __attribute__((NSObject)) pointers. BLOCK_FIELD_IS_WEAK is orthogonal to + * the other choices which are mutually exclusive. Only in a Block copy helper + * will one see BLOCK_FIELD_IS_BYREF. + */ +void _Block_object_assign(void *destAddr, void *object, const int flags) +{ + //printf("Copying %x to %x with flags %x\n", object, destAddr, flags); + // FIXME: Needs to be implemented + if(flags & BLOCK_FIELD_IS_WEAK) + { + } + else + { + if(flags & BLOCK_FIELD_IS_BYREF) + { + struct psy_block_byref_obj *src = object; + struct psy_block_byref_obj **dst = destAddr; + + /* I followed Apple's specs saying byref's "flags" field should + * represent the refcount but it still contains real flag, so this + * is a little hack... + */ + if((src->flags & ~BLOCK_HAS_COPY_DISPOSE) == 0) + { + *dst = malloc(src->size); + memcpy(*dst, src, src->size); + if (src->forwarding == src) + { + (*dst)->forwarding = *dst; + } + if(src->size >= sizeof(struct psy_block_byref_obj)) + { + src->byref_keep(*dst, src); + } + } + else *dst = src; + + (*dst)->flags++; + } + else if((flags & BLOCK_FIELD_IS_BLOCK) == BLOCK_FIELD_IS_BLOCK) + { + struct psy_block_literal *src = object; + struct psy_block_literal **dst = destAddr; + + *dst = Block_copy(src); + } + else if((flags & BLOCK_FIELD_IS_OBJECT) == BLOCK_FIELD_IS_OBJECT) + { + id src = object; + id *dst = destAddr; + *dst = [src retain]; + } + } +} + +/* Similarly a compiler generated dispose helper needs to call back for each + * field of the byref data structure. (Currently the implementation only packs + * one field into the byref structure but in principle there could be more). + * The same flags used in the copy helper should be used for each call + * generated to this function: + */ +void _Block_object_dispose(void *object, const int flags) +{ + // FIXME: Needs to be implemented + if(flags & BLOCK_FIELD_IS_WEAK) + { + } + else + { + if(flags & BLOCK_FIELD_IS_BYREF) + { + struct psy_block_byref_obj *src = object; + + src->flags--; + if((src->flags & ~BLOCK_HAS_COPY_DISPOSE) == 0) + { + if(src->size >= sizeof(struct psy_block_byref_obj)) + src->byref_dispose(src); + + free(src); + } + } + else if((flags & ~BLOCK_BYREF_CALLER) == BLOCK_FIELD_IS_BLOCK) + { + struct psy_block_literal *src = object; + Block_release(src); + } + else if((flags & ~BLOCK_BYREF_CALLER) == BLOCK_FIELD_IS_OBJECT) + { + id src = object; + [src release]; + } + } +} + +struct StackBlockClass { + void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock + int flags; + int reserved; + void (*invoke)(void *, ...); + struct { + unsigned long int reserved; // NULL + unsigned long int size; // sizeof(struct Block_literal_1) + // optional helper functions + void (*copy_helper)(void *dst, void *src); + void (*dispose_helper)(void *src); + } *descriptor; + const char *types; +}; + + +// Copy a block to the heap if it's still on the stack or increments its retain count. +void *_Block_copy(void *src) +{ + struct StackBlockClass *self = src; + struct StackBlockClass *ret = self; + + extern void _NSConcreteStackBlock __attribute__((weak)); + + // If the block is Global, there's no need to copy it on the heap. + if(self->isa == &_NSConcreteStackBlock && self->flags & BLOCK_HAS_DESCRIPTOR) + { + if(self->reserved == 0) + { + ret = malloc(self->descriptor->size); + memcpy(ret, self, self->descriptor->size); + if(self->flags & BLOCK_HAS_COPY_DISPOSE) + self->descriptor->copy_helper(ret, self); + memcpy(self, ret, self->descriptor->size); + } + ret->reserved++; + } + return ret; +} + +// Release a block and frees the memory when the retain count hits zero. +void _Block_release(void *src) +{ + struct StackBlockClass *self = src; + + extern void _NSConcreteStackBlock __attribute__((weak)); + + if(self->isa == &_NSConcreteStackBlock && // A Global block doesn't need to be released + self->flags & BLOCK_HAS_DESCRIPTOR && // Should always be true... + self->reserved > 0) // If false, then it's not allocated on the heap, we won't release auto memory ! + { + self->reserved--; + if(self->reserved == 0) + { + if(self->flags & BLOCK_HAS_COPY_DISPOSE) + self->descriptor->dispose_helper(self); + free(self); + } + } +} + +const char *_Block_get_types(void *blk) +{ + struct psy_block_literal *block = blk; + return block->types; +} diff --git a/Source/ObjectiveC2/properties.m b/Source/ObjectiveC2/properties.m new file mode 100644 index 000000000..903e2f05e --- /dev/null +++ b/Source/ObjectiveC2/properties.m @@ -0,0 +1,47 @@ +#include "runtime.h" + +// Subset of NSObject interface needed for properties. +@interface NSObject {} +- (id)retain; +- (id)copy; +- (id)autorelease; +- (void)release; +@end + +id objc_getProperty(id obj, SEL _cmd, ptrdiff_t offset, BOOL isAtomic) +{ + if (isAtomic) + { + @synchronized(obj) { + return objc_getProperty(obj, _cmd, offset, NO); + } + } + char *addr = (char*)obj; + addr += offset; + id ret = *(id*)addr; + return [[ret retain] autorelease]; +} + +void objc_setProperty(id obj, SEL _cmd, ptrdiff_t offset, id arg, BOOL isAtomic, BOOL isCopy) +{ + if (isAtomic) + { + @synchronized(obj) { + objc_setProperty(obj, _cmd, offset, arg, NO, isCopy); + return; + } + } + if (isCopy) + { + arg = [arg copy]; + } + else + { + arg = [arg retain]; + } + char *addr = (char*)obj; + addr += offset; + id old = *(id*)addr; + *(id*)addr = arg; + [old release]; +} diff --git a/Source/ObjectiveC2/runtime.c b/Source/ObjectiveC2/runtime.c new file mode 100644 index 000000000..99dfff677 --- /dev/null +++ b/Source/ObjectiveC2/runtime.c @@ -0,0 +1,898 @@ +#include "runtime.h" + +/* Make glibc export strdup() */ + +#if defined __GLIBC__ + #define __USE_BSD 1 +#endif + +#undef __objc_INCLUDE_GNU +#undef __thread_INCLUDE_GNU +#undef __objc_api_INCLUDE_GNU +#undef __encoding_INCLUDE_GNU + +#define objc_object objc_object_gnu +#define id object_ptr_gnu +#define IMP objc_imp_gnu +#define Method objc_method_gnu + +#define object_copy gnu_object_copy +#define object_dispose gnu_object_dispose +#define objc_super gnu_objc_super +#define objc_msg_lookup gnu_objc_msg_lookup +#define objc_msg_lookup_super gnu_objc_msg_lookup_super +#define BOOL GNU_BOOL +#define SEL GNU_SEL +#define Protocol GNU_Protocol +#define Class GNU_Class + +#undef YES +#undef NO +#undef Nil +#undef nil + +#include +#include +#undef GNU_BOOL +#include +#undef Class +#undef Protocol +#undef SEL +#undef objc_msg_lookup +#undef objc_msg_lookup_super +#undef objc_super +#undef Method +#undef IMP +#undef id +#undef objc_object + +// Reinstall definitions. +#undef YES +#undef NO +#undef Nil +#undef nil +#define YES ((BOOL)1) +#define NO ((BOOL)0) +#define nil ((id)_OBJC_NULL_PTR) +#define Nil ((Class)_OBJC_NULL_PTR) + +#include +#include +#include + +/** + * Private runtime function for updating a dtable. + */ +void __objc_update_dispatch_table_for_class(Class); +/** + * Private runtime function for determining whether a class responds to a + * selector. + */ +BOOL __objc_responds_to(Class, SEL); +/** + * Runtime library constant for uninitialized dispatch table. + */ +extern struct sarray *__objc_uninstalled_dtable; +/** + * Mutex used to protect the ObjC runtime library data structures. + */ +extern objc_mutex_t __objc_runtime_mutex; + +/** + * Looks up the instance method in a specific class, without recursing into + * superclasses. + */ +static Method class_getInstanceMethodNonrecursive(Class aClass, SEL aSelector) +{ + const char *name = sel_get_name(aSelector); + const char *types = sel_get_type(aSelector); + + for (struct objc_method_list *methods = aClass->methods; + methods != NULL ; methods = methods->method_next) + { + for (int i=0 ; imethod_count ; i++) + { + Method_t method = &methods->method_list[i]; + if (strcmp(sel_get_name(method->method_name), name) == 0) + { + if (NULL == types || + strcmp(types, method->method_types) == 0) + { + return method; + } + // Return NULL if the method exists with this name but has the + // wrong types + return NULL; + } + } + } + return NULL; +} + +static void objc_updateDtableForClassContainingMethod(Method m) +{ + Class nextClass = Nil; + void *state; + SEL sel = method_getName(m); + while (Nil != (nextClass = objc_next_class(&state))) + { + if (class_getInstanceMethodNonrecursive(nextClass, sel) == m) + { + __objc_update_dispatch_table_for_class(nextClass); + return; + } + } +} + + +BOOL class_addIvar(Class cls, + const char *name, + size_t size, + uint8_t alignment, + const char *types) +{ + if (CLS_ISRESOLV(cls) || CLS_ISMETA(cls)) + { + return NO; + } + + struct objc_ivar_list *ivarlist = cls->ivars; + + if (class_getInstanceVariable(cls, name) != NULL) { return NO; } + + if (NULL == ivarlist) + { + cls->ivars = objc_malloc(sizeof(struct objc_ivar_list)); + cls->ivars->ivar_count = 1; + } + else + { + ivarlist->ivar_count++; + // objc_ivar_list contains one ivar. Others follow it. + cls->ivars = objc_realloc(ivarlist, sizeof(struct objc_ivar_list) + + (ivarlist->ivar_count - 1) * sizeof(struct objc_ivar)); + } + + Ivar ivar = &cls->ivars->ivar_list[cls->ivars->ivar_count - 1]; + ivar->ivar_name = strdup(name); + ivar->ivar_type = strdup(types); + // Round up the offset of the ivar so it is correctly aligned. + ivar->ivar_offset = cls->instance_size + (cls->instance_size % alignment); + // Increase the instance size to make space for this. + cls->instance_size = ivar->ivar_offset + size; + return YES; +} + +BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types) +{ + const char *methodName = sel_get_name(name); + struct objc_method_list *methods; + for (methods=cls->methods; methods!=NULL ; methods=methods->method_next) + { + for (int i=0 ; imethod_count ; i++) + { + Method_t method = &methods->method_list[i]; + if (strcmp(sel_get_name(method->method_name), methodName) == 0) + { + return NO; + } + } + } + + methods = objc_malloc(sizeof(struct objc_method_list)); + methods->method_next = cls->methods; + cls->methods = methods; + + methods->method_count = 1; + methods->method_list[0].method_name = sel_register_typed_name(methodName, types); + methods->method_list[0].method_types = strdup(types); + methods->method_list[0].method_imp = (objc_imp_gnu)imp; + + if (CLS_ISRESOLV(cls)) + { + __objc_update_dispatch_table_for_class(cls); + } + + return YES; +} + +BOOL class_addProtocol(Class cls, Protocol *protocol) +{ + if (class_conformsToProtocol(cls, protocol)) { return NO; } + struct objc_protocol_list *protocols = cls->protocols; + protocols = objc_malloc(sizeof(struct objc_protocol_list)); + if (protocols == NULL) { return NO; } + protocols->next = cls->protocols; + protocols->count = 1; + protocols->list[0] = protocol; + cls->protocols = protocols; + + return YES; +} + +BOOL class_conformsToProtocol(Class cls, Protocol *protocol) +{ + for (struct objc_protocol_list *protocols = cls->protocols; + protocols != NULL ; protocols = protocols->next) + { + for (int i=0 ; icount ; i++) + { + if (strcmp(protocols->list[i]->protocol_name, + protocol->protocol_name) == 0) + { + return YES; + } + } + } + return NO; +} + +Ivar * class_copyIvarList(Class cls, unsigned int *outCount) +{ + struct objc_ivar_list *ivarlist = cls->ivars; + unsigned int count = 0; + if (ivarlist != NULL) + { + count = ivarlist->ivar_count; + } + if (outCount != NULL) + { + *outCount = count; + } + if (count == 0) + { + return NULL; + } + + Ivar *list = malloc(count * sizeof(struct objc_ivar *)); + for (unsigned int i=0; iivar_count; i++) + { + list[i] = &(ivarlist->ivar_list[i]); + } + return list; +} + +Method * class_copyMethodList(Class cls, unsigned int *outCount) +{ + unsigned int count = 0; + for (struct objc_method_list *methods = cls->methods; + methods != NULL ; methods = methods->method_next) + { + count += methods->method_count; + } + if (outCount != NULL) + { + *outCount = count; + } + if (count == 0) + { + return NULL; + } + + Method *list = malloc(count * sizeof(struct objc_method *)); + Method *copyDest = list; + + for (struct objc_method_list *methods = cls->methods; + methods != NULL ; methods = methods->method_next) + { + for (unsigned int i=0; imethod_count; i++) + { + copyDest[i] = &(methods->method_list[i]); + } + copyDest += methods->method_count; + } + + return list; +} + +Protocol ** class_copyProtocolList(Class cls, unsigned int *outCount) +{ + struct objc_protocol_list *protocolList = cls->protocols; + int listSize = 0; + for (struct objc_protocol_list *list = protocolList ; + list != NULL ; + list = list->next) + { + listSize += list->count; + } + if (listSize == 0) + { + *outCount = 0; + return NULL; + } + + Protocol **protocols = calloc(listSize, sizeof(Protocol*) + 1); + int index = 0; + for (struct objc_protocol_list *list = protocolList ; + list != NULL ; + list = list->next) + { + memcpy(&protocols[index], list->list, list->count * sizeof(Protocol*)); + index += list->count; + } + protocols[listSize] = NULL; + *outCount = listSize + 1; + return protocols; +} + +id class_createInstance(Class cls, size_t extraBytes) +{ + id obj = objc_malloc(cls->instance_size + extraBytes); + obj->isa = cls; + return obj; +} + +Method class_getInstanceMethod(Class aClass, SEL aSelector) +{ + Method method = class_getInstanceMethodNonrecursive(aClass, aSelector); + if (method == NULL) + { + // TODO: Check if this should be NULL or aClass + Class superclass = class_getSuperclass(aClass); + if (superclass == NULL) + { + return NULL; + } + return class_getInstanceMethod(superclass, aSelector); + } + return method; +} + +Method class_getClassMethod(Class aClass, SEL aSelector) +{ + return class_getInstanceMethod(aClass->class_pointer, aSelector); +} + +Ivar class_getClassVariable(Class cls, const char* name) +{ + assert(0 && "Class variables not implemented"); + return NULL; +} + +size_t class_getInstanceSize(Class cls) +{ + return cls->instance_size; +} + +Ivar class_getInstanceVariable(Class cls, const char* name) +{ + struct objc_ivar_list *ivarlist = cls->ivars; + if (NULL == ivarlist) { return NULL; } + + for (int i=0 ; iivar_count ; i++) + { + Ivar ivar = &ivarlist->ivar_list[i]; + if (strcmp(ivar->ivar_name, name) == 0) + { + return ivar; + } + } + return NULL; +} + +// The format of the char* is undocumented. This function is only ever used in +// conjunction with class_setIvarLayout(). +const char *class_getIvarLayout(Class cls) +{ + return (char*)cls->ivars; +} + +IMP class_getMethodImplementation(Class cls, SEL name) +{ + struct objc_object_gnu obj = { cls }; + return (IMP)objc_msg_lookup((id)&obj, name); +} + +IMP class_getMethodImplementation_stret(Class cls, SEL name) +{ + struct objc_object_gnu obj = { cls }; + return (IMP)objc_msg_lookup((id)&obj, name); +} + +const char * class_getName(Class cls) +{ + return class_get_class_name(cls); +} + +void __objc_resolve_class_links(void); +Class class_getSuperclass(Class cls) +{ + if (!CLS_ISRESOLV(cls)) + { + __objc_resolve_class_links(); + } + return cls->super_class; +} + +int class_getVersion(Class theClass) +{ + return class_get_version(theClass); +} + +const char *class_getWeakIvarLayout(Class cls) +{ + assert(0 && "Weak ivars not supported"); + return NULL; +} + +BOOL class_isMetaClass(Class cls) +{ + return CLS_ISMETA(cls); +} + +IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types) +{ + Method method = class_getInstanceMethodNonrecursive(cls, name); + if (method == NULL) + { + class_addMethod(cls, name, imp, types); + return NULL; + } + IMP old = (IMP)method->method_imp; + method->method_imp = (objc_imp_gnu)imp; + return old; +} + + +BOOL class_respondsToSelector(Class cls, SEL sel) +{ + return __objc_responds_to(cls, sel); +} + +void class_setIvarLayout(Class cls, const char *layout) +{ + struct objc_ivar_list *list = (struct objc_ivar_list*)layout; + size_t listsize = sizeof(struct objc_ivar_list) + + sizeof(struct objc_ivar) * (list->ivar_count - 1); + cls->ivars = malloc(listsize); + memcpy(cls->ivars, list, listsize); +} + +__attribute__((deprecated)) +Class class_setSuperclass(Class cls, Class newSuper) +{ + Class oldSuper = cls->super_class; + cls->super_class = newSuper; + return oldSuper; +} + +void class_setVersion(Class theClass, int version) +{ + class_set_version(theClass, version); +} + +void class_setWeakIvarLayout(Class cls, const char *layout) +{ + assert(0 && "Not implemented"); +} + +const char * ivar_getName(Ivar ivar) +{ + return ivar->ivar_name; +} + +ptrdiff_t ivar_getOffset(Ivar ivar) +{ + return ivar->ivar_offset; +} + +const char * ivar_getTypeEncoding(Ivar ivar) +{ + return ivar->ivar_type; +} + +static size_t lengthOfTypeEncoding(const char *types) +{ + const char *end = objc_skip_argspec(types); + end--; + while (isdigit(*end)) { end--; } + size_t length = end - types + 1; + return length; +} +static char *copyTypeEncoding(const char *types) +{ + size_t length = lengthOfTypeEncoding(types); + char *copy = malloc(length + 1); + memcpy(copy, types, length); + copy[length] = '\0'; + return copy; +} +static const char * findParameterStart(const char *types, unsigned int index) +{ + for (unsigned int i=0 ; imethod_types, index); + if (NULL == types) + { + return NULL; + } + return copyTypeEncoding(types); +} + +char * method_copyReturnType(Method method) +{ + return copyTypeEncoding(method->method_types); +} + +void method_exchangeImplementations(Method m1, Method m2) +{ + IMP tmp = (IMP)m1->method_imp; + m1->method_imp = m2->method_imp; + m2->method_imp = (objc_imp_gnu)tmp; + objc_updateDtableForClassContainingMethod(m1); + objc_updateDtableForClassContainingMethod(m2); +} +void method_getArgumentType(Method method, + unsigned int index, + char *dst, + size_t dst_len) +{ + const char *types = findParameterStart(method->method_types, index); + if (NULL == types) + { + strncpy(dst, "", dst_len); + return; + } + size_t length = lengthOfTypeEncoding(types); + if (length < dst_len) + { + memcpy(dst, types, length); + dst[length] = '\0'; + } + else + { + memcpy(dst, types, dst_len); + } +} + +IMP method_getImplementation(Method method) +{ + return (IMP)method->method_imp; +} + +SEL method_getName(Method method) +{ + return method->method_name; +} + +unsigned method_getNumberOfArguments(Method method) +{ + const char *types = method->method_types; + unsigned int count = 0; + while('\0' != *types) + { + types = objc_skip_argspec(types); + count++; + } + return count - 1; +} + +void method_getReturnType(Method method, char *dst, size_t dst_len) +{ + //TODO: Coped and pasted code. Factor it out. + const char *types = method->method_types; + size_t length = lengthOfTypeEncoding(types); + if (length < dst_len) + { + memcpy(dst, types, length); + dst[length] = '\0'; + } + else + { + memcpy(dst, types, dst_len); + } +} + +const char * method_getTypeEncoding(Method method) +{ + return method->method_types; +} + +IMP method_setImplementation(Method method, IMP imp) +{ + IMP old = (IMP)method->method_imp; + method->method_imp = (objc_imp_gnu)old; + objc_updateDtableForClassContainingMethod(method); + return old; +} + +id objc_getClass(const char *name) +{ + return (id)objc_get_class(name); +} + +int objc_getClassList(Class *buffer, int bufferLen) +{ + int count = 0; + if (buffer == NULL) + { + void *state = NULL; + while(Nil != objc_next_class(&state)) + { + count++; + } + } + else + { + Class nextClass; + void *state = NULL; + while (Nil != (nextClass = objc_next_class(&state)) && bufferLen > 0) + { + count++; + bufferLen--; + *(buffer++) = nextClass; + } + } + return count; +} + +id objc_getMetaClass(const char *name) +{ + Class cls = (Class)objc_getClass(name); + return cls == Nil ? nil : (id)cls->class_pointer; +} + +id objc_getRequiredClass(const char *name) +{ + id cls = objc_getClass(name); + if (nil == cls) + { + abort(); + } + return cls; +} + +id objc_lookUpClass(const char *name) +{ + // TODO: Check these are the right way around. + return (id)objc_lookup_class(name); +} + +static void freeMethodLists(Class aClass) +{ + struct objc_method_list *methods = aClass->methods; + while(methods != NULL) + { + for (int i=0 ; imethod_count ; i++) + { + free((void*)methods->method_list[i].method_types); + } + struct objc_method_list *current = methods; + methods = methods->method_next; + free(current); + } +} + +static void freeIvarLists(Class aClass) +{ + struct objc_ivar_list *ivarlist = aClass->ivars; + if (NULL == ivarlist) { return; } + + for (int i=0 ; iivar_count ; i++) + { + Ivar ivar = &ivarlist->ivar_list[i]; + free((void*)ivar->ivar_type); + free((void*)ivar->ivar_name); + } + free(ivarlist); +} + +/* + * Removes a class from the subclass list found on its super class. + * Must be called with the objc runtime mutex locked. + */ +static inline void safe_remove_from_subclass_list(Class cls) +{ + Class sub = cls->super_class->subclass_list; + if (sub == cls) + { + cls->super_class->subclass_list = cls->sibling_class; + } + else + { + while (sub != NULL) + { + if (sub->sibling_class == cls) + { + sub->sibling_class = cls->sibling_class; + break; + } + sub = sub->sibling_class; + } + } +} + +void objc_disposeClassPair(Class cls) +{ + Class meta = ((id)cls)->isa; + // Remove from the runtime system so nothing tries updating the dtable + // while we are freeing the class. + objc_mutex_lock(__objc_runtime_mutex); + safe_remove_from_subclass_list(meta); + safe_remove_from_subclass_list(cls); + objc_mutex_unlock(__objc_runtime_mutex); + + // Free the method and ivar lists. + freeMethodLists(cls); + freeMethodLists(meta); + freeIvarLists(cls); + + // Free the class and metaclass + free(meta); + free(cls); +} + +Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes) +{ + // Check the class doesn't already exist. + if (nil != objc_lookUpClass(name)) { return Nil; } + + Class newClass = calloc(1, sizeof(struct objc_class) + extraBytes); + + if (Nil == newClass) { return Nil; } + + // Create the metaclass + Class metaClass = calloc(1, sizeof(struct objc_class)); + + // Initialize the metaclass + metaClass->class_pointer = superclass->class_pointer->class_pointer; + metaClass->super_class = superclass->class_pointer; + metaClass->name = strdup(name); + metaClass->info = _CLS_META; + metaClass->dtable = __objc_uninstalled_dtable; + metaClass->instance_size = sizeof(struct objc_class); + + // Set up the new class + newClass->class_pointer = metaClass; + // Set the superclass pointer to the name. The runtime will fix this when + // the class links are resolved. + newClass->super_class = (Class)superclass->name; + newClass->name = strdup(name); + newClass->info = _CLS_CLASS; + newClass->dtable = __objc_uninstalled_dtable; + newClass->instance_size = superclass->instance_size; + + return newClass; +} + +void *object_getIndexedIvars(id obj) +{ + if (class_isMetaClass(obj->isa)) + { + return ((char*)obj) + sizeof(struct objc_class); + } + return ((char*)obj) + obj->isa->instance_size; +} + +Class object_getClass(id obj) +{ + if (nil != obj) + { + return obj->isa; + } + return Nil; +} + +Class object_setClass(id obj, Class cls) +{ + if (nil != obj) + { + Class oldClass = obj->isa; + obj->isa = cls; + return oldClass; + } + return Nil; +} + +const char *object_getClassName(id obj) +{ + return class_getName(object_getClass(obj)); +} + +void __objc_add_class_to_hash(Class cls); +void __objc_resolve_class_links(void); + +void objc_registerClassPair(Class cls) +{ + Class metaClass = cls->class_pointer; + + // Initialize the dispatch table for the class and metaclass. + __objc_update_dispatch_table_for_class(metaClass); + __objc_update_dispatch_table_for_class(cls); + __objc_add_class_to_hash(cls); + // Add pointer from super class + __objc_resolve_class_links(); +} + +static id objectNew(id cls) +{ + static SEL newSel = NULL; + if (NULL == newSel) + { + newSel = sel_get_uid("new"); + } + IMP newIMP = (IMP)objc_msg_lookup((void*)cls, newSel); + return newIMP((id)cls, newSel); +} + +Protocol *objc_getProtocol(const char *name) +{ + // Protocols are not centrally registered in the GNU runtime. + Protocol *protocol = (Protocol*)(objectNew(objc_getClass("Protocol"))); + protocol->protocol_name = (char*)name; + return protocol; +} + +BOOL protocol_conformsToProtocol(Protocol *p, Protocol *other) +{ + return NO; +} + +struct objc_method_description *protocol_copyMethodDescriptionList(Protocol *p, + BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *count) +{ + *count = 0; + return NULL; +} + +Protocol **protocol_copyProtocolList(Protocol *p, unsigned int *count) +{ + *count = 0; + return NULL; +} + +const char *protocol_getName(Protocol *p) +{ + if (NULL != p) + { + return p->protocol_name; + } + return NULL; +} + +BOOL protocol_isEqual(Protocol *p, Protocol *other) +{ + if (NULL == p || NULL == other) + { + return NO; + } + if (p == other || + 0 == strcmp(p->protocol_name, other->protocol_name)) + { + return YES; + } + return NO; +} + +const char *sel_getName(SEL sel) +{ + return sel_get_name(sel); +} + +SEL sel_getUid(const char *selName) +{ + return sel_get_uid(selName); +} + +BOOL sel_isEqual(SEL sel1, SEL sel2) +{ + // FIXME: Is this correct? + return (0 == strcmp(sel_get_name(sel1), sel_get_name(sel2))); +} + +SEL sel_registerName(const char *selName) +{ + return sel_register_name(selName); +} + diff --git a/Source/ObjectiveC2/runtime.h b/Source/ObjectiveC2/runtime.h new file mode 100644 index 000000000..695f5a40a --- /dev/null +++ b/Source/ObjectiveC2/runtime.h @@ -0,0 +1,270 @@ +#include +#include +#include + +#ifdef ERROR_UNSUPPORTED_RUNTIME_FUNCTIONS +# define OBJC_GNU_RUNTIME_UNSUPPORTED(x) \ + __attribute__((error(x " not supported by the GNU runtime"))) +#else +# define OBJC_GNU_RUNTIME_UNSUPPORTED(x) +#endif + +// Undo GNUstep substitutions +#ifdef class_setVersion +#undef class_setVersion +#endif +#ifdef class_getClassMethod +#undef class_getClassMethod +#endif +#ifdef objc_getClass +#undef objc_getClass +#endif +#ifdef objc_lookUpClass +#undef objc_lookUpClass +#endif + +typedef struct objc_ivar* Ivar; + +#ifndef __objc_INCLUDE_GNU +typedef const struct objc_selector *SEL; + +typedef struct objc_class *Class; + +typedef struct objc_object +{ + Class isa; +} *id; + +struct objc_super { + id receiver; +# if !defined(__cplusplus) && !__OBJC2__ + Class class; +# else + Class super_class; +# endif +}; + +typedef id (*IMP)(id, SEL, ...); +typedef struct objc_method *Method; + +# ifdef STRICT_APPLE_COMPATIBILITY +typedef signed char BOOL; +# else +# ifdef __vxwords +typedef int BOOL +# else +typedef unsigned char BOOL; +# endif +# endif + +#else +// Method in the GNU runtime is a struct, Method_t is the pointer +# define Method Method_t +#endif // __objc_INCLUDE_GNU + + + +typedef void *objc_property_t; +#ifdef __OBJC__ +@class Protocol; +#else +typedef struct objc_protocol Protocol; +#endif + +#ifndef YES +#define YES ((BOOL)1) +#endif +#ifndef NO +#define NO ((BOOL)0) +#endif + +#ifdef __GNUC +#define _OBJC_NULL_PTR __null +#elif defined(__cplusplus) +#define _OBJC_NULL_PTR 0 +#else +#define _OBJC_NULL_PTR ((void*)0) +#endif + +#ifndef nil +#define nil ((id)_OBJC_NULL_PTR) +#endif + +#ifndef Nil +#define Nil ((Class)_OBJC_NULL_PTR) +#endif + +BOOL class_addIvar(Class cls, + const char *name, + size_t size, + uint8_t alignment, + const char *types); + +BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types); + +BOOL class_addProtocol(Class cls, Protocol *protocol); + +BOOL class_conformsToProtocol(Class cls, Protocol *protocol); + +Ivar * class_copyIvarList(Class cls, unsigned int *outCount); + +Method * class_copyMethodList(Class cls, unsigned int *outCount); + +OBJC_GNU_RUNTIME_UNSUPPORTED("Property introspection") +objc_property_t * class_copyPropertyList(Class cls, unsigned int *outCount); + +Protocol ** class_copyProtocolList(Class cls, unsigned int *outCount); + +id class_createInstance(Class cls, size_t extraBytes); + +Method class_getClassMethod(Class aClass, SEL aSelector); + +Ivar class_getClassVariable(Class cls, const char* name); + +Method class_getInstanceMethod(Class aClass, SEL aSelector); + +size_t class_getInstanceSize(Class cls); + +Ivar class_getInstanceVariable(Class cls, const char* name); + +const char *class_getIvarLayout(Class cls); + +IMP class_getMethodImplementation(Class cls, SEL name); + +IMP class_getMethodImplementation_stret(Class cls, SEL name); + +const char * class_getName(Class cls); + +OBJC_GNU_RUNTIME_UNSUPPORTED("Property introspection") +objc_property_t class_getProperty(Class cls, const char *name); + +Class class_getSuperclass(Class cls); + +int class_getVersion(Class theClass); + +OBJC_GNU_RUNTIME_UNSUPPORTED("Weak instance variables") +const char *class_getWeakIvarLayout(Class cls); + +BOOL class_isMetaClass(Class cls); + +IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types); + +BOOL class_respondsToSelector(Class cls, SEL sel); + +void class_setIvarLayout(Class cls, const char *layout); + +__attribute__((deprecated)) +Class class_setSuperclass(Class cls, Class newSuper); + +void class_setVersion(Class theClass, int version); + +OBJC_GNU_RUNTIME_UNSUPPORTED("Weak instance variables") +void class_setWeakIvarLayout(Class cls, const char *layout); + +const char * ivar_getName(Ivar ivar); + +ptrdiff_t ivar_getOffset(Ivar ivar); + +const char * ivar_getTypeEncoding(Ivar ivar); + +char * method_copyArgumentType(Method method, unsigned int index); + +char * method_copyReturnType(Method method); + +void method_exchangeImplementations(Method m1, Method m2); + +void method_getArgumentType(Method method, unsigned int index, char *dst, size_t dst_len); + +IMP method_getImplementation(Method method); + +SEL method_getName(Method method); + +unsigned method_getNumberOfArguments(Method method); + +void method_getReturnType(Method method, char *dst, size_t dst_len); + +const char * method_getTypeEncoding(Method method); + +IMP method_setImplementation(Method method, IMP imp); + +Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes); + +void objc_disposeClassPair(Class cls); + +id objc_getClass(const char *name); + +int objc_getClassList(Class *buffer, int bufferLen); + +id objc_getMetaClass(const char *name); + +id objc_getRequiredClass(const char *name); + +id objc_lookUpClass(const char *name); + +Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes); + +Protocol *objc_getProtocol(const char *name); + +void objc_registerClassPair(Class cls); + +void *object_getIndexedIvars(id obj); + +// FIXME: The GNU runtime has a version of this which omits the size parameter +//id object_copy(id obj, size_t size); + +id object_dispose(id obj); + +Class object_getClass(id obj); +Class object_setClass(id obj, Class cls); + +const char *object_getClassName(id obj); + +IMP objc_msg_lookup(id, SEL); +IMP objc_msg_lookup_super(struct objc_super*, SEL); + +OBJC_GNU_RUNTIME_UNSUPPORTED("Protocol introspection") +BOOL protocol_conformsToProtocol(Protocol *p, Protocol *other); + +OBJC_GNU_RUNTIME_UNSUPPORTED("Protocol introspection") +struct objc_method_description *protocol_copyMethodDescriptionList(Protocol *p, + BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *count); + +OBJC_GNU_RUNTIME_UNSUPPORTED("Protocol introspection") +objc_property_t *protocol_copyPropertyList(Protocol *p, unsigned int *count); + +OBJC_GNU_RUNTIME_UNSUPPORTED("Protocol introspection") +Protocol **protocol_copyProtocolList(Protocol *p, unsigned int *count); + +OBJC_GNU_RUNTIME_UNSUPPORTED("Protocol introspection") +struct objc_method_description protocol_getMethodDescription(Protocol *p, + SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod); + +const char *protocol_getName(Protocol *p); + +OBJC_GNU_RUNTIME_UNSUPPORTED("Protocol introspection") +objc_property_t protocol_getProperty(Protocol *p, const char *name, + BOOL isRequiredProperty, BOOL isInstanceProperty); + +BOOL protocol_isEqual(Protocol *p, Protocol *other); + +// Only enable support for object planes when +// -fobjc-sender-dependent-dispatch is specified +#ifdef __OBJC_SENDER_AWARE_DISPATCH__ + +// Global self so that self is a valid symbol everywhere. Will be replaced by +// a real self in an inner scope if there is one. +static const id self = nil; +#define objc_msgSend(theReceiver, theSelector, ...) objc_msg_lookup_sender(theReceiver, theSelector, self)(theReceiver, theSelector, ## __VA_ARGS__) + +#endif + +#define objc_msgSend(theReceiver, theSelector, ...) objc_msg_lookup(theReceiver, theSelector)(theReceiver, theSelector, ## __VA_ARGS__) +#define objc_msgSendSuper(super, op, ...) objc_msg_lookup_super(super, op)(super->receiver, op, ## __VA_ARGS__) + +const char *sel_getName(SEL sel); + +SEL sel_getUid(const char *selName); + +BOOL sel_isEqual(SEL sel1, SEL sel2); + +SEL sel_registerName(const char *selName); diff --git a/Source/ObjectiveC2/sync.m b/Source/ObjectiveC2/sync.m new file mode 100644 index 000000000..571086dff --- /dev/null +++ b/Source/ObjectiveC2/sync.m @@ -0,0 +1,107 @@ +#include "runtime.h" + +/* Ensure Unix98 compatible pthreads for glibc */ +#if defined __GLIBC__ + #define __USE_UNIX98 1 +#endif + +#include +#include +#include + +@interface Fake ++ (void)dealloc; +@end + +static pthread_mutex_t at_sync_init_lock = PTHREAD_MUTEX_INITIALIZER; +static unsigned long long lockClassId; + +IMP objc_msg_lookup(id, SEL); + +static void deallocLockClass(id obj, SEL _cmd); + +static inline Class findLockClass(id obj) +{ + struct objc_object object = { obj->isa }; + SEL dealloc = @selector(dealloc); + // Find the first class where this lookup is correct + if (objc_msg_lookup((id)&object, dealloc) != (IMP)deallocLockClass) + { + do { + object.isa = class_getSuperclass(object.isa); + } while (Nil != object.isa && + objc_msg_lookup((id)&object, dealloc) != (IMP)deallocLockClass); + } + if (Nil == object.isa) { return Nil; } + // object->isa is now either the lock class, or a class which inherits from + // the lock class + Class lastClass; + do { + lastClass = object.isa; + object.isa = class_getSuperclass(object.isa); + } while (Nil != object.isa && + objc_msg_lookup((id)&object, dealloc) == (IMP)deallocLockClass); + return lastClass; +} + +static inline Class initLockObject(id obj) +{ + char nameBuffer[40]; + snprintf(nameBuffer, 39, "hiddenlockClass%lld", lockClassId++); + Class lockClass = objc_allocateClassPair(obj->isa, nameBuffer, + sizeof(pthread_mutex_t)); + const char *types = + method_getTypeEncoding(class_getInstanceMethod(obj->isa, + @selector(dealloc))); + class_addMethod(lockClass, @selector(dealloc), (IMP)deallocLockClass, + types); + objc_registerClassPair(lockClass); + + pthread_mutex_t *lock = object_getIndexedIvars(lockClass); + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(lock, &attr); + pthread_mutexattr_destroy(&attr); + + obj->isa = lockClass; + return lockClass; +} + +static void deallocLockClass(id obj, SEL _cmd) +{ + Class lockClass = findLockClass(obj); + Class realClass = class_getSuperclass(lockClass); + // Free the lock + pthread_mutex_t *lock = object_getIndexedIvars(lockClass); + pthread_mutex_destroy(lock); + // Free the class + objc_disposeClassPair(lockClass); + // Reset the class then call the real -dealloc + obj->isa = realClass; + [obj dealloc]; +} + +void objc_sync_enter(id obj) +{ + Class lockClass = findLockClass(obj); + if (Nil == lockClass) + { + pthread_mutex_lock(&at_sync_init_lock); + // Test again in case two threads call objc_sync_enter at once + lockClass = findLockClass(obj); + if (Nil == lockClass) + { + lockClass = initLockObject(obj); + } + pthread_mutex_unlock(&at_sync_init_lock); + } + pthread_mutex_t *lock = object_getIndexedIvars(lockClass); + pthread_mutex_lock(lock); +} +void objc_sync_exit(id obj) +{ + Class lockClass = findLockClass(obj); + pthread_mutex_t *lock = object_getIndexedIvars(lockClass); + pthread_mutex_unlock(lock); +}