summaryrefslogtreecommitdiffstats
path: root/Modules/_ctypes/callbacks.c
diff options
context:
space:
mode:
authorThomas Heller <theller@ctypes.org>2006-03-08 19:35:11 (GMT)
committerThomas Heller <theller@ctypes.org>2006-03-08 19:35:11 (GMT)
commitd4c9320412177895f598a93d73a0e654db27c351 (patch)
tree54ad90161f03318ec7ed4fe9b75055d6805e84e5 /Modules/_ctypes/callbacks.c
parent533ff6fc0622bfae5d13d3a3838fbec3d9d21092 (diff)
downloadcpython-d4c9320412177895f598a93d73a0e654db27c351.zip
cpython-d4c9320412177895f598a93d73a0e654db27c351.tar.gz
cpython-d4c9320412177895f598a93d73a0e654db27c351.tar.bz2
Copy ctypes-0.9.9.4 sources from external into the trunk.
Diffstat (limited to 'Modules/_ctypes/callbacks.c')
-rw-r--r--Modules/_ctypes/callbacks.c518
1 files changed, 518 insertions, 0 deletions
diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c
new file mode 100644
index 0000000..2948d98
--- /dev/null
+++ b/Modules/_ctypes/callbacks.c
@@ -0,0 +1,518 @@
+#include "Python.h"
+#include "compile.h" /* required only for 2.3, as it seems */
+#include "frameobject.h"
+
+#include <ffi.h>
+#ifdef MS_WIN32
+#include <windows.h>
+#endif
+#include "ctypes.h"
+
+static void
+PrintError(char *msg, ...)
+{
+ char buf[512];
+ PyObject *f = PySys_GetObject("stderr");
+ va_list marker;
+
+ va_start(marker, msg);
+ vsnprintf(buf, sizeof(buf), msg, marker);
+ va_end(marker);
+ if (f)
+ PyFile_WriteString(buf, f);
+ PyErr_Print();
+}
+
+
+/* after code that pyrex generates */
+void _AddTraceback(char *funcname, char *filename, int lineno)
+{
+ PyObject *py_srcfile = 0;
+ PyObject *py_funcname = 0;
+ PyObject *py_globals = 0;
+ PyObject *empty_tuple = 0;
+ PyObject *empty_string = 0;
+ PyCodeObject *py_code = 0;
+ PyFrameObject *py_frame = 0;
+
+ py_srcfile = PyString_FromString(filename);
+ if (!py_srcfile) goto bad;
+ py_funcname = PyString_FromString(funcname);
+ if (!py_funcname) goto bad;
+ py_globals = PyDict_New();
+ if (!py_globals) goto bad;
+ empty_tuple = PyTuple_New(0);
+ if (!empty_tuple) goto bad;
+ empty_string = PyString_FromString("");
+ if (!empty_string) goto bad;
+ py_code = PyCode_New(
+ 0, /*int argcount,*/
+ 0, /*int nlocals,*/
+ 0, /*int stacksize,*/
+ 0, /*int flags,*/
+ empty_string, /*PyObject *code,*/
+ empty_tuple, /*PyObject *consts,*/
+ empty_tuple, /*PyObject *names,*/
+ empty_tuple, /*PyObject *varnames,*/
+ empty_tuple, /*PyObject *freevars,*/
+ empty_tuple, /*PyObject *cellvars,*/
+ py_srcfile, /*PyObject *filename,*/
+ py_funcname, /*PyObject *name,*/
+ lineno, /*int firstlineno,*/
+ empty_string /*PyObject *lnotab*/
+ );
+ if (!py_code) goto bad;
+ py_frame = PyFrame_New(
+ PyThreadState_Get(), /*PyThreadState *tstate,*/
+ py_code, /*PyCodeObject *code,*/
+ py_globals, /*PyObject *globals,*/
+ 0 /*PyObject *locals*/
+ );
+ if (!py_frame) goto bad;
+ py_frame->f_lineno = lineno;
+ PyTraceBack_Here(py_frame);
+ bad:
+ Py_XDECREF(py_globals);
+ Py_XDECREF(py_srcfile);
+ Py_XDECREF(py_funcname);
+ Py_XDECREF(empty_tuple);
+ Py_XDECREF(empty_string);
+ Py_XDECREF(py_code);
+ Py_XDECREF(py_frame);
+}
+
+#ifdef MS_WIN32
+/*
+ * We must call AddRef() on non-NULL COM pointers we receive as arguments
+ * to callback functions - these functions are COM method implementations.
+ * The Python instances we create have a __del__ method which calls Release().
+ *
+ * The presence of a class attribute named '_needs_com_addref_' triggers this
+ * behaviour. It would also be possible to call the AddRef() Python method,
+ * after checking for PyObject_IsTrue(), but this would probably be somewhat
+ * slower.
+ */
+static void
+TryAddRef(StgDictObject *dict, CDataObject *obj)
+{
+ IUnknown *punk;
+
+ if (NULL == PyDict_GetItemString((PyObject *)dict, "_needs_com_addref_"))
+ return;
+
+ punk = *(IUnknown **)obj->b_ptr;
+ if (punk)
+ punk->lpVtbl->AddRef(punk);
+ return;
+}
+#endif
+
+/******************************************************************************
+ *
+ * Call the python object with all arguments
+ *
+ */
+static void _CallPythonObject(void *mem,
+ ffi_type *restype,
+ SETFUNC setfunc,
+ PyObject *callable,
+ PyObject *converters,
+ void **pArgs)
+{
+ int i;
+ PyObject *result;
+ PyObject *arglist = NULL;
+ int nArgs;
+ PyGILState_STATE state = PyGILState_Ensure();
+
+ nArgs = PySequence_Length(converters);
+ /* Hm. What to return in case of error?
+ For COM, 0xFFFFFFFF seems better than 0.
+ */
+ if (nArgs < 0) {
+ PrintError("BUG: PySequence_Length");
+ goto Done;
+ }
+
+ arglist = PyTuple_New(nArgs);
+ if (!arglist) {
+ PrintError("PyTuple_New()");
+ goto Done;
+ }
+ for (i = 0; i < nArgs; ++i) {
+ /* Note: new reference! */
+ PyObject *cnv = PySequence_GetItem(converters, i);
+ StgDictObject *dict;
+ if (cnv)
+ dict = PyType_stgdict(cnv);
+ else {
+ PrintError("Getting argument converter %d\n", i);
+ goto Done;
+ }
+
+ if (dict && dict->getfunc && !IsSimpleSubType(cnv)) {
+ PyObject *v = dict->getfunc(*pArgs, dict->size);
+ if (!v) {
+ PrintError("create argument %d:\n", i);
+ Py_DECREF(cnv);
+ goto Done;
+ }
+ PyTuple_SET_ITEM(arglist, i, v);
+ /* XXX XXX XX
+ We have the problem that c_byte or c_short have dict->size of
+ 1 resp. 4, but these parameters are pushed as sizeof(int) bytes.
+ BTW, the same problem occurrs when they are pushed as parameters
+ */
+ } else if (dict) {
+ /* Hm, shouldn't we use CData_AtAddress() or something like that instead? */
+ CDataObject *obj = (CDataObject *)PyObject_CallFunctionObjArgs(cnv, NULL);
+ if (!obj) {
+ PrintError("create argument %d:\n", i);
+ Py_DECREF(cnv);
+ goto Done;
+ }
+ if (!CDataObject_Check(obj)) {
+ Py_DECREF(obj);
+ Py_DECREF(cnv);
+ PrintError("unexpected result of create argument %d:\n", i);
+ goto Done;
+ }
+ memcpy(obj->b_ptr, *pArgs, dict->size);
+ PyTuple_SET_ITEM(arglist, i, (PyObject *)obj);
+#ifdef MS_WIN32
+ TryAddRef(dict, obj);
+#endif
+ } else {
+ PyErr_SetString(PyExc_TypeError,
+ "cannot build parameter");
+ PrintError("Parsing argument %d\n", i);
+ Py_DECREF(cnv);
+ goto Done;
+ }
+ Py_DECREF(cnv);
+ /* XXX error handling! */
+ pArgs++;
+ }
+
+#define CHECK(what, x) \
+if (x == NULL) _AddTraceback(what, __FILE__, __LINE__ - 1), PyErr_Print()
+
+ result = PyObject_CallObject(callable, arglist);
+ CHECK("'calling callback function'", result);
+ if ((restype != &ffi_type_void)
+ && result && result != Py_None) { /* XXX What is returned for Py_None ? */
+ /* another big endian hack */
+ union {
+ char c;
+ short s;
+ int i;
+ long l;
+ } r;
+ PyObject *keep;
+ assert(setfunc);
+ switch (restype->size) {
+ case 1:
+ keep = setfunc(&r, result, 0);
+ CHECK("'converting callback result'", keep);
+ *(ffi_arg *)mem = r.c;
+ break;
+ case SIZEOF_SHORT:
+ keep = setfunc(&r, result, 0);
+ CHECK("'converting callback result'", keep);
+ *(ffi_arg *)mem = r.s;
+ break;
+ case SIZEOF_INT:
+ keep = setfunc(&r, result, 0);
+ CHECK("'converting callback result'", keep);
+ *(ffi_arg *)mem = r.i;
+ break;
+#if (SIZEOF_LONG != SIZEOF_INT)
+ case SIZEOF_LONG:
+ keep = setfunc(&r, result, 0);
+ CHECK("'converting callback result'", keep);
+ *(ffi_arg *)mem = r.l;
+ break;
+#endif
+ default:
+ keep = setfunc(mem, result, 0);
+ CHECK("'converting callback result'", keep);
+ break;
+ }
+ /* keep is an object we have to keep alive so that the result
+ stays valid. If there is no such object, the setfunc will
+ have returned Py_None.
+
+ If there is such an object, we have no choice than to keep
+ it alive forever - but a refcount and/or memory leak will
+ be the result. EXCEPT when restype is py_object - Python
+ itself knows how to manage the refcount of these objects.
+ */
+ if (keep == NULL) /* Could not convert callback result. */
+ PyErr_WriteUnraisable(Py_None);
+ else if (keep == Py_None) /* Nothing to keep */
+ Py_DECREF(keep);
+ else if (setfunc != getentry("O")->setfunc) {
+ if (-1 == PyErr_Warn(PyExc_RuntimeWarning,
+ "memory leak in callback function."))
+ PyErr_WriteUnraisable(Py_None);
+ }
+ }
+ Py_XDECREF(result);
+ Done:
+ Py_XDECREF(arglist);
+
+ PyGILState_Release(state);
+}
+
+typedef struct {
+ ffi_closure *pcl; /* the C callable */
+ ffi_cif cif;
+ PyObject *converters;
+ PyObject *callable;
+ SETFUNC setfunc;
+ ffi_type *restype;
+ ffi_type *atypes[0];
+} ffi_info;
+
+static void closure_fcn(ffi_cif *cif,
+ void *resp,
+ void **args,
+ void *userdata)
+{
+ ffi_info *p = userdata;
+
+ _CallPythonObject(resp,
+ p->restype,
+ p->setfunc,
+ p->callable,
+ p->converters,
+ args);
+}
+
+void FreeCallback(THUNK thunk)
+{
+ FreeClosure(((ffi_info *)thunk)->pcl);
+ PyMem_Free(thunk);
+}
+
+THUNK AllocFunctionCallback(PyObject *callable,
+ PyObject *converters,
+ PyObject *restype,
+ int is_cdecl)
+{
+ int result;
+ ffi_info *p;
+ int nArgs, i;
+ ffi_abi cc;
+
+ nArgs = PySequence_Size(converters);
+ p = (ffi_info *)PyMem_Malloc(sizeof(ffi_info) + sizeof(ffi_type) * (nArgs + 1));
+ if (p == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ p->pcl = MallocClosure();
+ if (p->pcl == NULL) {
+ PyMem_Free(p);
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ for (i = 0; i < nArgs; ++i) {
+ PyObject *cnv = PySequence_GetItem(converters, i);
+ p->atypes[i] = GetType(cnv);
+ Py_DECREF(cnv);
+ }
+ p->atypes[i] = NULL;
+
+ if (restype == Py_None) {
+ p->setfunc = NULL;
+ p->restype = &ffi_type_void;
+ } else {
+ StgDictObject *dict = PyType_stgdict(restype);
+ if (dict == NULL) {
+ PyMem_Free(p);
+ return NULL;
+ }
+ p->setfunc = dict->setfunc;
+ p->restype = &dict->ffi_type;
+ }
+
+ cc = FFI_DEFAULT_ABI;
+#if defined(MS_WIN32) && !defined(_WIN32_WCE)
+ if (is_cdecl == 0)
+ cc = FFI_STDCALL;
+#endif
+ result = ffi_prep_cif(&p->cif, cc, nArgs,
+ GetType(restype),
+ &p->atypes[0]);
+ if (result != FFI_OK) {
+ PyErr_Format(PyExc_RuntimeError,
+ "ffi_prep_cif failed with %d", result);
+ PyMem_Free(p);
+ return NULL;
+ }
+ result = ffi_prep_closure(p->pcl, &p->cif, closure_fcn, p);
+ if (result != FFI_OK) {
+ PyErr_Format(PyExc_RuntimeError,
+ "ffi_prep_closure failed with %d", result);
+ PyMem_Free(p);
+ return NULL;
+ }
+
+ p->converters = converters;
+ p->callable = callable;
+
+ return (THUNK)p;
+}
+
+/****************************************************************************
+ *
+ * callback objects: initialization
+ */
+
+void init_callbacks_in_module(PyObject *m)
+{
+ if (PyType_Ready((PyTypeObject *)&PyType_Type) < 0)
+ return;
+}
+
+#ifdef MS_WIN32
+
+static void LoadPython(void)
+{
+ if (!Py_IsInitialized()) {
+ PyEval_InitThreads();
+ Py_Initialize();
+ }
+}
+
+/******************************************************************/
+
+long Call_GetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
+{
+ PyObject *mod, *func, *result;
+ long retval;
+ static PyObject *context;
+
+ if (context == NULL)
+ context = PyString_FromString("_ctypes.DllGetClassObject");
+
+ mod = PyImport_ImportModule("ctypes");
+ if (!mod) {
+ PyErr_WriteUnraisable(context ? context : Py_None);
+ /* There has been a warning before about this already */
+ return E_FAIL;
+ }
+
+ func = PyObject_GetAttrString(mod, "DllGetClassObject");
+ Py_DECREF(mod);
+ if (!func) {
+ PyErr_WriteUnraisable(context ? context : Py_None);
+ return E_FAIL;
+ }
+
+ result = PyObject_CallFunction(func,
+ "iii", rclsid, riid, ppv);
+ Py_DECREF(func);
+ if (!result) {
+ PyErr_WriteUnraisable(context ? context : Py_None);
+ return E_FAIL;
+ }
+
+ retval = PyInt_AsLong(result);
+ if (PyErr_Occurred()) {
+ PyErr_WriteUnraisable(context ? context : Py_None);
+ retval = E_FAIL;
+ }
+ Py_DECREF(result);
+ return retval;
+}
+
+STDAPI DllGetClassObject(REFCLSID rclsid,
+ REFIID riid,
+ LPVOID *ppv)
+{
+ long result;
+ PyGILState_STATE state;
+
+ LoadPython();
+ state = PyGILState_Ensure();
+ result = Call_GetClassObject(rclsid, riid, ppv);
+ PyGILState_Release(state);
+ return result;
+}
+
+long Call_CanUnloadNow(void)
+{
+ PyObject *mod, *func, *result;
+ long retval;
+ static PyObject *context;
+
+ if (context == NULL)
+ context = PyString_FromString("_ctypes.DllCanUnloadNow");
+
+ mod = PyImport_ImportModule("ctypes");
+ if (!mod) {
+/* OutputDebugString("Could not import ctypes"); */
+ /* We assume that this error can only occur when shutting
+ down, so we silently ignore it */
+ PyErr_Clear();
+ return E_FAIL;
+ }
+ /* Other errors cannot be raised, but are printed to stderr */
+ func = PyObject_GetAttrString(mod, "DllCanUnloadNow");
+ Py_DECREF(mod);
+ if (!func) {
+ PyErr_WriteUnraisable(context ? context : Py_None);
+ return E_FAIL;
+ }
+
+ result = PyObject_CallFunction(func, NULL);
+ Py_DECREF(func);
+ if (!result) {
+ PyErr_WriteUnraisable(context ? context : Py_None);
+ return E_FAIL;
+ }
+
+ retval = PyInt_AsLong(result);
+ if (PyErr_Occurred()) {
+ PyErr_WriteUnraisable(context ? context : Py_None);
+ retval = E_FAIL;
+ }
+ Py_DECREF(result);
+ return retval;
+}
+
+/*
+ DllRegisterServer and DllUnregisterServer still missing
+*/
+
+STDAPI DllCanUnloadNow(void)
+{
+ long result;
+ PyGILState_STATE state = PyGILState_Ensure();
+ result = Call_CanUnloadNow();
+ PyGILState_Release(state);
+ return result;
+}
+
+#ifndef Py_NO_ENABLE_SHARED
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvRes)
+{
+ switch(fdwReason) {
+ case DLL_PROCESS_ATTACH:
+ DisableThreadLibraryCalls(hinstDLL);
+ break;
+ }
+ return TRUE;
+}
+#endif
+
+#endif
+
+/*
+ Local Variables:
+ compile-command: "cd .. && python setup.py -q build_ext"
+ End:
+*/