diff options
Diffstat (limited to 'Objective-C/PyObjC')
-rw-r--r-- | Objective-C/PyObjC/objc-runtime-compat.h | 198 | ||||
-rw-r--r-- | Objective-C/PyObjC/objc-runtime-compat.m | 987 |
2 files changed, 1185 insertions, 0 deletions
diff --git a/Objective-C/PyObjC/objc-runtime-compat.h b/Objective-C/PyObjC/objc-runtime-compat.h new file mode 100644 index 0000000..3823f1f --- /dev/null +++ b/Objective-C/PyObjC/objc-runtime-compat.h @@ -0,0 +1,198 @@ +#ifndef PyObjC_RUNTIME_COMPAT +#define PyObjC_RUNTIME_COMPAT +/* + * Objective-C 2.0 runtime compatibility. + * + * This files makes it possible to use the Objective-C 2.0 API on versions + * of Mac OS X before 10.5. Use PyObjC_FUNCNAME to access FUNCNAME from the + * Objective-C 2.0 API, see the Objective-C 2.0 runtime documentation to see + * how it should be used. + * + * TODO: + * - completely move PyObjC to the Objective-C 2.0 API + * - add more wizardry to ensure that this code compiles on OSX 10.4 + * + * Special: + * - PyObjC_class_addMethodList is not a function in the ObjC 2.0 runtime API, + * but added here to (a) get semantics that are slightly nicer for what + * we do and (b) can be implemented efficiently on the "1.0" runtime. + * - PyObjC_methodlist_magic is meant to be used to determine if a class has + * changed in some way (such by loading a category). + * - Modifying a created but not yet registered class should be done using + * the preclass_* functions, not the regular ones because it isn't possible + * to emulate the entire ObjC 2.0 API on Tiger. + */ +#include <objc/objc-runtime.h> +#include <objc/Protocol.h> + +#define _C_CONST 'r' +#define _C_IN 'n' +#define _C_INOUT 'N' +#define _C_OUT 'o' +#define _C_BYCOPY 'O' +#define _C_ONEWAY 'V' +#define _C_LNG_LNG 'q' +#define _C_ULNG_LNG 'Q' +#define _C_BOOL 'B' /* (Objective-)C++ 'bool' */ + +struct PyObjC_method { + SEL name; + IMP imp; + const char* type; +}; + +#define objc_superSetReceiver(super, val) (super).receiver = (val) +#define objc_superGetReceiver(super) ((super).receiver) + +#ifdef __OBJC2__ + +#define objc_superSetClass(super, cls) (super).super_class = (cls) +#define objc_superGetClass(super) ((super).super_class) + +#else + +#define objc_superSetClass(super, cls) (super).class = (cls) +#define objc_superGetClass(super) ((super).class) + +#endif + +/* Some functions that are missing (oddly enough) */ +BOOL PyObjC_class_isSubclassOf(Class child, Class parent); +#define class_isSubclassOf PyObjC_class_isSubclassOf + +#if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) && !defined(__OBJC2__) + +#define preclass_addIvar PyObjC_preclass_addIvar +#define preclass_addMethod PyObjC_preclass_addMethod +#define preclass_addProtocol PyObjC_preclass_addProtocol + +extern void PyObjC_SetupRuntimeCompat(void); + +extern BOOL (*PyObjC_preclass_addMethod)(Class, SEL, IMP, const char*); +extern BOOL (*PyObjC_preclass_addIvar)(Class cls, + const char *name, size_t size, uint8_t alignment, + const char *types); +extern BOOL (*PyObjC_preclass_addProtocol)(Class cls, Protocol *protocol); + + +extern Class (*PyObjC_objc_allocateClassPair)(Class, const char*, size_t); +extern void (*PyObjC_objc_registerClassPair)(Class); +extern void (*PyObjC_objc_disposeClassPair)(Class cls); + + +extern Class (*PyObjC_object_getClass)(id obj); +extern Class (*PyObjC_object_setClass)(id obj, Class cls); +extern const char* (*PyObjC_object_getClassName)(id obj); + +extern Method* (*PyObjC_class_copyMethodList)(Class, unsigned int*); +extern const char* (*PyObjC_class_getName)(Class); +extern Class (*PyObjC_class_getSuperclass)(Class); +extern BOOL (*PyObjC_class_addMethod)(Class, SEL, IMP, const char*); +extern BOOL (*PyObjC_class_addMethodList)(Class, + struct PyObjC_method*, unsigned int); +extern Ivar* (*PyObjC_class_copyIvarList)(Class, unsigned int*); +extern Protocol** (*PyObjC_class_copyProtocolList)(Class, unsigned int*); + +extern BOOL (*PyObjC_class_isMetaClass)(Class); + +extern SEL (*PyObjC_method_getName)(Method m); +extern const char *(*PyObjC_method_getTypeEncoding)(Method m); +extern IMP (*PyObjC_method_getImplementation)(Method m); +extern IMP (*PyObjC_method_setImplementation)(Method m, IMP imp); + +extern BOOL (*PyObjC_sel_isEqual)(SEL, SEL); + +extern size_t (*PyObjC_methodlist_magic)(Class cls); + +extern const char* (*PyObjC_ivar_getName)(Ivar); +extern const char* (*PyObjC_ivar_getTypeEncoding)(Ivar); +extern ptrdiff_t (*PyObjC_ivar_getOffset)(Ivar); + +extern Protocol** (*PyObjC_objc_copyProtocolList)(unsigned int* outCount); +extern Protocol* (*PyObjC_objc_getProtocol)(char* name); +extern struct objc_method_description_list* (*PyObjC_protocol_copyInstanceMethodDescriptionList)(Protocol* proto); +extern struct objc_method_description_list* (*PyObjC_protocol_copyClassMethodDescriptionList)(Protocol *proto); +extern struct objc_method_description_list* (*PyObjC_protocol_copyOptionalInstanceMethodDescriptionList)(Protocol *proto); +extern struct objc_method_description_list* (*PyObjC_protocol_copyOptionalClassMethodDescriptionList)(Protocol *proto); + +extern BOOL (*PyObjC_protocol_conformsToProtocol)(Protocol *proto, Protocol *other); +extern const char *(*PyObjC_protocol_getName)(Protocol *p); +extern struct objc_method_description *(*PyObjC_protocol_copyMethodDescriptionList)(Protocol *p, BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *outCount); +extern Protocol **(*PyObjC_protocol_copyProtocolList)(Protocol *proto, unsigned int *outCount); +extern struct objc_method_description (*PyObjC_protocol_getMethodDescription)(Protocol *p, SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod); + +extern id (*PyObjC_object_getIvar)(id obj, Ivar ivar); +extern void (*PyObjC_object_setIvar)(id obj, Ivar ivar, id value); + +#define object_getIvar PyObjC_object_getIvar +#define object_setIvar PyObjC_object_setIvar +#define protocol_getName PyObjC_protocol_getName +#define protocol_conformsToProtocol PyObjC_protocol_conformsToProtocol +#define protocol_copyMethodDescriptionList PyObjC_protocol_copyMethodDescriptionList +#define protocol_copyProtocolList PyObjC_protocol_copyProtocolList +#define protocol_getMethodDescription PyObjC_protocol_getMethodDescription + +#define objc_allocateClassPair PyObjC_objc_allocateClassPair +#define objc_registerClassPair PyObjC_objc_registerClassPair +#define objc_disposeClassPair PyObjC_objc_disposeClassPair + +#define object_getClass PyObjC_object_getClass +#define object_setClass PyObjC_object_setClass +#define object_getClassName PyObjC_object_getClassName + +#define class_copyMethodList PyObjC_class_copyMethodList +#define class_getName PyObjC_class_getName +#define class_getSuperclass PyObjC_class_getSuperclass +#define class_addMethod PyObjC_class_addMethod +#define class_addMethodList PyObjC_class_addMethodList +#define class_copyIvarList PyObjC_class_copyIvarList +#define class_copyProtocolList PyObjC_class_copyProtocolList +#define class_conformsToProtocol PyObjC_class_conformsToProtocol +#define class_isMetaClass PyObjC_class_isMetaClass + +#define method_getName PyObjC_method_getName +#define method_getTypeEncoding PyObjC_method_getTypeEncoding +#define method_getImplementation PyObjC_method_getImplementation +#define method_setImplementation PyObjC_method_setImplementation + +#define sel_isEqual PyObjC_sel_isEqual + +#define ivar_getName PyObjC_ivar_getName +#define ivar_getTypeEncoding PyObjC_ivar_getTypeEncoding +#define ivar_getOffset PyObjC_ivar_getOffset + +#define objc_copyProtocolList PyObjC_objc_copyProtocolList +#define objc_getProtocol PyObjC_objc_getProtocol +#define protocol_copyInstanceMethodDescriptionList PyObjC_protocol_copyInstanceMethodDescriptionList +#define protocol_copyClassMethodDescriptionList PyObjC_protocol_copyClassMethodDescriptionList +#define protocol_copyOptionalInstanceMethodDescriptionList PyObjC_protocol_copyOptionalInstanceMethodDescriptionList +#define protocol_copyOptionalClassMethodDescriptionList PyObjC_protocol_copyOptionalClassMethodDescriptionList + +#else + +/* + * Compiled for 10.5 or later, use ObjC 2.0 runtime exclusively. + * + * + * Use the preclass_ versions to modify a Class between allocating it and + * registering it. This is needed for the 10.4 compatibility layer. + */ + +#define preclass_addIvar class_addIvar +#define preclass_addMethod class_addMethod +#define preclass_addProtocol class_addProtocol + +static inline void PyObjC_SetupRuntimeCompat(void) { } +extern BOOL PyObjC_class_addMethodList(Class class, + struct PyObjC_method* list, unsigned int count); + + +extern size_t PyObjC_methodlist_magic(Class cls); + +#define class_addMethodList PyObjC_class_addMethodList + + +#endif + + +#endif /* PyObjC_RUNTIME_COMPAT */ diff --git a/Objective-C/PyObjC/objc-runtime-compat.m b/Objective-C/PyObjC/objc-runtime-compat.m new file mode 100644 index 0000000..036b1a1 --- /dev/null +++ b/Objective-C/PyObjC/objc-runtime-compat.m @@ -0,0 +1,987 @@ +/* + * Objective-C runtime 2.0 compatibility for MacOS X 10.4 and earlier. + * + * This code works by poking into the ObjC runtime, which means loads of + * warnings on 10.5+ ;-) + */ +#include "pyobjc.h" + +BOOL PyObjC_class_isSubclassOf(Class child, Class parent) +{ + if (parent == nil) return YES; + + while (child != nil) { + if (child == parent) { + return YES; + } + child = class_getSuperclass(child); + } + return NO; +} + + + +#if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) &&!defined(__OBJC2__) + +#import <mach-o/dyld.h> +#import <mach-o/getsect.h> +#import <mach-o/loader.h> + +typedef struct _ProtocolTemplate { @defs(Protocol) } ProtocolTemplate; + + + +struct objc10_object { + Class isa; +}; + +struct PyObjC_ivar { + char* name; + size_t size; + uint8_t alignment; + char* types; +}; + + +static Protocol** compat_objc_copyProtocolList(unsigned int* outCount) +{ + Protocol** protocols = NULL; + *outCount = 0; + + uint32_t image_count, image_index; + image_count = _dyld_image_count(); + for (image_index = 0; image_index < image_count; image_index++) { + uint32_t size = 0; + const struct mach_header *mh = _dyld_get_image_header(image_index); + intptr_t slide = _dyld_get_image_vmaddr_slide(image_index); + ProtocolTemplate *protos = (ProtocolTemplate*)( + ((char *)getsectdatafromheader(mh, SEG_OBJC, "__protocol", &size)) + + slide); + uint32_t nprotos = size / sizeof(ProtocolTemplate); + uint32_t i; + + if (nprotos == 0) continue; + + if (protocols == NULL) { + protocols = malloc(sizeof(Protocol*) * nprotos); + if (protocols == NULL) { + return NULL; + } + } else { + Protocol** tmp = realloc(protocols, + sizeof(Protocol*) * (*outCount+nprotos)); + if (tmp == NULL) { + free(protocols); + return NULL; + } + protocols = tmp; + } + + for (i = 0; i < nprotos; i++) { + protocols[(*outCount)++] = (Protocol*)&protos[i]; + } + } + return protocols; +} + +static void +compat_objc_registerClassPair(Class cls) +{ + struct objc_method_list* list; + + /* Add the list in the official way, just in case class_addMethods + * does something special. + */ + list = cls->methodLists[0]; + cls->methodLists[0] = (struct objc_method_list*)-1; + class_addMethods(cls, list); + + list = cls->isa->methodLists[0]; + cls->isa->methodLists[0] = (struct objc_method_list*)-1; + class_addMethods(cls->isa, list); + + objc_addClass(cls); +} + +static Class +compat_objc_allocateClassPair(Class super_class, const char* name, size_t extra) +{ + struct objc_class* result; + Class root_class; + + if (objc_getClass(name)) { + return Nil; + } + + root_class = super_class; + while (root_class->super_class) { + root_class = root_class->super_class; + } + + result = malloc(sizeof(struct objc_class) * 2 + extra); + if (result == NULL) { + return Nil; + } + memset(result, 0, sizeof(struct objc_class) * 2 + extra); + + result->super_class = super_class; + result->isa = result + 1; + + result[0].methodLists = NULL; + result[1].methodLists = NULL; + + result[0].info = CLS_CLASS; + result[1].info = CLS_META; + + result[0].name = strdup(name); + if (result[0].name == NULL) goto error_cleanup; + + result[1].name = result[0].name; + result[0].methodLists = malloc(sizeof(struct objc_method_list*)); + if (result[0].methodLists == NULL) goto error_cleanup; + memset(result[0].methodLists, 0, sizeof(struct objc_method_list*)); + + result[1].methodLists = malloc(sizeof(struct objc_method_list*)); + if (result[1].methodLists == NULL) goto error_cleanup; + memset(result[1].methodLists, 0, sizeof(struct objc_method_list*)); + + /* + * This is MacOS X specific, and an undocumented feature (long live + * Open Source!). + * + * The code in the objc runtime assumes that the method lists are + * terminated by '-1', and will happily overwite existing data if + * they aren't. + * + * Ronald filed a bugreport for this: Radar #3317376 + */ + result[0].methodLists[0] = (struct objc_method_list*)-1; + result[1].methodLists[0] = (struct objc_method_list*)-1; + + result[0].methodLists[0] = malloc(sizeof(struct objc_method_list)); + if (result[0].methodLists[0] == NULL) goto error_cleanup; + result[0].methodLists[0]->method_count = 0; + result[0].methodLists[0]->obsolete = NULL; + + result[1].methodLists[0] = malloc(sizeof(struct objc_method_list)); + if (result[1].methodLists[0] == NULL) goto error_cleanup; + result[1].methodLists[0]->method_count = 0; + result[1].methodLists[0]->obsolete = NULL; + + + result[0].super_class = super_class; + result[1].super_class = ((struct objc10_object*)super_class)->isa; + result[1].isa = ((struct objc10_object*)root_class)->isa; + + result[0].instance_size = super_class->instance_size; + result[1].instance_size = result[1].super_class->instance_size; + + /* Initialize to NULL, otherwise poseAs: won't work */ + result[0].ivars = result[1].ivars = NULL; + + result[0].protocols = result[1].protocols = NULL; + + return result; + + +error_cleanup: + if (result) { + if (result[0].methodLists) { + if (result[0].methodLists[0] != 0 && + result[0].methodLists[0] != 0) { + free(result[0].methodLists[0]); + } + free(result[0].methodLists); + } + if (result[1].methodLists) { + if (result[1].methodLists[1] != 0 && + result[1].methodLists[0] != 0) { + free(result[1].methodLists[0]); + } + free(result[1].methodLists); + } + + if (result[0].ivars) { + free(result[0].ivars); + } + if (result[1].ivars) { + free(result[1].ivars); + } + + if (result[0].name) { + free((char*)result[0].name); + } + free(result); + } + return Nil; + +} + +static void +compat_objc_disposeClassPair(Class cls) +{ + struct objc_class* val = cls; + int i; + + for (i = 0; i < 2; i++) { + if (val[i].methodLists) { + struct objc_method_list** cur; + + cur = val[i].methodLists; + if (*cur != (struct objc_method_list*)-1) { + free(*cur); + *cur = NULL; + } + free(val[i].methodLists); + val[i].methodLists = NULL; + } + } + + free((char*)val[0].name); + val[0].name = val[1].name = NULL; + free(val); +} + + +static size_t +compat_methodlist_magic(Class cls) +{ + void* iterator = NULL; + struct objc_method_list* mlist; + size_t res = 0, cnt = 0; + + if (cls == NULL) return -1; + + while ( (mlist = class_nextMethodList(cls, &iterator )) != NULL ) { + res += mlist->method_count; + cnt++; + } + + return (cnt << 16) | (res & 0xFFFF); +} + +#ifndef NO_OBJC2_RUNTIME +static size_t +objc20_methodlist_magic(Class cls) +{ + Method* methods; + unsigned int count; + unsigned int i; + size_t result; + + methods = class_copyMethodList(cls, &count); + result = 0; + for (i = 0; i < count; i++) { + result = (1000003*result) ^ ((size_t)( + method_getImplementation(methods[i])) >> 3); + } + result = result | (count << 16); + free(methods); + return result; +} +#endif + +static Class +compat_object_setClass(id obj, Class cls) +{ + Class prev; + if (obj == nil) { + return Nil; + } + prev = ((struct objc10_object*)obj)->isa; + ((struct objc10_object*)obj)->isa = cls; + return prev; +} + + + +static Class +compat_object_getClass(id obj) +{ + return ((struct objc10_object*)obj)->isa; +} + +static const char* +compat_object_getClassName(id obj) +{ + return ((struct objc10_object*)obj)->isa->name; +} + + + +static Method* +compat_class_copyMethodList(Class cls, unsigned int* outCount) +{ + struct objc_method_list* mlist; + void* iterator; + unsigned int count; + Method* result; + Method* tmp; + + iterator = NULL; + count = 0; + + mlist = class_nextMethodList(cls, &iterator); + result = NULL; + while (mlist != NULL) { + int i; + + tmp = realloc(result, (count + mlist->method_count) * sizeof(Method)); + if (tmp == NULL) { + free(result); + return NULL; + } else { + result = tmp; + } + + for (i = 0; i < mlist->method_count; i++) { + result[count] = mlist->method_list + i; + if (result[count] == NULL) continue; + count++; + } + mlist = class_nextMethodList(cls, &iterator); + } + + if (outCount != NULL) { + *outCount = count; + } + return result; +} + +static Ivar* +compat_class_copyIvarList(Class cls, unsigned int* outCount) +{ + Ivar* list; + int i; + struct objc_ivar_list* ivars = cls->ivars; + + if (ivars) { + list = malloc(sizeof(Ivar) * ivars->ivar_count); + if (list == NULL) { + return NULL; + } + + + for (i = 0; i < ivars->ivar_count; i++) { + list[i] = ivars->ivar_list + i; + } + *outCount = ivars->ivar_count; + return list; + + } else { + list = malloc(1); + if (list == NULL) { + return NULL; + } + + *outCount = 0; + return list; + } + +} + +static Protocol** +compat_class_copyProtocolList(Class cls, unsigned int* outCount) +{ + Protocol** list; + unsigned int count = 0; + struct objc_protocol_list *protocol_list; + + protocol_list = cls->protocols; + list = malloc(0); + + while (protocol_list != NULL) { + list = realloc(list, (count + protocol_list->count)*sizeof(Protocol*)); + if (list == NULL) { + /* Whoops, memory leak */ + *outCount = 0; + return NULL; + } + memcpy(list + count, protocol_list->list, protocol_list->count*sizeof(Protocol*)); + count += protocol_list->count; + protocol_list = protocol_list->next; + } + + *outCount = count; + return list; +} + + +static const char* +compat_class_getName(Class cls) +{ + return cls->name; +} + +static Class +compat_class_getSuperclass(Class cls) +{ + return cls->super_class; +} + +static BOOL +compat_class_addMethod(Class cls, SEL name, IMP imp, const char* types) +{ + struct objc_method_list* methodsToAdd; + struct objc_method* objcMethod; + + methodsToAdd = malloc(sizeof(struct objc_method_list) + + 2*sizeof(struct objc_method)); + methodsToAdd->method_count = 1; + methodsToAdd->obsolete = NULL; + + objcMethod = methodsToAdd->method_list; + objcMethod->method_name = name; + objcMethod->method_imp = imp; + objcMethod->method_types = (char*)types; + + class_addMethods(cls, methodsToAdd); + return YES; /* Have to return something ... */ +} + +static BOOL +compat_preclass_addMethod(Class cls, SEL name, IMP imp, const char* types) +{ + /* Resize in-place, this is only valid during class construction. */ + struct objc_method_list* new_list; + struct objc_method* objcMethod; + + new_list = realloc(cls->methodLists[0], + sizeof(struct objc_method_list) + + (sizeof(struct objc_method + )*(cls->methodLists[0]->method_count+1))); + if (new_list == NULL) { + return NO; + } + cls->methodLists[0] = new_list; + objcMethod = new_list->method_list + (new_list->method_count)++; + + objcMethod->method_name = name; + objcMethod->method_imp = imp; + objcMethod->method_types = strdup(types); + if (objcMethod == NULL) { + new_list->method_count--; + return NO; + } + + return YES; +} + +static BOOL +compat_preclass_addIvar( + Class cls, + const char* name, + size_t size, + uint8_t align, const char* types) +{ + /* Update the class structure, only valid during class construction */ + struct objc_ivar_list* new_ivars; + struct objc_ivar* ivar; + + if (cls->ivars) { + new_ivars = realloc(cls->ivars, + sizeof(struct objc_ivar_list) + + ((cls->ivars->ivar_count+1) * sizeof(struct objc_ivar))); + } else { + new_ivars = malloc( + sizeof(struct objc_ivar_list) + + sizeof(struct objc_ivar)); + new_ivars->ivar_count = 0; + } + if (new_ivars == NULL) { + return NO; + } + cls->ivars = new_ivars; + ivar = new_ivars->ivar_list + new_ivars->ivar_count; + ivar->ivar_name = strdup(name); + if (ivar->ivar_name == NULL) { + return NO; + } + + ivar->ivar_type = strdup(types); + if (ivar->ivar_type == NULL) { + free(ivar->ivar_name); + return NO; + } + + ivar->ivar_offset = cls->instance_size; + if (ivar->ivar_offset % align) { + ivar->ivar_offset += align - (ivar->ivar_offset % align); + } + + new_ivars->ivar_count ++; + cls->instance_size = ivar->ivar_offset + size; + return YES; +} + +static BOOL +compat_preclass_addProtocol(Class cls, Protocol* protocol) +{ + struct objc_protocol_list* protocols; + + if (cls->protocols) { + protocols = realloc(cls->protocols, + sizeof(struct objc_protocol_list) + + (sizeof(Protocol*)*(cls->protocols->count+1))); + } else { + protocols = malloc(sizeof(struct objc_protocol_list) + + sizeof(Protocol*)); + protocols->count = 0; + protocols->next = NULL; + } + if (protocols == NULL) { + return NO; + } + cls->protocols = protocols; + protocols->list[protocols->count++] = protocol; + return YES; +} + +static SEL +compat_method_getName(Method m) +{ + return m->method_name; +} + +static IMP +compat_method_getImplementation(Method m) +{ + return m->method_imp; +} + +static IMP +compat_method_setImplementation(Method m, IMP imp) +{ + IMP result = m->method_imp; + m->method_imp = imp; + return result; +} + +static const char * +compat_method_getTypeEncoding(Method m) +{ + return m->method_types; +} + +static BOOL +compat_sel_isEqual(SEL lhs, SEL rhs) +{ + return lhs == rhs; +} + +static const char* +compat_ivar_getName(Ivar var) +{ + return var->ivar_name; +} + +static const char* +compat_ivar_getTypeEncoding(Ivar var) +{ + return var->ivar_type; +} + +static ptrdiff_t +compat_ivar_getOffset(Ivar var) +{ + return var->ivar_offset; +} + + +#ifndef NO_OBJC2_RUNTIME +static BOOL +objc20_class_addMethodList(Class cls, + struct PyObjC_method* list, unsigned int count) +{ + unsigned int i; + BOOL r; + Method m; + + for (i = 0; i < count; i++) { + r = class_addMethod(cls, + list[i].name, list[i].imp, list[i].type); + if (!r) { + m = class_getInstanceMethod(cls, list[i].name); + if (m != NULL) { + method_setImplementation(m, list[i].imp); + } else { + return NO; + } + } + } + return YES; +} +#endif + +static BOOL +compat_class_isMetaClass(Class cls) +{ + return CLS_GETINFO(cls, CLS_META) == CLS_META; +} + +static BOOL +compat_class_addMethodList(Class cls, + struct PyObjC_method* list, unsigned int count) +{ + unsigned int i; + struct objc_method_list* method_list; + + method_list = malloc( + sizeof(struct objc_method_list) + + ((count+1) * sizeof(struct objc_method))); + if (method_list == NULL) { + return NO; + } + memset(method_list, 0, + sizeof(struct objc_method_list) + + ((count+1) * sizeof(struct objc_method))); + + method_list->method_count = 0; + method_list->obsolete = 0; + + for (i = 0; i < count; i++) { + method_list->method_list[i].method_name = list[i].name; + method_list->method_list[i].method_types = (char*)list[i].type; + method_list->method_list[i].method_imp = list[i].imp; + } + method_list->method_count = count; + class_addMethods(cls, method_list); + return YES; +} + +static BOOL +compat_protocol_conformsToProtocol(Protocol *proto, Protocol *other) +{ + return [proto conformsTo:other]; +} + +static const char * +compat_protocol_getName(Protocol *p) +{ + return [p name]; +} + +static struct objc_method_description * +compat_protocol_copyMethodDescriptionList(Protocol *p, BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *outCount) +{ + if (!isRequiredMethod) { + *outCount = 0; + return NULL; + } + + struct objc_method_description_list* list; + + if (isInstanceMethod) { + list = ((ProtocolTemplate*)p)->instance_methods; + } else { + list = ((ProtocolTemplate*)p)->class_methods; + } + + if (list == NULL || list->count == 0) { + *outCount = 0; + return NULL; + } + + *outCount = list->count; + struct objc_method_description* result; + + result = malloc(sizeof(struct objc_method_description) * list->count); + if (result == NULL) { + return NULL; + } + + int i; + *outCount = 0; + for (i = 0; i < list->count; i++) { + if (list->list[i].name == NULL) continue; + result[*outCount++] = list->list[i]; + } + + return result; +} + +static Protocol ** +compat_protocol_copyProtocolList(Protocol *proto, unsigned int *outCount) +{ + struct objc_protocol_list* list = + ((ProtocolTemplate*)proto)->protocol_list; + + *outCount = 0; + + struct objc_protocol_list* cur; + for (cur = list; cur != NULL; cur = cur->next) { + *outCount += cur->count; + } + + Protocol** result = (Protocol**)malloc(sizeof(Protocol*) * *outCount); + if (result == NULL) { + return NULL; + } + + unsigned curIdx = 0; + for (cur = list; cur != NULL; cur = cur->next) { + int i; + for (i = 0; i < cur->count; i++) { + if (cur->list[i] != NULL) { + result[curIdx++] = cur->list[i]; + } else { + *outCount--; + } + } + } + return result; +} + +static struct objc_method_description +compat_protocol_getMethodDescription(Protocol *p, SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod) +{ +static struct objc_method_description empty_description = { NULL, NULL }; + if (!isRequiredMethod) { + return empty_description; + } + + struct objc_method_description* result; + + if (isInstanceMethod) { + result = [p descriptionForInstanceMethod: aSel]; + } else { + result = [p descriptionForClassMethod: aSel]; + } + + if (result == NULL) { + return empty_description; + } else { + return *result; + } +} + +static id +compat_object_getIvar(id obj, Ivar ivar) +{ + return *(id*)(((char*)obj) + ivar->ivar_offset); +} +static void +compat_object_setIvar(id obj, Ivar ivar, id value) +{ + *(id*)(((char*)obj) + ivar->ivar_offset) = value; +} + +/* Dispatch table */ +BOOL (*PyObjC_protocol_conformsToProtocol)(Protocol *proto, Protocol *other) = NULL; +const char *(*PyObjC_protocol_getName)(Protocol *p) = NULL; +struct objc_method_description *(*PyObjC_protocol_copyMethodDescriptionList)(Protocol *p, BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *outCount) = NULL; +Protocol **(*PyObjC_protocol_copyProtocolList)(Protocol *proto, unsigned int *outCount) = NULL; +struct objc_method_description (*PyObjC_protocol_getMethodDescription)(Protocol *p, SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod) = NULL; + +id (*PyObjC_object_getIvar)(id obj, Ivar ivar) = NULL; +void (*PyObjC_object_setIvar)(id obj, Ivar ivar, id value) = NULL; + + + +Class (*PyObjC_objc_allocateClassPair)(Class, const char*, size_t) = NULL; +void (*PyObjC_objc_registerClassPair)(Class) = NULL; +void (*PyObjC_objc_disposeClassPair)(Class) = NULL; +Protocol** (*PyObjC_objc_copyProtocolList)(unsigned int*) = NULL; + +BOOL (*PyObjC_preclass_addMethod)(Class, SEL, IMP, const char*) = NULL; +BOOL (*PyObjC_preclass_addIvar)(Class cls, + const char *name, size_t size, uint8_t alignment, + const char *types) = NULL; +BOOL (*PyObjC_preclass_addProtocol)(Class cls, Protocol *protocol) = NULL; + + +Class (*PyObjC_object_getClass)(id obj) = NULL; +Class (*PyObjC_object_setClass)(id obj, Class cls) = NULL; +const char* (*PyObjC_object_getClassName)(id obj) = NULL; + +Method* (*PyObjC_class_copyMethodList)(Class, unsigned int*) = NULL; +const char* (*PyObjC_class_getName)(Class) = NULL; +Class (*PyObjC_class_getSuperclass)(Class) = NULL; +BOOL (*PyObjC_class_addMethod)(Class, SEL, IMP, const char*) = NULL; +BOOL (*PyObjC_class_addMethodList)(Class, + struct PyObjC_method*, unsigned int) = NULL; +Ivar* (*PyObjC_class_copyIvarList)(Class, unsigned int*) = NULL; +Protocol** (*PyObjC_class_copyProtocolList)(Class, unsigned int*) = NULL; +BOOL (*PyObjC_class_isMetaClass)(Class) = NULL; + +SEL (*PyObjC_method_getName)(Method m) = NULL; +IMP (*PyObjC_method_getImplementation)(Method m) = NULL; +IMP (*PyObjC_method_setImplementation)(Method m, IMP imp) = NULL; +const char *(*PyObjC_method_getTypeEncoding)(Method m) = NULL; + +BOOL (*PyObjC_sel_isEqual)(SEL, SEL) = NULL; + +size_t (*PyObjC_methodlist_magic)(Class cls); + +const char* (*PyObjC_ivar_getName)(Ivar) = NULL; +const char* (*PyObjC_ivar_getTypeEncoding)(Ivar) = NULL; +ptrdiff_t (*PyObjC_ivar_getOffset)(Ivar) = NULL; + + + +void PyObjC_SetupRuntimeCompat(void) +{ +#ifdef NO_OBJC2_RUNTIME + /* + * Don't use ObjC 2.0 runtime (compiling on 10.4 or earlier), always + * use the compat implementation. + */ + PyObjC_class_addMethodList = compat_class_addMethodList; + PyObjC_methodlist_magic = compat_methodlist_magic; + PyObjC_objc_disposeClassPair = compat_objc_disposeClassPair; + PyObjC_preclass_addMethod = compat_preclass_addMethod; + PyObjC_preclass_addIvar = compat_preclass_addIvar; + PyObjC_preclass_addProtocol = compat_preclass_addProtocol; + +# define SETUP(funcname) \ + PyObjC_##funcname = compat_##funcname + +#else + if (class_addMethod) { + PyObjC_class_addMethodList = objc20_class_addMethodList; + PyObjC_preclass_addMethod = class_addMethod; + PyObjC_preclass_addIvar = class_addIvar; + PyObjC_preclass_addProtocol= class_addProtocol; + } else { + PyObjC_class_addMethodList = compat_class_addMethodList; + PyObjC_preclass_addMethod = compat_preclass_addMethod; + PyObjC_preclass_addIvar = compat_preclass_addIvar; + PyObjC_preclass_addProtocol= compat_preclass_addProtocol; + } + + + if (class_copyMethodList) { + PyObjC_methodlist_magic = objc20_methodlist_magic; + } else { + PyObjC_methodlist_magic = compat_methodlist_magic; + } + +# define SETUP(funcname) \ + if (funcname == NULL) { \ + PyObjC_##funcname = compat_##funcname; \ + } else { \ + PyObjC_##funcname = funcname; \ + } +#endif + SETUP(protocol_getName); + SETUP(protocol_conformsToProtocol); + SETUP(protocol_copyMethodDescriptionList); + SETUP(protocol_copyProtocolList); + SETUP(protocol_getMethodDescription); + + SETUP(objc_allocateClassPair); + SETUP(objc_registerClassPair); + SETUP(objc_disposeClassPair); + SETUP(objc_copyProtocolList); + + SETUP(object_getClass); + SETUP(object_setClass); + SETUP(object_getClassName); + SETUP(object_getIvar); + SETUP(object_setIvar); + + SETUP(class_getSuperclass); + SETUP(class_addMethod); + SETUP(class_copyIvarList); + SETUP(class_copyProtocolList); + SETUP(class_copyMethodList); + SETUP(class_getName); + SETUP(class_isMetaClass); + + SETUP(method_getName); + SETUP(method_getTypeEncoding); + SETUP(method_getImplementation); + SETUP(method_setImplementation); + + SETUP(sel_isEqual); + + SETUP(ivar_getName); + SETUP(ivar_getTypeEncoding); + SETUP(ivar_getOffset); +#undef SETUP + +} + +#else + +BOOL PyObjC_class_addMethodList(Class cls, + struct PyObjC_method* list, unsigned int count) +{ + unsigned int i; + BOOL r; + Method m; + + for (i = 0; i < count; i++) { + /* + * XXX: First try class_addMethod, if that fails assume this is + * because the method already exists in the class. + * Strictly speaking this isn't correct, but this is the best + * we can do through the 2.0 API (see 4809039 in RADAR) + */ + r = class_addMethod(cls, + list[i].name, list[i].imp, list[i].type); + if (!r) { + m = class_getInstanceMethod(cls, list[i].name); + if (m != NULL) { + method_setImplementation(m, list[i].imp); + } else { + return NO; + } + } + } + return YES; +} + +size_t PyObjC_methodlist_magic(Class cls) +{ + /* This is likely to be much slower than compat_methodlist_magic, + * but should works on the 64-bit runtime. Hopefully a callback will + * be added to the 2.0 runtime that will take away the need for this + * function... + */ + Method* methods; + unsigned int count; + + methods = class_copyMethodList(cls, &count); +#if 0 + unsigned int i; + size_t result; + result = 0; + for (i = 0; i < count; i++) { + result = (1000003*result) ^ ((size_t)( + method_getImplementation(methods[i])) >> 3); + } + result = result | (count << 16); + free(methods); + return result; +#endif + free(methods); + return (size_t)count; +} + +#endif + +#if defined(__x86_64__) + +@implementation Protocol (NSOBjectCompat) +- self +{ + return self; +} +@end + +@implementation Object (NSOBjectCompat) +- self +{ + return self; +} + +-doesNotRecognizeSelector:(SEL)sel +{ + printf("--> %s\n", sel_getName(sel)); + abort(); +} +@end + + +#endif |