1#include "Python.h"
2#include "frameobject.h"
3
4#include <ffi.h>
5#ifdef MS_WIN32
6#include <windows.h>
7#endif
8#include "ctypes.h"
9
10/**************************************************************/
11
12static void
13CThunkObject_dealloc(PyObject *myself)
14{
15    CThunkObject *self = (CThunkObject *)myself;
16    PyObject_GC_UnTrack(self);
17    Py_XDECREF(self->converters);
18    Py_XDECREF(self->callable);
19    Py_XDECREF(self->restype);
20    if (self->pcl_write)
21        ffi_closure_free(self->pcl_write);
22    PyObject_GC_Del(self);
23}
24
25static int
26CThunkObject_traverse(PyObject *myself, visitproc visit, void *arg)
27{
28    CThunkObject *self = (CThunkObject *)myself;
29    Py_VISIT(self->converters);
30    Py_VISIT(self->callable);
31    Py_VISIT(self->restype);
32    return 0;
33}
34
35static int
36CThunkObject_clear(PyObject *myself)
37{
38    CThunkObject *self = (CThunkObject *)myself;
39    Py_CLEAR(self->converters);
40    Py_CLEAR(self->callable);
41    Py_CLEAR(self->restype);
42    return 0;
43}
44
45PyTypeObject PyCThunk_Type = {
46    PyVarObject_HEAD_INIT(NULL, 0)
47    "_ctypes.CThunkObject",
48    sizeof(CThunkObject),                       /* tp_basicsize */
49    sizeof(ffi_type),                           /* tp_itemsize */
50    CThunkObject_dealloc,                       /* tp_dealloc */
51    0,                                          /* tp_print */
52    0,                                          /* tp_getattr */
53    0,                                          /* tp_setattr */
54    0,                                          /* tp_reserved */
55    0,                                          /* tp_repr */
56    0,                                          /* tp_as_number */
57    0,                                          /* tp_as_sequence */
58    0,                                          /* tp_as_mapping */
59    0,                                          /* tp_hash */
60    0,                                          /* tp_call */
61    0,                                          /* tp_str */
62    0,                                          /* tp_getattro */
63    0,                                          /* tp_setattro */
64    0,                                          /* tp_as_buffer */
65    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,                            /* tp_flags */
66    "CThunkObject",                             /* tp_doc */
67    CThunkObject_traverse,                      /* tp_traverse */
68    CThunkObject_clear,                         /* tp_clear */
69    0,                                          /* tp_richcompare */
70    0,                                          /* tp_weaklistoffset */
71    0,                                          /* tp_iter */
72    0,                                          /* tp_iternext */
73    0,                                          /* tp_methods */
74    0,                                          /* tp_members */
75};
76
77/**************************************************************/
78
79static void
80PrintError(const char *msg, ...)
81{
82    char buf[512];
83    PyObject *f = PySys_GetObject("stderr");
84    va_list marker;
85
86    va_start(marker, msg);
87    vsnprintf(buf, sizeof(buf), msg, marker);
88    va_end(marker);
89    if (f != NULL && f != Py_None)
90        PyFile_WriteString(buf, f);
91    PyErr_Print();
92}
93
94
95#ifdef MS_WIN32
96/*
97 * We must call AddRef() on non-NULL COM pointers we receive as arguments
98 * to callback functions - these functions are COM method implementations.
99 * The Python instances we create have a __del__ method which calls Release().
100 *
101 * The presence of a class attribute named '_needs_com_addref_' triggers this
102 * behaviour.  It would also be possible to call the AddRef() Python method,
103 * after checking for PyObject_IsTrue(), but this would probably be somewhat
104 * slower.
105 */
106static void
107TryAddRef(StgDictObject *dict, CDataObject *obj)
108{
109    IUnknown *punk;
110
111    if (NULL == PyDict_GetItemString((PyObject *)dict, "_needs_com_addref_"))
112        return;
113
114    punk = *(IUnknown **)obj->b_ptr;
115    if (punk)
116        punk->lpVtbl->AddRef(punk);
117    return;
118}
119#endif
120
121/******************************************************************************
122 *
123 * Call the python object with all arguments
124 *
125 */
126static void _CallPythonObject(void *mem,
127                              ffi_type *restype,
128                              SETFUNC setfunc,
129                              PyObject *callable,
130                              PyObject *converters,
131                              int flags,
132                              void **pArgs)
133{
134    Py_ssize_t i;
135    PyObject *result;
136    PyObject *arglist = NULL;
137    Py_ssize_t nArgs;
138    PyObject *error_object = NULL;
139    int *space;
140#ifdef WITH_THREAD
141    PyGILState_STATE state = PyGILState_Ensure();
142#endif
143
144    nArgs = PySequence_Length(converters);
145    /* Hm. What to return in case of error?
146       For COM, 0xFFFFFFFF seems better than 0.
147    */
148    if (nArgs < 0) {
149        PrintError("BUG: PySequence_Length");
150        goto Done;
151    }
152
153    arglist = PyTuple_New(nArgs);
154    if (!arglist) {
155        PrintError("PyTuple_New()");
156        goto Done;
157    }
158    for (i = 0; i < nArgs; ++i) {
159        /* Note: new reference! */
160        PyObject *cnv = PySequence_GetItem(converters, i);
161        StgDictObject *dict;
162        if (cnv)
163            dict = PyType_stgdict(cnv);
164        else {
165            PrintError("Getting argument converter %d\n", i);
166            goto Done;
167        }
168
169        if (dict && dict->getfunc && !_ctypes_simple_instance(cnv)) {
170            PyObject *v = dict->getfunc(*pArgs, dict->size);
171            if (!v) {
172                PrintError("create argument %d:\n", i);
173                Py_DECREF(cnv);
174                goto Done;
175            }
176            PyTuple_SET_ITEM(arglist, i, v);
177            /* XXX XXX XX
178               We have the problem that c_byte or c_short have dict->size of
179               1 resp. 4, but these parameters are pushed as sizeof(int) bytes.
180               BTW, the same problem occurs when they are pushed as parameters
181            */
182        } else if (dict) {
183            /* Hm, shouldn't we use PyCData_AtAddress() or something like that instead? */
184            CDataObject *obj = (CDataObject *)PyObject_CallFunctionObjArgs(cnv, NULL);
185            if (!obj) {
186                PrintError("create argument %d:\n", i);
187                Py_DECREF(cnv);
188                goto Done;
189            }
190            if (!CDataObject_Check(obj)) {
191                Py_DECREF(obj);
192                Py_DECREF(cnv);
193                PrintError("unexpected result of create argument %d:\n", i);
194                goto Done;
195            }
196            memcpy(obj->b_ptr, *pArgs, dict->size);
197            PyTuple_SET_ITEM(arglist, i, (PyObject *)obj);
198#ifdef MS_WIN32
199            TryAddRef(dict, obj);
200#endif
201        } else {
202            PyErr_SetString(PyExc_TypeError,
203                            "cannot build parameter");
204            PrintError("Parsing argument %d\n", i);
205            Py_DECREF(cnv);
206            goto Done;
207        }
208        Py_DECREF(cnv);
209        /* XXX error handling! */
210        pArgs++;
211    }
212
213#define CHECK(what, x) \
214if (x == NULL) _PyTraceback_Add(what, "_ctypes/callbacks.c", __LINE__ - 1), PyErr_Print()
215
216    if (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) {
217        error_object = _ctypes_get_errobj(&space);
218        if (error_object == NULL)
219            goto Done;
220        if (flags & FUNCFLAG_USE_ERRNO) {
221            int temp = space[0];
222            space[0] = errno;
223            errno = temp;
224        }
225#ifdef MS_WIN32
226        if (flags & FUNCFLAG_USE_LASTERROR) {
227            int temp = space[1];
228            space[1] = GetLastError();
229            SetLastError(temp);
230        }
231#endif
232    }
233
234    result = PyObject_CallObject(callable, arglist);
235    CHECK("'calling callback function'", result);
236
237#ifdef MS_WIN32
238    if (flags & FUNCFLAG_USE_LASTERROR) {
239        int temp = space[1];
240        space[1] = GetLastError();
241        SetLastError(temp);
242    }
243#endif
244    if (flags & FUNCFLAG_USE_ERRNO) {
245        int temp = space[0];
246        space[0] = errno;
247        errno = temp;
248    }
249    Py_XDECREF(error_object);
250
251    if ((restype != &ffi_type_void) && result) {
252        PyObject *keep;
253        assert(setfunc);
254#ifdef WORDS_BIGENDIAN
255        /* See the corresponding code in callproc.c, around line 961 */
256        if (restype->type != FFI_TYPE_FLOAT && restype->size < sizeof(ffi_arg))
257            mem = (char *)mem + sizeof(ffi_arg) - restype->size;
258#endif
259        keep = setfunc(mem, result, 0);
260        CHECK("'converting callback result'", keep);
261        /* keep is an object we have to keep alive so that the result
262           stays valid.  If there is no such object, the setfunc will
263           have returned Py_None.
264
265           If there is such an object, we have no choice than to keep
266           it alive forever - but a refcount and/or memory leak will
267           be the result.  EXCEPT when restype is py_object - Python
268           itself knows how to manage the refcount of these objects.
269        */
270        if (keep == NULL) /* Could not convert callback result. */
271            PyErr_WriteUnraisable(callable);
272        else if (keep == Py_None) /* Nothing to keep */
273            Py_DECREF(keep);
274        else if (setfunc != _ctypes_get_fielddesc("O")->setfunc) {
275            if (-1 == PyErr_WarnEx(PyExc_RuntimeWarning,
276                                   "memory leak in callback function.",
277                                   1))
278                PyErr_WriteUnraisable(callable);
279        }
280    }
281    Py_XDECREF(result);
282  Done:
283    Py_XDECREF(arglist);
284#ifdef WITH_THREAD
285    PyGILState_Release(state);
286#endif
287}
288
289static void closure_fcn(ffi_cif *cif,
290                        void *resp,
291                        void **args,
292                        void *userdata)
293{
294    CThunkObject *p = (CThunkObject *)userdata;
295
296    _CallPythonObject(resp,
297                      p->ffi_restype,
298                      p->setfunc,
299                      p->callable,
300                      p->converters,
301                      p->flags,
302                      args);
303}
304
305static CThunkObject* CThunkObject_new(Py_ssize_t nArgs)
306{
307    CThunkObject *p;
308    Py_ssize_t i;
309
310    p = PyObject_GC_NewVar(CThunkObject, &PyCThunk_Type, nArgs);
311    if (p == NULL) {
312        PyErr_NoMemory();
313        return NULL;
314    }
315
316    p->pcl_write = NULL;
317    p->pcl_exec = NULL;
318    memset(&p->cif, 0, sizeof(p->cif));
319    p->flags = 0;
320    p->converters = NULL;
321    p->callable = NULL;
322    p->restype = NULL;
323    p->setfunc = NULL;
324    p->ffi_restype = NULL;
325
326    for (i = 0; i < nArgs + 1; ++i)
327        p->atypes[i] = NULL;
328    PyObject_GC_Track((PyObject *)p);
329    return p;
330}
331
332CThunkObject *_ctypes_alloc_callback(PyObject *callable,
333                                    PyObject *converters,
334                                    PyObject *restype,
335                                    int flags)
336{
337    int result;
338    CThunkObject *p;
339    Py_ssize_t nArgs, i;
340    ffi_abi cc;
341
342    nArgs = PySequence_Size(converters);
343    p = CThunkObject_new(nArgs);
344    if (p == NULL)
345        return NULL;
346
347    assert(CThunk_CheckExact((PyObject *)p));
348
349    p->pcl_write = ffi_closure_alloc(sizeof(ffi_closure),
350									 &p->pcl_exec);
351    if (p->pcl_write == NULL) {
352        PyErr_NoMemory();
353        goto error;
354    }
355
356    p->flags = flags;
357    for (i = 0; i < nArgs; ++i) {
358        PyObject *cnv = PySequence_GetItem(converters, i);
359        if (cnv == NULL)
360            goto error;
361        p->atypes[i] = _ctypes_get_ffi_type(cnv);
362        Py_DECREF(cnv);
363    }
364    p->atypes[i] = NULL;
365
366    Py_INCREF(restype);
367    p->restype = restype;
368    if (restype == Py_None) {
369        p->setfunc = NULL;
370        p->ffi_restype = &ffi_type_void;
371    } else {
372        StgDictObject *dict = PyType_stgdict(restype);
373        if (dict == NULL || dict->setfunc == NULL) {
374          PyErr_SetString(PyExc_TypeError,
375                          "invalid result type for callback function");
376          goto error;
377        }
378        p->setfunc = dict->setfunc;
379        p->ffi_restype = &dict->ffi_type_pointer;
380    }
381
382    cc = FFI_DEFAULT_ABI;
383#if defined(MS_WIN32) && !defined(_WIN32_WCE) && !defined(MS_WIN64)
384    if ((flags & FUNCFLAG_CDECL) == 0)
385        cc = FFI_STDCALL;
386#endif
387    result = ffi_prep_cif(&p->cif, cc,
388                          Py_SAFE_DOWNCAST(nArgs, Py_ssize_t, int),
389                          _ctypes_get_ffi_type(restype),
390                          &p->atypes[0]);
391    if (result != FFI_OK) {
392        PyErr_Format(PyExc_RuntimeError,
393                     "ffi_prep_cif failed with %d", result);
394        goto error;
395    }
396#if defined(X86_DARWIN) || defined(POWERPC_DARWIN)
397    result = ffi_prep_closure(p->pcl_write, &p->cif, closure_fcn, p);
398#else
399    result = ffi_prep_closure_loc(p->pcl_write, &p->cif, closure_fcn,
400				  p,
401				  p->pcl_exec);
402#endif
403    if (result != FFI_OK) {
404        PyErr_Format(PyExc_RuntimeError,
405                     "ffi_prep_closure failed with %d", result);
406        goto error;
407    }
408
409    Py_INCREF(converters);
410    p->converters = converters;
411    Py_INCREF(callable);
412    p->callable = callable;
413    return p;
414
415  error:
416    Py_XDECREF(p);
417    return NULL;
418}
419
420#ifdef MS_WIN32
421
422static void LoadPython(void)
423{
424    if (!Py_IsInitialized()) {
425#ifdef WITH_THREAD
426        PyEval_InitThreads();
427#endif
428        Py_Initialize();
429    }
430}
431
432/******************************************************************/
433
434long Call_GetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
435{
436    PyObject *mod, *func, *result;
437    long retval;
438    static PyObject *context;
439
440    if (context == NULL)
441        context = PyUnicode_InternFromString("_ctypes.DllGetClassObject");
442
443    mod = PyImport_ImportModuleNoBlock("ctypes");
444    if (!mod) {
445        PyErr_WriteUnraisable(context ? context : Py_None);
446        /* There has been a warning before about this already */
447        return E_FAIL;
448    }
449
450    func = PyObject_GetAttrString(mod, "DllGetClassObject");
451    Py_DECREF(mod);
452    if (!func) {
453        PyErr_WriteUnraisable(context ? context : Py_None);
454        return E_FAIL;
455    }
456
457    {
458        PyObject *py_rclsid = PyLong_FromVoidPtr((void *)rclsid);
459        PyObject *py_riid = PyLong_FromVoidPtr((void *)riid);
460        PyObject *py_ppv = PyLong_FromVoidPtr(ppv);
461        if (!py_rclsid || !py_riid || !py_ppv) {
462            Py_XDECREF(py_rclsid);
463            Py_XDECREF(py_riid);
464            Py_XDECREF(py_ppv);
465            Py_DECREF(func);
466            PyErr_WriteUnraisable(context ? context : Py_None);
467            return E_FAIL;
468        }
469        result = PyObject_CallFunctionObjArgs(func,
470                                              py_rclsid,
471                                              py_riid,
472                                              py_ppv,
473                                              NULL);
474        Py_DECREF(py_rclsid);
475        Py_DECREF(py_riid);
476        Py_DECREF(py_ppv);
477    }
478    Py_DECREF(func);
479    if (!result) {
480        PyErr_WriteUnraisable(context ? context : Py_None);
481        return E_FAIL;
482    }
483
484    retval = PyLong_AsLong(result);
485    if (PyErr_Occurred()) {
486        PyErr_WriteUnraisable(context ? context : Py_None);
487        retval = E_FAIL;
488    }
489    Py_DECREF(result);
490    return retval;
491}
492
493STDAPI DllGetClassObject(REFCLSID rclsid,
494                         REFIID riid,
495                         LPVOID *ppv)
496{
497    long result;
498#ifdef WITH_THREAD
499    PyGILState_STATE state;
500#endif
501
502    LoadPython();
503#ifdef WITH_THREAD
504    state = PyGILState_Ensure();
505#endif
506    result = Call_GetClassObject(rclsid, riid, ppv);
507#ifdef WITH_THREAD
508    PyGILState_Release(state);
509#endif
510    return result;
511}
512
513long Call_CanUnloadNow(void)
514{
515    PyObject *mod, *func, *result;
516    long retval;
517    static PyObject *context;
518
519    if (context == NULL)
520        context = PyUnicode_InternFromString("_ctypes.DllCanUnloadNow");
521
522    mod = PyImport_ImportModuleNoBlock("ctypes");
523    if (!mod) {
524/*              OutputDebugString("Could not import ctypes"); */
525        /* We assume that this error can only occur when shutting
526           down, so we silently ignore it */
527        PyErr_Clear();
528        return E_FAIL;
529    }
530    /* Other errors cannot be raised, but are printed to stderr */
531    func = PyObject_GetAttrString(mod, "DllCanUnloadNow");
532    Py_DECREF(mod);
533    if (!func) {
534        PyErr_WriteUnraisable(context ? context : Py_None);
535        return E_FAIL;
536    }
537
538    result = PyObject_CallFunction(func, NULL);
539    Py_DECREF(func);
540    if (!result) {
541        PyErr_WriteUnraisable(context ? context : Py_None);
542        return E_FAIL;
543    }
544
545    retval = PyLong_AsLong(result);
546    if (PyErr_Occurred()) {
547        PyErr_WriteUnraisable(context ? context : Py_None);
548        retval = E_FAIL;
549    }
550    Py_DECREF(result);
551    return retval;
552}
553
554/*
555  DllRegisterServer and DllUnregisterServer still missing
556*/
557
558STDAPI DllCanUnloadNow(void)
559{
560    long result;
561#ifdef WITH_THREAD
562    PyGILState_STATE state = PyGILState_Ensure();
563#endif
564    result = Call_CanUnloadNow();
565#ifdef WITH_THREAD
566    PyGILState_Release(state);
567#endif
568    return result;
569}
570
571#ifndef Py_NO_ENABLE_SHARED
572BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvRes)
573{
574    switch(fdwReason) {
575    case DLL_PROCESS_ATTACH:
576        DisableThreadLibraryCalls(hinstDLL);
577        break;
578    }
579    return TRUE;
580}
581#endif
582
583#endif
584
585/*
586 Local Variables:
587 compile-command: "cd .. && python setup.py -q build_ext"
588 End:
589*/
590