blob: 591b944d76fcbef77a34646f1fc05ede1dfc345e [file] [log] [blame]
#ifndef Py_BUILD_CORE_BUILTIN
# define Py_BUILD_CORE_MODULE 1
#endif
#define NEEDS_PY_IDENTIFIER
#include "Python.h"
// windows.h must be included before pycore internal headers
#ifdef MS_WIN32
# include <windows.h>
#endif
#include "pycore_call.h" // _PyObject_CallNoArgs()
#include "frameobject.h"
#include <stdbool.h>
#ifdef MS_WIN32
# include <malloc.h>
#endif
#include <ffi.h>
#include "ctypes.h"
#ifdef HAVE_ALLOCA_H
/* AIX needs alloca.h for alloca() */
#include <alloca.h>
#endif
/**************************************************************/
static void
CThunkObject_dealloc(PyObject *myself)
{
CThunkObject *self = (CThunkObject *)myself;
PyObject_GC_UnTrack(self);
Py_XDECREF(self->converters);
Py_XDECREF(self->callable);
Py_XDECREF(self->restype);
if (self->pcl_write)
Py_ffi_closure_free(self->pcl_write);
PyObject_GC_Del(self);
}
static int
CThunkObject_traverse(PyObject *myself, visitproc visit, void *arg)
{
CThunkObject *self = (CThunkObject *)myself;
Py_VISIT(self->converters);
Py_VISIT(self->callable);
Py_VISIT(self->restype);
return 0;
}
static int
CThunkObject_clear(PyObject *myself)
{
CThunkObject *self = (CThunkObject *)myself;
Py_CLEAR(self->converters);
Py_CLEAR(self->callable);
Py_CLEAR(self->restype);
return 0;
}
PyTypeObject PyCThunk_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"_ctypes.CThunkObject",
sizeof(CThunkObject), /* tp_basicsize */
sizeof(ffi_type), /* tp_itemsize */
CThunkObject_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
"CThunkObject", /* tp_doc */
CThunkObject_traverse, /* tp_traverse */
CThunkObject_clear, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
};
/**************************************************************/
static void
PrintError(const char *msg, ...)
{
char buf[512];
PyObject *f = PySys_GetObject("stderr");
va_list marker;
va_start(marker, msg);
PyOS_vsnprintf(buf, sizeof(buf), msg, marker);
va_end(marker);
if (f != NULL && f != Py_None)
PyFile_WriteString(buf, f);
PyErr_Print();
}
#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;
_Py_IDENTIFIER(_needs_com_addref_);
int r = _PyDict_ContainsId((PyObject *)dict, &PyId__needs_com_addref_);
if (r <= 0) {
if (r < 0) {
PrintError("getting _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,
int flags,
void **pArgs)
{
PyObject *result = NULL;
Py_ssize_t i = 0, j = 0, nargs = 0;
PyObject *error_object = NULL;
int *space;
PyGILState_STATE state = PyGILState_Ensure();
assert(PyTuple_Check(converters));
nargs = PyTuple_GET_SIZE(converters);
assert(nargs <= CTYPES_MAX_ARGCOUNT);
PyObject **args = NULL;
if (nargs > 0) {
args = alloca(nargs * sizeof(PyObject *));
}
PyObject **cnvs = PySequence_Fast_ITEMS(converters);
for (i = 0; i < nargs; i++) {
PyObject *cnv = cnvs[i]; // borrowed ref
StgDictObject *dict;
dict = PyType_stgdict(cnv);
if (dict && dict->getfunc && !_ctypes_simple_instance(cnv)) {
PyObject *v = dict->getfunc(*pArgs, dict->size);
if (!v) {
PrintError("create argument %zd:\n", i);
goto Done;
}
args[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 occurs when they are pushed as parameters
*/
} else if (dict) {
/* Hm, shouldn't we use PyCData_AtAddress() or something like that instead? */
CDataObject *obj = (CDataObject *)_PyObject_CallNoArgs(cnv);
if (!obj) {
PrintError("create argument %zd:\n", i);
Py_DECREF(cnv);
goto Done;
}
if (!CDataObject_Check(obj)) {
Py_DECREF(obj);
PrintError("unexpected result of create argument %zd:\n", i);
goto Done;
}
memcpy(obj->b_ptr, *pArgs, dict->size);
args[i] = (PyObject *)obj;
#ifdef MS_WIN32
TryAddRef(dict, obj);
#endif
} else {
PyErr_SetString(PyExc_TypeError,
"cannot build parameter");
PrintError("Parsing argument %zd\n", i);
goto Done;
}
/* XXX error handling! */
pArgs++;
}
if (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) {
error_object = _ctypes_get_errobj(&space);
if (error_object == NULL)
goto Done;
if (flags & FUNCFLAG_USE_ERRNO) {
int temp = space[0];
space[0] = errno;
errno = temp;
}
#ifdef MS_WIN32
if (flags & FUNCFLAG_USE_LASTERROR) {
int temp = space[1];
space[1] = GetLastError();
SetLastError(temp);
}
#endif
}
result = PyObject_Vectorcall(callable, args, nargs, NULL);
if (result == NULL) {
_PyErr_WriteUnraisableMsg("on calling ctypes callback function",
callable);
}
#ifdef MS_WIN32
if (flags & FUNCFLAG_USE_LASTERROR) {
int temp = space[1];
space[1] = GetLastError();
SetLastError(temp);
}
#endif
if (flags & FUNCFLAG_USE_ERRNO) {
int temp = space[0];
space[0] = errno;
errno = temp;
}
Py_XDECREF(error_object);
if (restype != &ffi_type_void && result) {
assert(setfunc);
#ifdef WORDS_BIGENDIAN
/* See the corresponding code in _ctypes_callproc():
in callproc.c, around line 1219. */
if (restype->type != FFI_TYPE_FLOAT && restype->size < sizeof(ffi_arg)) {
mem = (char *)mem + sizeof(ffi_arg) - restype->size;
}
#endif
/* 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.
*/
PyObject *keep = setfunc(mem, result, 0);
if (keep == NULL) {
/* Could not convert callback result. */
_PyErr_WriteUnraisableMsg("on converting result "
"of ctypes callback function",
callable);
}
else if (keep == Py_None) {
/* Nothing to keep */
Py_DECREF(keep);
}
else if (setfunc != _ctypes_get_fielddesc("O")->setfunc) {
if (-1 == PyErr_WarnEx(PyExc_RuntimeWarning,
"memory leak in callback function.",
1))
{
_PyErr_WriteUnraisableMsg("on converting result "
"of ctypes callback function",
callable);
}
}
}
Py_XDECREF(result);
Done:
for (j = 0; j < i; j++) {
Py_DECREF(args[j]);
}
PyGILState_Release(state);
}
static void closure_fcn(ffi_cif *cif,
void *resp,
void **args,
void *userdata)
{
CThunkObject *p = (CThunkObject *)userdata;
_CallPythonObject(resp,
p->ffi_restype,
p->setfunc,
p->callable,
p->converters,
p->flags,
args);
}
static CThunkObject* CThunkObject_new(Py_ssize_t nargs)
{
CThunkObject *p;
Py_ssize_t i;
p = PyObject_GC_NewVar(CThunkObject, &PyCThunk_Type, nargs);
if (p == NULL) {
return NULL;
}
p->pcl_write = NULL;
p->pcl_exec = NULL;
memset(&p->cif, 0, sizeof(p->cif));
p->flags = 0;
p->converters = NULL;
p->callable = NULL;
p->restype = NULL;
p->setfunc = NULL;
p->ffi_restype = NULL;
for (i = 0; i < nargs + 1; ++i)
p->atypes[i] = NULL;
PyObject_GC_Track((PyObject *)p);
return p;
}
CThunkObject *_ctypes_alloc_callback(PyObject *callable,
PyObject *converters,
PyObject *restype,
int flags)
{
int result;
CThunkObject *p;
Py_ssize_t nargs, i;
ffi_abi cc;
assert(PyTuple_Check(converters));
nargs = PyTuple_GET_SIZE(converters);
p = CThunkObject_new(nargs);
if (p == NULL)
return NULL;
assert(CThunk_CheckExact((PyObject *)p));
p->pcl_write = Py_ffi_closure_alloc(sizeof(ffi_closure), &p->pcl_exec);
if (p->pcl_write == NULL) {
PyErr_NoMemory();
goto error;
}
p->flags = flags;
PyObject **cnvs = PySequence_Fast_ITEMS(converters);
for (i = 0; i < nargs; ++i) {
PyObject *cnv = cnvs[i]; // borrowed ref
p->atypes[i] = _ctypes_get_ffi_type(cnv);
}
p->atypes[i] = NULL;
Py_INCREF(restype);
p->restype = restype;
if (restype == Py_None) {
p->setfunc = NULL;
p->ffi_restype = &ffi_type_void;
} else {
StgDictObject *dict = PyType_stgdict(restype);
if (dict == NULL || dict->setfunc == NULL) {
PyErr_SetString(PyExc_TypeError,
"invalid result type for callback function");
goto error;
}
p->setfunc = dict->setfunc;
p->ffi_restype = &dict->ffi_type_pointer;
}
cc = FFI_DEFAULT_ABI;
#if defined(MS_WIN32) && !defined(_WIN32_WCE) && !defined(MS_WIN64) && !defined(_M_ARM)
if ((flags & FUNCFLAG_CDECL) == 0)
cc = FFI_STDCALL;
#endif
result = ffi_prep_cif(&p->cif, cc,
Py_SAFE_DOWNCAST(nargs, Py_ssize_t, int),
_ctypes_get_ffi_type(restype),
&p->atypes[0]);
if (result != FFI_OK) {
PyErr_Format(PyExc_RuntimeError,
"ffi_prep_cif failed with %d", result);
goto error;
}
#if HAVE_FFI_PREP_CLOSURE_LOC
# ifdef USING_APPLE_OS_LIBFFI
# define HAVE_FFI_PREP_CLOSURE_LOC_RUNTIME __builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)
# else
# define HAVE_FFI_PREP_CLOSURE_LOC_RUNTIME 1
# endif
if (HAVE_FFI_PREP_CLOSURE_LOC_RUNTIME) {
result = ffi_prep_closure_loc(p->pcl_write, &p->cif, closure_fcn,
p,
p->pcl_exec);
} else
#endif
{
#if defined(USING_APPLE_OS_LIBFFI) && defined(__arm64__)
PyErr_Format(PyExc_NotImplementedError, "ffi_prep_closure_loc() is missing");
goto error;
#else
#if defined(__clang__) || defined(MACOSX)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
#if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
result = ffi_prep_closure(p->pcl_write, &p->cif, closure_fcn, p);
#if defined(__clang__) || defined(MACOSX)
#pragma clang diagnostic pop
#endif
#if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))
#pragma GCC diagnostic pop
#endif
#endif
}
if (result != FFI_OK) {
PyErr_Format(PyExc_RuntimeError,
"ffi_prep_closure failed with %d", result);
goto error;
}
Py_INCREF(converters);
p->converters = converters;
Py_INCREF(callable);
p->callable = callable;
return p;
error:
Py_XDECREF(p);
return NULL;
}
#ifdef MS_WIN32
static void LoadPython(void)
{
if (!Py_IsInitialized()) {
Py_Initialize();
}
}
/******************************************************************/
long Call_GetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
{
PyObject *mod, *func, *result;
long retval;
static PyObject *context;
if (context == NULL)
context = PyUnicode_InternFromString("_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;
}
{
PyObject *py_rclsid = PyLong_FromVoidPtr((void *)rclsid);
PyObject *py_riid = PyLong_FromVoidPtr((void *)riid);
PyObject *py_ppv = PyLong_FromVoidPtr(ppv);
if (!py_rclsid || !py_riid || !py_ppv) {
Py_XDECREF(py_rclsid);
Py_XDECREF(py_riid);
Py_XDECREF(py_ppv);
Py_DECREF(func);
PyErr_WriteUnraisable(context ? context : Py_None);
return E_FAIL;
}
result = PyObject_CallFunctionObjArgs(func,
py_rclsid,
py_riid,
py_ppv,
NULL);
Py_DECREF(py_rclsid);
Py_DECREF(py_riid);
Py_DECREF(py_ppv);
}
Py_DECREF(func);
if (!result) {
PyErr_WriteUnraisable(context ? context : Py_None);
return E_FAIL;
}
retval = PyLong_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 = PyUnicode_InternFromString("_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_CallNoArgs(func);
Py_DECREF(func);
if (!result) {
PyErr_WriteUnraisable(context ? context : Py_None);
return E_FAIL;
}
retval = PyLong_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:
*/