mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-22 16:33:29 +00:00
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:
parent
a9afec4f75
commit
15aa0e6fbb
9 changed files with 1650 additions and 0 deletions
20
Source/ObjectiveC2/COPYING
Normal file
20
Source/ObjectiveC2/COPYING
Normal 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.
|
||||
|
41
Source/ObjectiveC2/GNUmakefile
Normal file
41
Source/ObjectiveC2/GNUmakefile
Normal 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
|
3
Source/ObjectiveC2/README
Normal file
3
Source/ObjectiveC2/README
Normal 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
|
17
Source/ObjectiveC2/blocks_runtime.h
Normal file
17
Source/ObjectiveC2/blocks_runtime.h
Normal 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))
|
247
Source/ObjectiveC2/blocks_runtime.m
Normal file
247
Source/ObjectiveC2/blocks_runtime.m
Normal 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;
|
||||
}
|
47
Source/ObjectiveC2/properties.m
Normal file
47
Source/ObjectiveC2/properties.m
Normal 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];
|
||||
}
|
898
Source/ObjectiveC2/runtime.c
Normal file
898
Source/ObjectiveC2/runtime.c
Normal 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);
|
||||
}
|
||||
|
270
Source/ObjectiveC2/runtime.h
Normal file
270
Source/ObjectiveC2/runtime.h
Normal 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
107
Source/ObjectiveC2/sync.m
Normal 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);
|
||||
}
|
Loading…
Reference in a new issue