Imported ObjectiveC2 framework.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@29625 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
David Chisnall 2010-02-14 15:29:20 +00:00
parent a9afec4f75
commit 15aa0e6fbb
9 changed files with 1650 additions and 0 deletions

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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))

View file

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
/* 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;
}

View file

@ -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];
}

View file

@ -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 <objc/objc.h>
#include <objc/objc-api.h>
#undef GNU_BOOL
#include <objc/encoding.h>
#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 <string.h>
#include <stdlib.h>
#include <assert.h>
/**
* 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 ; i<methods->method_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 ; i<methods->method_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 ; i<protocols->count ; 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; i<ivarlist->ivar_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; i<methods->method_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 ; i<ivarlist->ivar_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 ; i<index ; i++)
{
types = objc_skip_argspec(types);
if ('\0' == *types)
{
return NULL;
}
}
return types;
}
char * method_copyArgumentType(Method method, unsigned int index)
{
const char *types = findParameterStart(method->method_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 ; i<methods->method_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 ; i<ivarlist->ivar_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);
}

View file

@ -0,0 +1,270 @@
#include <stdint.h>
#include <stddef.h>
#include <sys/types.h>
#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);

107
Source/ObjectiveC2/sync.m Normal file
View file

@ -0,0 +1,107 @@
#include "runtime.h"
/* Ensure Unix98 compatible pthreads for glibc */
#if defined __GLIBC__
#define __USE_UNIX98 1
#endif
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
@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);
}