diff options
author | Matthias Benkard <code@mail.matthias.benkard.de> | 2007-09-15 15:33:46 +0200 |
---|---|---|
committer | Matthias Benkard <code@mail.matthias.benkard.de> | 2007-09-15 15:33:46 +0200 |
commit | 585f55e70e44cccb0807c2bc2d1f0904c8fbc5e1 (patch) | |
tree | a94bec4ba2b3a0e298c5619d656cdd013aa2f93d /Objective-C | |
parent | d0dd29fcf87b51280dd5c2685063ea42cf4fc115 (diff) |
Add libffi support code from PyObjC.
darcs-hash:0cb8e75aa1ea694562f5087aa400840349adcac8
Diffstat (limited to 'Objective-C')
-rw-r--r-- | Objective-C/GNUmakefile | 4 | ||||
-rw-r--r-- | Objective-C/libffi_support.h | 36 | ||||
-rw-r--r-- | Objective-C/libffi_support.m | 1061 | ||||
-rw-r--r-- | Objective-C/libobjcl.h | 4 | ||||
-rw-r--r-- | Objective-C/libobjcl.m | 6 |
5 files changed, 1101 insertions, 10 deletions
diff --git a/Objective-C/GNUmakefile b/Objective-C/GNUmakefile index 33b91ac..92934ed 100644 --- a/Objective-C/GNUmakefile +++ b/Objective-C/GNUmakefile @@ -7,12 +7,8 @@ LIBRARY_NAME = libobjcl RPM_DISABLE_RELOCATABLE = YES ADDITIONAL_OBJCFLAGS = -Wall -g -DVERSION=\"$(VERSION)\" -I/usr/local/include -ifdef USE_LIBFFI ADDITIONAL_LDFLAGS = -lffi ADDITIONAL_OBJCFLAGS += -DUSE_LIBFFI -else -ADDITIONAL_LDFLAGS = -lavcall -endif libobjcl_OBJC_FILES = libobjcl.m objc_support.m objc-runtime-apple.m objc-runtime-gnu.m LIBRARIES_DEPEND_UPON = $(FND_LIBS) $(GUI_LIBS) $(OBJC_LIBS) $(SYSTEM_LIBS) $(CONFIG_SYSTEM_LIBS) diff --git a/Objective-C/libffi_support.h b/Objective-C/libffi_support.h new file mode 100644 index 0000000..d9a7f40 --- /dev/null +++ b/Objective-C/libffi_support.h @@ -0,0 +1,36 @@ +#ifndef PyObjC_FFI_SUPPORT_H +#define PyObjC_FFI_SUPPORT_H + +#include "ffi.h" + +typedef void (*PyObjCFFI_ClosureFunc)(ffi_cif*, void*, void**, void*); + +void PyObjCFFI_FreeCIF(ffi_cif* cif); +ffi_cif* PyObjCFFI_CIFForSignature(PyObjCMethodSignature* signature); +IMP PyObjCFFI_MakeClosure(PyObjCMethodSignature* signature, + PyObjCFFI_ClosureFunc func, void* userdata); +void* PyObjCFFI_FreeClosure(IMP closure); + +IMP PyObjCFFI_MakeIMPForSignature(char* signature, PyObject* callable); +IMP PyObjCFFI_MakeIMPForPyObjCSelector(PyObjCSelector *aSelector); +PyObject *PyObjCFFI_Caller(PyObject *aMeth, PyObject* self, PyObject *args); + +int PyObjCFFI_CountArguments( + PyObjCMethodSignature* methinfo, Py_ssize_t argOffset, + Py_ssize_t* byref_in_count, + Py_ssize_t* byref_out_count, + Py_ssize_t* plain_count, + Py_ssize_t* argbuf_len); + +int PyObjCFFI_ParseArguments( + PyObjCMethodSignature* methinfo, Py_ssize_t argOffset, + PyObject* args, Py_ssize_t argbuf_cur, unsigned char* argbuf, + void** byref, + ffi_type** arglist, void** values); + +PyObject* PyObjCFFI_BuildResult( + PyObjCMethodSignature* methinfo, Py_ssize_t argOffset, + void* pRetval, void** byref, Py_ssize_t byref_out_count, + PyObject* self, int flags); + +#endif /* PyObjC_FFI_SUPPORT_H */ diff --git a/Objective-C/libffi_support.m b/Objective-C/libffi_support.m new file mode 100644 index 0000000..a431fdd --- /dev/null +++ b/Objective-C/libffi_support.m @@ -0,0 +1,1061 @@ +/* + * Support for libffi (http://sources.redhat.com/libffi) + * + * libffi is a library that makes it possible to dynamicly create calls + * to C functions (without knowing the signature at compile-time). It also + * provides a way to create closures, that is dynamicly create functions with + * a runtime specified interface. + * + * This file contains functions to dynamicly call objc_msgSendSuper and to + * dynamicly create IMPs for use in Objective-C method dispatch tables. The + * file 'register.m' contains compile-time generated equivalents of these. + */ +#include "pyobjc.h" + +#import <Foundation/NSHost.h> + +#ifdef MACOSX +/* + * Define SMALL_STRUCT_LIMIT as the largest struct that will be returned + * in registers instead of with a hidden pointer argument. + */ + +#if defined(__ppc__) + +# define SMALL_STRUCT_LIMIT 4 + +#elif defined(__i386__) + +# define SMALL_STRUCT_LIMIT 8 + +#else + +# error "Unsupported MACOSX platform" + +#endif + +#endif /* MACOSX */ + + + +#ifndef FFI_CLOSURES +# error "Need FFI_CLOSURES!" +#endif + +#if 0 /* Usefull during debugging, only used in the debugger */ +static void describe_ffitype(ffi_type* type) +{ + switch (type->type) { + case FFI_TYPE_VOID: printf("%s", "void"); break; + case FFI_TYPE_INT: printf("%s", "int"); break; + case FFI_TYPE_FLOAT: printf("%s", "float"); break; + case FFI_TYPE_DOUBLE: printf("%s", "double"); break; + case FFI_TYPE_UINT8: printf("%s", "uint8"); break; + case FFI_TYPE_SINT8: printf("%s", "sint8"); break; + case FFI_TYPE_UINT16: printf("%s", "uint16"); break; + case FFI_TYPE_SINT16: printf("%s", "sint16"); break; + case FFI_TYPE_UINT32: printf("%s", "uint32"); break; + case FFI_TYPE_SINT32: printf("%s", "sint32"); break; + case FFI_TYPE_UINT64: printf("%s", "uint64"); break; + case FFI_TYPE_SINT64: printf("%s", "sint64"); break; + case FFI_TYPE_POINTER: printf("%s", "*"); break; + case FFI_TYPE_STRUCT: { + ffi_type** elems = type->elements; + + printf("%s", "struct { "); + if (elems) { + while (*elems) { + describe_ffitype(*(elems++)); + printf("%s", "; "); + } + } + printf("%s", "}"); + } + break; + + default: + // Don't abort, this is called from the debugger + printf("?(%d)", type->type); + } +} + +static void describe_cif(ffi_cif* cif) +{ + size_t i; + printf("<ffi_cif abi=%d nargs=%d bytes=%d flags=%#x args=[", + cif->abi, cif->nargs, cif->bytes, cif->flags); + for (i = 0; i < cif->nargs; i++) { + describe_ffitype(cif->arg_types[i]); + printf("%s", ", "); + } + printf("%s", "] rettype="); + describe_ffitype(cif->rtype); + printf("%s", ">\n"); +} + +#endif + +static Py_ssize_t align(Py_ssize_t offset, Py_ssize_t alignment) +{ + Py_ssize_t rest = offset % alignment; + if (rest == 0) return offset; + return offset + (alignment - rest); +} + +static Py_ssize_t +num_struct_fields(const char* argtype) +{ + Py_ssize_t res = 0; + + if (*argtype != _C_STRUCT_B) return -1; + while (*argtype != _C_STRUCT_E && *argtype != '=') argtype++; + if (*argtype == _C_STRUCT_E) return 0; + + argtype++; + while (*argtype != _C_STRUCT_E) { + argtype = PyObjCRT_SkipTypeSpec(argtype); + if (argtype == NULL) return -1; + res ++; + } + return res; +} + + +static void +free_type(void *obj) +{ + PyMem_Free(((ffi_type*)obj)->elements); + PyMem_Free(obj); +} + +static ffi_type* signature_to_ffi_type(const char* argtype); + +static ffi_type* +array_to_ffi_type(const char* argtype) +{ +static PyObject* array_types = NULL; /* XXX: Use NSMap */ + PyObject* v; + ffi_type* type; + Py_ssize_t field_count; + Py_ssize_t i; + const char* key = argtype; + + if (array_types == NULL) { + array_types = PyDict_New(); + if (array_types == NULL) return NULL; + } + + v = PyDict_GetItemString(array_types, (char*)argtype); + if (v != NULL) { + return (ffi_type*)PyCObject_AsVoidPtr(v); + } + + /* We don't have a type description yet, dynamicly + * create it. + */ + field_count = atoi(argtype+1); + + type = PyMem_Malloc(sizeof(*type)); + if (type == NULL) { + PyErr_NoMemory(); + return NULL; + } + type->size = PyObjCRT_SizeOfType(argtype); + type->alignment = PyObjCRT_AlignOfType(argtype); + + /* Libffi doesn't really know about arrays as part of larger + * data-structres (e.g. struct foo { int field[3]; };). We fake it + * by treating the nested array as a struct. These seems to work + * fine on MacOS X. + */ + type->type = FFI_TYPE_STRUCT; + type->elements = PyMem_Malloc((1+field_count) * sizeof(ffi_type*)); + if (type->elements == NULL) { + PyMem_Free(type); + PyErr_NoMemory(); + return NULL; + } + + while (isdigit(*++argtype)); + type->elements[0] = signature_to_ffi_type(argtype); + for (i = 1; i < field_count; i++) { + type->elements[i] = type->elements[0]; + } + type->elements[field_count] = 0; + + v = PyCObject_FromVoidPtr(type, free_type); + if (v == NULL) { + free_type(type); + return NULL; + } + + PyDict_SetItemString(array_types, (char*)key, v); + if (PyErr_Occurred()) { + Py_DECREF(v); + return NULL; + } + Py_DECREF(v); + return type; +} + +static ffi_type* +struct_to_ffi_type(const char* argtype) +{ + static PyObject* struct_types = NULL; /* XXX: Use NSMap */ + PyObject* v; + ffi_type* type; + Py_ssize_t field_count; + const char* curtype; + + if (struct_types == NULL) { + struct_types = PyDict_New(); + if (struct_types == NULL) return NULL; + } + + v = PyDict_GetItemString(struct_types, (char*)argtype); + if (v != NULL) { + return (ffi_type*)PyCObject_AsVoidPtr(v); + } + + /* We don't have a type description yet, dynamicly + * create it. + */ + field_count = num_struct_fields(argtype); + if (field_count == -1) { + PyErr_Format(PyObjCExc_InternalError, + "Cannot determine layout of %s", argtype); + return NULL; + } + + type = PyMem_Malloc(sizeof(*type)); + if (type == NULL) { + PyErr_NoMemory(); + return NULL; + } + type->size = PyObjCRT_SizeOfType(argtype); + type->alignment = PyObjCRT_AlignOfType(argtype); + type->type = FFI_TYPE_STRUCT; + type->elements = PyMem_Malloc((1+field_count) * sizeof(ffi_type*)); + if (type->elements == NULL) { + PyMem_Free(type); + PyErr_NoMemory(); + return NULL; + } + + field_count = 0; + curtype = argtype+1; + while (*curtype != _C_STRUCT_E && *curtype != '=') curtype++; + if (*curtype == '=') { + curtype ++; + while (*curtype != _C_STRUCT_E) { + type->elements[field_count] = + signature_to_ffi_type(curtype); + if (type->elements[field_count] == NULL) { + PyMem_Free(type->elements); + return NULL; + } + field_count++; + curtype = PyObjCRT_SkipTypeSpec(curtype); + if (curtype == NULL) { + PyMem_Free(type->elements); + return NULL; + } + } + } + type->elements[field_count] = NULL; + + v = PyCObject_FromVoidPtr(type, free_type); + if (v == NULL) { + free_type(type); + return NULL; + } + + PyDict_SetItemString(struct_types, (char*)argtype, v); + if (PyErr_Occurred()) { + Py_DECREF(v); + return NULL; + } + Py_DECREF(v); + return type; +} + +static ffi_type* +signature_to_ffi_return_type(const char* argtype) +{ + switch (*argtype) { + case _C_CHR: case _C_SHT: + return &ffi_type_sint; + case _C_UCHR: case _C_USHT: + return &ffi_type_uint; +#ifdef _C_BOOL + case _C_BOOL: return &ffi_type_sint; +#endif + default: + return signature_to_ffi_type(argtype); + } +} + + +static ffi_type* +signature_to_ffi_type(const char* argtype) +{ + switch (*argtype) { + case _C_VOID: return &ffi_type_void; + case _C_ID: return &ffi_type_pointer; + case _C_CLASS: return &ffi_type_pointer; + case _C_SEL: return &ffi_type_pointer; + case _C_CHR: return &ffi_type_schar; +#ifdef _C_BOOL + case _C_BOOL: return &ffi_type_sint; +#endif + case _C_UCHR: return &ffi_type_uchar; + case _C_SHT: return &ffi_type_sshort; + case _C_USHT: return &ffi_type_ushort; + case _C_INT: return &ffi_type_sint; + case _C_UINT: return &ffi_type_uint; + + /* The next to defintions are incorrect, but the correct definitions + * don't work (e.g. give testsuite failures). We should be fine + * as long as sizeof(long) == sizeof(int) + */ + case _C_LNG: return &ffi_type_sint; /* ffi_type_slong */ + case _C_ULNG: return &ffi_type_uint; /* ffi_type_ulong */ + case _C_LNGLNG: return &ffi_type_sint64; + case _C_ULNGLNG: return &ffi_type_uint64; + case _C_FLT: return &ffi_type_float; + case _C_DBL: return &ffi_type_double; + case _C_CHARPTR: return &ffi_type_pointer; + case _C_PTR: return &ffi_type_pointer; + case _C_ARY_B: + return array_to_ffi_type(argtype); + case _C_IN: case _C_OUT: case _C_INOUT: case _C_CONST: + return signature_to_ffi_type(argtype+1); + case _C_STRUCT_B: + return struct_to_ffi_type(argtype); + default: + PyErr_Format(PyExc_NotImplementedError, + "Type '%#x' not supported", *argtype); + return NULL; + } +} + +/* + * arg_signature_to_ffi_type: Make the ffi_type for the call to the method IMP, + * on MacOS X this is the same as the normal signature_to_ffi_type, but on + * Linux/GNUstep we need a slightly different function. + */ +#ifdef MACOSX + +#ifdef __ppc__ +#define arg_signature_to_ffi_type signature_to_ffi_type + +#else +static inline ffi_type* +arg_signature_to_ffi_type(const char* argtype) +{ + /* NOTE: This is the minimal change to pass the unittests, it is not + * based on analysis of the calling conventions. + */ + switch (*argtype) { + case _C_CHR: return &ffi_type_sint; + case _C_UCHR: return &ffi_type_uint; + case _C_SHT: return &ffi_type_sint; + case _C_USHT: return &ffi_type_uint; + default: return signature_to_ffi_type(argtype); + } +} +#endif + +#else /* GNUstep */ + +static inline ffi_type* +arg_signature_to_ffi_type(const char* argtype) +{ + /* NOTE: This is the minimal change to pass the unittests, it is not + * based on analysis of the calling conventions. + */ + switch (*argtype) { + case _C_CHR: return &ffi_type_sint; + case _C_UCHR: return &ffi_type_uint; + case _C_SHT: return &ffi_type_sint; + case _C_USHT: return &ffi_type_uint; + default: return signature_to_ffi_type(argtype); + } +} + +#endif /* GNUstep */ + +/* This function decodes its arguments into Python values, then + * calls the python method and finally encodes the return value + */ + +typedef struct { + PyObject* callable; + PyObjCMethodSignature* methinfo; +} _method_stub_userdata; + +static void +method_stub(ffi_cif* cif __attribute__((__unused__)), void* resp, void** args, void* _userdata) +{ + _method_stub_userdata* userdata = (_method_stub_userdata*)_userdata; + PyObject* callable = userdata->callable; + PyObjCMethodSignature* methinfo = userdata->methinfo; + int isAlloc = 0; + Py_ssize_t i; + PyObject* arglist; + PyObject* res; + PyObject* v; + int have_output = 0; + const char* rettype; + PyObject* pyself; + int cookie; + + PyGILState_STATE state = PyGILState_Ensure(); + + rettype = methinfo->rettype; + + arglist = PyList_New(0); + + pyself = PyObjCObject_NewTransient(*(id*)args[0], &cookie); + if (pyself == NULL) { + goto error; + } + if (PyList_Append(arglist, pyself) == -1) { + goto error; + } + + /* First translate from Objective-C to python */ + + for (i = 2; i < methinfo->nargs; i++) { + + const char* argtype = methinfo->argtype[i]; + + switch (*argtype) { + case _C_INOUT: + if (argtype[1] == _C_PTR) { + have_output ++; + } + /* FALL THROUGH */ + case _C_IN: case _C_CONST: + if (argtype[1] == _C_PTR) { + if (*(void**)args[i]) { + v = pythonify_c_value(argtype+2, + *(void**)args[i]); + } else { + v = PyObjC_NULL; + Py_INCREF(v); + } + } else { + v = pythonify_c_value(argtype+1, + args[i]); + } + break; + case _C_OUT: + /* Skip output parameter */ + if (argtype[1] == _C_PTR) { + have_output ++; + } + continue; + default: + v = pythonify_c_value(argtype, args[i]); + } + if (v == NULL) { + Py_DECREF(arglist); + goto error; + } + if (PyList_Append(arglist, v) == -1) { + Py_DECREF(v); + Py_DECREF(arglist); + goto error; + } + Py_DECREF(v); + } + + v = PyList_AsTuple(arglist); + if (v == NULL) { + Py_DECREF(arglist); + PyObjCObject_ReleaseTransient(pyself, cookie); + goto error; + } + Py_DECREF(arglist); + arglist = v; + + if (!callable) { + abort(); + } + + res = PyObject_Call(callable, arglist, NULL); + isAlloc = PyObjCSelector_DonatesRef(callable); + Py_DECREF(arglist); + PyObjCObject_ReleaseTransient(pyself, cookie); + if (res == NULL) { + goto error; + } + + if (!have_output) { + int err; + + if (*rettype != _C_VOID) { + err = depythonify_c_return_value(rettype, res, resp); + + if (isAlloc && *rettype == _C_ID) { + /* Must return a 'new' instead of a borrowed + * reference. + */ + [(*(id*)resp) retain]; + } else if (*rettype == _C_ID && res->ob_refcnt == 1) { + /* make sure return value doesn't die before + * the caller can get its hands on it. + */ + [[(*(id*)resp) retain] autorelease]; + } + Py_DECREF(res); + if (err == -1) { + if (res == Py_None) { + PyErr_Format(PyExc_ValueError, + "%s: returned None, expecting " + "a value", + PyObjCRT_SELName(*(SEL*)args[1])); + } + goto error; + } + } else { + if (res != Py_None) { + PyErr_Format(PyExc_ValueError, + "%s: did not return None, expecting " + "void return value", + PyObjCRT_SELName(*(SEL*)args[1])); + goto error; + } + *((int*)resp) = 0; + } + } else { + /* We have some output parameters, locate them and encode + * their values + */ + Py_ssize_t idx; + PyObject* real_res; + + if (*rettype == _C_VOID && have_output == 1) { + /* Special case: the python method returned only + * the return value, not a tuple. + */ + for (i = 2; i < methinfo->nargs; i++) { + const char* argtype = methinfo->argtype[i]; + int err; + + switch (*argtype) { + case _C_INOUT: case _C_OUT: + if (argtype[1] != _C_PTR) { + continue; + } + argtype += 2; + break; + default: continue; + } + + err = depythonify_c_value(argtype, res, *(void**)args[i]); + if (err == -1) { + goto error; + } + if (res->ob_refcnt == 1 && argtype[0] == _C_ID) { + /* make sure return value doesn't die before + * the caller can get its hands on it. + */ + [[**(id**)args[i] retain] autorelease]; + } + + break; + } + + PyGILState_Release(state); + return; + } + + if (*rettype != _C_VOID) { + if (!PyTuple_Check(res) || PyTuple_Size(res) != have_output+1) { + PyErr_Format(PyExc_TypeError, + "%s: Need tuple of %d arguments as result", + PyObjCRT_SELName(*(SEL*)args[1]), have_output+1); + Py_DECREF(res); + goto error; + } + + real_res = PyTuple_GET_ITEM(res, 0); + idx = 1; + } else { + if (!PyTuple_Check(res) || PyTuple_Size(res) != have_output) { + PyErr_Format(PyExc_TypeError, + "%s: Need tuple of %d arguments as result", + PyObjCRT_SELName(*(SEL*)args[1]), have_output); + Py_DECREF(res); + goto error; + } + real_res = NULL; + idx = 0; + } + + + for (i = 2; i < methinfo->nargs; i++) { + const char* argtype = methinfo->argtype[i]; + int err; + + switch (*argtype) { + case _C_INOUT: case _C_OUT: + if (argtype[1] != _C_PTR) { + continue; + } + argtype += 2; + break; + default: continue; + } + + if (*(void**)args[i] != NULL) { + /* The output pointer might be NULL */ + + v = PyTuple_GET_ITEM(res, idx++); + err = depythonify_c_value(argtype, v, *(void**)args[i]); + if (err == -1) { + goto error; + } + if (v->ob_refcnt == 1 && argtype[0] == _C_ID) { + /* make sure return value doesn't die before + * the caller can get its hands on it. + */ + [[**(id**)args[i] retain] autorelease]; + } + } + } + + if (*rettype != _C_VOID) { + int err = depythonify_c_return_value(rettype, + real_res, resp); + + if (isAlloc && *rettype == _C_ID) { + /* Must return a 'new' instead of a borrowed + * reference. + */ + [(*(id*)resp) retain]; + } else if (*rettype == _C_ID && real_res->ob_refcnt == 1) { + /* make sure return value doesn't die before + * the caller can get its hands on it. + */ + [[(*(id*)resp) retain] autorelease]; + } + if (err == -1) { + if (real_res == Py_None) { + PyErr_Format(PyExc_ValueError, + "%s: returned None, expecting " + "a value", + PyObjCRT_SELName(*(SEL*)args[1])); + } + Py_DECREF(res); + goto error; + } + } else { + if (res != Py_None) { + PyErr_Format(PyExc_ValueError, + "%s: did not return None, expecting " + "void return value", + PyObjCRT_SELName(*(SEL*)args[1])); + goto error; + } + *((int*)resp) = 0; + } + + + Py_DECREF(res); + + } + + PyGILState_Release(state); + + return; + +error: + PyObjCErr_ToObjCWithGILState(&state); +} + +/* + * Return an IMP that is suitable for forwarding a method with the specified + * signature from Objective-C to Python. + */ +IMP +PyObjCFFI_MakeIMPForSignature(char* signature, PyObject* callable) +{ + _method_stub_userdata* stubUserdata; + PyObjCMethodSignature* methinfo; + IMP closure; + + methinfo = PyObjCMethodSignature_FromSignature(signature); + if (methinfo == NULL) { + return NULL; + } + + stubUserdata = PyMem_Malloc(sizeof(*stubUserdata)); + if (stubUserdata == NULL) { + PyObjCMethodSignature_Free(methinfo); + return NULL; + } + + stubUserdata->methinfo = methinfo; + + if (callable) { + stubUserdata->callable = callable; + Py_INCREF(stubUserdata->callable); + } else { + stubUserdata->callable = NULL; + } + + closure = PyObjCFFI_MakeClosure(methinfo, method_stub, stubUserdata); + if (closure == NULL) { + PyObjCMethodSignature_Free(methinfo); + if (stubUserdata->callable) { + Py_DECREF(stubUserdata->callable); + } + PyMem_Free(stubUserdata); + return NULL; + } + + return closure; +} + +IMP +PyObjCFFI_MakeIMPForPyObjCSelector(PyObjCSelector *aSelector) +{ + if (PyObjCNativeSelector_Check(aSelector)) { + PyObjCNativeSelector *nativeSelector = + (PyObjCNativeSelector *) aSelector; + PyObjCRT_Method_t aMeth; + + if (nativeSelector->sel_flags & PyObjCSelector_kCLASS_METHOD) { + aMeth = class_getClassMethod(nativeSelector->sel_class, nativeSelector->sel_selector); + } else { + aMeth = class_getInstanceMethod(nativeSelector->sel_class, nativeSelector->sel_selector); + } + return aMeth->method_imp; + } else { + PyObjCPythonSelector *pythonSelector = (PyObjCPythonSelector *) aSelector; + return PyObjCFFI_MakeIMPForSignature(pythonSelector->sel_signature, pythonSelector->callable); + } +} + +/* Count the number of arguments and their total size */ +/* argument_size is not cleared and should be initialized to the amount of + * bufferspace that will be allocated just before the argument array + */ +int PyObjCFFI_CountArguments( + PyObjCMethodSignature* methinfo, Py_ssize_t argOffset, + Py_ssize_t* byref_in_count, + Py_ssize_t* byref_out_count, + Py_ssize_t* plain_count, + Py_ssize_t* argbuf_len) +{ + Py_ssize_t i; + Py_ssize_t itemAlign; + Py_ssize_t itemSize; + + *byref_in_count = *byref_out_count = *plain_count = 0; + + for (i = argOffset; i < methinfo->nargs; i++) { + const char *argtype = methinfo->argtype[i]; + + switch (*argtype) { + case _C_INOUT: + if (argtype[1] == _C_PTR) { + (*byref_out_count) ++; + (*byref_in_count) ++; + itemAlign = PyObjCRT_AlignOfType(argtype+2); + itemSize = PyObjCRT_SizeOfType(argtype+2); + if (itemSize == -1) { + return -1; + } + } else { + itemSize = PyObjCRT_SizeOfType(argtype+1); + itemAlign = PyObjCRT_AlignOfType(argtype+1); + if (itemSize == -1) { + return -1; + } + } + *argbuf_len = align(*argbuf_len, itemAlign); + (*argbuf_len) += itemSize; + break; + + case _C_IN: case _C_CONST: + if (argtype[1] == _C_PTR) { + (*byref_in_count) ++; + itemSize = PyObjCRT_SizeOfType(argtype+2); + itemAlign = PyObjCRT_AlignOfType(argtype+2); + if (itemSize == -1) { + return -1; + } + } else { + (*plain_count) ++; + itemSize = PyObjCRT_SizeOfType(argtype+1); + itemAlign = PyObjCRT_AlignOfType(argtype+1); + if (itemSize == -1) { + return -1; + } + } + *argbuf_len = align(*argbuf_len, itemAlign); + (*argbuf_len) += itemSize; + break; + + case _C_OUT: + if (argtype[1] == _C_PTR) { + (*byref_out_count) ++; + itemSize = PyObjCRT_SizeOfType(argtype+2); + itemAlign = PyObjCRT_AlignOfType(argtype+2); + if (itemSize == -1) { + return -1; + } + } else { + (*plain_count)++; + itemSize = PyObjCRT_SizeOfType(argtype+1); + itemAlign = PyObjCRT_AlignOfType(argtype+1); + if (itemSize == -1) { + return -1; + } + } + *argbuf_len = align(*argbuf_len, itemAlign); + (*argbuf_len) += itemSize; + break; + + case _C_STRUCT_B: case _C_UNION_B: case _C_ARY_B: + (*plain_count)++; + itemSize = PyObjCRT_SizeOfType(argtype); + itemAlign = PyObjCRT_AlignOfType(argtype); + if (itemSize == -1) { + return -1; + } + *argbuf_len = align(*argbuf_len, itemAlign); + (*argbuf_len) += itemSize; + break; + + default: + itemSize = PyObjCRT_SizeOfType(argtype); + itemAlign = PyObjCRT_AlignOfType(argtype); + if (itemSize == -1) { + return -1; + } + *argbuf_len = align(*argbuf_len, itemAlign); + (*argbuf_len) += itemSize; + (*plain_count)++; + break; + } + } + return 0; +} + +int PyObjCFFI_ParseArguments( + PyObjCMethodSignature* methinfo, Py_ssize_t argOffset, + PyObject* args, + Py_ssize_t argbuf_cur, unsigned char* argbuf, + void** byref, + ffi_type** arglist, void** values) +{ + Py_ssize_t py_arg = 0; + Py_ssize_t i; + void* arg; + + for (i = argOffset; i < methinfo->nargs; i++) { + + int error; + PyObject *argument; + const char *argtype = methinfo->argtype[i]; + + if (argtype[0] == _C_OUT && argtype[1] == _C_PTR) { + /* Just allocate room in argbuf and set that*/ + Py_ssize_t sz; + + argbuf_cur = align(argbuf_cur, + PyObjCRT_AlignOfType(argtype+2)); + arg = argbuf + argbuf_cur; + byref[i] = arg; + + arglist[i] = &ffi_type_pointer; + values[i] = byref+i; + + sz = PyObjCRT_SizeOfType(argtype+2); + argbuf_cur += sz; + + /* Clear the output buffer, just in case the called + * function doesn't write anything into the buffer. + */ + memset(arg, 0, sz); + } else { + /* Encode argument, maybe after allocating space */ + + if (argtype[0] == _C_OUT) argtype ++; + + argument = PyTuple_GET_ITEM (args, py_arg); + switch (*argtype) { + case _C_STRUCT_B: case _C_ARY_B: case _C_UNION_B: + /* Allocate space and encode */ + argbuf_cur = align(argbuf_cur, + PyObjCRT_AlignOfType(argtype)); + arg = argbuf + argbuf_cur; + argbuf_cur += PyObjCRT_SizeOfType(argtype); + byref[i] = arg; + error = depythonify_c_value ( + argtype, + argument, + arg); + + arglist[i] = signature_to_ffi_type(argtype); + values[i] = arg; + break; + case _C_INOUT: + case _C_IN: + case _C_CONST: + + if (argtype[1] == _C_PTR) { + /* Allocate space and encode */ + + if (argument == PyObjC_NULL) { + byref[i] = NULL; + error = 0; + + } else { + argbuf_cur = align(argbuf_cur, PyObjCRT_AlignOfType(argtype+2)); + arg = argbuf + argbuf_cur; + argbuf_cur += PyObjCRT_SizeOfType(argtype+2); + byref[i] = arg; + error = depythonify_c_value ( + argtype+2, + argument, + arg); + } + + arglist[i] = &ffi_type_pointer; + values[i] = byref + i; + + } else { + /* just encode */ + argbuf_cur = align(argbuf_cur, PyObjCRT_AlignOfType(argtype+1)); + arg = argbuf + argbuf_cur; + argbuf_cur += PyObjCRT_SizeOfType(argtype+1); + error = depythonify_c_value ( + argtype+1, + argument, + arg); + + arglist[i] = signature_to_ffi_type( + argtype+1); + values[i] = arg; + + } + break; + default: + argbuf_cur = align(argbuf_cur, PyObjCRT_AlignOfType(argtype)); + arg = argbuf + argbuf_cur; + argbuf_cur += PyObjCRT_SizeOfType(argtype); + + error = depythonify_c_value ( + argtype, + argument, + arg); + + arglist[i] = signature_to_ffi_type(argtype); + values[i] = arg; + } + + if (error == -1) { + return -1; + } + py_arg++; + } + } + return 0; +} + +/* XXX: Need to refactor to deal with 'self' */ +{ + PyObject* objc_result = NULL; + PyObject* result = NULL; + int py_arg; + void* arg; + Py_ssize_t i; + + if ( (*methinfo->rettype != _C_VOID) /* && ([methinfo isOneway] == NO) */ ) { + objc_result = pythonify_c_return_value (methinfo->rettype, pRetval); + } else { + Py_INCREF(Py_None); + objc_result = Py_None; + } + + /* XXX: This is for selectors only, need to change this !!!! */ + + if (self != NULL && objc_result != self + && PyObjCObject_Check(self) && PyObjCObject_Check(objc_result) + && !(flags & PyObjCSelector_kRETURNS_UNINITIALIZED) + && (((PyObjCObject*)self)->flags & PyObjCObject_kUNINITIALIZED)) { + [PyObjCObject_GetObject(objc_result) release]; + PyObjCObject_ClearObject(self); + } + + if (byref_out_count == 0) { + return objc_result; + + } else { + + if (*methinfo->rettype == _C_VOID) { + if (byref_out_count > 1) { + result = PyTuple_New(byref_out_count); + if (result == NULL) { + return NULL; + } + } else { + result = NULL; + } + Py_DECREF(objc_result); + py_arg = 0; + } else { + result = PyTuple_New(byref_out_count+1); + if (result == NULL) { + return NULL; + } + PyTuple_SET_ITEM(result, 0, objc_result); + py_arg = 1; + } + objc_result = NULL; + + for (i = argOffset; i < methinfo->nargs; i++) { + const char *argtype = methinfo->argtype[i]; + PyObject* v; + + switch (*argtype) { + case _C_INOUT: + case _C_OUT: + if (argtype[1] == _C_PTR) { + arg = byref[i]; + + if (arg == NULL) { + v = PyObjC_NULL; + Py_INCREF(v); + } else { + v = pythonify_c_value(argtype+2, arg); + } + if (!v) goto error_cleanup; + + if (result != NULL) { + if (PyTuple_SetItem(result, + py_arg++, v) < 0) { + + Py_DECREF(v); + goto error_cleanup; + } + } else { + result = v; + } + } + break; + } + } + } + return result; + +error_cleanup: + Py_XDECREF(result); + return NULL; +} diff --git a/Objective-C/libobjcl.h b/Objective-C/libobjcl.h index 5ca49f4..b2c012b 100644 --- a/Objective-C/libobjcl.h +++ b/Objective-C/libobjcl.h @@ -5,9 +5,6 @@ #ifdef USE_LIBFFI #include <ffi.h> -#else -#include <vacall.h> -#include <avcall.h> #endif @@ -54,7 +51,6 @@ void objcl_invoke_with_types (void *receiver, SEL method_selector, char *(types[]), - size_t arg_sizes[], id *exception, void *return_value, int argc, diff --git a/Objective-C/libobjcl.m b/Objective-C/libobjcl.m index bd1e410..3bcdf4b 100644 --- a/Objective-C/libobjcl.m +++ b/Objective-C/libobjcl.m @@ -260,8 +260,7 @@ objcl_invoke_method (OBJCL_OBJ_DATA receiver, void objcl_invoke_with_types (void *receiver, SEL method_selector, - char *(types[]), - size_t arg_sizes[], + char *types[], id *exception, void *return_value, int argc, @@ -271,6 +270,9 @@ objcl_invoke_with_types (void *receiver, IMP method; int i; + char *return_type = types[0]; + char **arg_types = types + 1; + *exception = NULL; #ifdef __NEXT_RUNTIME__ |