diff options
author | Lucia Li <luciali@google.com> | 2021-11-08 21:45:14 +0800 |
---|---|---|
committer | Lucia Li <luciali@google.com> | 2021-11-09 11:01:46 +0800 |
commit | 6aa63b08263172a979012747497f4207cc341bb0 (patch) | |
tree | 64f8c90740adbe25c30379d6567fd2acafcbd2dd /c | |
parent | 3e013ad5e4325c9a301ddde47e997bfd797167cd (diff) | |
download | cffi-6aa63b08263172a979012747497f4207cc341bb0.tar.gz |
Upgrade cffi from 1.12.2 to 1.15.0
Source is fetched from
https://foss.heptapod.net/pypy/cffi/-/archive/branch/release-1.15/cffi-branch-release-1.15.zip
Build cffi manually and update Android.bp
Bug: 205265538
Test: None
Change-Id: I91a5ed3b96029b3988602aba6a00c0e0748d0600
Diffstat (limited to 'c')
-rw-r--r-- | c/.libs_cffi_backend/libffi-45372312.so.6.0.4 | bin | 149064 -> 0 bytes | |||
-rwxr-xr-x | c/.libs_cffi_backend/libffi-9c61262e.so.8.1.0 | bin | 0 -> 63080 bytes | |||
-rw-r--r-- | c/Android.bp | 2 | ||||
-rw-r--r-- | c/_cffi_backend.c | 803 | ||||
-rwxr-xr-x[-rw-r--r--] | c/_cffi_backend.so | bin | 787992 -> 791800 bytes | |||
-rw-r--r-- | c/call_python.c | 38 | ||||
-rw-r--r-- | c/cdlopen.c | 5 | ||||
-rw-r--r-- | c/cffi1_module.c | 17 | ||||
-rw-r--r-- | c/cglob.c | 2 | ||||
-rw-r--r-- | c/ffi_obj.c | 4 | ||||
-rw-r--r-- | c/lib_obj.c | 12 | ||||
-rw-r--r-- | c/libffi_arm64/README | 5 | ||||
-rw-r--r-- | c/libffi_arm64/ffi.lib | bin | 0 -> 7872 bytes | |||
-rw-r--r-- | c/libffi_arm64/include/ffi.h | 515 | ||||
-rw-r--r-- | c/libffi_arm64/include/fficonfig.h | 215 | ||||
-rw-r--r-- | c/libffi_arm64/include/ffitarget.h | 92 | ||||
-rw-r--r-- | c/libffi_x86_x64/LICENSE (renamed from c/libffi_msvc/LICENSE) | 0 | ||||
-rw-r--r-- | c/libffi_x86_x64/README (renamed from c/libffi_msvc/README) | 0 | ||||
-rw-r--r-- | c/libffi_x86_x64/README.ctypes (renamed from c/libffi_msvc/README.ctypes) | 0 | ||||
-rw-r--r-- | c/libffi_x86_x64/ffi.c (renamed from c/libffi_msvc/ffi.c) | 37 | ||||
-rw-r--r-- | c/libffi_x86_x64/ffi.h (renamed from c/libffi_msvc/ffi.h) | 0 | ||||
-rw-r--r-- | c/libffi_x86_x64/ffi_common.h (renamed from c/libffi_msvc/ffi_common.h) | 0 | ||||
-rw-r--r-- | c/libffi_x86_x64/fficonfig.h (renamed from c/libffi_msvc/fficonfig.h) | 0 | ||||
-rw-r--r-- | c/libffi_x86_x64/ffitarget.h (renamed from c/libffi_msvc/ffitarget.h) | 0 | ||||
-rw-r--r-- | c/libffi_x86_x64/prep_cif.c (renamed from c/libffi_msvc/prep_cif.c) | 5 | ||||
-rw-r--r-- | c/libffi_x86_x64/types.c (renamed from c/libffi_msvc/types.c) | 0 | ||||
-rw-r--r-- | c/libffi_x86_x64/win32.c (renamed from c/libffi_msvc/win32.c) | 0 | ||||
-rw-r--r-- | c/libffi_x86_x64/win64.asm (renamed from c/libffi_msvc/win64.asm) | 0 | ||||
-rw-r--r-- | c/libffi_x86_x64/win64.obj (renamed from c/libffi_msvc/win64.obj) | bin | 1176 -> 1176 bytes | |||
-rw-r--r-- | c/misc_win32.h | 5 | ||||
-rw-r--r-- | c/realize_c_type.c | 43 | ||||
-rw-r--r-- | c/test_c.py | 567 |
32 files changed, 1975 insertions, 392 deletions
diff --git a/c/.libs_cffi_backend/libffi-45372312.so.6.0.4 b/c/.libs_cffi_backend/libffi-45372312.so.6.0.4 Binary files differdeleted file mode 100644 index 59e65c0..0000000 --- a/c/.libs_cffi_backend/libffi-45372312.so.6.0.4 +++ /dev/null diff --git a/c/.libs_cffi_backend/libffi-9c61262e.so.8.1.0 b/c/.libs_cffi_backend/libffi-9c61262e.so.8.1.0 Binary files differnew file mode 100755 index 0000000..82b4232 --- /dev/null +++ b/c/.libs_cffi_backend/libffi-9c61262e.so.8.1.0 diff --git a/c/Android.bp b/c/Android.bp index 7ddc006..a187416 100644 --- a/c/Android.bp +++ b/c/Android.bp @@ -55,6 +55,6 @@ python_library { filegroup { name: "py-cffi-backend-libffi-files", srcs: [ - ".libs_cffi_backend/libffi-45372312.so.6.0.4", + ".libs_cffi_backend/libffi-9c61262e.so.8.1.0", ], } 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; diff --git a/c/_cffi_backend.so b/c/_cffi_backend.so Binary files differindex 7055ae6..31f7528 100644..100755 --- a/c/_cffi_backend.so +++ b/c/_cffi_backend.so diff --git a/c/call_python.c b/c/call_python.c index 8fdcb90..d3d2e17 100644 --- a/c/call_python.c +++ b/c/call_python.c @@ -1,10 +1,18 @@ #if PY_VERSION_HEX >= 0x03080000 -# define Py_BUILD_CORE -/* for access to the fields of PyInterpreterState */ -# include "internal/pycore_pystate.h" -# undef Py_BUILD_CORE +# define HAVE_PYINTERPSTATE_GETDICT #endif + +static PyObject *_current_interp_key(void) +{ + PyInterpreterState *interp = PyThreadState_GET()->interp; +#ifdef HAVE_PYINTERPSTATE_GETDICT + return PyInterpreterState_GetDict(interp); /* shared reference */ +#else + return interp->modules; +#endif +} + static PyObject *_get_interpstate_dict(void) { /* Hack around to return a dict that is subinterpreter-local. @@ -14,8 +22,9 @@ static PyObject *_get_interpstate_dict(void) */ static PyObject *attr_name = NULL; PyThreadState *tstate; - PyObject *d, *builtins; + PyObject *d, *interpdict; int err; + PyInterpreterState *interp; tstate = PyThreadState_GET(); if (tstate == NULL) { @@ -23,8 +32,13 @@ static PyObject *_get_interpstate_dict(void) return NULL; } - builtins = tstate->interp->builtins; - if (builtins == NULL) { + interp = tstate->interp; +#ifdef HAVE_PYINTERPSTATE_GETDICT + interpdict = PyInterpreterState_GetDict(interp); /* shared reference */ +#else + interpdict = interp->builtins; +#endif + if (interpdict == NULL) { /* subinterpreter was cleared already, or is being cleared right now, to a point that is too much for us to continue */ return NULL; @@ -38,13 +52,13 @@ static PyObject *_get_interpstate_dict(void) goto error; } - d = PyDict_GetItem(builtins, attr_name); + d = PyDict_GetItem(interpdict, attr_name); if (d == NULL) { d = PyDict_New(); if (d == NULL) goto error; - err = PyDict_SetItem(builtins, attr_name, d); - Py_DECREF(d); /* if successful, there is one ref left in builtins */ + err = PyDict_SetItem(interpdict, attr_name, d); + Py_DECREF(d); /* if successful, there is one ref left in interpdict */ if (err < 0) goto error; } @@ -163,7 +177,7 @@ static int _update_cache_to_call_python(struct _cffi_externpy_s *externpy) if (infotuple == NULL) return 3; /* no ffi.def_extern() from this subinterpreter */ - new1 = PyThreadState_GET()->interp->modules; + new1 = _current_interp_key(); Py_INCREF(new1); Py_INCREF(infotuple); old1 = (PyObject *)externpy->reserved1; @@ -252,7 +266,7 @@ static void cffi_call_python(struct _cffi_externpy_s *externpy, char *args) } else { PyGILState_STATE state = gil_ensure(); - if (externpy->reserved1 != PyThreadState_GET()->interp->modules) { + if (externpy->reserved1 != _current_interp_key()) { /* Update the (reserved1, reserved2) cache. This will fail if we didn't call @ffi.def_extern() in this particular subinterpreter. */ diff --git a/c/cdlopen.c b/c/cdlopen.c index ad33bbd..0ed319b 100644 --- a/c/cdlopen.c +++ b/c/cdlopen.c @@ -43,12 +43,13 @@ static PyObject *ffi_dlopen(PyObject *self, PyObject *args) const char *modname; PyObject *temp, *result = NULL; void *handle; + int auto_close; - handle = b_do_dlopen(args, &modname, &temp); + handle = b_do_dlopen(args, &modname, &temp, &auto_close); if (handle != NULL) { result = (PyObject *)lib_internal_new((FFIObject *)self, - modname, handle); + modname, handle, auto_close); } Py_XDECREF(temp); return result; diff --git a/c/cffi1_module.c b/c/cffi1_module.c index 2b98e8e..06a84fe 100644 --- a/c/cffi1_module.c +++ b/c/cffi1_module.c @@ -26,11 +26,6 @@ static int init_ffi_lib(PyObject *m) int i, res; static char init_done = 0; - if (PyType_Ready(&FFI_Type) < 0) - return -1; - if (PyType_Ready(&Lib_Type) < 0) - return -1; - if (!init_done) { if (init_global_types_dict(FFI_Type.tp_dict) < 0) return -1; @@ -62,16 +57,6 @@ static int init_ffi_lib(PyObject *m) } init_done = 1; } - - x = (PyObject *)&FFI_Type; - Py_INCREF(x); - if (PyModule_AddObject(m, "FFI", x) < 0) - return -1; - x = (PyObject *)&Lib_Type; - Py_INCREF(x); - if (PyModule_AddObject(m, "Lib", x) < 0) - return -1; - return 0; } @@ -199,7 +184,7 @@ static PyObject *b_init_cffi_1_0_external_module(PyObject *self, PyObject *arg) if (ffi == NULL || PyModule_AddObject(m, "ffi", (PyObject *)ffi) < 0) return NULL; - lib = lib_internal_new(ffi, module_name, NULL); + lib = lib_internal_new(ffi, module_name, NULL, 0); if (lib == NULL || PyModule_AddObject(m, "lib", (PyObject *)lib) < 0) return NULL; @@ -20,7 +20,7 @@ static void glob_support_dealloc(GlobSupportObject *gs) static PyTypeObject GlobSupport_Type = { PyVarObject_HEAD_INIT(NULL, 0) - "FFIGlobSupport", + "_cffi_backend.__FFIGlobSupport", sizeof(GlobSupportObject), 0, (destructor)glob_support_dealloc, /* tp_dealloc */ diff --git a/c/ffi_obj.c b/c/ffi_obj.c index 1e8cc6f..f154146 100644 --- a/c/ffi_obj.c +++ b/c/ffi_obj.c @@ -1070,10 +1070,10 @@ static PyObject *ffi_init_once(FFIObject *self, PyObject *args, PyObject *kwds) if (res != NULL) { tup = PyTuple_Pack(2, Py_True, res); if (tup == NULL || PyDict_SetItem(cache, tag, tup) < 0) { - Py_XDECREF(tup); Py_DECREF(res); res = NULL; } + Py_XDECREF(tup); } } @@ -1137,7 +1137,7 @@ static PyGetSetDef ffi_getsets[] = { static PyTypeObject FFI_Type = { PyVarObject_HEAD_INIT(NULL, 0) - "CompiledFFI", + "_cffi_backend.FFI", sizeof(FFIObject), 0, (destructor)ffi_dealloc, /* tp_dealloc */ diff --git a/c/lib_obj.c b/c/lib_obj.c index 7cd40ec..38bf3d5 100644 --- a/c/lib_obj.c +++ b/c/lib_obj.c @@ -29,6 +29,7 @@ struct LibObject_s { PyObject *l_libname; /* some string that gives the name of the lib */ FFIObject *l_ffi; /* reference back to the ffi object */ void *l_libhandle; /* the dlopen()ed handle, if any */ + int l_auto_close; /* if we must dlclose() this handle */ }; static struct CPyExtFunc_s *_cpyextfunc_get(PyObject *x) @@ -91,7 +92,8 @@ static void *cdlopen_fetch(PyObject *libname, void *libhandle, static void lib_dealloc(LibObject *lib) { PyObject_GC_UnTrack(lib); - cdlopen_close_ignore_errors(lib->l_libhandle); + if (lib->l_auto_close) + cdlopen_close_ignore_errors(lib->l_libhandle); Py_DECREF(lib->l_dict); Py_DECREF(lib->l_libname); Py_DECREF(lib->l_ffi); @@ -587,7 +589,7 @@ static PyMethodDef lib_methods[] = { static PyTypeObject Lib_Type = { PyVarObject_HEAD_INIT(NULL, 0) - "CompiledLib", + "_cffi_backend.Lib", sizeof(LibObject), 0, (destructor)lib_dealloc, /* tp_dealloc */ @@ -624,7 +626,7 @@ static PyTypeObject Lib_Type = { }; static LibObject *lib_internal_new(FFIObject *ffi, const char *module_name, - void *dlopen_libhandle) + void *dlopen_libhandle, int auto_close) { LibObject *lib; PyObject *libname, *dict; @@ -647,6 +649,7 @@ static LibObject *lib_internal_new(FFIObject *ffi, const char *module_name, Py_INCREF(ffi); lib->l_ffi = ffi; lib->l_libhandle = dlopen_libhandle; + lib->l_auto_close = auto_close; return lib; err3: @@ -654,7 +657,8 @@ static LibObject *lib_internal_new(FFIObject *ffi, const char *module_name, err2: Py_DECREF(libname); err1: - cdlopen_close_ignore_errors(dlopen_libhandle); + if (auto_close) + cdlopen_close_ignore_errors(dlopen_libhandle); return NULL; } diff --git a/c/libffi_arm64/README b/c/libffi_arm64/README new file mode 100644 index 0000000..3b8f133 --- /dev/null +++ b/c/libffi_arm64/README @@ -0,0 +1,5 @@ +Libffi package for ARM64 is copied from cpython binary dependencies + +https://github.com/python/cpython-bin-deps/archive/libffi.zip + +The library file has been renamed from libffi-7.lib to ffi.lib to avoid special casing
\ No newline at end of file diff --git a/c/libffi_arm64/ffi.lib b/c/libffi_arm64/ffi.lib Binary files differnew file mode 100644 index 0000000..4a8b84b --- /dev/null +++ b/c/libffi_arm64/ffi.lib diff --git a/c/libffi_arm64/include/ffi.h b/c/libffi_arm64/include/ffi.h new file mode 100644 index 0000000..d91c3e1 --- /dev/null +++ b/c/libffi_arm64/include/ffi.h @@ -0,0 +1,515 @@ +/* -----------------------------------------------------------------*-C-*- + libffi 3.3-rc0 - Copyright (c) 2011, 2014 Anthony Green + - Copyright (c) 1996-2003, 2007, 2008 Red Hat, Inc. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the ``Software''), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + ----------------------------------------------------------------------- */ + +/* ------------------------------------------------------------------- + Most of the API is documented in doc/libffi.texi. + + The raw API is designed to bypass some of the argument packing and + unpacking on architectures for which it can be avoided. Routines + are provided to emulate the raw API if the underlying platform + doesn't allow faster implementation. + + More details on the raw API can be found in: + + http://gcc.gnu.org/ml/java/1999-q3/msg00138.html + + and + + http://gcc.gnu.org/ml/java/1999-q3/msg00174.html + -------------------------------------------------------------------- */ + +#ifndef LIBFFI_H +#define LIBFFI_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Specify which architecture libffi is configured for. */ +#ifndef ARM_WIN64 +#define ARM_WIN64 +#endif + +/* ---- System configuration information --------------------------------- */ + +#include <ffitarget.h> + +#ifndef LIBFFI_ASM + +#if defined(_MSC_VER) && !defined(__clang__) +#define __attribute__(X) +#endif + +#include <stddef.h> +#include <limits.h> + +/* LONG_LONG_MAX is not always defined (not if STRICT_ANSI, for example). + But we can find it either under the correct ANSI name, or under GNU + C's internal name. */ + +#define FFI_64_BIT_MAX 9223372036854775807 + +#ifdef LONG_LONG_MAX +# define FFI_LONG_LONG_MAX LONG_LONG_MAX +#else +# ifdef LLONG_MAX +# define FFI_LONG_LONG_MAX LLONG_MAX +# ifdef _AIX52 /* or newer has C99 LLONG_MAX */ +# undef FFI_64_BIT_MAX +# define FFI_64_BIT_MAX 9223372036854775807LL +# endif /* _AIX52 or newer */ +# else +# ifdef __GNUC__ +# define FFI_LONG_LONG_MAX __LONG_LONG_MAX__ +# endif +# ifdef _AIX /* AIX 5.1 and earlier have LONGLONG_MAX */ +# ifndef __PPC64__ +# if defined (__IBMC__) || defined (__IBMCPP__) +# define FFI_LONG_LONG_MAX LONGLONG_MAX +# endif +# endif /* __PPC64__ */ +# undef FFI_64_BIT_MAX +# define FFI_64_BIT_MAX 9223372036854775807LL +# endif +# endif +#endif + +/* The closure code assumes that this works on pointers, i.e. a size_t + can hold a pointer. */ + +typedef struct _ffi_type +{ + size_t size; + unsigned short alignment; + unsigned short type; + struct _ffi_type **elements; +} ffi_type; + +/* Need minimal decorations for DLLs to work on Windows. GCC has + autoimport and autoexport. Always mark externally visible symbols + as dllimport for MSVC clients, even if it means an extra indirection + when using the static version of the library. + Besides, as a workaround, they can define FFI_BUILDING if they + *know* they are going to link with the static library. */ +#if defined _MSC_VER +# if defined FFI_BUILDING_DLL /* Building libffi.DLL with msvcc.sh */ +# define FFI_API __declspec(dllexport) +# elif !defined FFI_BUILDING /* Importing libffi.DLL */ +# define FFI_API __declspec(dllimport) +# else /* Building/linking static library */ +# define FFI_API +# endif +#else +# define FFI_API +#endif + +/* The externally visible type declarations also need the MSVC DLL + decorations, or they will not be exported from the object file. */ +#if defined LIBFFI_HIDE_BASIC_TYPES +# define FFI_EXTERN FFI_API +#else +# define FFI_EXTERN extern FFI_API +#endif + +#ifndef LIBFFI_HIDE_BASIC_TYPES +#if SCHAR_MAX == 127 +# define ffi_type_uchar ffi_type_uint8 +# define ffi_type_schar ffi_type_sint8 +#else + #error "char size not supported" +#endif + +#if SHRT_MAX == 32767 +# define ffi_type_ushort ffi_type_uint16 +# define ffi_type_sshort ffi_type_sint16 +#elif SHRT_MAX == 2147483647 +# define ffi_type_ushort ffi_type_uint32 +# define ffi_type_sshort ffi_type_sint32 +#else + #error "short size not supported" +#endif + +#if INT_MAX == 32767 +# define ffi_type_uint ffi_type_uint16 +# define ffi_type_sint ffi_type_sint16 +#elif INT_MAX == 2147483647 +# define ffi_type_uint ffi_type_uint32 +# define ffi_type_sint ffi_type_sint32 +#elif INT_MAX == 9223372036854775807 +# define ffi_type_uint ffi_type_uint64 +# define ffi_type_sint ffi_type_sint64 +#else + #error "int size not supported" +#endif + +#if LONG_MAX == 2147483647 +# if FFI_LONG_LONG_MAX != FFI_64_BIT_MAX + #error "no 64-bit data type supported" +# endif +#elif LONG_MAX != FFI_64_BIT_MAX + #error "long size not supported" +#endif + +#if LONG_MAX == 2147483647 +# define ffi_type_ulong ffi_type_uint32 +# define ffi_type_slong ffi_type_sint32 +#elif LONG_MAX == FFI_64_BIT_MAX +# define ffi_type_ulong ffi_type_uint64 +# define ffi_type_slong ffi_type_sint64 +#else + #error "long size not supported" +#endif + +/* These are defined in types.c. */ +FFI_EXTERN ffi_type ffi_type_void; +FFI_EXTERN ffi_type ffi_type_uint8; +FFI_EXTERN ffi_type ffi_type_sint8; +FFI_EXTERN ffi_type ffi_type_uint16; +FFI_EXTERN ffi_type ffi_type_sint16; +FFI_EXTERN ffi_type ffi_type_uint32; +FFI_EXTERN ffi_type ffi_type_sint32; +FFI_EXTERN ffi_type ffi_type_uint64; +FFI_EXTERN ffi_type ffi_type_sint64; +FFI_EXTERN ffi_type ffi_type_float; +FFI_EXTERN ffi_type ffi_type_double; +FFI_EXTERN ffi_type ffi_type_pointer; + +#if 0 +FFI_EXTERN ffi_type ffi_type_longdouble; +#else +#define ffi_type_longdouble ffi_type_double +#endif + +#ifdef FFI_TARGET_HAS_COMPLEX_TYPE +FFI_EXTERN ffi_type ffi_type_complex_float; +FFI_EXTERN ffi_type ffi_type_complex_double; +#if 0 +FFI_EXTERN ffi_type ffi_type_complex_longdouble; +#else +#define ffi_type_complex_longdouble ffi_type_complex_double +#endif +#endif +#endif /* LIBFFI_HIDE_BASIC_TYPES */ + +typedef enum { + FFI_OK = 0, + FFI_BAD_TYPEDEF, + FFI_BAD_ABI +} ffi_status; + +typedef struct { + ffi_abi abi; + unsigned nargs; + ffi_type **arg_types; + ffi_type *rtype; + unsigned bytes; + unsigned flags; +#ifdef FFI_EXTRA_CIF_FIELDS + FFI_EXTRA_CIF_FIELDS; +#endif +} ffi_cif; + +/* ---- Definitions for the raw API -------------------------------------- */ + +#ifndef FFI_SIZEOF_ARG +# if LONG_MAX == 2147483647 +# define FFI_SIZEOF_ARG 4 +# elif LONG_MAX == FFI_64_BIT_MAX +# define FFI_SIZEOF_ARG 8 +# endif +#endif + +#ifndef FFI_SIZEOF_JAVA_RAW +# define FFI_SIZEOF_JAVA_RAW FFI_SIZEOF_ARG +#endif + +typedef union { + ffi_sarg sint; + ffi_arg uint; + float flt; + char data[FFI_SIZEOF_ARG]; + void* ptr; +} ffi_raw; + +#if FFI_SIZEOF_JAVA_RAW == 4 && FFI_SIZEOF_ARG == 8 +/* This is a special case for mips64/n32 ABI (and perhaps others) where + sizeof(void *) is 4 and FFI_SIZEOF_ARG is 8. */ +typedef union { + signed int sint; + unsigned int uint; + float flt; + char data[FFI_SIZEOF_JAVA_RAW]; + void* ptr; +} ffi_java_raw; +#else +typedef ffi_raw ffi_java_raw; +#endif + + +FFI_API +void ffi_raw_call (ffi_cif *cif, + void (*fn)(void), + void *rvalue, + ffi_raw *avalue); + +FFI_API void ffi_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_raw *raw); +FFI_API void ffi_raw_to_ptrarray (ffi_cif *cif, ffi_raw *raw, void **args); +FFI_API size_t ffi_raw_size (ffi_cif *cif); + +/* This is analogous to the raw API, except it uses Java parameter + packing, even on 64-bit machines. I.e. on 64-bit machines longs + and doubles are followed by an empty 64-bit word. */ + +#if !FFI_NATIVE_RAW_API +FFI_API +void ffi_java_raw_call (ffi_cif *cif, + void (*fn)(void), + void *rvalue, + ffi_java_raw *avalue); +#endif + +FFI_API +void ffi_java_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_java_raw *raw); +FFI_API +void ffi_java_raw_to_ptrarray (ffi_cif *cif, ffi_java_raw *raw, void **args); +FFI_API +size_t ffi_java_raw_size (ffi_cif *cif); + +/* ---- Definitions for closures ----------------------------------------- */ + +#if FFI_CLOSURES + +#ifdef _MSC_VER +__declspec(align(8)) +#endif +typedef struct { +#if 0 + void *trampoline_table; + void *trampoline_table_entry; +#else + char tramp[FFI_TRAMPOLINE_SIZE]; +#endif + ffi_cif *cif; + void (*fun)(ffi_cif*,void*,void**,void*); + void *user_data; +} ffi_closure +#ifdef __GNUC__ + __attribute__((aligned (8))) +#endif + ; + +#ifndef __GNUC__ +# ifdef __sgi +# pragma pack 0 +# endif +#endif + +FFI_API void *ffi_closure_alloc (size_t size, void **code); +FFI_API void ffi_closure_free (void *); + +FFI_API ffi_status +ffi_prep_closure (ffi_closure*, + ffi_cif *, + void (*fun)(ffi_cif*,void*,void**,void*), + void *user_data) +#if defined(__GNUC__) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 405) + __attribute__((deprecated ("use ffi_prep_closure_loc instead"))) +#elif defined(__GNUC__) && __GNUC__ >= 3 + __attribute__((deprecated)) +#endif + ; + +FFI_API ffi_status +ffi_prep_closure_loc (ffi_closure*, + ffi_cif *, + void (*fun)(ffi_cif*,void*,void**,void*), + void *user_data, + void*codeloc); + +#ifdef __sgi +# pragma pack 8 +#endif +typedef struct { +#if 0 + void *trampoline_table; + void *trampoline_table_entry; +#else + char tramp[FFI_TRAMPOLINE_SIZE]; +#endif + ffi_cif *cif; + +#if !FFI_NATIVE_RAW_API + + /* If this is enabled, then a raw closure has the same layout + as a regular closure. We use this to install an intermediate + handler to do the transaltion, void** -> ffi_raw*. */ + + void (*translate_args)(ffi_cif*,void*,void**,void*); + void *this_closure; + +#endif + + void (*fun)(ffi_cif*,void*,ffi_raw*,void*); + void *user_data; + +} ffi_raw_closure; + +typedef struct { +#if 0 + void *trampoline_table; + void *trampoline_table_entry; +#else + char tramp[FFI_TRAMPOLINE_SIZE]; +#endif + + ffi_cif *cif; + +#if !FFI_NATIVE_RAW_API + + /* If this is enabled, then a raw closure has the same layout + as a regular closure. We use this to install an intermediate + handler to do the translation, void** -> ffi_raw*. */ + + void (*translate_args)(ffi_cif*,void*,void**,void*); + void *this_closure; + +#endif + + void (*fun)(ffi_cif*,void*,ffi_java_raw*,void*); + void *user_data; + +} ffi_java_raw_closure; + +FFI_API ffi_status +ffi_prep_raw_closure (ffi_raw_closure*, + ffi_cif *cif, + void (*fun)(ffi_cif*,void*,ffi_raw*,void*), + void *user_data); + +FFI_API ffi_status +ffi_prep_raw_closure_loc (ffi_raw_closure*, + ffi_cif *cif, + void (*fun)(ffi_cif*,void*,ffi_raw*,void*), + void *user_data, + void *codeloc); + +#if !FFI_NATIVE_RAW_API +FFI_API ffi_status +ffi_prep_java_raw_closure (ffi_java_raw_closure*, + ffi_cif *cif, + void (*fun)(ffi_cif*,void*,ffi_java_raw*,void*), + void *user_data); + +FFI_API ffi_status +ffi_prep_java_raw_closure_loc (ffi_java_raw_closure*, + ffi_cif *cif, + void (*fun)(ffi_cif*,void*,ffi_java_raw*,void*), + void *user_data, + void *codeloc); +#endif + +#endif /* FFI_CLOSURES */ + +#if FFI_GO_CLOSURES + +typedef struct { + void *tramp; + ffi_cif *cif; + void (*fun)(ffi_cif*,void*,void**,void*); +} ffi_go_closure; + +FFI_API ffi_status ffi_prep_go_closure (ffi_go_closure*, ffi_cif *, + void (*fun)(ffi_cif*,void*,void**,void*)); + +FFI_API void ffi_call_go (ffi_cif *cif, void (*fn)(void), void *rvalue, + void **avalue, void *closure); + +#endif /* FFI_GO_CLOSURES */ + +/* ---- Public interface definition -------------------------------------- */ + +FFI_API +ffi_status ffi_prep_cif(ffi_cif *cif, + ffi_abi abi, + unsigned int nargs, + ffi_type *rtype, + ffi_type **atypes); + +FFI_API +ffi_status ffi_prep_cif_var(ffi_cif *cif, + ffi_abi abi, + unsigned int nfixedargs, + unsigned int ntotalargs, + ffi_type *rtype, + ffi_type **atypes); + +FFI_API +void ffi_call(ffi_cif *cif, + void (*fn)(void), + void *rvalue, + void **avalue); + +FFI_API +ffi_status ffi_get_struct_offsets (ffi_abi abi, ffi_type *struct_type, + size_t *offsets); + +/* Useful for eliminating compiler warnings. */ +#define FFI_FN(f) ((void (*)(void))f) + +/* ---- Definitions shared with assembly code ---------------------------- */ + +#endif + +/* If these change, update src/mips/ffitarget.h. */ +#define FFI_TYPE_VOID 0 +#define FFI_TYPE_INT 1 +#define FFI_TYPE_FLOAT 2 +#define FFI_TYPE_DOUBLE 3 +#if 0 +#define FFI_TYPE_LONGDOUBLE 4 +#else +#define FFI_TYPE_LONGDOUBLE FFI_TYPE_DOUBLE +#endif +#define FFI_TYPE_UINT8 5 +#define FFI_TYPE_SINT8 6 +#define FFI_TYPE_UINT16 7 +#define FFI_TYPE_SINT16 8 +#define FFI_TYPE_UINT32 9 +#define FFI_TYPE_SINT32 10 +#define FFI_TYPE_UINT64 11 +#define FFI_TYPE_SINT64 12 +#define FFI_TYPE_STRUCT 13 +#define FFI_TYPE_POINTER 14 +#define FFI_TYPE_COMPLEX 15 + +/* This should always refer to the last type code (for sanity checks). */ +#define FFI_TYPE_LAST FFI_TYPE_COMPLEX + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/libffi_arm64/include/fficonfig.h b/c/libffi_arm64/include/fficonfig.h new file mode 100644 index 0000000..5768c29 --- /dev/null +++ b/c/libffi_arm64/include/fficonfig.h @@ -0,0 +1,215 @@ +/* fficonfig.h. Generated from fficonfig.h.in by configure. */ +/* fficonfig.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP + systems. This function is required for `alloca.c' support on those systems. + */ +/* #undef CRAY_STACKSEG_END */ + +/* Define to 1 if using `alloca.c'. */ +/* #undef C_ALLOCA */ + +/* Define to the flags needed for the .section .eh_frame directive. */ +/* #undef EH_FRAME_FLAGS */ + +/* Define this if you want extra debugging. */ +/* #undef FFI_DEBUG */ + +/* Cannot use PROT_EXEC on this target, so, we revert to alternative means */ +/* #undef FFI_EXEC_TRAMPOLINE_TABLE */ + +/* Define this if you want to enable pax emulated trampolines */ +/* #undef FFI_MMAP_EXEC_EMUTRAMP_PAX */ + +/* Cannot use malloc on this target, so, we revert to alternative means */ +/* #undef FFI_MMAP_EXEC_WRIT */ + +/* Define this if you do not want support for the raw API. */ +/* #undef FFI_NO_RAW_API */ + +/* Define this if you do not want support for aggregate types. */ +/* #undef FFI_NO_STRUCTS */ + +/* Define to 1 if you have `alloca', as a function or macro. */ +#define HAVE_ALLOCA 1 + +/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix). + */ +/* #undef HAVE_ALLOCA_H */ + +/* Define if your assembler supports .cfi_* directives. */ +/* #undef HAVE_AS_CFI_PSEUDO_OP */ + +/* Define if your assembler supports .register. */ +/* #undef HAVE_AS_REGISTER_PSEUDO_OP */ + +/* Define if the compiler uses zarch features. */ +/* #undef HAVE_AS_S390_ZARCH */ + +/* Define if your assembler and linker support unaligned PC relative relocs. + */ +/* #undef HAVE_AS_SPARC_UA_PCREL */ + +/* Define if your assembler supports unwind section type. */ +/* #undef HAVE_AS_X86_64_UNWIND_SECTION_TYPE */ + +/* Define if your assembler supports PC relative relocs. */ +/* #undef HAVE_AS_X86_PCREL */ + +/* Define to 1 if you have the <dlfcn.h> header file. */ +/* #undef HAVE_DLFCN_H */ + +/* Define if __attribute__((visibility("hidden"))) is supported. */ +/* #undef HAVE_HIDDEN_VISIBILITY_ATTRIBUTE */ + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define if you have the long double type and it is bigger than a double */ +/* #undef HAVE_LONG_DOUBLE */ + +/* Define if you support more than one size of the long double type */ +/* #undef HAVE_LONG_DOUBLE_VARIANT */ + +/* Define to 1 if you have the `memcpy' function. */ +/* #undef HAVE_MEMCPY */ + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `mkostemp' function. */ +/* #undef HAVE_MKOSTEMP */ + +/* Define to 1 if you have the `mmap' function. */ +/* #undef HAVE_MMAP */ + +/* Define if mmap with MAP_ANON(YMOUS) works. */ +/* #undef HAVE_MMAP_ANON */ + +/* Define if mmap of /dev/zero works. */ +/* #undef HAVE_MMAP_DEV_ZERO */ + +/* Define if read-only mmap of a plain file works. */ +/* #undef HAVE_MMAP_FILE */ + +/* Define if .eh_frame sections should be read-only. */ +/* #undef HAVE_RO_EH_FRAME */ + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +/* #undef HAVE_STRINGS_H */ + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the <sys/mman.h> header file. */ +/* #undef HAVE_SYS_MMAN_H */ + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +/* #undef HAVE_UNISTD_H */ + +/* Define to 1 if GNU symbol versioning is used for libatomic. */ +/* #undef LIBFFI_GNU_SYMBOL_VERSIONING */ + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "libffi" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "http://github.com/libffi/libffi/issues" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "libffi" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "libffi 3.3-rc0" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "libffi" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "3.3-rc0" + +/* The size of `double', as computed by sizeof. */ +#define SIZEOF_DOUBLE 8 + +/* The size of `long double', as computed by sizeof. */ +#define SIZEOF_LONG_DOUBLE 8 + +/* The size of `size_t', as computed by sizeof. */ +#define SIZEOF_SIZE_T 8 + +/* If using the C implementation of alloca, define if you know the + direction of stack growth for your system; otherwise it will be + automatically deduced at runtime. + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ +/* #undef STACK_DIRECTION */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if symbols are underscored. */ +/* #undef SYMBOL_UNDERSCORE */ + +/* Define this if you are using Purify and want to suppress spurious messages. + */ +/* #undef USING_PURIFY */ + +/* Version number of package */ +#define VERSION "3.3-rc0" + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif + +/* Define to `unsigned int' if <sys/types.h> does not define. */ +/* #undef size_t */ + + +#ifdef HAVE_HIDDEN_VISIBILITY_ATTRIBUTE +#ifdef LIBFFI_ASM +#ifdef __APPLE__ +#define FFI_HIDDEN(name) .private_extern name +#else +#define FFI_HIDDEN(name) .hidden name +#endif +#else +#define FFI_HIDDEN __attribute__ ((visibility ("hidden"))) +#endif +#else +#ifdef LIBFFI_ASM +#define FFI_HIDDEN(name) +#else +#define FFI_HIDDEN +#endif +#endif + diff --git a/c/libffi_arm64/include/ffitarget.h b/c/libffi_arm64/include/ffitarget.h new file mode 100644 index 0000000..ecb6d2d --- /dev/null +++ b/c/libffi_arm64/include/ffitarget.h @@ -0,0 +1,92 @@ +/* Copyright (c) 2009, 2010, 2011, 2012 ARM Ltd. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +``Software''), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef LIBFFI_TARGET_H +#define LIBFFI_TARGET_H + +#ifndef LIBFFI_H +#error "Please do not include ffitarget.h directly into your source. Use ffi.h instead." +#endif + +#ifndef LIBFFI_ASM +#ifdef __ILP32__ +#define FFI_SIZEOF_ARG 8 +#define FFI_SIZEOF_JAVA_RAW 4 +typedef unsigned long long ffi_arg; +typedef signed long long ffi_sarg; +#elif defined(_M_ARM64) +#define FFI_SIZEOF_ARG 8 +typedef unsigned long long ffi_arg; +typedef signed long long ffi_sarg; +#else +typedef unsigned long ffi_arg; +typedef signed long ffi_sarg; +#endif + +typedef enum ffi_abi + { + FFI_FIRST_ABI = 0, + FFI_SYSV, + FFI_LAST_ABI, + FFI_DEFAULT_ABI = FFI_SYSV + } ffi_abi; +#endif + +/* ---- Definitions for closures ----------------------------------------- */ + +#define FFI_CLOSURES 1 +#define FFI_NATIVE_RAW_API 0 + +#if defined (FFI_EXEC_TRAMPOLINE_TABLE) && FFI_EXEC_TRAMPOLINE_TABLE + +#ifdef __MACH__ +#define FFI_TRAMPOLINE_SIZE 16 +#define FFI_TRAMPOLINE_CLOSURE_OFFSET 16 +#else +#error "No trampoline table implementation" +#endif + +#else +#define FFI_TRAMPOLINE_SIZE 24 +#define FFI_TRAMPOLINE_CLOSURE_OFFSET FFI_TRAMPOLINE_SIZE +#endif + +#ifdef _M_ARM64 +#define FFI_EXTRA_CIF_FIELDS unsigned is_variadic +#endif + +/* ---- Internal ---- */ + +#if defined (__APPLE__) +#define FFI_TARGET_SPECIFIC_VARIADIC +#define FFI_EXTRA_CIF_FIELDS unsigned aarch64_nfixedargs +#elif !defined(_M_ARM64) +/* iOS and Windows reserve x18 for the system. Disable Go closures until + a new static chain is chosen. */ +#define FFI_GO_CLOSURES 1 +#endif + +#ifndef _M_ARM64 +/* No complex type on Windows */ +#define FFI_TARGET_HAS_COMPLEX_TYPE +#endif + +#endif diff --git a/c/libffi_msvc/LICENSE b/c/libffi_x86_x64/LICENSE index f591795..f591795 100644 --- a/c/libffi_msvc/LICENSE +++ b/c/libffi_x86_x64/LICENSE diff --git a/c/libffi_msvc/README b/c/libffi_x86_x64/README index 97a12cf..97a12cf 100644 --- a/c/libffi_msvc/README +++ b/c/libffi_x86_x64/README diff --git a/c/libffi_msvc/README.ctypes b/c/libffi_x86_x64/README.ctypes index 17e8a40..17e8a40 100644 --- a/c/libffi_msvc/README.ctypes +++ b/c/libffi_x86_x64/README.ctypes diff --git a/c/libffi_msvc/ffi.c b/c/libffi_x86_x64/ffi.c index 836f171..b9e324f 100644 --- a/c/libffi_msvc/ffi.c +++ b/c/libffi_x86_x64/ffi.c @@ -103,7 +103,7 @@ void ffi_prep_args(char *stack, extended_cif *ecif) } } #ifdef _WIN64 - else if (z > 8) + else if (z != 1 && z != 2 && z != 4 && z != 8) { /* On Win64, if a single argument takes more than 8 bytes, then it is always passed by reference. */ @@ -144,9 +144,11 @@ ffi_status ffi_prep_cif_machdep(ffi_cif *cif) /* MSVC returns small structures in registers. Put in cif->flags the value FFI_TYPE_STRUCT only if the structure is big enough; otherwise, put the 4- or 8-bytes integer type. */ - if (cif->rtype->size <= 4) + if (cif->rtype->size == 1 || + cif->rtype->size == 2 || + cif->rtype->size == 4) cif->flags = FFI_TYPE_INT; - else if (cif->rtype->size <= 8) + else if (cif->rtype->size == 8) cif->flags = FFI_TYPE_SINT64; else cif->flags = FFI_TYPE_STRUCT; @@ -287,16 +289,12 @@ ffi_closure_SYSV (ffi_closure *closure, char *argp) _asm fld DWORD PTR [eax] ; // asm ("flds (%0)" : : "r" (resp) : "st" ); } - else if (rtype == FFI_TYPE_DOUBLE) + else if (rtype == FFI_TYPE_DOUBLE || rtype == FFI_TYPE_LONGDOUBLE) { _asm mov eax, resp ; _asm fld QWORD PTR [eax] ; // asm ("fldl (%0)" : : "r" (resp) : "st", "st(1)" ); } - else if (rtype == FFI_TYPE_LONGDOUBLE) - { -// asm ("fldt (%0)" : : "r" (resp) : "st", "st(1)" ); - } else if (rtype == FFI_TYPE_SINT64) { _asm mov edx, resp ; @@ -307,6 +305,10 @@ ffi_closure_SYSV (ffi_closure *closure, char *argp) // : : "r"(resp) // : "eax", "edx"); } + else if (rtype == FFI_TYPE_STRUCT) + { + _asm mov eax, resp ; + } #else /* now, do a generic return based on the value of rtype */ if (rtype == FFI_TYPE_INT) @@ -317,14 +319,10 @@ ffi_closure_SYSV (ffi_closure *closure, char *argp) { asm ("flds (%0)" : : "r" (resp) : "st" ); } - else if (rtype == FFI_TYPE_DOUBLE) + else if (rtype == FFI_TYPE_DOUBLE || rtype == FFI_TYPE_LONGDOUBLE) { asm ("fldl (%0)" : : "r" (resp) : "st", "st(1)" ); } - else if (rtype == FFI_TYPE_LONGDOUBLE) - { - asm ("fldt (%0)" : : "r" (resp) : "st", "st(1)" ); - } else if (rtype == FFI_TYPE_SINT64) { asm ("movl 0(%0),%%eax;" @@ -332,6 +330,10 @@ ffi_closure_SYSV (ffi_closure *closure, char *argp) : : "r"(resp) : "eax", "edx"); } + else if (rtype == FFI_TYPE_STRUCT) + { + asm ("movl %0,%%eax" : : "r" (resp) : "eax"); + } #endif #endif @@ -340,6 +342,8 @@ ffi_closure_SYSV (ffi_closure *closure, char *argp) result types except for floats; we have to 'mov xmm0, rax' in the caller to correct this. */ + if (rtype == FFI_TYPE_STRUCT) + return resp; return *(void **)resp; #endif } @@ -378,7 +382,7 @@ ffi_prep_incoming_args_SYSV(char *stack, void **rvalue, /* because we're little endian, this is what it turns into. */ #ifdef _WIN64 - if (z > 8) + if (z != 1 && z != 2 && z != 4 && z != 8) { /* On Win64, if a single argument takes more than 8 bytes, then it is always passed by reference. */ @@ -447,6 +451,11 @@ ffi_prep_closure_loc (ffi_closure* closure, || cif->arg_types[3]->type == FFI_TYPE_DOUBLE)) mask |= 8; + /* if we return a non-small struct, then the first argument is a pointer + * to the return area, and all real arguments are shifted by one */ + if (cif->flags == FFI_TYPE_STRUCT) + mask = (mask & ~8) << 1; + /* 41 BB ---- mov r11d,mask */ BYTES("\x41\xBB"); INT(mask); diff --git a/c/libffi_msvc/ffi.h b/c/libffi_x86_x64/ffi.h index 97cdb59..97cdb59 100644 --- a/c/libffi_msvc/ffi.h +++ b/c/libffi_x86_x64/ffi.h diff --git a/c/libffi_msvc/ffi_common.h b/c/libffi_x86_x64/ffi_common.h index 43fb83b..43fb83b 100644 --- a/c/libffi_msvc/ffi_common.h +++ b/c/libffi_x86_x64/ffi_common.h diff --git a/c/libffi_msvc/fficonfig.h b/c/libffi_x86_x64/fficonfig.h index c14f653..c14f653 100644 --- a/c/libffi_msvc/fficonfig.h +++ b/c/libffi_x86_x64/fficonfig.h diff --git a/c/libffi_msvc/ffitarget.h b/c/libffi_x86_x64/ffitarget.h index 85f5ee8..85f5ee8 100644 --- a/c/libffi_msvc/ffitarget.h +++ b/c/libffi_x86_x64/ffitarget.h diff --git a/c/libffi_msvc/prep_cif.c b/c/libffi_x86_x64/prep_cif.c index 5dacfff..df94a98 100644 --- a/c/libffi_msvc/prep_cif.c +++ b/c/libffi_x86_x64/prep_cif.c @@ -117,7 +117,10 @@ ffi_status ffi_prep_cif(/*@out@*/ /*@partial@*/ ffi_cif *cif, /* Make space for the return structure pointer */ if (cif->rtype->type == FFI_TYPE_STRUCT #ifdef _WIN32 - && (cif->rtype->size > 8) /* MSVC returns small structs in registers */ + && (cif->rtype->size != 1) /* MSVC returns small structs in registers */ + && (cif->rtype->size != 2) + && (cif->rtype->size != 4) + && (cif->rtype->size != 8) #endif #ifdef SPARC && (cif->abi != FFI_V9 || cif->rtype->size > 32) diff --git a/c/libffi_msvc/types.c b/c/libffi_x86_x64/types.c index 4433ac2..4433ac2 100644 --- a/c/libffi_msvc/types.c +++ b/c/libffi_x86_x64/types.c diff --git a/c/libffi_msvc/win32.c b/c/libffi_x86_x64/win32.c index d1149a8..d1149a8 100644 --- a/c/libffi_msvc/win32.c +++ b/c/libffi_x86_x64/win32.c diff --git a/c/libffi_msvc/win64.asm b/c/libffi_x86_x64/win64.asm index 301188b..301188b 100644 --- a/c/libffi_msvc/win64.asm +++ b/c/libffi_x86_x64/win64.asm diff --git a/c/libffi_msvc/win64.obj b/c/libffi_x86_x64/win64.obj Binary files differindex 38d3cd1..38d3cd1 100644 --- a/c/libffi_msvc/win64.obj +++ b/c/libffi_x86_x64/win64.obj diff --git a/c/misc_win32.h b/c/misc_win32.h index 07b76c1..156cf5d 100644 --- a/c/misc_win32.h +++ b/c/misc_win32.h @@ -124,8 +124,10 @@ static PyObject *b_getwinerror(PyObject *self, PyObject *args, PyObject *kwds) s_buf[--len] = L'\0'; message = PyUnicode_FromWideChar(s_buf, len); } - if (message != NULL) + if (message != NULL) { v = Py_BuildValue("(iO)", err, message); + Py_DECREF(message); + } else v = NULL; LocalFree(s_buf); @@ -168,7 +170,6 @@ static PyObject *b_getwinerror(PyObject *self, PyObject *args, PyObject *kwds) /* Only seen this in out of mem situations */ sprintf(s_small_buf, "Windows Error 0x%X", err); s = s_small_buf; - s_buf = NULL; } else { s = s_buf; /* remove trailing cr/lf and dots */ diff --git a/c/realize_c_type.c b/c/realize_c_type.c index 082c488..82629b7 100644 --- a/c/realize_c_type.c +++ b/c/realize_c_type.c @@ -413,19 +413,12 @@ _realize_c_struct_or_union(builder_c_t *builder, int sindex) } static PyObject * -realize_c_type_or_func(builder_c_t *builder, - _cffi_opcode_t opcodes[], int index) +realize_c_type_or_func_now(builder_c_t *builder, _cffi_opcode_t op, + _cffi_opcode_t opcodes[], int index) { PyObject *x, *y, *z; - _cffi_opcode_t op = opcodes[index]; Py_ssize_t length = -1; - if ((((uintptr_t)op) & 1) == 0) { - x = (PyObject *)op; - Py_INCREF(x); - return x; - } - switch (_CFFI_GETOP(op)) { case _CFFI_OP_PRIMITIVE: @@ -643,6 +636,36 @@ realize_c_type_or_func(builder_c_t *builder, return NULL; } + return x; +} + +static int _realize_recursion_level; + +static PyObject * +realize_c_type_or_func(builder_c_t *builder, + _cffi_opcode_t opcodes[], int index) +{ + PyObject *x; + _cffi_opcode_t op = opcodes[index]; + + if ((((uintptr_t)op) & 1) == 0) { + x = (PyObject *)op; + Py_INCREF(x); + return x; + } + + if (_realize_recursion_level >= 1000) { + PyErr_Format(PyExc_RuntimeError, + "type-building recursion too deep or infinite. " + "This is known to occur e.g. in ``struct s { void(*callable)" + "(struct s); }''. Please report if you get this error and " + "really need support for your case."); + return NULL; + } + _realize_recursion_level++; + x = realize_c_type_or_func_now(builder, op, opcodes, index); + _realize_recursion_level--; + if (x != NULL && opcodes == builder->ctx.types && opcodes[index] != x) { assert((((uintptr_t)x) & 1) == 0); assert((((uintptr_t)opcodes[index]) & 1) == 1); @@ -650,7 +673,7 @@ realize_c_type_or_func(builder_c_t *builder, opcodes[index] = x; } return x; -}; +} static CTypeDescrObject * realize_c_func_return_type(builder_c_t *builder, diff --git a/c/test_c.py b/c/test_c.py index da5f751..654584d 100644 --- a/c/test_c.py +++ b/c/test_c.py @@ -1,18 +1,23 @@ import py +import pytest + def _setup_path(): import os, sys - if '__pypy__' in sys.builtin_module_names: - py.test.skip("_cffi_backend.c: not tested on top of pypy, " - "use pypy/module/_cffi_backend/test/ instead.") sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) _setup_path() from _cffi_backend import * -from _cffi_backend import _testfunc, _get_types, _get_common_types, __version__ +from _cffi_backend import _get_types, _get_common_types +try: + from _cffi_backend import _testfunc +except ImportError: + def _testfunc(num): + pytest.skip("_testunc() not available") +from _cffi_backend import __version__ # ____________________________________________________________ import sys -assert __version__ == "1.12.2", ("This test_c.py file is for testing a version" +assert __version__ == "1.15.0", ("This test_c.py file is for testing a version" " of cffi that differs from the one that we" " get from 'import _cffi_backend'") if sys.version_info < (3,): @@ -63,8 +68,10 @@ def find_and_load_library(name, flags=RTLD_NOW): path = ctypes.util.find_library(name) if path is None and name == 'c': assert sys.platform == 'win32' - assert sys.version_info >= (3,) - py.test.skip("dlopen(None) cannot work on Windows with Python 3") + assert (sys.version_info >= (3,) or + '__pypy__' in sys.builtin_module_names) + py.test.skip("dlopen(None) cannot work on Windows " + "with PyPy or Python 3") return load_library(path, flags) def test_load_library(): @@ -107,7 +114,7 @@ def test_cast_to_signed_char(): p = new_primitive_type("signed char") x = cast(p, -65 + 17*256) assert repr(x) == "<cdata 'signed char' -65>" - assert repr(type(x)) == "<%s '_cffi_backend.CData'>" % type_or_class + assert repr(type(x)) == "<%s '_cffi_backend._CDataBase'>" % type_or_class assert int(x) == -65 x = cast(p, -66 + (1<<199)*256) assert repr(x) == "<cdata 'signed char' -66>" @@ -315,8 +322,10 @@ def test_reading_pointer_to_int(): assert p[0] == 0 p = newp(BPtr, 5000) assert p[0] == 5000 - py.test.raises(IndexError, "p[1]") - py.test.raises(IndexError, "p[-1]") + with pytest.raises(IndexError): + p[1] + with pytest.raises(IndexError): + p[-1] def test_reading_pointer_to_float(): BFloat = new_primitive_type("float") @@ -444,7 +453,8 @@ def test_cmp_none(): def test_invalid_indexing(): p = new_primitive_type("int") x = cast(p, 42) - py.test.raises(TypeError, "x[0]") + with pytest.raises(TypeError): + x[0] def test_default_str(): BChar = new_primitive_type("char") @@ -537,13 +547,16 @@ def test_array_instance(): assert len(a) == LENGTH for i in range(LENGTH): assert a[i] == 0 - py.test.raises(IndexError, "a[LENGTH]") - py.test.raises(IndexError, "a[-1]") + with pytest.raises(IndexError): + a[LENGTH] + with pytest.raises(IndexError): + a[-1] for i in range(LENGTH): a[i] = i * i + 1 for i in range(LENGTH): assert a[i] == i * i + 1 - e = py.test.raises(IndexError, "a[LENGTH+100] = 500") + with pytest.raises(IndexError) as e: + a[LENGTH+100] = 500 assert ('(expected %d < %d)' % (LENGTH+100, LENGTH)) in str(e.value) py.test.raises(TypeError, int, a) @@ -558,10 +571,14 @@ def test_array_of_unknown_length_instance(): a[i] -= i for i in range(42): assert a[i] == -i - py.test.raises(IndexError, "a[42]") - py.test.raises(IndexError, "a[-1]") - py.test.raises(IndexError, "a[42] = 123") - py.test.raises(IndexError, "a[-1] = 456") + with pytest.raises(IndexError): + a[42] + with pytest.raises(IndexError): + a[-1] + with pytest.raises(IndexError): + a[42] = 123 + with pytest.raises(IndexError): + a[-1] = 456 def test_array_of_unknown_length_instance_with_initializer(): p = new_primitive_type("int") @@ -609,10 +626,14 @@ def test_array_sub(): assert a == (p - 1) BPtr = new_pointer_type(new_primitive_type("short")) q = newp(BPtr, None) - py.test.raises(TypeError, "p - q") - py.test.raises(TypeError, "q - p") - py.test.raises(TypeError, "a - q") - e = py.test.raises(TypeError, "q - a") + with pytest.raises(TypeError): + p - q + with pytest.raises(TypeError): + q - p + with pytest.raises(TypeError): + a - q + with pytest.raises(TypeError) as e: + q - a assert str(e.value) == "cannot subtract cdata 'short *' and cdata 'int *'" def test_ptr_sub_unaligned(): @@ -625,8 +646,10 @@ def test_ptr_sub_unaligned(): assert b - a == (bi - 1240) // size_of_int() assert a - b == (1240 - bi) // size_of_int() else: - py.test.raises(ValueError, "b - a") - py.test.raises(ValueError, "a - b") + with pytest.raises(ValueError): + b - a + with pytest.raises(ValueError): + a - b def test_cast_primitive_from_cdata(): p = new_primitive_type("int") @@ -777,10 +800,12 @@ def test_struct_instance(): BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) p = cast(BStructPtr, 42) - e = py.test.raises(AttributeError, "p.a1") # opaque + with pytest.raises(AttributeError) as e: + p.a1 # opaque assert str(e.value) == ("cdata 'struct foo *' points to an opaque type: " "cannot read fields") - e = py.test.raises(AttributeError, "p.a1 = 10") # opaque + with pytest.raises(AttributeError) as e: + p.a1 = 10 # opaque assert str(e.value) == ("cdata 'struct foo *' points to an opaque type: " "cannot write fields") @@ -792,30 +817,41 @@ def test_struct_instance(): s.a2 = 123 assert s.a1 == 0 assert s.a2 == 123 - py.test.raises(OverflowError, "s.a1 = sys.maxsize+1") + with pytest.raises(OverflowError): + s.a1 = sys.maxsize+1 assert s.a1 == 0 - e = py.test.raises(AttributeError, "p.foobar") + with pytest.raises(AttributeError) as e: + p.foobar assert str(e.value) == "cdata 'struct foo *' has no field 'foobar'" - e = py.test.raises(AttributeError, "p.foobar = 42") + with pytest.raises(AttributeError) as e: + p.foobar = 42 assert str(e.value) == "cdata 'struct foo *' has no field 'foobar'" - e = py.test.raises(AttributeError, "s.foobar") + with pytest.raises(AttributeError) as e: + s.foobar assert str(e.value) == "cdata 'struct foo' has no field 'foobar'" - e = py.test.raises(AttributeError, "s.foobar = 42") + with pytest.raises(AttributeError) as e: + s.foobar = 42 assert str(e.value) == "cdata 'struct foo' has no field 'foobar'" j = cast(BInt, 42) - e = py.test.raises(AttributeError, "j.foobar") + with pytest.raises(AttributeError) as e: + j.foobar assert str(e.value) == "cdata 'int' has no attribute 'foobar'" - e = py.test.raises(AttributeError, "j.foobar = 42") + with pytest.raises(AttributeError) as e: + j.foobar = 42 assert str(e.value) == "cdata 'int' has no attribute 'foobar'" j = cast(new_pointer_type(BInt), 42) - e = py.test.raises(AttributeError, "j.foobar") + with pytest.raises(AttributeError) as e: + j.foobar assert str(e.value) == "cdata 'int *' has no attribute 'foobar'" - e = py.test.raises(AttributeError, "j.foobar = 42") + with pytest.raises(AttributeError) as e: + j.foobar = 42 assert str(e.value) == "cdata 'int *' has no attribute 'foobar'" pp = newp(new_pointer_type(BStructPtr), p) - e = py.test.raises(AttributeError, "pp.a1") + with pytest.raises(AttributeError) as e: + pp.a1 assert str(e.value) == "cdata 'struct foo * *' has no attribute 'a1'" - e = py.test.raises(AttributeError, "pp.a1 = 42") + with pytest.raises(AttributeError) as e: + pp.a1 = 42 assert str(e.value) == "cdata 'struct foo * *' has no attribute 'a1'" def test_union_instance(): @@ -1295,7 +1331,9 @@ def test_callback_exception(): except ImportError: import io as cStringIO # Python 3 import linecache - def matches(istr, ipattern): + def matches(istr, ipattern, ipattern38): + if sys.version_info >= (3, 8): + ipattern = ipattern38 str, pattern = istr, ipattern while '$' in pattern: i = pattern.index('$') @@ -1328,6 +1366,8 @@ def test_callback_exception(): try: linecache.getline = lambda *args: 'LINE' # hack: speed up PyPy tests sys.stderr = cStringIO.StringIO() + if hasattr(sys, '__unraisablehook__'): # work around pytest + sys.unraisablehook = sys.__unraisablehook__ # on recent CPythons assert f(100) == 300 assert sys.stderr.getvalue() == '' assert f(10000) == -42 @@ -1339,6 +1379,14 @@ Traceback (most recent call last): File "$", line $, in check_value $ ValueError: 42 +""", """\ +Exception ignored from cffi callback <function$Zcb1 at 0x$>: +Traceback (most recent call last): + File "$", line $, in Zcb1 + $ + File "$", line $, in check_value + $ +ValueError: 42 """) sys.stderr = cStringIO.StringIO() bigvalue = 20000 @@ -1347,6 +1395,12 @@ ValueError: 42 From cffi callback <function$Zcb1 at 0x$>: Trying to convert the result back to C: OverflowError: integer 60000 does not fit 'short' +""", """\ +Exception ignored from cffi callback <function$Zcb1 at 0x$>, trying to convert the result back to C: +Traceback (most recent call last): + File "$", line $, in test_callback_exception + $ +OverflowError: integer 60000 does not fit 'short' """) sys.stderr = cStringIO.StringIO() bigvalue = 20000 @@ -1384,11 +1438,24 @@ OverflowError: integer 60000 does not fit 'short' During the call to 'onerror', another exception occurred: TypeError: $integer$ +""", """\ +Exception ignored from cffi callback <function$Zcb1 at 0x$>, trying to convert the result back to C: +Traceback (most recent call last): + File "$", line $, in test_callback_exception + $ +OverflowError: integer 60000 does not fit 'short' +Exception ignored during handling of the above exception by 'onerror': +Traceback (most recent call last): + File "$", line $, in test_callback_exception + $ +TypeError: $integer$ """) # sys.stderr = cStringIO.StringIO() seen = "not a list" # this makes the oops() function crash assert ff(bigvalue) == -42 + # the $ after the AttributeError message are for the suggestions that + # will be added in Python 3.10 assert matches(sys.stderr.getvalue(), """\ From cffi callback <function$Zcb1 at 0x$>: Trying to convert the result back to C: @@ -1399,7 +1466,18 @@ During the call to 'onerror', another exception occurred: Traceback (most recent call last): File "$", line $, in oops $ -AttributeError: 'str' object has no attribute 'append' +AttributeError: 'str' object has no attribute 'append$ +""", """\ +Exception ignored from cffi callback <function$Zcb1 at 0x$>, trying to convert the result back to C: +Traceback (most recent call last): + File "$", line $, in test_callback_exception + $ +OverflowError: integer 60000 does not fit 'short' +Exception ignored during handling of the above exception by 'onerror': +Traceback (most recent call last): + File "$", line $, in oops + $ +AttributeError: 'str' object has no attribute 'append$ """) finally: sys.stderr = orig_stderr @@ -1434,7 +1512,7 @@ def test_a_lot_of_callbacks(): def make_callback(m): def cb(n): return n + m - return callback(BFunc, cb, 42) # 'cb' and 'BFunc' go out of scope + return callback(BFunc, cb, 42) # 'cb' goes out of scope # flist = [make_callback(i) for i in range(BIGNUM)] for i, f in enumerate(flist): @@ -1636,7 +1714,8 @@ def test_enum_in_struct(): assert ("an integer is required" in msg or # CPython "unsupported operand type for int(): 'NoneType'" in msg or # old PyPys "expected integer, got NoneType object" in msg) # newer PyPys - py.test.raises(TypeError, 'p.a1 = "def"') + with pytest.raises(TypeError): + p.a1 = "def" if sys.version_info < (3,): BEnum2 = new_enum_type(unicode("foo"), (unicode('abc'),), (5,), BInt) assert string(cast(BEnum2, 5)) == 'abc' @@ -1766,14 +1845,17 @@ def test_bitfield_instance(): p.a1 = -1 assert p.a1 == -1 p.a1 = 0 - py.test.raises(OverflowError, "p.a1 = 2") + with pytest.raises(OverflowError): + p.a1 = 2 assert p.a1 == 0 # p.a1 = -1 p.a2 = 3 p.a3 = -4 - py.test.raises(OverflowError, "p.a3 = 4") - e = py.test.raises(OverflowError, "p.a3 = -5") + with pytest.raises(OverflowError): + p.a3 = 4 + with pytest.raises(OverflowError) as e: + p.a3 = -5 assert str(e.value) == ("value -5 outside the range allowed by the " "bit field width: -4 <= x <= 3") assert p.a1 == -1 and p.a2 == 3 and p.a3 == -4 @@ -1782,7 +1864,8 @@ def test_bitfield_instance(): # allows also setting the value "1" (it still gets read back as -1) p.a1 = 1 assert p.a1 == -1 - e = py.test.raises(OverflowError, "p.a1 = -2") + with pytest.raises(OverflowError) as e: + p.a1 = -2 assert str(e.value) == ("value -2 outside the range allowed by the " "bit field width: -1 <= x <= 1") @@ -1842,14 +1925,17 @@ def test_assign_string(): assert string(a[2]) == b"." a[2] = b"12345" assert string(a[2]) == b"12345" - e = py.test.raises(IndexError, 'a[2] = b"123456"') + with pytest.raises(IndexError) as e: + a[2] = b"123456" assert 'char[5]' in str(e.value) assert 'got 6 characters' in str(e.value) def test_add_error(): x = cast(new_primitive_type("int"), 42) - py.test.raises(TypeError, "x + 1") - py.test.raises(TypeError, "x - 1") + with pytest.raises(TypeError): + x + 1 + with pytest.raises(TypeError): + x - 1 def test_void_errors(): py.test.raises(ValueError, alignof, new_void_type()) @@ -2181,8 +2267,10 @@ def _test_wchar_variant(typename): s = newp(BStructPtr) s.a1 = u+'\x00' assert s.a1 == u+'\x00' - py.test.raises(TypeError, "s.a1 = b'a'") - py.test.raises(TypeError, "s.a1 = bytechr(0xFF)") + with pytest.raises(TypeError): + s.a1 = b'a' + with pytest.raises(TypeError): + s.a1 = bytechr(0xFF) s.a1 = u+'\u1234' assert s.a1 == u+'\u1234' if pyuni4: @@ -2196,7 +2284,8 @@ def _test_wchar_variant(typename): s.a1 = u+'\ud807\udf44' assert s.a1 == u+'\U00011f44' else: - py.test.raises(TypeError, "s.a1 = u+'\U00012345'") + with pytest.raises(TypeError): + s.a1 = u+'\U00012345' # BWCharArray = new_array_type(BWCharP, None) a = newp(BWCharArray, u+'hello \u1234 world') @@ -2220,7 +2309,8 @@ def _test_wchar_variant(typename): assert list(a) == expected got = [a[i] for i in range(4)] assert got == expected - py.test.raises(IndexError, 'a[4]') + with pytest.raises(IndexError): + a[4] # w = cast(BWChar, 'a') assert repr(w) == "<cdata '%s' %s'a'>" % (typename, mandatory_u_prefix) @@ -2352,9 +2442,11 @@ def test_owning_repr(): def test_cannot_dereference_void(): BVoidP = new_pointer_type(new_void_type()) p = cast(BVoidP, 123456) - py.test.raises(TypeError, "p[0]") + with pytest.raises(TypeError): + p[0] p = cast(BVoidP, 0) - py.test.raises((TypeError, RuntimeError), "p[0]") + with pytest.raises((TypeError, RuntimeError)): + p[0] def test_iter(): BInt = new_primitive_type("int") @@ -2377,12 +2469,12 @@ def test_cmp(): assert (q == p) is False assert (q != p) is True if strict_compare: - py.test.raises(TypeError, "p < q") - py.test.raises(TypeError, "p <= q") - py.test.raises(TypeError, "q < p") - py.test.raises(TypeError, "q <= p") - py.test.raises(TypeError, "p > q") - py.test.raises(TypeError, "p >= q") + with pytest.raises(TypeError): p < q + with pytest.raises(TypeError): p <= q + with pytest.raises(TypeError): q < p + with pytest.raises(TypeError): q <= p + with pytest.raises(TypeError): p > q + with pytest.raises(TypeError): p >= q r = cast(BVoidP, p) assert (p < r) is False assert (p <= r) is True @@ -2428,7 +2520,8 @@ def test_buffer(): try: expected = b"hi there\x00"[i] except IndexError: - py.test.raises(IndexError, "buf[i]") + with pytest.raises(IndexError): + buf[i] else: assert buf[i] == bitem2bchr(expected) # --mb_slice-- @@ -2455,15 +2548,18 @@ def test_buffer(): try: expected[i] = bytechr(i & 0xff) except IndexError: - py.test.raises(IndexError, "buf[i] = bytechr(i & 0xff)") + with pytest.raises(IndexError): + buf[i] = bytechr(i & 0xff) else: buf[i] = bytechr(i & 0xff) assert list(buf) == expected # --mb_ass_slice-- buf[:] = b"hi there\x00" assert list(buf) == list(c) == list(map(bitem2bchr, b"hi there\x00")) - py.test.raises(ValueError, 'buf[:] = b"shorter"') - py.test.raises(ValueError, 'buf[:] = b"this is much too long!"') + with pytest.raises(ValueError): + buf[:] = b"shorter" + with pytest.raises(ValueError): + buf[:] = b"this is much too long!" buf[4:2] = b"" # no effect, but should work assert buf[:] == b"hi there\x00" buf[:2] = b"HI" @@ -2499,8 +2595,8 @@ def test_errno(): assert get_errno() == 95 def test_errno_callback(): - if globals().get('PY_DOT_PY') == '2.5': - py.test.skip("cannot run this test on py.py with Python 2.5") + if globals().get('PY_DOT_PY'): + py.test.skip("cannot run this test on py.py (e.g. fails on Windows)") set_errno(95) def cb(): e = get_errno() @@ -2537,14 +2633,16 @@ def test_bug_delitem(): BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) x = newp(BCharP) - py.test.raises(TypeError, "del x[0]") + with pytest.raises(TypeError): + del x[0] def test_bug_delattr(): BLong = new_primitive_type("long") BStruct = new_struct_type("struct foo") complete_struct_or_union(BStruct, [('a1', BLong, -1)]) x = newp(new_pointer_type(BStruct)) - py.test.raises(AttributeError, "del x.a1") + with pytest.raises(AttributeError): + del x.a1 def test_variable_length_struct(): py.test.skip("later") @@ -2562,7 +2660,8 @@ def test_variable_length_struct(): assert sizeof(x) == 6 * size_of_long() x[4] = 123 assert x[4] == 123 - py.test.raises(IndexError, "x[5]") + with pytest.raises(IndexError): + x[5] assert len(x.a2) == 5 # py.test.raises(TypeError, newp, BStructP, [123]) @@ -2814,7 +2913,8 @@ def test_bool_forbidden_cases(): BCharP = new_pointer_type(new_primitive_type("char")) p = newp(BCharP, b'X') q = cast(BBoolP, p) - py.test.raises(ValueError, "q[0]") + with pytest.raises(ValueError): + q[0] py.test.raises(TypeError, newp, BBoolP, b'\x00') assert newp(BBoolP, 0)[0] is False assert newp(BBoolP, 1)[0] is True @@ -3114,8 +3214,10 @@ def test_slice(): assert c[1] == 123 assert c[3] == 456 assert d[2] == 456 - py.test.raises(IndexError, "d[3]") - py.test.raises(IndexError, "d[-1]") + with pytest.raises(IndexError): + d[3] + with pytest.raises(IndexError): + d[-1] def test_slice_ptr(): BIntP = new_pointer_type(new_primitive_type("int")) @@ -3133,7 +3235,8 @@ def test_slice_array_checkbounds(): c = newp(BIntArray, 5) c[0:5] assert len(c[5:5]) == 0 - py.test.raises(IndexError, "c[-1:1]") + with pytest.raises(IndexError): + c[-1:1] cp = c + 0 cp[-1:1] @@ -3141,17 +3244,23 @@ def test_nonstandard_slice(): BIntP = new_pointer_type(new_primitive_type("int")) BIntArray = new_array_type(BIntP, None) c = newp(BIntArray, 5) - e = py.test.raises(IndexError, "c[:5]") + with pytest.raises(IndexError) as e: + c[:5] assert str(e.value) == "slice start must be specified" - e = py.test.raises(IndexError, "c[4:]") + with pytest.raises(IndexError) as e: + c[4:] assert str(e.value) == "slice stop must be specified" - e = py.test.raises(IndexError, "c[1:2:3]") + with pytest.raises(IndexError) as e: + c[1:2:3] assert str(e.value) == "slice with step not supported" - e = py.test.raises(IndexError, "c[1:2:1]") + with pytest.raises(IndexError) as e: + c[1:2:1] assert str(e.value) == "slice with step not supported" - e = py.test.raises(IndexError, "c[4:2]") + with pytest.raises(IndexError) as e: + c[4:2] assert str(e.value) == "slice start > stop" - e = py.test.raises(IndexError, "c[6:6]") + with pytest.raises(IndexError) as e: + c[6:6] assert str(e.value) == "index too large (expected 6 <= 5)" def test_setslice(): @@ -3165,9 +3274,11 @@ def test_setslice(): assert list(c) == [0, 100, 300, 400, 0] cp[-1:1] = iter([500, 600]) assert list(c) == [0, 100, 500, 600, 0] - py.test.raises(ValueError, "cp[-1:1] = [1000]") + with pytest.raises(ValueError): + cp[-1:1] = [1000] assert list(c) == [0, 100, 1000, 600, 0] - py.test.raises(ValueError, "cp[-1:1] = (700, 800, 900)") + with pytest.raises(ValueError): + cp[-1:1] = (700, 800, 900) assert list(c) == [0, 100, 700, 800, 0] def test_setslice_array(): @@ -3427,10 +3538,14 @@ def test_struct_array_no_length(): assert sizeof(q[0]) == sizeof(BStruct) # # error cases - py.test.raises(IndexError, "p.y[4]") - py.test.raises(TypeError, "p.y = cast(BIntP, 0)") - py.test.raises(TypeError, "p.y = 15") - py.test.raises(TypeError, "p.y = None") + with pytest.raises(IndexError): + p.y[4] + with pytest.raises(TypeError): + p.y = cast(BIntP, 0) + with pytest.raises(TypeError): + p.y = 15 + with pytest.raises(TypeError): + p.y = None # # accepting this may be specified by the C99 standard, # or a GCC strangeness... @@ -3452,6 +3567,15 @@ def test_struct_array_no_length(): assert p.a[1] == 20 assert p.a[2] == 30 assert p.a[3] == 0 + # + # struct of struct of varsized array + BStruct2 = new_struct_type("bar") + complete_struct_or_union(BStruct2, [('head', BInt), + ('tail', BStruct)]) + for i in range(2): # try to detect heap overwrites + p = newp(new_pointer_type(BStruct2), [100, [200, list(range(50))]]) + assert p.tail.y[49] == 49 + def test_struct_array_no_length_explicit_position(): BInt = new_primitive_type("int") @@ -3526,8 +3650,10 @@ def test_ass_slice(): p[2:5] = [b"*", b"Z", b"T"] p[1:3] = b"XY" assert list(p) == [b"f", b"X", b"Y", b"Z", b"T", b"r", b"\x00"] - py.test.raises(TypeError, "p[1:5] = u+'XYZT'") - py.test.raises(TypeError, "p[1:5] = [1, 2, 3, 4]") + with pytest.raises(TypeError): + p[1:5] = u+'XYZT' + with pytest.raises(TypeError): + p[1:5] = [1, 2, 3, 4] # for typename in ["wchar_t", "char16_t", "char32_t"]: BUniChar = new_primitive_type(typename) @@ -3536,8 +3662,10 @@ def test_ass_slice(): p[2:5] = [u+"*", u+"Z", u+"T"] p[1:3] = u+"XY" assert list(p) == [u+"f", u+"X", u+"Y", u+"Z", u+"T", u+"r", u+"\x00"] - py.test.raises(TypeError, "p[1:5] = b'XYZT'") - py.test.raises(TypeError, "p[1:5] = [1, 2, 3, 4]") + with pytest.raises(TypeError): + p[1:5] = b'XYZT' + with pytest.raises(TypeError): + p[1:5] = [1, 2, 3, 4] def test_void_p_arithmetic(): BVoid = new_void_type() @@ -3548,10 +3676,14 @@ def test_void_p_arithmetic(): assert int(cast(BInt, p - (-42))) == 100042 assert (p + 42) - p == 42 q = cast(new_pointer_type(new_primitive_type("char")), 100000) - py.test.raises(TypeError, "p - q") - py.test.raises(TypeError, "q - p") - py.test.raises(TypeError, "p + cast(new_primitive_type('int'), 42)") - py.test.raises(TypeError, "p - cast(new_primitive_type('int'), 42)") + with pytest.raises(TypeError): + p - q + with pytest.raises(TypeError): + q - p + with pytest.raises(TypeError): + p + cast(new_primitive_type('int'), 42) + with pytest.raises(TypeError): + p - cast(new_primitive_type('int'), 42) def test_sizeof_sliced_array(): BInt = new_primitive_type("int") @@ -3758,7 +3890,9 @@ def test_from_buffer_types(): BIntP = new_pointer_type(BInt) BIntA = new_array_type(BIntP, None) lst = [-12345678, 87654321, 489148] - bytestring = buffer(newp(BIntA, lst))[:] + b'XYZ' + bytestring = bytearray(buffer(newp(BIntA, lst))[:] + b'XYZ') + lst2 = lst + [42, -999999999] + bytestring2 = bytearray(buffer(newp(BIntA, lst2))[:] + b'XYZ') # p1 = from_buffer(BIntA, bytestring) # int[] assert typeof(p1) is BIntA @@ -3766,11 +3900,25 @@ def test_from_buffer_types(): assert p1[0] == lst[0] assert p1[1] == lst[1] assert p1[2] == lst[2] - py.test.raises(IndexError, "p1[3]") - py.test.raises(IndexError, "p1[-1]") + with pytest.raises(IndexError): + p1[3] + with pytest.raises(IndexError): + p1[-1] # py.test.raises(TypeError, from_buffer, BInt, bytestring) - py.test.raises(TypeError, from_buffer, BIntP, bytestring) + # + p2 = from_buffer(BIntP, bytestring) # int * + assert p2 == p1 or 'PY_DOT_PY' in globals() + # note: on py.py ^^^, bytearray buffers are not emulated well enough + assert typeof(p2) is BIntP + assert p2[0] == lst[0] + assert p2[1] == lst[1] + assert p2[2] == lst[2] + # hopefully does not crash, but doesn't raise an exception: + p2[3] + p2[-1] + # not enough data even for one, but this is not enforced: + from_buffer(BIntP, b"") # BIntA2 = new_array_type(BIntP, 2) p2 = from_buffer(BIntA2, bytestring) # int[2] @@ -3778,9 +3926,11 @@ def test_from_buffer_types(): assert len(p2) == 2 assert p2[0] == lst[0] assert p2[1] == lst[1] - py.test.raises(IndexError, "p2[2]") - py.test.raises(IndexError, "p2[-1]") - assert p2 == p1 + with pytest.raises(IndexError): + p2[2] + with pytest.raises(IndexError): + p2[-1] + assert p2 == p1 or 'PY_DOT_PY' in globals() # BIntA4 = new_array_type(BIntP, 4) # int[4]: too big py.test.raises(ValueError, from_buffer, BIntA4, bytestring) @@ -3790,12 +3940,37 @@ def test_from_buffer_types(): ('a2', BInt, -1)]) BStructP = new_pointer_type(BStruct) BStructA = new_array_type(BStructP, None) - p1 = from_buffer(BStructA, bytestring) # struct[] - assert len(p1) == 1 + p1 = from_buffer(BStructA, bytestring2) # struct[] + assert len(p1) == 2 assert typeof(p1) is BStructA - assert p1[0].a1 == lst[0] - assert p1[0].a2 == lst[1] - py.test.raises(IndexError, "p1[1]") + assert p1[0].a1 == lst2[0] + assert p1[0].a2 == lst2[1] + assert p1[1].a1 == lst2[2] + assert p1[1].a2 == lst2[3] + with pytest.raises(IndexError): + p1[2] + with pytest.raises(IndexError): + p1[-1] + assert repr(p1) == "<cdata 'foo[]' buffer len 2 from 'bytearray' object>" + # + p2 = from_buffer(BStructP, bytestring2) # 'struct *' + assert p2 == p1 or 'PY_DOT_PY' in globals() + assert typeof(p2) is BStructP + assert p2.a1 == lst2[0] + assert p2.a2 == lst2[1] + assert p2[0].a1 == lst2[0] + assert p2[0].a2 == lst2[1] + assert p2[1].a1 == lst2[2] + assert p2[1].a2 == lst2[3] + # does not crash: + p2[2] + p2[-1] + # not enough data even for one, but this is not enforced: + from_buffer(BStructP, b"") + from_buffer(BStructP, b"1234567") + # + release(p1) + assert repr(p1) == "<cdata 'foo[]' buffer RELEASED>" # BEmptyStruct = new_struct_type("empty") complete_struct_or_union(BEmptyStruct, [], Ellipsis, 0) @@ -3809,7 +3984,51 @@ def test_from_buffer_types(): p1 = from_buffer(BEmptyStructA5, bytestring) # struct empty[5] assert typeof(p1) is BEmptyStructA5 assert len(p1) == 5 - assert cast(BIntP, p1) == from_buffer(BIntA, bytestring) + assert (cast(BIntP, p1) == from_buffer(BIntA, bytestring) + or 'PY_DOT_PY' in globals()) + # + BVarStruct = new_struct_type("varfoo") + BVarStructP = new_pointer_type(BVarStruct) + complete_struct_or_union(BVarStruct, [('a1', BInt, -1), + ('va', BIntA, -1)]) + with pytest.raises(TypeError): + from_buffer(BVarStruct, bytestring) + pv = from_buffer(BVarStructP, bytestring) # varfoo * + assert pv.a1 == lst[0] + assert pv.va[0] == lst[1] + assert pv.va[1] == lst[2] + assert sizeof(pv[0]) == 1 * size_of_int() + with pytest.raises(TypeError): + len(pv.va) + # hopefully does not crash, but doesn't raise an exception: + pv.va[2] + pv.va[-1] + # not enough data even for one, but this is not enforced: + from_buffer(BVarStructP, b"") + assert repr(pv) == "<cdata 'varfoo *' buffer from 'bytearray' object>" + assert repr(pv[0]).startswith("<cdata 'varfoo &' ") + # + release(pv) + assert repr(pv) == "<cdata 'varfoo *' buffer RELEASED>" + assert repr(pv[0]).startswith("<cdata 'varfoo &' ") + # + pv = from_buffer(BVarStructP, bytestring) # make a fresh one + with pytest.raises(ValueError): + release(pv[0]) + +def test_issue483(): + BInt = new_primitive_type("int") + BIntP = new_pointer_type(BInt) + BIntA = new_array_type(BIntP, None) + lst = list(range(25)) + bytestring = bytearray(buffer(newp(BIntA, lst))[:] + b'XYZ') + p1 = from_buffer(BIntA, bytestring) # int[] + assert len(buffer(p1)) == 25 * size_of_int() + assert sizeof(p1) == 25 * size_of_int() + # + p2 = from_buffer(BIntP, bytestring) + assert sizeof(p2) == size_of_ptr() + assert len(buffer(p2)) == size_of_int() # first element only, by default def test_memmove(): Short = new_primitive_type("short") @@ -3887,10 +4106,14 @@ def test_dereference_null_ptr(): BInt = new_primitive_type("int") BIntPtr = new_pointer_type(BInt) p = cast(BIntPtr, 0) - py.test.raises(RuntimeError, "p[0]") - py.test.raises(RuntimeError, "p[0] = 42") - py.test.raises(RuntimeError, "p[42]") - py.test.raises(RuntimeError, "p[42] = -1") + with pytest.raises(RuntimeError): + p[0] + with pytest.raises(RuntimeError): + p[0] = 42 + with pytest.raises(RuntimeError): + p[42] + with pytest.raises(RuntimeError): + p[42] = -1 def test_mixup(): BStruct1 = new_struct_type("foo") @@ -3906,10 +4129,12 @@ def test_mixup(): pp2 = newp(BStruct2PtrPtr) pp3 = newp(BStruct3PtrPtr) pp1[0] = pp1[0] - e = py.test.raises(TypeError, "pp3[0] = pp1[0]") + with pytest.raises(TypeError) as e: + pp3[0] = pp1[0] assert str(e.value).startswith("initializer for ctype 'bar *' must be a ") assert str(e.value).endswith(", not cdata 'foo *'") - e = py.test.raises(TypeError, "pp2[0] = pp1[0]") + with pytest.raises(TypeError) as e: + pp2[0] = pp1[0] assert str(e.value) == ("initializer for ctype 'foo *' appears indeed to " "be 'foo *', but the types are different (check " "that you are not e.g. mixing up different ffi " @@ -4098,14 +4323,14 @@ def test_primitive_comparison(): assert (a != b) is True assert (b != a) is True if strict_compare: - py.test.raises(TypeError, "a < b") - py.test.raises(TypeError, "a <= b") - py.test.raises(TypeError, "a > b") - py.test.raises(TypeError, "a >= b") - py.test.raises(TypeError, "b < a") - py.test.raises(TypeError, "b <= a") - py.test.raises(TypeError, "b > a") - py.test.raises(TypeError, "b >= a") + with pytest.raises(TypeError): a < b + with pytest.raises(TypeError): a <= b + with pytest.raises(TypeError): a > b + with pytest.raises(TypeError): a >= b + with pytest.raises(TypeError): b < a + with pytest.raises(TypeError): b <= a + with pytest.raises(TypeError): b > a + with pytest.raises(TypeError): b >= a elif a < b: assert_lt(a, b) else: @@ -4151,7 +4376,8 @@ def test_explicit_release_new(): BIntP = new_pointer_type(new_primitive_type("int")) p = newp(BIntP) p[0] = 42 - py.test.raises(IndexError, "p[1]") + with pytest.raises(IndexError): + p[1] release(p) # here, reading p[0] might give garbage or segfault... release(p) # no effect @@ -4187,8 +4413,12 @@ def test_explicit_release_badtype(): def test_explicit_release_badtype_contextmgr(): BIntP = new_pointer_type(new_primitive_type("int")) p = cast(BIntP, 12345) - py.test.raises(ValueError, "with p: pass") - py.test.raises(ValueError, "with p: pass") + with pytest.raises(ValueError): + with p: + pass + with pytest.raises(ValueError): + with p: + pass def test_explicit_release_gc(): BIntP = new_pointer_type(new_primitive_type("int")) @@ -4224,8 +4454,10 @@ def test_explicit_release_from_buffer(): BCharA = new_array_type(BCharP, None) p = from_buffer(BCharA, a) assert p[2] == b"z" + assert repr(p) == "<cdata 'char[]' buffer len 3 from 'bytearray' object>" release(p) assert p[2] == b"z" # true so far, but might change to raise RuntimeError + assert repr(p) == "<cdata 'char[]' buffer RELEASED>" release(p) # no effect def test_explicit_release_from_buffer_contextmgr(): @@ -4237,6 +4469,7 @@ def test_explicit_release_from_buffer_contextmgr(): with p: assert p[2] == b"z" assert p[2] == b"z" # true so far, but might change to raise RuntimeError + assert repr(p) == "<cdata 'char[]' buffer RELEASED>" release(p) # no effect def test_explicit_release_bytearray_on_cpython(): @@ -4248,9 +4481,95 @@ def test_explicit_release_bytearray_on_cpython(): BCharA = new_array_type(BCharP, None) a += b't' * 10 p = from_buffer(BCharA, a) - py.test.raises(BufferError, "a += b'u' * 100") + with pytest.raises(BufferError): + a += b'u' * 100 release(p) a += b'v' * 100 release(p) # no effect a += b'w' * 1000 assert a == bytearray(b"xyz" + b't' * 10 + b'v' * 100 + b'w' * 1000) + +def test_int_doesnt_give_bool(): + BBool = new_primitive_type("_Bool") + x = int(cast(BBool, 42)) + assert type(x) is int and x == 1 + x = long(cast(BBool, 42)) + assert type(x) is long and x == 1 + with pytest.raises(TypeError): + float(cast(BBool, 42)) + with pytest.raises(TypeError): + complex(cast(BBool, 42)) + +def test_cannot_call_null_function_pointer(): + BInt = new_primitive_type("int") + BFunc = new_function_type((BInt, BInt), BInt, False) + f = cast(BFunc, 0) + with pytest.raises(RuntimeError): + f(40, 2) + +def test_huge_structure(): + BChar = new_primitive_type("char") + BArray = new_array_type(new_pointer_type(BChar), sys.maxsize) + assert sizeof(BArray) == sys.maxsize + BStruct = new_struct_type("struct foo") + complete_struct_or_union(BStruct, [('a1', BArray, -1)]) + assert sizeof(BStruct) == sys.maxsize + +def test_get_types(): + import _cffi_backend + CData, CType = _get_types() + assert CData is _cffi_backend._CDataBase + assert CType is _cffi_backend.CType + +def test_type_available_with_correct_names(): + import _cffi_backend + check_names = [ + 'CType', + 'CField', + 'CLibrary', + '_CDataBase', + 'FFI', + 'Lib', + 'buffer', + ] + if '__pypy__' in sys.builtin_module_names: + check_names += [ + '__CData_iterator', + '__FFIGlobSupport', + '__FFIAllocator', + '__FFIFunctionWrapper', + ] + else: + check_names += [ + '__CDataOwn', + '__CDataOwnGC', + '__CDataFromBuf', + '__CDataGCP', + '__CData_iterator', + '__FFIGlobSupport', + ] + for name in check_names: + tp = getattr(_cffi_backend, name) + assert isinstance(tp, type) + assert (tp.__module__, tp.__name__) == ('_cffi_backend', name) + +def test_unaligned_types(): + BByteArray = new_array_type( + new_pointer_type(new_primitive_type("unsigned char")), None) + pbuf = newp(BByteArray, 40) + buf = buffer(pbuf) + # + for name in ['short', 'int', 'long', 'long long', 'float', 'double', + 'float _Complex', 'double _Complex']: + p = new_primitive_type(name) + if name.endswith(' _Complex'): + num = cast(p, 1.23 - 4.56j) + else: + num = cast(p, 0x0123456789abcdef) + size = sizeof(p) + buf[0:40] = b"\x00" * 40 + pbuf1 = cast(new_pointer_type(p), pbuf + 1) + pbuf1[0] = num + assert pbuf1[0] == num + assert buf[0] == b'\x00' + assert buf[1 + size] == b'\x00' |