summaryrefslogtreecommitdiff
path: root/c/_cffi_backend.c
diff options
context:
space:
mode:
Diffstat (limited to 'c/_cffi_backend.c')
-rw-r--r--c/_cffi_backend.c803
1 files changed, 600 insertions, 203 deletions
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
index c39866a..ffecbf9 100644
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -2,7 +2,7 @@
#include <Python.h>
#include "structmember.h"
-#define CFFI_VERSION "1.12.2"
+#define CFFI_VERSION "1.15.0"
#ifdef MS_WIN32
#include <windows.h>
@@ -80,18 +80,46 @@
* That sounds like a horribly bad idea to me, and is the reason for why
* I prefer CFFI crashing cleanly.
*
- * Currently, we use libffi's ffi_closure_alloc() only on NetBSD. It is
+ * Currently, we use libffi's ffi_closure_alloc() on NetBSD. It is
* known that on the NetBSD kernel, a different strategy is used which
* should not be open to the fork() bug.
+ *
+ * This is also used on macOS, provided we are executing on macOS 10.15 or
+ * above. It's a mess because it needs runtime checks in that case.
*/
#ifdef __NetBSD__
-# define CFFI_TRUST_LIBFFI
-#endif
-#ifndef CFFI_TRUST_LIBFFI
-# include "malloc_closure.h"
+# define CFFI_CHECK_FFI_CLOSURE_ALLOC 1
+# define CFFI_CHECK_FFI_CLOSURE_ALLOC_MAYBE 1
+# define CFFI_CHECK_FFI_PREP_CLOSURE_LOC 1
+# define CFFI_CHECK_FFI_PREP_CLOSURE_LOC_MAYBE 1
+# define CFFI_CHECK_FFI_PREP_CIF_VAR 0
+# define CFFI_CHECK_FFI_PREP_CIF_VAR_MAYBE 0
+
+#elif defined(__APPLE__) && defined(FFI_AVAILABLE_APPLE)
+
+# define CFFI_CHECK_FFI_CLOSURE_ALLOC __builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)
+# define CFFI_CHECK_FFI_CLOSURE_ALLOC_MAYBE 1
+# define CFFI_CHECK_FFI_PREP_CLOSURE_LOC __builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)
+# define CFFI_CHECK_FFI_PREP_CLOSURE_LOC_MAYBE 1
+# define CFFI_CHECK_FFI_PREP_CIF_VAR __builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)
+# define CFFI_CHECK_FFI_PREP_CIF_VAR_MAYBE 1
+
+#else
+
+# define CFFI_CHECK_FFI_CLOSURE_ALLOC 0
+# define CFFI_CHECK_FFI_CLOSURE_ALLOC_MAYBE 0
+# define CFFI_CHECK_FFI_PREP_CLOSURE_LOC 0
+# define CFFI_CHECK_FFI_PREP_CLOSURE_LOC_MAYBE 0
+# define CFFI_CHECK_FFI_PREP_CIF_VAR 0
+# define CFFI_CHECK_FFI_PREP_CIF_VAR_MAYBE 0
+
#endif
+/* always includes this, even if it turns out not to be used on NetBSD
+ because calls are behind "if (0)" */
+#include "malloc_closure.h"
+
#if PY_MAJOR_VERSION >= 3
# define STR_OR_BYTES "bytes"
@@ -148,6 +176,14 @@
(PyCObject_FromVoidPtr(pointer, destructor))
#endif
+#if PY_VERSION_HEX < 0x030900a4
+# define Py_SET_REFCNT(obj, val) (Py_REFCNT(obj) = (val))
+#endif
+
+#if PY_VERSION_HEX >= 0x03080000
+# define USE_WRITEUNRAISABLEMSG
+#endif
+
/************************************************************/
/* base type flag: exactly one of the following: */
@@ -174,7 +210,7 @@
#define CT_IS_BOOL 0x00080000
#define CT_IS_FILE 0x00100000
#define CT_IS_VOID_PTR 0x00200000
-#define CT_WITH_VAR_ARRAY 0x00400000
+#define CT_WITH_VAR_ARRAY 0x00400000 /* with open-ended array, anywhere */
/* unused 0x00800000 */
#define CT_LAZY_FIELD_LIST 0x01000000
#define CT_WITH_PACKED_CHANGE 0x02000000
@@ -238,12 +274,14 @@ static PyTypeObject CField_Type;
static PyTypeObject CData_Type;
static PyTypeObject CDataOwning_Type;
static PyTypeObject CDataOwningGC_Type;
+static PyTypeObject CDataFromBuf_Type;
static PyTypeObject CDataGCP_Type;
#define CTypeDescr_Check(ob) (Py_TYPE(ob) == &CTypeDescr_Type)
#define CData_Check(ob) (Py_TYPE(ob) == &CData_Type || \
Py_TYPE(ob) == &CDataOwning_Type || \
Py_TYPE(ob) == &CDataOwningGC_Type || \
+ Py_TYPE(ob) == &CDataFromBuf_Type || \
Py_TYPE(ob) == &CDataGCP_Type)
#define CDataOwn_Check(ob) (Py_TYPE(ob) == &CDataOwning_Type || \
Py_TYPE(ob) == &CDataOwningGC_Type)
@@ -277,14 +315,14 @@ typedef struct {
typedef struct {
CDataObject head;
- PyObject *structobj;
+ PyObject *structobj; /* for ffi.new_handle() or ffi.new("struct *") */
} CDataObject_own_structptr;
typedef struct {
CDataObject head;
Py_ssize_t length; /* same as CDataObject_own_length up to here */
Py_buffer *bufferview;
-} CDataObject_owngc_frombuf;
+} CDataObject_frombuf;
typedef struct {
CDataObject head;
@@ -402,10 +440,10 @@ ctypedescr_dealloc(CTypeDescrObject *ct)
if (ct->ct_unique_key != NULL) {
/* revive dead object temporarily for DelItem */
- Py_REFCNT(ct) = 43;
+ Py_SET_REFCNT(ct, 43);
PyDict_DelItem(unique_cache, ct->ct_unique_key);
assert(Py_REFCNT(ct) == 42);
- Py_REFCNT(ct) = 0;
+ Py_SET_REFCNT(ct, 0);
Py_DECREF(ct->ct_unique_key);
}
Py_XDECREF(ct->ct_itemdescr);
@@ -651,7 +689,7 @@ static PyMethodDef ctypedescr_methods[] = {
static PyTypeObject CTypeDescr_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
- "_cffi_backend.CTypeDescr",
+ "_cffi_backend.CType",
offsetof(CTypeDescrObject, ct_name),
sizeof(char),
(destructor)ctypedescr_dealloc, /* tp_dealloc */
@@ -1331,6 +1369,29 @@ convert_field_from_object(char *data, CFieldObject *cf, PyObject *value)
}
static int
+add_varsize_length(Py_ssize_t offset, Py_ssize_t itemsize,
+ Py_ssize_t varsizelength, Py_ssize_t *optvarsize)
+{
+ /* update '*optvarsize' to account for an array of 'varsizelength'
+ elements, each of size 'itemsize', that starts at 'offset'. */
+ Py_ssize_t size = ADD_WRAPAROUND(offset,
+ MUL_WRAPAROUND(itemsize, varsizelength));
+ if (size < 0 ||
+ ((size - offset) / itemsize) != varsizelength) {
+ PyErr_SetString(PyExc_OverflowError,
+ "array size would overflow a Py_ssize_t");
+ return -1;
+ }
+ if (size > *optvarsize)
+ *optvarsize = size;
+ return 0;
+}
+
+static int
+convert_struct_from_object(char *data, CTypeDescrObject *ct, PyObject *init,
+ Py_ssize_t *optvarsize); /* forward */
+
+static int
convert_vfield_from_object(char *data, CFieldObject *cf, PyObject *value,
Py_ssize_t *optvarsize)
{
@@ -1343,20 +1404,11 @@ convert_vfield_from_object(char *data, CFieldObject *cf, PyObject *value,
if (optvarsize != NULL) {
/* in this mode, the only purpose of this function is to compute
the real size of the structure from a var-sized C99 array */
- Py_ssize_t size, itemsize;
assert(data == NULL);
- itemsize = cf->cf_type->ct_itemdescr->ct_size;
- size = ADD_WRAPAROUND(cf->cf_offset,
- MUL_WRAPAROUND(itemsize, varsizelength));
- if (size < 0 ||
- ((size - cf->cf_offset) / itemsize) != varsizelength) {
- PyErr_SetString(PyExc_OverflowError,
- "array size would overflow a Py_ssize_t");
- return -1;
- }
- if (size > *optvarsize)
- *optvarsize = size;
- return 0;
+ return add_varsize_length(cf->cf_offset,
+ cf->cf_type->ct_itemdescr->ct_size,
+ varsizelength,
+ optvarsize);
}
/* if 'value' was only an integer, get_new_array_length() returns
it and convert 'value' to be None. Detect if this was the case,
@@ -1365,8 +1417,16 @@ convert_vfield_from_object(char *data, CFieldObject *cf, PyObject *value,
if (value == Py_None)
return 0;
}
- if (optvarsize == NULL)
+ if (optvarsize == NULL) {
return convert_field_from_object(data, cf, value);
+ }
+ else if ((cf->cf_type->ct_flags & CT_WITH_VAR_ARRAY) != 0 &&
+ !CData_Check(value)) {
+ Py_ssize_t subsize = cf->cf_type->ct_size;
+ if (convert_struct_from_object(NULL, cf->cf_type, value, &subsize) < 0)
+ return -1;
+ return add_varsize_length(cf->cf_offset, 1, subsize, optvarsize);
+ }
else
return 0;
}
@@ -1831,6 +1891,7 @@ static void cdataowning_dealloc(CDataObject *cd)
assert(!(cd->c_type->ct_flags & (CT_IS_VOID_PTR | CT_FUNCTIONPTR)));
if (cd->c_type->ct_flags & CT_IS_PTR_TO_OWNED) {
+ /* for ffi.new("struct *") */
Py_DECREF(((CDataObject_own_structptr *)cd)->structobj);
}
#if defined(CFFI_MEM_DEBUG) || defined(CFFI_MEM_LEAK)
@@ -1851,9 +1912,6 @@ static void cdataowning_dealloc(CDataObject *cd)
static void cdataowninggc_dealloc(CDataObject *cd)
{
- assert(!(cd->c_type->ct_flags & (CT_IS_PTR_TO_OWNED |
- CT_PRIMITIVE_ANY |
- CT_STRUCT | CT_UNION)));
PyObject_GC_UnTrack(cd);
if (cd->c_type->ct_flags & CT_IS_VOID_PTR) { /* a handle */
@@ -1864,20 +1922,28 @@ static void cdataowninggc_dealloc(CDataObject *cd)
ffi_closure *closure = ((CDataObject_closure *)cd)->closure;
PyObject *args = (PyObject *)(closure->user_data);
Py_XDECREF(args);
-#ifdef CFFI_TRUST_LIBFFI
- ffi_closure_free(closure);
-#else
- cffi_closure_free(closure);
+#if CFFI_CHECK_FFI_CLOSURE_ALLOC_MAYBE
+ if (CFFI_CHECK_FFI_CLOSURE_ALLOC) {
+ ffi_closure_free(closure);
+ } else
#endif
+ cffi_closure_free(closure);
}
- else if (cd->c_type->ct_flags & CT_ARRAY) { /* from_buffer */
- Py_buffer *view = ((CDataObject_owngc_frombuf *)cd)->bufferview;
- PyBuffer_Release(view);
- PyObject_Free(view);
+ else {
+ Py_FatalError("cdata CDataOwningGC_Type with unexpected type flags");
}
cdata_dealloc(cd);
}
+static void cdatafrombuf_dealloc(CDataObject *cd)
+{
+ Py_buffer *view = ((CDataObject_frombuf *)cd)->bufferview;
+ cdata_dealloc(cd);
+
+ PyBuffer_Release(view);
+ PyObject_Free(view);
+}
+
static int cdataowninggc_traverse(CDataObject *cd, visitproc visit, void *arg)
{
if (cd->c_type->ct_flags & CT_IS_VOID_PTR) { /* a handle */
@@ -1889,10 +1955,13 @@ static int cdataowninggc_traverse(CDataObject *cd, visitproc visit, void *arg)
PyObject *args = (PyObject *)(closure->user_data);
Py_VISIT(args);
}
- else if (cd->c_type->ct_flags & CT_ARRAY) { /* from_buffer */
- Py_buffer *view = ((CDataObject_owngc_frombuf *)cd)->bufferview;
- Py_VISIT(view->obj);
- }
+ return 0;
+}
+
+static int cdatafrombuf_traverse(CDataObject *cd, visitproc visit, void *arg)
+{
+ Py_buffer *view = ((CDataObject_frombuf *)cd)->bufferview;
+ Py_VISIT(view->obj);
return 0;
}
@@ -1911,10 +1980,13 @@ static int cdataowninggc_clear(CDataObject *cd)
closure->user_data = NULL;
Py_XDECREF(args);
}
- else if (cd->c_type->ct_flags & CT_ARRAY) { /* from_buffer */
- Py_buffer *view = ((CDataObject_owngc_frombuf *)cd)->bufferview;
- PyBuffer_Release(view);
- }
+ return 0;
+}
+
+static int cdatafrombuf_clear(CDataObject *cd)
+{
+ Py_buffer *view = ((CDataObject_frombuf *)cd)->bufferview;
+ PyBuffer_Release(view);
return 0;
}
@@ -2096,6 +2168,35 @@ static Py_ssize_t _cdata_var_byte_size(CDataObject *cd)
return -1;
}
+static PyObject *_frombuf_repr(CDataObject *cd, const char *cd_type_name)
+{
+ Py_buffer *view = ((CDataObject_frombuf *)cd)->bufferview;
+ const char *obj_tp_name;
+ if (view->obj == NULL) {
+ return PyText_FromFormat(
+ "<cdata '%s' buffer RELEASED>",
+ cd_type_name);
+ }
+
+ obj_tp_name = Py_TYPE(view->obj)->tp_name;
+ if (cd->c_type->ct_flags & CT_ARRAY)
+ {
+ Py_ssize_t buflen = get_array_length(cd);
+ return PyText_FromFormat(
+ "<cdata '%s' buffer len %zd from '%.200s' object>",
+ cd_type_name,
+ buflen,
+ obj_tp_name);
+ }
+ else
+ {
+ return PyText_FromFormat(
+ "<cdata '%s' buffer from '%.200s' object>",
+ cd_type_name,
+ obj_tp_name);
+ }
+}
+
static PyObject *cdataowning_repr(CDataObject *cd)
{
Py_ssize_t size = _cdata_var_byte_size(cd);
@@ -2125,16 +2226,12 @@ static PyObject *cdataowninggc_repr(CDataObject *cd)
else
return _cdata_repr2(cd, "calling", PyTuple_GET_ITEM(args, 1));
}
- else if (cd->c_type->ct_flags & CT_ARRAY) { /* from_buffer */
- Py_buffer *view = ((CDataObject_owngc_frombuf *)cd)->bufferview;
- Py_ssize_t buflen = get_array_length(cd);
- return PyText_FromFormat(
- "<cdata '%s' buffer len %zd from '%.200s' object>",
- cd->c_type->ct_name,
- buflen,
- view->obj ? Py_TYPE(view->obj)->tp_name : "(null)");
- }
- return cdataowning_repr(cd);
+ return cdataowning_repr(cd); /* but should be unreachable */
+}
+
+static PyObject *cdatafrombuf_repr(CDataObject *cd)
+{
+ return _frombuf_repr(cd, cd->c_type->ct_name);
}
static int cdata_nonzero(CDataObject *cd)
@@ -2171,7 +2268,10 @@ static PyObject *cdata_int(CDataObject *cd)
return PyInt_FromLong(value);
}
if (cd->c_type->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_UNSIGNED)) {
- return convert_to_object(cd->c_data, cd->c_type);
+ PyObject *result = convert_to_object(cd->c_data, cd->c_type);
+ if (result != NULL && PyBool_Check(result))
+ result = PyInt_FromLong(PyInt_AsLong(result));
+ return result;
}
else if (cd->c_type->ct_flags & CT_PRIMITIVE_CHAR) {
/*READ(cd->c_data, cd->c_type->ct_size)*/
@@ -2313,7 +2413,11 @@ static PyObject *cdata_richcompare(PyObject *v, PyObject *w, int op)
return pyres;
}
-static long cdata_hash(CDataObject *v)
+#if PY_MAJOR_VERSION < 3
+typedef long Py_hash_t;
+#endif
+
+static Py_hash_t cdata_hash(PyObject *v)
{
if (((CDataObject *)v)->c_type->ct_flags & CT_PRIMITIVE_ANY) {
PyObject *vv = convert_to_object(((CDataObject *)v)->c_data,
@@ -2321,13 +2425,13 @@ static long cdata_hash(CDataObject *v)
if (vv == NULL)
return -1;
if (!CData_Check(vv)) {
- long hash = PyObject_Hash(vv);
+ Py_hash_t hash = PyObject_Hash(vv);
Py_DECREF(vv);
return hash;
}
Py_DECREF(vv);
}
- return _Py_HashPointer(v->c_data);
+ return _Py_HashPointer(((CDataObject *)v)->c_data);
}
static Py_ssize_t
@@ -2821,7 +2925,8 @@ static PyObject *
convert_struct_to_owning_object(char *data, CTypeDescrObject *ct); /*forward*/
static cif_description_t *
-fb_prepare_cif(PyObject *fargs, CTypeDescrObject *, ffi_abi); /*forward*/
+fb_prepare_cif(PyObject *fargs, CTypeDescrObject *, Py_ssize_t, ffi_abi);
+ /*forward*/
static PyObject *new_primitive_type(const char *name); /*forward*/
@@ -2924,12 +3029,22 @@ cdata_call(CDataObject *cd, PyObject *args, PyObject *kwds)
CTypeDescrObject *fresult;
char *resultdata;
char *errormsg;
+ struct freeme_s {
+ struct freeme_s *next;
+ union_alignment alignment;
+ } *freeme = NULL;
if (!(cd->c_type->ct_flags & CT_FUNCTIONPTR)) {
PyErr_Format(PyExc_TypeError, "cdata '%s' is not callable",
cd->c_type->ct_name);
return NULL;
}
+ if (cd->c_data == NULL) {
+ PyErr_Format(PyExc_RuntimeError,
+ "cannot call null pointer pointer from cdata '%s'",
+ cd->c_type->ct_name);
+ return NULL;
+ }
if (kwds != NULL && PyDict_Size(kwds) != 0) {
PyErr_SetString(PyExc_TypeError,
"a cdata function cannot be called with keyword arguments");
@@ -3004,7 +3119,7 @@ cdata_call(CDataObject *cd, PyObject *args, PyObject *kwds)
#else
fabi = PyLong_AS_LONG(PyTuple_GET_ITEM(signature, 0));
#endif
- cif_descr = fb_prepare_cif(fvarargs, fresult, fabi);
+ cif_descr = fb_prepare_cif(fvarargs, fresult, nargs_declared, fabi);
if (cif_descr == NULL)
goto error;
}
@@ -3038,7 +3153,21 @@ cdata_call(CDataObject *cd, PyObject *args, PyObject *kwds)
else if (datasize < 0)
goto error;
else {
- tmpbuf = alloca(datasize);
+ if (datasize <= 512) {
+ tmpbuf = alloca(datasize);
+ }
+ else {
+ struct freeme_s *fp = (struct freeme_s *)PyObject_Malloc(
+ offsetof(struct freeme_s, alignment) +
+ (size_t)datasize);
+ if (fp == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+ fp->next = freeme;
+ freeme = fp;
+ tmpbuf = (char *)&fp->alignment;
+ }
memset(tmpbuf, 0, datasize);
*(char **)data = tmpbuf;
if (convert_array_from_object(tmpbuf, argtype, obj) < 0)
@@ -3083,6 +3212,11 @@ cdata_call(CDataObject *cd, PyObject *args, PyObject *kwds)
/* fall-through */
error:
+ while (freeme != NULL) {
+ void *p = (void *)freeme;
+ freeme = freeme->next;
+ PyObject_Free(p);
+ }
if (buffer)
PyObject_Free(buffer);
if (fvarargs != NULL) {
@@ -3139,9 +3273,8 @@ static int explicit_release_case(PyObject *cd)
if ((ct->ct_flags & (CT_POINTER | CT_ARRAY)) != 0) /* ffi.new() */
return 0;
}
- else if (Py_TYPE(cd) == &CDataOwningGC_Type) {
- if (ct->ct_flags & CT_ARRAY) /* ffi.from_buffer() */
- return 1;
+ else if (Py_TYPE(cd) == &CDataFromBuf_Type) {
+ return 1; /* ffi.from_buffer() */
}
else if (Py_TYPE(cd) == &CDataGCP_Type) {
return 2; /* ffi.gc() */
@@ -3178,14 +3311,14 @@ static PyObject *cdata_exit(PyObject *cd, PyObject *args)
PyObject *x = ((CDataObject_own_structptr *)cd)->structobj;
if (Py_TYPE(x) == &CDataGCP_Type) {
/* this is a special case for
- ffi.new_allocator()("struct-or-union") */
+ ffi.new_allocator()("struct-or-union *") */
cdatagcp_finalize((CDataObject_gcp *)x);
}
}
break;
case 1: /* ffi.from_buffer() */
- view = ((CDataObject_owngc_frombuf *)cd)->bufferview;
+ view = ((CDataObject_frombuf *)cd)->bufferview;
PyBuffer_Release(view);
break;
@@ -3259,7 +3392,7 @@ static PyMethodDef cdata_methods[] = {
static PyTypeObject CData_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
- "_cffi_backend.CData",
+ "_cffi_backend._CDataBase",
sizeof(CDataObject),
0,
(destructor)cdata_dealloc, /* tp_dealloc */
@@ -3271,14 +3404,16 @@ static PyTypeObject CData_Type = {
&CData_as_number, /* tp_as_number */
0, /* tp_as_sequence */
&CData_as_mapping, /* tp_as_mapping */
- (hashfunc)cdata_hash, /* tp_hash */
+ cdata_hash, /* tp_hash */
(ternaryfunc)cdata_call, /* tp_call */
0, /* tp_str */
(getattrofunc)cdata_getattro, /* tp_getattro */
(setattrofunc)cdata_setattro, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */
- 0, /* tp_doc */
+ "The internal base type for CData objects. Use FFI.CData to access "
+ "it. Always check with isinstance(): subtypes are sometimes returned "
+ "on CPython, for performance reasons.", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
cdata_richcompare, /* tp_richcompare */
@@ -3301,7 +3436,7 @@ static PyTypeObject CData_Type = {
static PyTypeObject CDataOwning_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
- "_cffi_backend.CDataOwn",
+ "_cffi_backend.__CDataOwn",
sizeof(CDataObject),
0,
(destructor)cdataowning_dealloc, /* tp_dealloc */
@@ -3320,7 +3455,8 @@ static PyTypeObject CDataOwning_Type = {
0, /* inherited */ /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */
- 0, /* tp_doc */
+ "This is an internal subtype of _CDataBase for performance only on "
+ "CPython. Check with isinstance(x, ffi.CData).", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* inherited */ /* tp_richcompare */
@@ -3343,8 +3479,8 @@ static PyTypeObject CDataOwning_Type = {
static PyTypeObject CDataOwningGC_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
- "_cffi_backend.CDataOwnGC",
- sizeof(CDataObject_owngc_frombuf),
+ "_cffi_backend.__CDataOwnGC",
+ sizeof(CDataObject_own_structptr),
0,
(destructor)cdataowninggc_dealloc, /* tp_dealloc */
0, /* tp_print */
@@ -3363,7 +3499,8 @@ static PyTypeObject CDataOwningGC_Type = {
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES /* tp_flags */
| Py_TPFLAGS_HAVE_GC,
- 0, /* tp_doc */
+ "This is an internal subtype of _CDataBase for performance only on "
+ "CPython. Check with isinstance(x, ffi.CData).", /* tp_doc */
(traverseproc)cdataowninggc_traverse, /* tp_traverse */
(inquiry)cdataowninggc_clear, /* tp_clear */
0, /* inherited */ /* tp_richcompare */
@@ -3384,9 +3521,53 @@ static PyTypeObject CDataOwningGC_Type = {
PyObject_GC_Del, /* tp_free */
};
+static PyTypeObject CDataFromBuf_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "_cffi_backend.__CDataFromBuf",
+ sizeof(CDataObject_frombuf),
+ 0,
+ (destructor)cdatafrombuf_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ (reprfunc)cdatafrombuf_repr, /* tp_repr */
+ 0, /* inherited */ /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* inherited */ /* tp_as_mapping */
+ 0, /* inherited */ /* tp_hash */
+ 0, /* inherited */ /* tp_call */
+ 0, /* tp_str */
+ 0, /* inherited */ /* tp_getattro */
+ 0, /* inherited */ /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES /* tp_flags */
+ | Py_TPFLAGS_HAVE_GC,
+ "This is an internal subtype of _CDataBase for performance only on "
+ "CPython. Check with isinstance(x, ffi.CData).", /* tp_doc */
+ (traverseproc)cdatafrombuf_traverse, /* tp_traverse */
+ (inquiry)cdatafrombuf_clear, /* tp_clear */
+ 0, /* inherited */ /* tp_richcompare */
+ 0, /* inherited */ /* tp_weaklistoffset */
+ 0, /* inherited */ /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* inherited */ /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ &CData_Type, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ PyObject_GC_Del, /* tp_free */
+};
+
static PyTypeObject CDataGCP_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
- "_cffi_backend.CDataGCP",
+ "_cffi_backend.__CDataGCP",
sizeof(CDataObject_gcp),
0,
(destructor)cdatagcp_dealloc, /* tp_dealloc */
@@ -3409,7 +3590,8 @@ static PyTypeObject CDataGCP_Type = {
| Py_TPFLAGS_HAVE_FINALIZE
#endif
| Py_TPFLAGS_HAVE_GC,
- 0, /* tp_doc */
+ "This is an internal subtype of _CDataBase for performance only on "
+ "CPython. Check with isinstance(x, ffi.CData).", /* tp_doc */
(traverseproc)cdatagcp_traverse, /* tp_traverse */
0, /* tp_clear */
0, /* inherited */ /* tp_richcompare */
@@ -3470,7 +3652,7 @@ cdataiter_dealloc(CDataIterObject *it)
static PyTypeObject CDataIter_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
- "_cffi_backend.CDataIter", /* tp_name */
+ "_cffi_backend.__CData_iterator", /* tp_name */
sizeof(CDataIterObject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
@@ -4086,11 +4268,12 @@ typedef struct {
PyObject_HEAD
void *dl_handle;
char *dl_name;
+ int dl_auto_close;
} DynLibObject;
static void dl_dealloc(DynLibObject *dlobj)
{
- if (dlobj->dl_handle != NULL)
+ if (dlobj->dl_handle != NULL && dlobj->dl_auto_close)
dlclose(dlobj->dl_handle);
free(dlobj->dl_name);
PyObject_Del(dlobj);
@@ -4224,7 +4407,7 @@ static PyMethodDef dl_methods[] = {
static PyTypeObject dl_type = {
PyVarObject_HEAD_INIT(NULL, 0)
- "_cffi_backend.Library", /* tp_name */
+ "_cffi_backend.CLibrary", /* tp_name */
sizeof(DynLibObject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
@@ -4255,7 +4438,7 @@ static PyTypeObject dl_type = {
};
static void *b_do_dlopen(PyObject *args, const char **p_printable_filename,
- PyObject **p_temp)
+ PyObject **p_temp, int *auto_close)
{
/* Logic to call the correct version of dlopen(). Returns NULL in case of error.
Otherwise, '*p_printable_filename' will point to a printable char version of
@@ -4266,6 +4449,7 @@ static void *b_do_dlopen(PyObject *args, const char **p_printable_filename,
char *filename_or_null;
int flags = 0;
*p_temp = NULL;
+ *auto_close = 1;
if (PyTuple_GET_SIZE(args) == 0 || PyTuple_GET_ITEM(args, 0) == Py_None) {
PyObject *dummy;
@@ -4275,13 +4459,37 @@ static void *b_do_dlopen(PyObject *args, const char **p_printable_filename,
filename_or_null = NULL;
*p_printable_filename = "<None>";
}
+ else if (CData_Check(PyTuple_GET_ITEM(args, 0)))
+ {
+ CDataObject *cd;
+ if (!PyArg_ParseTuple(args, "O|i:load_library", &cd, &flags))
+ return NULL;
+ /* 'flags' is accepted but ignored in this case */
+ if ((cd->c_type->ct_flags & CT_IS_VOID_PTR) == 0) {
+ PyErr_Format(PyExc_TypeError,
+ "dlopen() takes a file name or 'void *' handle, not '%s'",
+ cd->c_type->ct_name);
+ return NULL;
+ }
+ handle = cd->c_data;
+ if (handle == NULL) {
+ PyErr_Format(PyExc_RuntimeError, "cannot call dlopen(NULL)");
+ return NULL;
+ }
+ *p_temp = PyText_FromFormat("%p", handle);
+ *p_printable_filename = PyText_AsUTF8(*p_temp);
+ *auto_close = 0;
+ return handle;
+ }
else
{
PyObject *s = PyTuple_GET_ITEM(args, 0);
#ifdef MS_WIN32
- Py_UNICODE *filenameW;
- if (PyArg_ParseTuple(args, "u|i:load_library", &filenameW, &flags))
+ PyObject *filename_unicode;
+ if (PyArg_ParseTuple(args, "U|i:load_library", &filename_unicode, &flags))
{
+ Py_ssize_t sz1;
+ wchar_t *w1;
#if PY_MAJOR_VERSION < 3
s = PyUnicode_AsUTF8String(s);
if (s == NULL)
@@ -4292,7 +4500,15 @@ static void *b_do_dlopen(PyObject *args, const char **p_printable_filename,
if (*p_printable_filename == NULL)
return NULL;
- handle = dlopenW(filenameW);
+ sz1 = PyUnicode_GetSize(filename_unicode) + 1;
+ sz1 *= 2; /* should not be needed, but you never know */
+ w1 = alloca(sizeof(wchar_t) * sz1);
+ sz1 = PyUnicode_AsWideChar((PyUnicodeObject *)filename_unicode,
+ w1, sz1 - 1);
+ if (sz1 < 0)
+ return NULL;
+ w1[sz1] = 0;
+ handle = dlopenW(w1);
goto got_handle;
}
PyErr_Clear();
@@ -4304,19 +4520,31 @@ static void *b_do_dlopen(PyObject *args, const char **p_printable_filename,
if (PyUnicode_Check(s))
{
s = PyUnicode_AsUTF8String(s);
- if (s == NULL)
+ if (s == NULL) {
+ PyMem_Free(filename_or_null);
return NULL;
+ }
*p_temp = s;
}
#endif
*p_printable_filename = PyText_AsUTF8(s);
- if (*p_printable_filename == NULL)
+ if (*p_printable_filename == NULL) {
+ PyMem_Free(filename_or_null);
return NULL;
+ }
}
if ((flags & (RTLD_NOW | RTLD_LAZY)) == 0)
flags |= RTLD_NOW;
+#ifdef MS_WIN32
+ if (filename_or_null == NULL) {
+ PyErr_SetString(PyExc_OSError, "dlopen(None) not supported on Windows");
+ return NULL;
+ }
+#endif
+
handle = dlopen(filename_or_null, flags);
+ PyMem_Free(filename_or_null);
#ifdef MS_WIN32
got_handle:
@@ -4336,8 +4564,9 @@ static PyObject *b_load_library(PyObject *self, PyObject *args)
PyObject *temp;
void *handle;
DynLibObject *dlobj = NULL;
+ int auto_close;
- handle = b_do_dlopen(args, &printable_filename, &temp);
+ handle = b_do_dlopen(args, &printable_filename, &temp, &auto_close);
if (handle == NULL)
goto error;
@@ -4348,6 +4577,7 @@ static PyObject *b_load_library(PyObject *self, PyObject *args)
}
dlobj->dl_handle = handle;
dlobj->dl_name = strdup(printable_filename);
+ dlobj->dl_auto_close = auto_close;
error:
Py_XDECREF(temp);
@@ -4831,7 +5061,9 @@ static int complete_sflags(int sflags)
#ifdef MS_WIN32
sflags |= SF_MSVC_BITFIELDS;
#else
-# if defined(__arm__) || defined(__aarch64__)
+# if defined(__APPLE__) && defined(__arm64__)
+ sflags |= SF_GCC_X86_BITFIELDS;
+# elif defined(__arm__) || defined(__aarch64__)
sflags |= SF_GCC_ARM_BITFIELDS;
# else
sflags |= SF_GCC_X86_BITFIELDS;
@@ -4859,8 +5091,8 @@ static int detect_custom_layout(CTypeDescrObject *ct, int sflags,
if (sflags & SF_STD_FIELD_POS) {
PyErr_Format(FFIError,
"%s: %s%s%s (cdef says %zd, but C compiler says %zd)."
- " fix it or use \"...;\" in the cdef for %s to "
- "make it flexible",
+ " fix it or use \"...;\" as the last field in the "
+ "cdef for %s to make it flexible",
ct->ct_name, msg1, txt, msg2,
cdef_value, compiler_value,
ct->ct_name);
@@ -4871,12 +5103,16 @@ static int detect_custom_layout(CTypeDescrObject *ct, int sflags,
return 0;
}
+#define ROUNDUP_BYTES(bytes, bits) ((bytes) + ((bits) > 0))
+
static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args)
{
CTypeDescrObject *ct;
PyObject *fields, *interned_fields, *ignored;
int is_union, alignment;
- Py_ssize_t boffset, i, nb_fields, boffsetmax, alignedsize, boffsetorg;
+ Py_ssize_t byteoffset, i, nb_fields, byteoffsetmax, alignedsize;
+ int bitoffset;
+ Py_ssize_t byteoffsetorg;
Py_ssize_t totalsize = -1;
int totalalignment = -1;
CFieldObject **previous;
@@ -4915,8 +5151,9 @@ static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args)
ct->ct_flags &= ~(CT_CUSTOM_FIELD_POS | CT_WITH_PACKED_CHANGE);
alignment = 1;
- boffset = 0; /* this number is in *bits*, not bytes! */
- boffsetmax = 0; /* the maximum value of boffset, in bits too */
+ byteoffset = 0; /* the real value is 'byteoffset+bitoffset*8', which */
+ bitoffset = 0; /* counts the offset in bits */
+ byteoffsetmax = 0; /* the maximum value of byteoffset-rounded-up-to-byte */
prev_bitfield_size = 0;
prev_bitfield_free = 0;
nb_fields = PyList_GET_SIZE(fields);
@@ -4951,9 +5188,22 @@ static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args)
goto error;
}
}
+ else if (ftype->ct_flags & (CT_STRUCT|CT_UNION)) {
+ if (force_lazy_struct(ftype) < 0) /* for CT_WITH_VAR_ARRAY */
+ return NULL;
+
+ /* GCC (or maybe C99) accepts var-sized struct fields that are not
+ the last field of a larger struct. That's why there is no
+ check here for "last field": we propagate the flag
+ CT_WITH_VAR_ARRAY to any struct that contains either an open-
+ ended array or another struct that recursively contains an
+ open-ended array. */
+ if (ftype->ct_flags & CT_WITH_VAR_ARRAY)
+ ct->ct_flags |= CT_WITH_VAR_ARRAY;
+ }
if (is_union)
- boffset = 0; /* reset each field at offset 0 */
+ byteoffset = bitoffset = 0; /* reset each field at offset 0 */
/* update the total alignment requirement, but skip it if the
field is an anonymous bitfield or if SF_PACKED */
@@ -4988,20 +5238,26 @@ static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args)
bs_flag = BS_REGULAR;
/* align this field to its own 'falign' by inserting padding */
- boffsetorg = (boffset + falignorg*8-1) & ~(falignorg*8-1); /*bits!*/
- boffset = (boffset + falign*8-1) & ~(falign*8-1); /* bits! */
- if (boffsetorg != boffset) {
+
+ /* first, pad to the next byte,
+ * then pad to 'falign' or 'falignorg' bytes */
+ byteoffset = ROUNDUP_BYTES(byteoffset, bitoffset);
+ bitoffset = 0;
+ byteoffsetorg = (byteoffset + falignorg-1) & ~(falignorg-1);
+ byteoffset = (byteoffset + falign-1) & ~(falign-1);
+
+ if (byteoffsetorg != byteoffset) {
ct->ct_flags |= CT_WITH_PACKED_CHANGE;
}
if (foffset >= 0) {
/* a forced field position: ignore the offset just computed,
except to know if we must set CT_CUSTOM_FIELD_POS */
- if (detect_custom_layout(ct, sflags, boffset / 8, foffset,
+ if (detect_custom_layout(ct, sflags, byteoffset, foffset,
"wrong offset for field '",
PyText_AS_UTF8(fname), "'") < 0)
goto error;
- boffset = foffset * 8;
+ byteoffset = foffset;
}
if (PyText_GetSize(fname) == 0 &&
@@ -5015,7 +5271,7 @@ static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args)
*previous = _add_field(interned_fields,
get_field_name(ftype, cfsrc),
cfsrc->cf_type,
- boffset / 8 + cfsrc->cf_offset,
+ byteoffset + cfsrc->cf_offset,
cfsrc->cf_bitshift,
cfsrc->cf_bitsize,
cfsrc->cf_flags | fflags);
@@ -5028,13 +5284,13 @@ static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args)
}
else {
*previous = _add_field(interned_fields, fname, ftype,
- boffset / 8, bs_flag, -1, fflags);
+ byteoffset, bs_flag, -1, fflags);
if (*previous == NULL)
goto error;
previous = &(*previous)->cf_next;
}
if (ftype->ct_size >= 0)
- boffset += ftype->ct_size * 8;
+ byteoffset += ftype->ct_size;
prev_bitfield_size = 0;
}
else {
@@ -5071,7 +5327,7 @@ static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args)
/* compute the starting position of the theoretical field
that covers a complete 'ftype', inside of which we will
locate the real bitfield */
- field_offset_bytes = boffset / 8;
+ field_offset_bytes = byteoffset;
field_offset_bytes &= ~(falign - 1);
if (fbitsize == 0) {
@@ -5084,12 +5340,13 @@ static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args)
if (!(sflags & SF_MSVC_BITFIELDS)) {
/* GCC's notion of "ftype :0;" */
- /* pad boffset to a value aligned for "ftype" */
- if (boffset > field_offset_bytes * 8) {
+ /* pad byteoffset to a value aligned for "ftype" */
+ if (ROUNDUP_BYTES(byteoffset, bitoffset) > field_offset_bytes) {
field_offset_bytes += falign;
- assert(boffset < field_offset_bytes * 8);
+ assert(byteoffset < field_offset_bytes);
}
- boffset = field_offset_bytes * 8;
+ byteoffset = field_offset_bytes;
+ bitoffset = 0;
}
else {
/* MSVC's notion of "ftype :0;" */
@@ -5106,7 +5363,8 @@ static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args)
/* Can the field start at the offset given by 'boffset'? It
can if it would entirely fit into an aligned ftype field. */
- bits_already_occupied = boffset - (field_offset_bytes * 8);
+ bits_already_occupied = (byteoffset-field_offset_bytes) * 8
+ + bitoffset;
if (bits_already_occupied + fbitsize > 8 * ftype->ct_size) {
/* it would not fit, we need to start at the next
@@ -5120,15 +5378,18 @@ static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args)
goto error;
}
field_offset_bytes += falign;
- assert(boffset < field_offset_bytes * 8);
- boffset = field_offset_bytes * 8;
+ assert(byteoffset < field_offset_bytes);
+ byteoffset = field_offset_bytes;
+ bitoffset = 0;
bitshift = 0;
}
else {
bitshift = bits_already_occupied;
assert(bitshift >= 0);
}
- boffset += fbitsize;
+ bitoffset += fbitsize;
+ byteoffset += (bitoffset >> 3);
+ bitoffset &= 7;
}
else {
/* MSVC's algorithm */
@@ -5144,38 +5405,43 @@ static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args)
}
else {
/* no: start a new full field */
- boffset = (boffset + falign*8-1) & ~(falign*8-1); /*align*/
- boffset += ftype->ct_size * 8;
+ byteoffset = ROUNDUP_BYTES(byteoffset, bitoffset);
+ bitoffset = 0;
+ /* align */
+ byteoffset = (byteoffset + falign-1) & ~(falign-1);
+ byteoffset += ftype->ct_size;
bitshift = 0;
prev_bitfield_size = ftype->ct_size;
prev_bitfield_free = 8 * prev_bitfield_size;
}
prev_bitfield_free -= fbitsize;
- field_offset_bytes = boffset / 8 - ftype->ct_size;
+ field_offset_bytes = byteoffset - ftype->ct_size;
}
-
if (sflags & SF_GCC_BIG_ENDIAN)
bitshift = 8 * ftype->ct_size - fbitsize - bitshift;
- *previous = _add_field(interned_fields, fname, ftype,
+ if (PyText_GetSize(fname) > 0) {
+
+ *previous = _add_field(interned_fields, fname, ftype,
field_offset_bytes, bitshift, fbitsize,
fflags);
- if (*previous == NULL)
- goto error;
- previous = &(*previous)->cf_next;
+ if (*previous == NULL)
+ goto error;
+ previous = &(*previous)->cf_next;
+ }
}
}
- if (boffset > boffsetmax)
- boffsetmax = boffset;
+ assert(bitoffset == (bitoffset & 7));
+ if (ROUNDUP_BYTES(byteoffset, bitoffset) > byteoffsetmax)
+ byteoffsetmax = ROUNDUP_BYTES(byteoffset, bitoffset);
}
*previous = NULL;
/* Like C, if the size of this structure would be zero, we compute it
as 1 instead. But for ctypes support, we allow the manually-
specified totalsize to be zero in this case. */
- boffsetmax = (boffsetmax + 7) / 8; /* bits -> bytes */
- alignedsize = (boffsetmax + alignment - 1) & ~(alignment-1);
+ alignedsize = (byteoffsetmax + alignment - 1) & ~(alignment-1);
if (alignedsize == 0)
alignedsize = 1;
@@ -5186,10 +5452,10 @@ static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args)
if (detect_custom_layout(ct, sflags, alignedsize,
totalsize, "wrong total size", "", "") < 0)
goto error;
- if (totalsize < boffsetmax) {
+ if (totalsize < byteoffsetmax) {
PyErr_Format(PyExc_TypeError,
"%s cannot be of size %zd: there are fields at least "
- "up to %zd", ct->ct_name, totalsize, boffsetmax);
+ "up to %zd", ct->ct_name, totalsize, byteoffsetmax);
goto error;
}
}
@@ -5585,11 +5851,14 @@ static CTypeDescrObject *fb_prepare_ctype(struct funcbuilder_s *fb,
static cif_description_t *fb_prepare_cif(PyObject *fargs,
CTypeDescrObject *fresult,
+ Py_ssize_t variadic_nargs_declared,
ffi_abi fabi)
+
{
char *buffer;
cif_description_t *cif_descr;
struct funcbuilder_s funcbuffer;
+ ffi_status status = (ffi_status)-1;
funcbuffer.nb_bytes = 0;
funcbuffer.bufferp = NULL;
@@ -5612,8 +5881,24 @@ static cif_description_t *fb_prepare_cif(PyObject *fargs,
assert(funcbuffer.bufferp == buffer + funcbuffer.nb_bytes);
cif_descr = (cif_description_t *)buffer;
- if (ffi_prep_cif(&cif_descr->cif, fabi, funcbuffer.nargs,
- funcbuffer.rtype, funcbuffer.atypes) != FFI_OK) {
+
+ /* use `ffi_prep_cif_var` if necessary and available */
+#if CFFI_CHECK_FFI_PREP_CIF_VAR_MAYBE
+ if (variadic_nargs_declared >= 0) {
+ if (CFFI_CHECK_FFI_PREP_CIF_VAR) {
+ status = ffi_prep_cif_var(&cif_descr->cif, fabi,
+ variadic_nargs_declared, funcbuffer.nargs,
+ funcbuffer.rtype, funcbuffer.atypes);
+ }
+ }
+#endif
+
+ if (status == (ffi_status)-1) {
+ status = ffi_prep_cif(&cif_descr->cif, fabi, funcbuffer.nargs,
+ funcbuffer.rtype, funcbuffer.atypes);
+ }
+
+ if (status != FFI_OK) {
PyErr_SetString(PyExc_SystemError,
"libffi failed to build this function type");
goto error;
@@ -5657,7 +5942,7 @@ static PyObject *new_function_type(PyObject *fargs, /* tuple */
is computed here. */
cif_description_t *cif_descr;
- cif_descr = fb_prepare_cif(fargs, fresult, fabi);
+ cif_descr = fb_prepare_cif(fargs, fresult, -1, fabi);
if (cif_descr == NULL) {
if (PyErr_ExceptionMatches(PyExc_NotImplementedError)) {
PyErr_Clear(); /* will get the exception if we see an
@@ -5785,6 +6070,43 @@ static void _my_PyErr_WriteUnraisable(PyObject *t, PyObject *v, PyObject *tb,
char *extra_error_line)
{
/* like PyErr_WriteUnraisable(), but write a full traceback */
+#ifdef USE_WRITEUNRAISABLEMSG
+
+ /* PyErr_WriteUnraisable actually writes the full traceback anyway
+ from Python 3.4, but we can't really get the formatting of the
+ custom text to be what we want. We can do better from Python
+ 3.8 by calling the new _PyErr_WriteUnraisableMsg().
+ Luckily it's also Python 3.8 that adds new functionality that
+ people might want: the new sys.unraisablehook().
+ */
+ PyObject *s;
+ int first_char;
+ assert(objdescr != NULL && objdescr[0] != 0); /* non-empty */
+ first_char = objdescr[0];
+ if (first_char >= 'A' && first_char <= 'Z')
+ first_char += 'a' - 'A'; /* lower() the very first character */
+ if (extra_error_line == NULL)
+ extra_error_line = "";
+
+ if (obj != NULL)
+ s = PyUnicode_FromFormat("%c%s%R%s",
+ first_char, objdescr + 1, obj, extra_error_line);
+ else
+ s = PyUnicode_FromFormat("%c%s%s",
+ first_char, objdescr + 1, extra_error_line);
+
+ PyErr_Restore(t, v, tb);
+ if (s != NULL) {
+ _PyErr_WriteUnraisableMsg(PyText_AS_UTF8(s), NULL);
+ Py_DECREF(s);
+ }
+ else
+ PyErr_WriteUnraisable(obj); /* best effort */
+ PyErr_Clear();
+
+#else
+
+ /* version for Python 2.7 and < 3.8 */
PyObject *f;
#if PY_MAJOR_VERSION >= 3
/* jump through hoops to ensure the tb is attached to v, on Python 3 */
@@ -5809,6 +6131,8 @@ static void _my_PyErr_WriteUnraisable(PyObject *t, PyObject *v, PyObject *tb,
Py_XDECREF(t);
Py_XDECREF(v);
Py_XDECREF(tb);
+
+#endif
}
static void general_invoke_callback(int decode_args_from_libffi,
@@ -5858,7 +6182,11 @@ static void general_invoke_callback(int decode_args_from_libffi,
goto error;
if (convert_from_object_fficallback(result, SIGNATURE(1), py_res,
decode_args_from_libffi) < 0) {
+#ifdef USE_WRITEUNRAISABLEMSG
+ extra_error_line = ", trying to convert the result back to C";
+#else
extra_error_line = "Trying to convert the result back to C:\n";
+#endif
goto error;
}
done:
@@ -5910,10 +6238,16 @@ static void general_invoke_callback(int decode_args_from_libffi,
_my_PyErr_WriteUnraisable(exc1, val1, tb1,
"From cffi callback ", py_ob,
extra_error_line);
+#ifdef USE_WRITEUNRAISABLEMSG
+ _my_PyErr_WriteUnraisable(exc2, val2, tb2,
+ "during handling of the above exception by 'onerror'",
+ NULL, NULL);
+#else
extra_error_line = ("\nDuring the call to 'onerror', "
"another exception occurred:\n\n");
_my_PyErr_WriteUnraisable(exc2, val2, tb2,
NULL, NULL, extra_error_line);
+#endif
_cffi_stop_error_capture(ecap);
}
}
@@ -5981,15 +6315,28 @@ static PyObject *prepare_callback_info_tuple(CTypeDescrObject *ct,
infotuple = Py_BuildValue("OOOO", ct, ob, py_rawerr, onerror_ob);
Py_DECREF(py_rawerr);
-#ifdef WITH_THREAD
+#if defined(WITH_THREAD) && PY_VERSION_HEX < 0x03070000
/* We must setup the GIL here, in case the callback is invoked in
- some other non-Pythonic thread. This is the same as ctypes. */
+ some other non-Pythonic thread. This is the same as ctypes.
+ But PyEval_InitThreads() is always a no-op from CPython 3.7
+ (the call from ctypes was removed some time later I think). */
PyEval_InitThreads();
#endif
return infotuple;
}
+/* messily try to silence a gcc/clang deprecation warning for
+ ffi_prep_closure. Don't miss the "pragma pop" after the function.
+ This is done around the whole function because very old GCCs don't
+ support it inside a function. */
+#if defined(__clang__)
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#elif defined(__GNUC__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
static PyObject *b_callback(PyObject *self, PyObject *args)
{
CTypeDescrObject *ct;
@@ -5998,6 +6345,7 @@ static PyObject *b_callback(PyObject *self, PyObject *args)
PyObject *infotuple;
cif_description_t *cif_descr;
ffi_closure *closure;
+ ffi_status status;
void *closure_exec;
if (!PyArg_ParseTuple(args, "O!O|OO:callback", &CTypeDescr_Type, &ct, &ob,
@@ -6008,14 +6356,23 @@ static PyObject *b_callback(PyObject *self, PyObject *args)
if (infotuple == NULL)
return NULL;
-#ifdef CFFI_TRUST_LIBFFI
- closure = ffi_closure_alloc(sizeof(ffi_closure), &closure_exec);
-#else
- closure = cffi_closure_alloc();
- closure_exec = closure;
+#if CFFI_CHECK_FFI_CLOSURE_ALLOC_MAYBE
+ if (CFFI_CHECK_FFI_CLOSURE_ALLOC) {
+ closure = ffi_closure_alloc(sizeof(ffi_closure), &closure_exec);
+ } else
#endif
+ {
+ closure = cffi_closure_alloc();
+ closure_exec = closure;
+ }
+
if (closure == NULL) {
Py_DECREF(infotuple);
+ PyErr_SetString(PyExc_MemoryError,
+ "Cannot allocate write+execute memory for ffi.callback(). "
+ "You might be running on a system that prevents this. "
+ "For more information, see "
+ "https://cffi.readthedocs.io/en/latest/using.html#callbacks");
return NULL;
}
cd = PyObject_GC_New(CDataObject_closure, &CDataOwningGC_Type);
@@ -6025,8 +6382,8 @@ static PyObject *b_callback(PyObject *self, PyObject *args)
cd->head.c_type = ct;
cd->head.c_data = (char *)closure_exec;
cd->head.c_weakreflist = NULL;
+ closure->user_data = NULL;
cd->closure = closure;
- PyObject_GC_Track(cd);
cif_descr = (cif_description_t *)ct->ct_extra;
if (cif_descr == NULL) {
@@ -6035,17 +6392,30 @@ static PyObject *b_callback(PyObject *self, PyObject *args)
"return type or with '...'", ct->ct_name);
goto error;
}
-#ifdef CFFI_TRUST_LIBFFI
- if (ffi_prep_closure_loc(closure, &cif_descr->cif,
- invoke_callback, infotuple, closure_exec) != FFI_OK) {
+
+#if CFFI_CHECK_FFI_PREP_CLOSURE_LOC_MAYBE
+ if (CFFI_CHECK_FFI_PREP_CLOSURE_LOC) {
+ status = ffi_prep_closure_loc(closure, &cif_descr->cif,
+ invoke_callback, infotuple, closure_exec);
+ }
+ else
+#endif
+ {
+#if defined(__APPLE__) && defined(FFI_AVAILABLE_APPLE) && !FFI_LEGACY_CLOSURE_API
+ PyErr_Format(PyExc_SystemError, "ffi_prep_closure_loc() is missing");
+ goto error;
#else
- if (ffi_prep_closure(closure, &cif_descr->cif,
- invoke_callback, infotuple) != FFI_OK) {
+ status = ffi_prep_closure(closure, &cif_descr->cif,
+ invoke_callback, infotuple);
#endif
+ }
+
+ if (status != FFI_OK) {
PyErr_SetString(PyExc_SystemError,
"libffi failed to build this callback");
goto error;
}
+
if (closure->user_data != infotuple) {
/* Issue #266. Should not occur, but could, if we are using
at runtime a version of libffi compiled with a different
@@ -6060,22 +6430,30 @@ static PyObject *b_callback(PyObject *self, PyObject *args)
"different from the 'ffi.h' file seen at compile-time)");
goto error;
}
+ PyObject_GC_Track(cd);
return (PyObject *)cd;
error:
closure->user_data = NULL;
if (cd == NULL) {
-#ifdef CFFI_TRUST_LIBFFI
- ffi_closure_free(closure);
-#else
- cffi_closure_free(closure);
+#if CFFI_CHECK_FFI_CLOSURE_ALLOC_MAYBE
+ if (CFFI_CHECK_FFI_CLOSURE_ALLOC) {
+ ffi_closure_free(closure);
+ }
+ else
#endif
+ cffi_closure_free(closure);
}
else
Py_DECREF(cd);
Py_XDECREF(infotuple);
return NULL;
}
+#if defined(__clang__)
+# pragma clang diagnostic pop
+#elif defined(__GNUC__)
+# pragma GCC diagnostic pop
+#endif
static PyObject *b_new_enum_type(PyObject *self, PyObject *args)
{
@@ -6697,7 +7075,7 @@ static PyObject *newp_handle(CTypeDescrObject *ct_voidp, PyObject *x)
&CDataOwningGC_Type);
if (cd == NULL)
return NULL;
- Py_INCREF(ct_voidp);
+ Py_INCREF(ct_voidp); /* must be "void *" */
cd->head.c_type = ct_voidp;
cd->head.c_data = (char *)cd;
cd->head.c_weakreflist = NULL;
@@ -6815,10 +7193,11 @@ static PyObject *direct_from_buffer(CTypeDescrObject *ct, PyObject *x,
{
CDataObject *cd;
Py_buffer *view;
- Py_ssize_t arraylength;
+ Py_ssize_t arraylength, minimumlength = 0;
- if (!(ct->ct_flags & CT_ARRAY)) {
- PyErr_Format(PyExc_TypeError, "expected an array ctype, got '%s'",
+ if (!(ct->ct_flags & (CT_ARRAY | CT_POINTER))) {
+ PyErr_Format(PyExc_TypeError,
+ "expected a pointer or array ctype, got '%s'",
ct->ct_name);
return NULL;
}
@@ -6841,43 +7220,51 @@ static PyObject *direct_from_buffer(CTypeDescrObject *ct, PyObject *x,
if (_my_PyObject_GetContiguousBuffer(x, view, require_writable) < 0)
goto error1;
- if (ct->ct_length >= 0) {
- /* it's an array with a fixed length; make sure that the
- buffer contains enough bytes. */
- if (view->len < ct->ct_size) {
- PyErr_Format(PyExc_ValueError,
- "buffer is too small (%zd bytes) for '%s' (%zd bytes)",
- view->len, ct->ct_name, ct->ct_size);
- goto error2;
- }
- arraylength = ct->ct_length;
+ if (ct->ct_flags & CT_POINTER)
+ {
+ arraylength = view->len; /* number of bytes, not used so far */
}
else {
- /* it's an open 'array[]' */
- if (ct->ct_itemdescr->ct_size == 1) {
- /* fast path, performance only */
- arraylength = view->len;
- }
- else if (ct->ct_itemdescr->ct_size > 0) {
- /* give it as many items as fit the buffer. Ignore a
- partial last element. */
- arraylength = view->len / ct->ct_itemdescr->ct_size;
+ /* ct->ct_flags & CT_ARRAY */
+ if (ct->ct_length >= 0) {
+ /* it's an array with a fixed length; make sure that the
+ buffer contains enough bytes. */
+ minimumlength = ct->ct_size;
+ arraylength = ct->ct_length;
}
else {
- /* it's an array 'empty[]'. Unsupported obscure case:
- the problem is that setting the length of the result
- to anything large (like SSIZE_T_MAX) is dangerous,
- because if someone tries to loop over it, it will
- turn effectively into an infinite loop. */
- PyErr_Format(PyExc_ZeroDivisionError,
- "from_buffer('%s', ..): the actual length of the array "
- "cannot be computed", ct->ct_name);
- goto error2;
+ /* it's an open 'array[]' */
+ if (ct->ct_itemdescr->ct_size == 1) {
+ /* fast path, performance only */
+ arraylength = view->len;
+ }
+ else if (ct->ct_itemdescr->ct_size > 0) {
+ /* give it as many items as fit the buffer. Ignore a
+ partial last element. */
+ arraylength = view->len / ct->ct_itemdescr->ct_size;
+ }
+ else {
+ /* it's an array 'empty[]'. Unsupported obscure case:
+ the problem is that setting the length of the result
+ to anything large (like SSIZE_T_MAX) is dangerous,
+ because if someone tries to loop over it, it will
+ turn effectively into an infinite loop. */
+ PyErr_Format(PyExc_ZeroDivisionError,
+ "from_buffer('%s', ..): the actual length of the array "
+ "cannot be computed", ct->ct_name);
+ goto error2;
+ }
}
}
+ if (view->len < minimumlength) {
+ PyErr_Format(PyExc_ValueError,
+ "buffer is too small (%zd bytes) for '%s' (%zd bytes)",
+ view->len, ct->ct_name, minimumlength);
+ goto error2;
+ }
- cd = (CDataObject *)PyObject_GC_New(CDataObject_owngc_frombuf,
- &CDataOwningGC_Type);
+ cd = (CDataObject *)PyObject_GC_New(CDataObject_frombuf,
+ &CDataFromBuf_Type);
if (cd == NULL)
goto error2;
@@ -6885,8 +7272,8 @@ static PyObject *direct_from_buffer(CTypeDescrObject *ct, PyObject *x,
cd->c_type = ct;
cd->c_data = view->buf;
cd->c_weakreflist = NULL;
- ((CDataObject_owngc_frombuf *)cd)->length = arraylength;
- ((CDataObject_owngc_frombuf *)cd)->bufferview = view;
+ ((CDataObject_frombuf *)cd)->length = arraylength;
+ ((CDataObject_frombuf *)cd)->bufferview = view;
PyObject_GC_Track(cd);
return (PyObject *)cd;
@@ -7576,6 +7963,22 @@ init_cffi_backend(void)
PyObject *m, *v;
int i;
static char init_done = 0;
+ static PyTypeObject *all_types[] = {
+ &dl_type,
+ &CTypeDescr_Type,
+ &CField_Type,
+ &CData_Type,
+ &CDataOwning_Type,
+ &CDataOwningGC_Type,
+ &CDataFromBuf_Type,
+ &CDataGCP_Type,
+ &CDataIter_Type,
+ &MiniBuffer_Type,
+ &FFI_Type,
+ &Lib_Type,
+ &GlobSupport_Type,
+ NULL
+ };
v = PySys_GetObject("version");
if (v == NULL || !PyText_Check(v) ||
@@ -7601,24 +8004,22 @@ init_cffi_backend(void)
INITERROR;
}
- if (PyType_Ready(&dl_type) < 0)
- INITERROR;
- if (PyType_Ready(&CTypeDescr_Type) < 0)
- INITERROR;
- if (PyType_Ready(&CField_Type) < 0)
- INITERROR;
- if (PyType_Ready(&CData_Type) < 0)
- INITERROR;
- if (PyType_Ready(&CDataOwning_Type) < 0)
- INITERROR;
- if (PyType_Ready(&CDataOwningGC_Type) < 0)
- INITERROR;
- if (PyType_Ready(&CDataGCP_Type) < 0)
- INITERROR;
- if (PyType_Ready(&CDataIter_Type) < 0)
- INITERROR;
- if (PyType_Ready(&MiniBuffer_Type) < 0)
- INITERROR;
+ /* readify all types and add them to the module */
+ for (i = 0; all_types[i] != NULL; i++) {
+ PyTypeObject *tp = all_types[i];
+ PyObject *tpo = (PyObject *)tp;
+ if (strncmp(tp->tp_name, "_cffi_backend.", 14) != 0) {
+ PyErr_Format(PyExc_ImportError,
+ "'%s' is an ill-formed type name", tp->tp_name);
+ INITERROR;
+ }
+ if (PyType_Ready(tp) < 0)
+ INITERROR;
+
+ Py_INCREF(tpo);
+ if (PyModule_AddObject(m, tp->tp_name + 14, tpo) < 0)
+ INITERROR;
+ }
if (!init_done) {
v = PyText_FromString("_cffi_backend");
@@ -7664,10 +8065,6 @@ init_cffi_backend(void)
INITERROR;
}
- Py_INCREF(&MiniBuffer_Type);
- if (PyModule_AddObject(m, "buffer", (PyObject *)&MiniBuffer_Type) < 0)
- INITERROR;
-
init_cffi_tls();
if (PyErr_Occurred())
INITERROR;