diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-03-04 22:02:17 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-03-04 22:02:17 +0000 |
commit | dc69202beb1bdc3901471678d8e901a0879fe9f7 (patch) | |
tree | 4b825dc642cb6eb9a060e54bf8d69288fbee4904 | |
parent | a9fa1902f8514bb1c1a85ef451b463af06bbf331 (diff) | |
parent | de77edbe02d4cc29c5f218144e41d4e708244566 (diff) | |
download | cffi-simpleperf-release.tar.gz |
Snap for 11526323 from de77edbe02d4cc29c5f218144e41d4e708244566 to simpleperf-releasesimpleperf-release
Change-Id: I258475f2efabdf85794bd03675d44ac129a7d7b8
201 files changed, 0 insertions, 59358 deletions
diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index 370a25d..0000000 --- a/AUTHORS +++ /dev/null @@ -1,8 +0,0 @@ -This package has been mostly done by Armin Rigo with help from -Maciej Fijałkowski. The idea is heavily based (although not directly -copied) from LuaJIT ffi by Mike Pall. - - -Other contributors: - - Google Inc. diff --git a/Android.bp b/Android.bp deleted file mode 100644 index 13f23dd..0000000 --- a/Android.bp +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright (C) 2021 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package { - default_applicable_licenses: ["external_python_cffi_license"], -} - -// Added automatically by a large-scale-change -// See: http://go/android-license-faq -license { - name: "external_python_cffi_license", - visibility: [":__subpackages__"], - license_kinds: [ - "SPDX-license-identifier-MIT", - ], - license_text: [ - "LICENSE", - ], -} diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 29225ee..0000000 --- a/LICENSE +++ /dev/null @@ -1,26 +0,0 @@ - -Except when otherwise stated (look for LICENSE files in directories or -information at the beginning of each file) all software and -documentation is licensed as follows: - - The MIT License - - 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. - diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index b8ca2e0..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,6 +0,0 @@ -recursive-include cffi *.py *.h -recursive-include c *.c *.h *.asm *.py win64.obj ffi.lib -recursive-include testing *.py *.c *.h -recursive-include doc *.py *.rst Makefile *.bat -recursive-include demo py.cleanup *.py embedding_test.c manual.c -include AUTHORS LICENSE setup.py setup_base.py diff --git a/METADATA b/METADATA deleted file mode 100644 index db9d268..0000000 --- a/METADATA +++ /dev/null @@ -1,17 +0,0 @@ -name: "cffi" -description: - "Foreign Function Interface for Python calling C code." - -third_party { - url { - type: HOMEPAGE - value: "https://bitbucket.org/cffi/cffi" - } - url { - type: HG - value: "https://bitbucket.org/cffi/cffi/src" - } - version: "1.15.0" - last_upgrade_date { year: 2020 month: 11 day: 8 } - license_type: NOTICE -} diff --git a/MODULE_LICENSE_MIT b/MODULE_LICENSE_MIT deleted file mode 100644 index e69de29..0000000 --- a/MODULE_LICENSE_MIT +++ /dev/null diff --git a/README.md b/README.md deleted file mode 100644 index a68639e..0000000 --- a/README.md +++ /dev/null @@ -1,30 +0,0 @@ -CFFI -==== - -Foreign Function Interface for Python calling C code. -Please see the [Documentation](http://cffi.readthedocs.org/) or uncompiled -in the doc/ subdirectory. - -Download --------- - -[Download page](https://foss.heptapod.net/pypy/cffi/-/tags) - -Contact -------- - -[Mailing list](https://groups.google.com/forum/#!forum/python-cffi) - -Testing/development tips ------------------------- - -To run tests under CPython, run:: - - pip install pytest # if you don't have py.test already - pip install pycparser - python setup.py build_ext -f -i - py.test c/ testing/ - -If you run in another directory (either the tests or another program), -you should use the environment variable ``PYTHONPATH=/path`` to point -to the location that contains the ``_cffi_backend.so`` just compiled. 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 differdeleted file mode 100755 index 82b4232..0000000 --- a/c/.libs_cffi_backend/libffi-9c61262e.so.8.1.0 +++ /dev/null diff --git a/c/Android.bp b/c/Android.bp deleted file mode 100644 index 96565df..0000000 --- a/c/Android.bp +++ /dev/null @@ -1,44 +0,0 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "external_python_cffi_license" - // to get the below license kinds: - // SPDX-license-identifier-MIT - default_applicable_licenses: ["external_python_cffi_license"], -} - -python_library { - name: "py-cffi-backend", - host_supported: true, - srcs: [ - "_dummy_file_cffi_backend.py", - ], - data: [ - ":py-cffi-backend-files" - ], -} - -filegroup { - name: "py-cffi-backend-files", - srcs: [ - "_cffi_backend.so", - ], -} - -python_library { - name: "py-cffi-backend-libffi", - host_supported: true, - srcs: [ - "_dummy_file_libffi.py", - ], - data: [ - ":py-cffi-backend-libffi-files" - ], -} - -filegroup { - name: "py-cffi-backend-libffi-files", - srcs: [ - ".libs_cffi_backend/libffi-9c61262e.so.8.1.0", - ], -} diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c deleted file mode 100644 index ffecbf9..0000000 --- a/c/_cffi_backend.c +++ /dev/null @@ -1,8083 +0,0 @@ -#define PY_SSIZE_T_CLEAN -#include <Python.h> -#include "structmember.h" - -#define CFFI_VERSION "1.15.0" - -#ifdef MS_WIN32 -#include <windows.h> -#include "misc_win32.h" -#else -#include <stddef.h> -#include <stdint.h> -#include <dlfcn.h> -#include <errno.h> -#include <ffi.h> -#include <sys/mman.h> -#endif - -/* this block of #ifs should be kept exactly identical between - c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py */ -#if defined(_MSC_VER) -# include <malloc.h> /* for alloca() */ -# if _MSC_VER < 1600 /* MSVC < 2010 */ - typedef __int8 int8_t; - typedef __int16 int16_t; - typedef __int32 int32_t; - typedef __int64 int64_t; - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; - typedef unsigned __int64 uint64_t; - typedef __int8 int_least8_t; - typedef __int16 int_least16_t; - typedef __int32 int_least32_t; - typedef __int64 int_least64_t; - typedef unsigned __int8 uint_least8_t; - typedef unsigned __int16 uint_least16_t; - typedef unsigned __int32 uint_least32_t; - typedef unsigned __int64 uint_least64_t; - typedef __int8 int_fast8_t; - typedef __int16 int_fast16_t; - typedef __int32 int_fast32_t; - typedef __int64 int_fast64_t; - typedef unsigned __int8 uint_fast8_t; - typedef unsigned __int16 uint_fast16_t; - typedef unsigned __int32 uint_fast32_t; - typedef unsigned __int64 uint_fast64_t; - typedef __int64 intmax_t; - typedef unsigned __int64 uintmax_t; -# else -# include <stdint.h> -# endif -# if _MSC_VER < 1800 /* MSVC < 2013 */ - typedef unsigned char _Bool; -# endif -#else -# include <stdint.h> -# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) -# include <alloca.h> -# endif -#endif - - -/* Define the following macro ONLY if you trust libffi's version of - * ffi_closure_alloc() more than the code in malloc_closure.h. - * IMPORTANT: DO NOT ENABLE THIS ON LINUX, unless you understand exactly - * why I recommend against it and decide that you trust it more than my - * analysis below. - * - * There are two versions of this code: one inside libffi itself, and - * one inside malloc_closure.h here. Both should be fine as long as the - * Linux distribution does _not_ enable extra security features. If it - * does, then the code in malloc_closure.h will cleanly crash because - * there is no reasonable way to obtain a read-write-execute memory - * page. On the other hand, the code in libffi will appear to - * work---but will actually randomly crash after a fork() if the child - * does not immediately call exec(). This second crash is of the kind - * that can be turned into an attack vector by a motivated attacker. - * So, _enabling_ extra security features _opens_ an attack vector. - * 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() 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_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" -# define PyText_Type PyUnicode_Type -# define PyText_Check PyUnicode_Check -# define PyTextAny_Check PyUnicode_Check -# define PyText_FromFormat PyUnicode_FromFormat -# define PyText_AsUTF8 _PyUnicode_AsString /* PyUnicode_AsUTF8 in Py3.3 */ -# define PyText_AS_UTF8 _PyUnicode_AsString -# if PY_VERSION_HEX >= 0x03030000 -# define PyText_GetSize PyUnicode_GetLength -# else -# define PyText_GetSize PyUnicode_GetSize -# endif -# define PyText_FromString PyUnicode_FromString -# define PyText_FromStringAndSize PyUnicode_FromStringAndSize -# define PyText_InternInPlace PyUnicode_InternInPlace -# define PyText_InternFromString PyUnicode_InternFromString -# define PyIntOrLong_Check PyLong_Check -#else -# define STR_OR_BYTES "str" -# define PyText_Type PyString_Type -# define PyText_Check PyString_Check -# define PyTextAny_Check(op) (PyString_Check(op) || PyUnicode_Check(op)) -# define PyText_FromFormat PyString_FromFormat -# define PyText_AsUTF8 PyString_AsString -# define PyText_AS_UTF8 PyString_AS_STRING -# define PyText_GetSize PyString_Size -# define PyText_FromString PyString_FromString -# define PyText_FromStringAndSize PyString_FromStringAndSize -# define PyText_InternInPlace PyString_InternInPlace -# define PyText_InternFromString PyString_InternFromString -# define PyIntOrLong_Check(op) (PyInt_Check(op) || PyLong_Check(op)) -#endif - -#if PY_MAJOR_VERSION >= 3 -# define PyInt_FromLong PyLong_FromLong -# define PyInt_FromSsize_t PyLong_FromSsize_t -# define PyInt_AsSsize_t PyLong_AsSsize_t -# define PyInt_AsLong PyLong_AsLong -#endif - -#if PY_MAJOR_VERSION >= 3 -/* This is the default on Python3 and constant has been removed. */ -# define Py_TPFLAGS_CHECKTYPES 0 -#endif - -#if PY_MAJOR_VERSION < 3 -# undef PyCapsule_GetPointer -# undef PyCapsule_New -# define PyCapsule_GetPointer(capsule, name) \ - (PyCObject_AsVoidPtr(capsule)) -# define PyCapsule_New(pointer, name, destructor) \ - (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: */ -#define CT_PRIMITIVE_SIGNED 0x001 /* signed integer */ -#define CT_PRIMITIVE_UNSIGNED 0x002 /* unsigned integer */ -#define CT_PRIMITIVE_CHAR 0x004 /* char, wchar_t, charN_t */ -#define CT_PRIMITIVE_FLOAT 0x008 /* float, double, long double */ -#define CT_POINTER 0x010 /* pointer, excluding ptr-to-func */ -#define CT_ARRAY 0x020 /* array */ -#define CT_STRUCT 0x040 /* struct */ -#define CT_UNION 0x080 /* union */ -#define CT_FUNCTIONPTR 0x100 /* pointer to function */ -#define CT_VOID 0x200 /* void */ -#define CT_PRIMITIVE_COMPLEX 0x400 /* float _Complex, double _Complex */ - -/* other flags that may also be set in addition to the base flag: */ -#define CT_IS_VOIDCHAR_PTR 0x00001000 -#define CT_PRIMITIVE_FITS_LONG 0x00002000 -#define CT_IS_OPAQUE 0x00004000 -#define CT_IS_ENUM 0x00008000 -#define CT_IS_PTR_TO_OWNED 0x00010000 /* only owned if CDataOwning_Type */ -#define CT_CUSTOM_FIELD_POS 0x00020000 -#define CT_IS_LONGDOUBLE 0x00040000 -#define CT_IS_BOOL 0x00080000 -#define CT_IS_FILE 0x00100000 -#define CT_IS_VOID_PTR 0x00200000 -#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 -#define CT_IS_SIGNED_WCHAR 0x04000000 -#define CT_PRIMITIVE_ANY (CT_PRIMITIVE_SIGNED | \ - CT_PRIMITIVE_UNSIGNED | \ - CT_PRIMITIVE_CHAR | \ - CT_PRIMITIVE_FLOAT | \ - CT_PRIMITIVE_COMPLEX) - -typedef struct _ctypedescr { - PyObject_VAR_HEAD - - struct _ctypedescr *ct_itemdescr; /* ptrs and arrays: the item type */ - PyObject *ct_stuff; /* structs: dict of the fields - arrays: ctypedescr of the ptr type - function: tuple(abi, ctres, ctargs..) - enum: pair {"name":x},{x:"name"} - ptrs: lazily, ctypedescr of array */ - void *ct_extra; /* structs: first field (not a ref!) - function types: cif_description - primitives: prebuilt "cif" object */ - - PyObject *ct_weakreflist; /* weakref support */ - - PyObject *ct_unique_key; /* key in unique_cache (a string, but not - human-readable) */ - - Py_ssize_t ct_size; /* size of instances, or -1 if unknown */ - Py_ssize_t ct_length; /* length of arrays, or -1 if unknown; - or alignment of primitive and struct types; - always -1 for pointers */ - int ct_flags; /* CT_xxx flags */ - - int ct_name_position; /* index in ct_name of where to put a var name */ - char ct_name[1]; /* string, e.g. "int *" for pointers to ints */ -} CTypeDescrObject; - -typedef struct { - PyObject_HEAD - CTypeDescrObject *c_type; - char *c_data; - PyObject *c_weakreflist; -} CDataObject; - -typedef struct cfieldobject_s { - PyObject_HEAD - CTypeDescrObject *cf_type; - Py_ssize_t cf_offset; - short cf_bitshift; /* >= 0: bitshift; or BS_REGULAR or BS_EMPTY_ARRAY */ - short cf_bitsize; - unsigned char cf_flags; /* BF_... */ - struct cfieldobject_s *cf_next; -} CFieldObject; -#define BS_REGULAR (-1) /* a regular field, not with bitshift */ -#define BS_EMPTY_ARRAY (-2) /* a field declared 'type[0]' or 'type[]' */ -#define BF_IGNORE_IN_CTOR 0x01 /* union field not in the first place */ - -static PyTypeObject CTypeDescr_Type; -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) - -typedef union { - unsigned char m_char; - unsigned short m_short; - unsigned int m_int; - unsigned long m_long; - unsigned long long m_longlong; - float m_float; - double m_double; - long double m_longdouble; -} union_alignment; - -typedef struct { - CDataObject head; - union_alignment alignment; -} CDataObject_casted_primitive; - -typedef struct { - CDataObject head; - union_alignment alignment; -} CDataObject_own_nolength; - -typedef struct { - CDataObject head; - Py_ssize_t length; - union_alignment alignment; -} CDataObject_own_length; - -typedef struct { - CDataObject head; - 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_frombuf; - -typedef struct { - CDataObject head; - Py_ssize_t length; /* same as CDataObject_own_length up to here */ - PyObject *origobj; - PyObject *destructor; -} CDataObject_gcp; - -typedef struct { - CDataObject head; - ffi_closure *closure; -} CDataObject_closure; - -typedef struct { - ffi_cif cif; - /* the following information is used when doing the call: - - a buffer of size 'exchange_size' is malloced - - the arguments are converted from Python objects to raw data - - the i'th raw data is stored at 'buffer + exchange_offset_arg[1+i]' - - the call is done - - the result is read back from 'buffer + exchange_offset_arg[0]' */ - Py_ssize_t exchange_size; - Py_ssize_t exchange_offset_arg[1]; -} cif_description_t; - -#define ADD_WRAPAROUND(x, y) ((Py_ssize_t)(((size_t)(x)) + ((size_t)(y)))) -#define MUL_WRAPAROUND(x, y) ((Py_ssize_t)(((size_t)(x)) * ((size_t)(y)))) - - -/* whenever running Python code, the errno is saved in this thread-local - variable */ -#ifndef MS_WIN32 -# include "misc_thread_posix.h" -#endif - -#include "minibuffer.h" - -#if PY_MAJOR_VERSION >= 3 -# include "file_emulator.h" -#endif - -#ifdef PyUnicode_KIND /* Python >= 3.3 */ -# include "wchar_helper_3.h" -#else -# include "wchar_helper.h" -#endif - -#include "../cffi/_cffi_errors.h" - -typedef struct _cffi_allocator_s { - PyObject *ca_alloc, *ca_free; - int ca_dont_clear; -} cffi_allocator_t; -static const cffi_allocator_t default_allocator = { NULL, NULL, 0 }; -static PyObject *FFIError; -static PyObject *unique_cache; - -/************************************************************/ - -static CTypeDescrObject * -ctypedescr_new(int name_size) -{ - CTypeDescrObject *ct = PyObject_GC_NewVar(CTypeDescrObject, - &CTypeDescr_Type, - name_size); - if (ct == NULL) - return NULL; - - ct->ct_itemdescr = NULL; - ct->ct_stuff = NULL; - ct->ct_weakreflist = NULL; - ct->ct_unique_key = NULL; - PyObject_GC_Track(ct); - return ct; -} - -static CTypeDescrObject * -ctypedescr_new_on_top(CTypeDescrObject *ct_base, const char *extra_text, - int extra_position) -{ - int base_name_len = strlen(ct_base->ct_name); - int extra_name_len = strlen(extra_text); - CTypeDescrObject *ct = ctypedescr_new(base_name_len + extra_name_len + 1); - char *p; - if (ct == NULL) - return NULL; - - Py_INCREF(ct_base); - ct->ct_itemdescr = ct_base; - ct->ct_name_position = ct_base->ct_name_position + extra_position; - - p = ct->ct_name; - memcpy(p, ct_base->ct_name, ct_base->ct_name_position); - p += ct_base->ct_name_position; - memcpy(p, extra_text, extra_name_len); - p += extra_name_len; - memcpy(p, ct_base->ct_name + ct_base->ct_name_position, - base_name_len - ct_base->ct_name_position + 1); - - return ct; -} - -static PyObject * -ctypedescr_repr(CTypeDescrObject *ct) -{ - return PyText_FromFormat("<ctype '%s'>", ct->ct_name); -} - -static void -ctypedescr_dealloc(CTypeDescrObject *ct) -{ - PyObject_GC_UnTrack(ct); - if (ct->ct_weakreflist != NULL) - PyObject_ClearWeakRefs((PyObject *) ct); - - if (ct->ct_unique_key != NULL) { - /* revive dead object temporarily for DelItem */ - Py_SET_REFCNT(ct, 43); - PyDict_DelItem(unique_cache, ct->ct_unique_key); - assert(Py_REFCNT(ct) == 42); - Py_SET_REFCNT(ct, 0); - Py_DECREF(ct->ct_unique_key); - } - Py_XDECREF(ct->ct_itemdescr); - Py_XDECREF(ct->ct_stuff); - if (ct->ct_flags & CT_FUNCTIONPTR) - PyObject_Free(ct->ct_extra); - Py_TYPE(ct)->tp_free((PyObject *)ct); -} - -static int -ctypedescr_traverse(CTypeDescrObject *ct, visitproc visit, void *arg) -{ - Py_VISIT(ct->ct_itemdescr); - Py_VISIT(ct->ct_stuff); - return 0; -} - -static int -ctypedescr_clear(CTypeDescrObject *ct) -{ - Py_CLEAR(ct->ct_itemdescr); - Py_CLEAR(ct->ct_stuff); - return 0; -} - - -static PyObject *nosuchattr(const char *attr) -{ - PyErr_SetString(PyExc_AttributeError, attr); - return NULL; -} - -static PyObject *ctypeget_kind(CTypeDescrObject *ct, void *context) -{ - char *result; - if (ct->ct_flags & CT_PRIMITIVE_ANY) { - if (ct->ct_flags & CT_IS_ENUM) - result = "enum"; - else - result = "primitive"; - } - else if (ct->ct_flags & CT_POINTER) { - result = "pointer"; - } - else if (ct->ct_flags & CT_ARRAY) { - result = "array"; - } - else if (ct->ct_flags & CT_VOID) { - result = "void"; - } - else if (ct->ct_flags & CT_STRUCT) { - result = "struct"; - } - else if (ct->ct_flags & CT_UNION) { - result = "union"; - } - else if (ct->ct_flags & CT_FUNCTIONPTR) { - result = "function"; - } - else - result = "?"; - - return PyText_FromString(result); -} - -static PyObject *ctypeget_cname(CTypeDescrObject *ct, void *context) -{ - return PyText_FromString(ct->ct_name); -} - -static PyObject *ctypeget_item(CTypeDescrObject *ct, void *context) -{ - if (ct->ct_flags & (CT_POINTER | CT_ARRAY)) { - Py_INCREF(ct->ct_itemdescr); - return (PyObject *)ct->ct_itemdescr; - } - return nosuchattr("item"); -} - -static PyObject *ctypeget_length(CTypeDescrObject *ct, void *context) -{ - if (ct->ct_flags & CT_ARRAY) { - if (ct->ct_length >= 0) { - return PyInt_FromSsize_t(ct->ct_length); - } - else { - Py_INCREF(Py_None); - return Py_None; - } - } - return nosuchattr("length"); -} - -static PyObject * -get_field_name(CTypeDescrObject *ct, CFieldObject *cf); /* forward */ - -/* returns 0 if the struct ctype is opaque, 1 if it is not, or -1 if - an exception occurs */ -#define force_lazy_struct(ct) \ - ((ct)->ct_stuff != NULL ? 1 : do_realize_lazy_struct(ct)) - -static int do_realize_lazy_struct(CTypeDescrObject *ct); -/* forward, implemented in realize_c_type.c */ - -static PyObject *ctypeget_fields(CTypeDescrObject *ct, void *context) -{ - if (ct->ct_flags & (CT_STRUCT | CT_UNION)) { - if (!(ct->ct_flags & CT_IS_OPAQUE)) { - CFieldObject *cf; - PyObject *res; - if (force_lazy_struct(ct) < 0) - return NULL; - res = PyList_New(0); - if (res == NULL) - return NULL; - for (cf = (CFieldObject *)ct->ct_extra; - cf != NULL; cf = cf->cf_next) { - PyObject *o = PyTuple_Pack(2, get_field_name(ct, cf), - (PyObject *)cf); - int err = (o != NULL) ? PyList_Append(res, o) : -1; - Py_XDECREF(o); - if (err < 0) { - Py_DECREF(res); - return NULL; - } - } - return res; - } - else { - Py_INCREF(Py_None); - return Py_None; - } - } - return nosuchattr("fields"); -} - -static PyObject *ctypeget_args(CTypeDescrObject *ct, void *context) -{ - if (ct->ct_flags & CT_FUNCTIONPTR) { - PyObject *t = ct->ct_stuff; - return PyTuple_GetSlice(t, 2, PyTuple_GET_SIZE(t)); - } - return nosuchattr("args"); -} - -static PyObject *ctypeget_result(CTypeDescrObject *ct, void *context) -{ - if (ct->ct_flags & CT_FUNCTIONPTR) { - PyObject *res = PyTuple_GetItem(ct->ct_stuff, 1); - Py_XINCREF(res); - return res; - } - return nosuchattr("result"); -} - -static PyObject *ctypeget_ellipsis(CTypeDescrObject *ct, void *context) -{ - if (ct->ct_flags & CT_FUNCTIONPTR) { - PyObject *res = ct->ct_extra ? Py_False : Py_True; - Py_INCREF(res); - return res; - } - return nosuchattr("ellipsis"); -} - -static PyObject *ctypeget_abi(CTypeDescrObject *ct, void *context) -{ - if (ct->ct_flags & CT_FUNCTIONPTR) { - PyObject *res = PyTuple_GetItem(ct->ct_stuff, 0); - Py_XINCREF(res); - return res; - } - return nosuchattr("abi"); -} - -static PyObject *ctypeget_elements(CTypeDescrObject *ct, void *context) -{ - if (ct->ct_flags & CT_IS_ENUM) { - PyObject *res = PyTuple_GetItem(ct->ct_stuff, 1); - if (res) res = PyDict_Copy(res); - return res; - } - return nosuchattr("elements"); -} - -static PyObject *ctypeget_relements(CTypeDescrObject *ct, void *context) -{ - if (ct->ct_flags & CT_IS_ENUM) { - PyObject *res = PyTuple_GetItem(ct->ct_stuff, 0); - if (res) res = PyDict_Copy(res); - return res; - } - return nosuchattr("relements"); -} - -static PyGetSetDef ctypedescr_getsets[] = { - {"kind", (getter)ctypeget_kind, NULL, "kind"}, - {"cname", (getter)ctypeget_cname, NULL, "C name"}, - {"item", (getter)ctypeget_item, NULL, "pointer to, or array of"}, - {"length", (getter)ctypeget_length, NULL, "array length or None"}, - {"fields", (getter)ctypeget_fields, NULL, "struct or union fields"}, - {"args", (getter)ctypeget_args, NULL, "function argument types"}, - {"result", (getter)ctypeget_result, NULL, "function result type"}, - {"ellipsis", (getter)ctypeget_ellipsis, NULL, "function has '...'"}, - {"abi", (getter)ctypeget_abi, NULL, "function ABI"}, - {"elements", (getter)ctypeget_elements, NULL, "enum elements"}, - {"relements", (getter)ctypeget_relements, NULL, "enum elements, reverse"}, - {NULL} /* sentinel */ -}; - -static PyObject * -ctypedescr_dir(PyObject *ct, PyObject *noarg) -{ - int err; - struct PyGetSetDef *gsdef; - PyObject *res = PyList_New(0); - if (res == NULL) - return NULL; - - for (gsdef = ctypedescr_getsets; gsdef->name; gsdef++) { - PyObject *x = PyObject_GetAttrString(ct, gsdef->name); - if (x == NULL) { - PyErr_Clear(); - } - else { - Py_DECREF(x); - x = PyText_FromString(gsdef->name); - err = (x != NULL) ? PyList_Append(res, x) : -1; - Py_XDECREF(x); - if (err < 0) { - Py_DECREF(res); - return NULL; - } - } - } - return res; -} - -static PyMethodDef ctypedescr_methods[] = { - {"__dir__", ctypedescr_dir, METH_NOARGS}, - {NULL, NULL} /* sentinel */ -}; - -static PyTypeObject CTypeDescr_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_cffi_backend.CType", - offsetof(CTypeDescrObject, ct_name), - sizeof(char), - (destructor)ctypedescr_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - (reprfunc)ctypedescr_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - 0, /* tp_doc */ - (traverseproc)ctypedescr_traverse, /* tp_traverse */ - (inquiry)ctypedescr_clear, /* tp_clear */ - 0, /* tp_richcompare */ - offsetof(CTypeDescrObject, ct_weakreflist), /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - ctypedescr_methods, /* tp_methods */ - 0, /* tp_members */ - ctypedescr_getsets, /* tp_getset */ -}; - -/************************************************************/ - -static PyObject * -get_field_name(CTypeDescrObject *ct, CFieldObject *cf) -{ - Py_ssize_t i = 0; - PyObject *d_key, *d_value; - while (PyDict_Next(ct->ct_stuff, &i, &d_key, &d_value)) { - if (d_value == (PyObject *)cf) - return d_key; - } - Py_FatalError("_cffi_backend: get_field_name()"); - return NULL; -} - -static void -cfield_dealloc(CFieldObject *cf) -{ - Py_DECREF(cf->cf_type); - PyObject_Del(cf); -} - -#undef OFF -#define OFF(x) offsetof(CFieldObject, x) - -static PyMemberDef cfield_members[] = { - {"type", T_OBJECT, OFF(cf_type), READONLY}, - {"offset", T_PYSSIZET, OFF(cf_offset), READONLY}, - {"bitshift", T_SHORT, OFF(cf_bitshift), READONLY}, - {"bitsize", T_SHORT, OFF(cf_bitsize), READONLY}, - {"flags", T_UBYTE, OFF(cf_flags), READONLY}, - {NULL} /* Sentinel */ -}; -#undef OFF - -static PyTypeObject CField_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_cffi_backend.CField", - sizeof(CFieldObject), - 0, - (destructor)cfield_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - cfield_members, /* tp_members */ -}; - -/************************************************************/ - -static int -CDataObject_Or_PyFloat_Check(PyObject *ob) -{ - return (PyFloat_Check(ob) || - (CData_Check(ob) && - (((CDataObject *)ob)->c_type->ct_flags & CT_PRIMITIVE_FLOAT))); -} - -static PY_LONG_LONG -_my_PyLong_AsLongLong(PyObject *ob) -{ - /* (possibly) convert and cast a Python object to a long long. - Like PyLong_AsLongLong(), this version accepts a Python int too, and - does convertions from other types of objects. The difference is that - this version refuses floats. */ -#if PY_MAJOR_VERSION < 3 - if (PyInt_Check(ob)) { - return PyInt_AS_LONG(ob); - } - else -#endif - if (PyLong_Check(ob)) { - return PyLong_AsLongLong(ob); - } - else { - PyObject *io; - PY_LONG_LONG res; - PyNumberMethods *nb = ob->ob_type->tp_as_number; - - if (CDataObject_Or_PyFloat_Check(ob) || - nb == NULL || nb->nb_int == NULL) { - PyErr_SetString(PyExc_TypeError, "an integer is required"); - return -1; - } - io = (*nb->nb_int) (ob); - if (io == NULL) - return -1; - - if (PyIntOrLong_Check(io)) { - res = _my_PyLong_AsLongLong(io); - } - else { - PyErr_SetString(PyExc_TypeError, "integer conversion failed"); - res = -1; - } - Py_DECREF(io); - return res; - } -} - -static unsigned PY_LONG_LONG -_my_PyLong_AsUnsignedLongLong(PyObject *ob, int strict) -{ - /* (possibly) convert and cast a Python object to an unsigned long long. - Like PyLong_AsLongLong(), this version accepts a Python int too, and - does convertions from other types of objects. If 'strict', complains - with OverflowError and refuses floats. If '!strict', rounds floats - and masks the result. */ -#if PY_MAJOR_VERSION < 3 - if (PyInt_Check(ob)) { - long value1 = PyInt_AS_LONG(ob); - if (strict && value1 < 0) - goto negative; - return (unsigned PY_LONG_LONG)(PY_LONG_LONG)value1; - } - else -#endif - if (PyLong_Check(ob)) { - if (strict) { - if (_PyLong_Sign(ob) < 0) - goto negative; - return PyLong_AsUnsignedLongLong(ob); - } - else { - return PyLong_AsUnsignedLongLongMask(ob); - } - } - else { - PyObject *io; - unsigned PY_LONG_LONG res; - PyNumberMethods *nb = ob->ob_type->tp_as_number; - - if ((strict && CDataObject_Or_PyFloat_Check(ob)) || - nb == NULL || nb->nb_int == NULL) { - PyErr_SetString(PyExc_TypeError, "an integer is required"); - return (unsigned PY_LONG_LONG)-1; - } - io = (*nb->nb_int) (ob); - if (io == NULL) - return (unsigned PY_LONG_LONG)-1; - - if (PyIntOrLong_Check(io)) { - res = _my_PyLong_AsUnsignedLongLong(io, strict); - } - else { - PyErr_SetString(PyExc_TypeError, "integer conversion failed"); - res = (unsigned PY_LONG_LONG)-1; - } - Py_DECREF(io); - return res; - } - - negative: - PyErr_SetString(PyExc_OverflowError, - "can't convert negative number to unsigned"); - return (unsigned PY_LONG_LONG)-1; -} - -#define _read_raw_data(type) \ - do { \ - if (size == sizeof(type)) { \ - type r; \ - memcpy(&r, target, sizeof(type)); \ - return r; \ - } \ - } while(0) - -static PY_LONG_LONG -read_raw_signed_data(char *target, int size) -{ - _read_raw_data(signed char); - _read_raw_data(short); - _read_raw_data(int); - _read_raw_data(long); - _read_raw_data(PY_LONG_LONG); - Py_FatalError("read_raw_signed_data: bad integer size"); - return 0; -} - -static unsigned PY_LONG_LONG -read_raw_unsigned_data(char *target, int size) -{ - _read_raw_data(unsigned char); - _read_raw_data(unsigned short); - _read_raw_data(unsigned int); - _read_raw_data(unsigned long); - _read_raw_data(unsigned PY_LONG_LONG); - Py_FatalError("read_raw_unsigned_data: bad integer size"); - return 0; -} - -#ifdef __GNUC__ -/* This is a workaround for what I think is a GCC bug on several - platforms. See issue #378. */ -__attribute__((noinline)) -#endif -void _cffi_memcpy(char *target, const void *src, size_t size) -{ - memcpy(target, src, size); -} - -#define _write_raw_data(type) \ - do { \ - if (size == sizeof(type)) { \ - type r = (type)source; \ - _cffi_memcpy(target, &r, sizeof(type)); \ - return; \ - } \ - } while(0) - -static void -write_raw_integer_data(char *target, unsigned PY_LONG_LONG source, int size) -{ - _write_raw_data(unsigned char); - _write_raw_data(unsigned short); - _write_raw_data(unsigned int); - _write_raw_data(unsigned long); - _write_raw_data(unsigned PY_LONG_LONG); - Py_FatalError("write_raw_integer_data: bad integer size"); -} - -static double -read_raw_float_data(char *target, int size) -{ - _read_raw_data(float); - _read_raw_data(double); - Py_FatalError("read_raw_float_data: bad float size"); - return 0; -} - -static long double -read_raw_longdouble_data(char *target) -{ - int size = sizeof(long double); - _read_raw_data(long double); - Py_FatalError("read_raw_longdouble_data: bad long double size"); - return 0; -} - -static Py_complex -read_raw_complex_data(char *target, int size) -{ - Py_complex r = {0.0, 0.0}; - if (size == 2*sizeof(float)) { - float real_part, imag_part; - memcpy(&real_part, target + 0, sizeof(float)); - memcpy(&imag_part, target + sizeof(float), sizeof(float)); - r.real = real_part; - r.imag = imag_part; - return r; - } - if (size == 2*sizeof(double)) { - memcpy(&r, target, 2*sizeof(double)); - return r; - } - Py_FatalError("read_raw_complex_data: bad complex size"); - return r; -} - -static void -write_raw_float_data(char *target, double source, int size) -{ - _write_raw_data(float); - _write_raw_data(double); - Py_FatalError("write_raw_float_data: bad float size"); -} - -static void -write_raw_longdouble_data(char *target, long double source) -{ - int size = sizeof(long double); - _write_raw_data(long double); -} - -#define _write_raw_complex_data(type) \ - do { \ - if (size == 2*sizeof(type)) { \ - type r = (type)source.real; \ - type i = (type)source.imag; \ - _cffi_memcpy(target, &r, sizeof(type)); \ - _cffi_memcpy(target+sizeof(type), &i, sizeof(type)); \ - return; \ - } \ - } while(0) - -static void -write_raw_complex_data(char *target, Py_complex source, int size) -{ - _write_raw_complex_data(float); - _write_raw_complex_data(double); - Py_FatalError("write_raw_complex_data: bad complex size"); -} - -static PyObject * -new_simple_cdata(char *data, CTypeDescrObject *ct) -{ - CDataObject *cd = PyObject_New(CDataObject, &CData_Type); - if (cd == NULL) - return NULL; - Py_INCREF(ct); - cd->c_data = data; - cd->c_type = ct; - cd->c_weakreflist = NULL; - return (PyObject *)cd; -} - -static PyObject * -new_sized_cdata(char *data, CTypeDescrObject *ct, Py_ssize_t length) -{ - CDataObject_own_length *scd; - - scd = (CDataObject_own_length *)PyObject_Malloc( - offsetof(CDataObject_own_length, alignment)); - if (PyObject_Init((PyObject *)scd, &CData_Type) == NULL) - return NULL; - Py_INCREF(ct); - scd->head.c_type = ct; - scd->head.c_data = data; - scd->head.c_weakreflist = NULL; - scd->length = length; - return (PyObject *)scd; -} - -static CDataObject *_new_casted_primitive(CTypeDescrObject *ct); /*forward*/ - -static PyObject * -convert_to_object(char *data, CTypeDescrObject *ct) -{ - if (!(ct->ct_flags & CT_PRIMITIVE_ANY)) { - /* non-primitive types (check done just for performance) */ - if (ct->ct_flags & (CT_POINTER|CT_FUNCTIONPTR)) { - char *ptrdata = *(char **)data; - /*READ(data, sizeof(char *))*/ - return new_simple_cdata(ptrdata, ct); - } - else if (ct->ct_flags & CT_IS_OPAQUE) { - PyErr_Format(PyExc_TypeError, "cdata '%s' is opaque", - ct->ct_name); - return NULL; - } - else if (ct->ct_flags & (CT_STRUCT|CT_UNION)) { - return new_simple_cdata(data, ct); - } - else if (ct->ct_flags & CT_ARRAY) { - if (ct->ct_length < 0) { - /* we can't return a <cdata 'int[]'> here, because we don't - know the length to give it. As a compromize, returns - <cdata 'int *'> in this case. */ - ct = (CTypeDescrObject *)ct->ct_stuff; - } - return new_simple_cdata(data, ct); - } - } - else if (ct->ct_flags & CT_PRIMITIVE_SIGNED) { - PY_LONG_LONG value; - /*READ(data, ct->ct_size)*/ - value = read_raw_signed_data(data, ct->ct_size); - if (ct->ct_flags & CT_PRIMITIVE_FITS_LONG) - return PyInt_FromLong((long)value); - else - return PyLong_FromLongLong(value); - } - else if (ct->ct_flags & CT_PRIMITIVE_UNSIGNED) { - unsigned PY_LONG_LONG value; - /*READ(data, ct->ct_size)*/ - value = read_raw_unsigned_data(data, ct->ct_size); - - if (ct->ct_flags & CT_PRIMITIVE_FITS_LONG) { - if (ct->ct_flags & CT_IS_BOOL) { - PyObject *x; - switch ((int)value) { - case 0: x = Py_False; break; - case 1: x = Py_True; break; - default: - PyErr_Format(PyExc_ValueError, - "got a _Bool of value %d, expected 0 or 1", - (int)value); - return NULL; - } - Py_INCREF(x); - return x; - } - return PyInt_FromLong((long)value); - } - else - return PyLong_FromUnsignedLongLong(value); - } - else if (ct->ct_flags & CT_PRIMITIVE_FLOAT) { - /*READ(data, ct->ct_size)*/ - if (!(ct->ct_flags & CT_IS_LONGDOUBLE)) { - double value = read_raw_float_data(data, ct->ct_size); - return PyFloat_FromDouble(value); - } - else { - long double value = read_raw_longdouble_data(data); - CDataObject *cd = _new_casted_primitive(ct); - if (cd != NULL) - write_raw_longdouble_data(cd->c_data, value); - return (PyObject *)cd; - } - } - else if (ct->ct_flags & CT_PRIMITIVE_CHAR) { - /*READ(data, ct->ct_size)*/ - switch (ct->ct_size) { - case sizeof(char): - return PyBytes_FromStringAndSize(data, 1); - case 2: - return _my_PyUnicode_FromChar16((cffi_char16_t *)data, 1); - case 4: - return _my_PyUnicode_FromChar32((cffi_char32_t *)data, 1); - } - } - else if (ct->ct_flags & CT_PRIMITIVE_COMPLEX) { - Py_complex value = read_raw_complex_data(data, ct->ct_size); - return PyComplex_FromCComplex(value); - } - - PyErr_Format(PyExc_SystemError, - "convert_to_object: '%s'", ct->ct_name); - return NULL; -} - -static PyObject * -convert_to_object_bitfield(char *data, CFieldObject *cf) -{ - CTypeDescrObject *ct = cf->cf_type; - /*READ(data, ct->ct_size)*/ - - if (ct->ct_flags & CT_PRIMITIVE_SIGNED) { - unsigned PY_LONG_LONG value, valuemask, shiftforsign; - PY_LONG_LONG result; - - value = (unsigned PY_LONG_LONG)read_raw_signed_data(data, ct->ct_size); - valuemask = (1ULL << cf->cf_bitsize) - 1ULL; - shiftforsign = 1ULL << (cf->cf_bitsize - 1); - value = ((value >> cf->cf_bitshift) + shiftforsign) & valuemask; - result = ((PY_LONG_LONG)value) - (PY_LONG_LONG)shiftforsign; - - if (ct->ct_flags & CT_PRIMITIVE_FITS_LONG) - return PyInt_FromLong((long)result); - else - return PyLong_FromLongLong(result); - } - else { - unsigned PY_LONG_LONG value, valuemask; - - value = read_raw_unsigned_data(data, ct->ct_size); - valuemask = (1ULL << cf->cf_bitsize) - 1ULL; - value = (value >> cf->cf_bitshift) & valuemask; - - if (ct->ct_flags & CT_PRIMITIVE_FITS_LONG) - return PyInt_FromLong((long)value); - else - return PyLong_FromUnsignedLongLong(value); - } -} - -static int _convert_overflow(PyObject *init, const char *ct_name) -{ - PyObject *s; - if (PyErr_Occurred()) /* already an exception pending */ - return -1; - s = PyObject_Str(init); - if (s == NULL) - return -1; - PyErr_Format(PyExc_OverflowError, "integer %s does not fit '%s'", - PyText_AS_UTF8(s), ct_name); - Py_DECREF(s); - return -1; -} - -static int _convert_to_char(PyObject *init) -{ - if (PyBytes_Check(init) && PyBytes_GET_SIZE(init) == 1) { - return (unsigned char)(PyBytes_AS_STRING(init)[0]); - } - if (CData_Check(init) && - (((CDataObject *)init)->c_type->ct_flags & CT_PRIMITIVE_CHAR) && - (((CDataObject *)init)->c_type->ct_size == sizeof(char))) { - char *data = ((CDataObject *)init)->c_data; - /*READ(data, 1)*/ - return *(unsigned char *)data; - } - PyErr_Format(PyExc_TypeError, - "initializer for ctype 'char' must be a "STR_OR_BYTES - " of length 1, not %.200s", Py_TYPE(init)->tp_name); - return -1; -} - -static cffi_char16_t _convert_to_char16_t(PyObject *init) -{ - char err_got[80]; - err_got[0] = 0; - - if (PyUnicode_Check(init)) { - cffi_char16_t ordinal; - if (_my_PyUnicode_AsSingleChar16(init, &ordinal, err_got) == 0) - return ordinal; - } - if (CData_Check(init) && - (((CDataObject *)init)->c_type->ct_flags & CT_PRIMITIVE_CHAR) && - (((CDataObject *)init)->c_type->ct_size == 2)) { - char *data = ((CDataObject *)init)->c_data; - /*READ(data, 2)*/ - return *(cffi_char16_t *)data; - } - PyErr_Format(PyExc_TypeError, - "initializer for ctype 'char16_t' must be a unicode string " - "of length 1, not %.200s", - err_got[0] == 0 ? Py_TYPE(init)->tp_name : err_got); - return (cffi_char16_t)-1; -} - -static cffi_char32_t _convert_to_char32_t(PyObject *init) -{ - char err_got[80]; - err_got[0] = 0; - - if (PyUnicode_Check(init)) { - cffi_char32_t ordinal; - if (_my_PyUnicode_AsSingleChar32(init, &ordinal, err_got) == 0) - return ordinal; - } - if (CData_Check(init) && - (((CDataObject *)init)->c_type->ct_flags & CT_PRIMITIVE_CHAR) && - (((CDataObject *)init)->c_type->ct_size == 4)) { - char *data = ((CDataObject *)init)->c_data; - /*READ(data, 4)*/ - return *(cffi_char32_t *)data; - } - PyErr_Format(PyExc_TypeError, - "initializer for ctype 'char32_t' must be a unicode string " - "of length 1, not %.200s", - err_got[0] == 0 ? Py_TYPE(init)->tp_name : err_got); - return (cffi_char32_t)-1; -} - -static int _convert_error(PyObject *init, CTypeDescrObject *ct, - const char *expected) -{ - if (CData_Check(init)) { - CTypeDescrObject *ct2 = ((CDataObject *)init)->c_type; - if (strcmp(ct->ct_name, ct2->ct_name) != 0) - PyErr_Format(PyExc_TypeError, - "initializer for ctype '%s' must be a %s, " - "not cdata '%s'", - ct->ct_name, expected, ct2->ct_name); - else if (ct != ct2) { - /* in case we'd give the error message "initializer for - ctype 'A' must be a pointer to same type, not cdata - 'B'", but with A=B, then give instead a different error - message to try to clear up the confusion */ - PyErr_Format(PyExc_TypeError, - "initializer for ctype '%s' appears indeed to be '%s'," - " but the types are different (check that you are not" - " e.g. mixing up different ffi instances)", - ct->ct_name, ct2->ct_name); - } - else - { - PyErr_Format(PyExc_SystemError, - "initializer for ctype '%s' is correct, but we get " - "an internal mismatch--please report a bug", - ct->ct_name); - } - } - else - PyErr_Format(PyExc_TypeError, - "initializer for ctype '%s' must be a %s, " - "not %.200s", - ct->ct_name, expected, Py_TYPE(init)->tp_name); - return -1; -} - -static int /* forward */ -convert_from_object(char *data, CTypeDescrObject *ct, PyObject *init); -static int /* forward */ -convert_from_object_bitfield(char *data, CFieldObject *cf, PyObject *init); - -static Py_ssize_t -get_new_array_length(CTypeDescrObject *ctitem, PyObject **pvalue) -{ - PyObject *value = *pvalue; - - if (PyList_Check(value) || PyTuple_Check(value)) { - return PySequence_Fast_GET_SIZE(value); - } - else if (PyBytes_Check(value)) { - /* from a string, we add the null terminator */ - return PyBytes_GET_SIZE(value) + 1; - } - else if (PyUnicode_Check(value)) { - /* from a unicode, we add the null terminator */ - int length; - if (ctitem->ct_size == 2) - length = _my_PyUnicode_SizeAsChar16(value); - else - length = _my_PyUnicode_SizeAsChar32(value); - return length + 1; - } - else { - Py_ssize_t explicitlength; - explicitlength = PyNumber_AsSsize_t(value, PyExc_OverflowError); - if (explicitlength < 0) { - if (PyErr_Occurred()) { - if (PyErr_ExceptionMatches(PyExc_TypeError)) - PyErr_Format(PyExc_TypeError, - "expected new array length or list/tuple/str, " - "not %.200s", Py_TYPE(value)->tp_name); - } - else - PyErr_SetString(PyExc_ValueError, "negative array length"); - return -1; - } - *pvalue = Py_None; - return explicitlength; - } -} - -static int -convert_field_from_object(char *data, CFieldObject *cf, PyObject *value) -{ - data += cf->cf_offset; - if (cf->cf_bitshift >= 0) - return convert_from_object_bitfield(data, cf, value); - else - return convert_from_object(data, cf->cf_type, 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) -{ - /* a special case for var-sized C99 arrays */ - if ((cf->cf_type->ct_flags & CT_ARRAY) && cf->cf_type->ct_size < 0) { - Py_ssize_t varsizelength = get_new_array_length( - cf->cf_type->ct_itemdescr, &value); - if (varsizelength < 0) - return -1; - 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 */ - assert(data == NULL); - 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, - and if so, stop here, leaving the content uninitialized - (it should be zero-initialized from somewhere else). */ - if (value == Py_None) - return 0; - } - 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; -} - -static int -must_be_array_of_zero_or_one(const char *data, Py_ssize_t n) -{ - Py_ssize_t i; - for (i = 0; i < n; i++) { - if (((unsigned char)data[i]) > 1) { - PyErr_SetString(PyExc_ValueError, - "an array of _Bool can only contain \\x00 or \\x01"); - return -1; - } - } - return 0; -} - -static Py_ssize_t -get_array_length(CDataObject *cd) -{ - if (cd->c_type->ct_length < 0) - return ((CDataObject_own_length *)cd)->length; - else - return cd->c_type->ct_length; -} - -static int -convert_array_from_object(char *data, CTypeDescrObject *ct, PyObject *init) -{ - /* used by convert_from_object(), and also to decode lists/tuples/unicodes - passed as function arguments. 'ct' is an CT_ARRAY in the first case - and a CT_POINTER in the second case. */ - const char *expected; - CTypeDescrObject *ctitem = ct->ct_itemdescr; - - if (PyList_Check(init) || PyTuple_Check(init)) { - PyObject **items; - Py_ssize_t i, n; - n = PySequence_Fast_GET_SIZE(init); - if (ct->ct_length >= 0 && n > ct->ct_length) { - PyErr_Format(PyExc_IndexError, - "too many initializers for '%s' (got %zd)", - ct->ct_name, n); - return -1; - } - items = PySequence_Fast_ITEMS(init); - for (i=0; i<n; i++) { - if (convert_from_object(data, ctitem, items[i]) < 0) - return -1; - data += ctitem->ct_size; - } - return 0; - } - else if ((ctitem->ct_flags & CT_PRIMITIVE_CHAR) || - ((ctitem->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_UNSIGNED)) - && (ctitem->ct_size == sizeof(char)))) { - if (ctitem->ct_size == sizeof(char)) { - char *srcdata; - Py_ssize_t n; - if (!PyBytes_Check(init)) { - expected = STR_OR_BYTES" or list or tuple"; - goto cannot_convert; - } - n = PyBytes_GET_SIZE(init); - if (ct->ct_length >= 0 && n > ct->ct_length) { - PyErr_Format(PyExc_IndexError, - "initializer "STR_OR_BYTES" is too long for '%s' " - "(got %zd characters)", ct->ct_name, n); - return -1; - } - if (n != ct->ct_length) - n++; - srcdata = PyBytes_AS_STRING(init); - if (ctitem->ct_flags & CT_IS_BOOL) - if (must_be_array_of_zero_or_one(srcdata, n) < 0) - return -1; - memcpy(data, srcdata, n); - return 0; - } - else { - Py_ssize_t n; - if (!PyUnicode_Check(init)) { - expected = "unicode or list or tuple"; - goto cannot_convert; - } - - if (ctitem->ct_size == 4) - n = _my_PyUnicode_SizeAsChar32(init); - else - n = _my_PyUnicode_SizeAsChar16(init); - - if (ct->ct_length >= 0 && n > ct->ct_length) { - PyErr_Format(PyExc_IndexError, - "initializer unicode is too long for '%s' " - "(got %zd characters)", ct->ct_name, n); - return -1; - } - if (n != ct->ct_length) - n++; - if (ctitem->ct_size == 4) - return _my_PyUnicode_AsChar32(init, (cffi_char32_t *)data, n); - else - return _my_PyUnicode_AsChar16(init, (cffi_char16_t *)data, n); - } - } - else { - expected = "list or tuple"; - goto cannot_convert; - } - - cannot_convert: - if ((ct->ct_flags & CT_ARRAY) && CData_Check(init)) - { - CDataObject *cd = (CDataObject *)init; - if (cd->c_type == ct) - { - Py_ssize_t n = get_array_length(cd); - memcpy(data, cd->c_data, n * ctitem->ct_size); - return 0; - } - } - return _convert_error(init, ct, expected); -} - -static int -convert_struct_from_object(char *data, CTypeDescrObject *ct, PyObject *init, - Py_ssize_t *optvarsize) -{ - /* does not accept 'init' being already a CData */ - const char *expected; - - if (force_lazy_struct(ct) <= 0) { - if (!PyErr_Occurred()) - PyErr_Format(PyExc_TypeError, "'%s' is opaque", ct->ct_name); - return -1; - } - - if (PyList_Check(init) || PyTuple_Check(init)) { - PyObject **items = PySequence_Fast_ITEMS(init); - Py_ssize_t i, n = PySequence_Fast_GET_SIZE(init); - CFieldObject *cf = (CFieldObject *)ct->ct_extra; - - for (i=0; i<n; i++) { - while (cf != NULL && (cf->cf_flags & BF_IGNORE_IN_CTOR)) - cf = cf->cf_next; - if (cf == NULL) { - PyErr_Format(PyExc_ValueError, - "too many initializers for '%s' (got %zd)", - ct->ct_name, n); - return -1; - } - if (convert_vfield_from_object(data, cf, items[i], optvarsize) < 0) - return -1; - cf = cf->cf_next; - } - return 0; - } - if (PyDict_Check(init)) { - PyObject *d_key, *d_value; - Py_ssize_t i = 0; - CFieldObject *cf; - - while (PyDict_Next(init, &i, &d_key, &d_value)) { - cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, d_key); - if (cf == NULL) { - PyErr_SetObject(PyExc_KeyError, d_key); - return -1; - } - if (convert_vfield_from_object(data, cf, d_value, optvarsize) < 0) - return -1; - } - return 0; - } - expected = optvarsize == NULL ? "list or tuple or dict or struct-cdata" - : "list or tuple or dict"; - return _convert_error(init, ct, expected); -} - -#ifdef __GNUC__ -# if __GNUC__ >= 4 -/* Don't go inlining this huge function. Needed because occasionally - it gets inlined in places where is causes a warning: call to - __builtin___memcpy_chk will always overflow destination buffer - (which is places where the 'ct' should never represent such a large - primitive type anyway). */ -__attribute__((noinline)) -# endif -#endif -static int -convert_from_object(char *data, CTypeDescrObject *ct, PyObject *init) -{ - const char *expected; - char buf[sizeof(PY_LONG_LONG)]; - - /*if (ct->ct_size > 0)*/ - /*WRITE(data, ct->ct_size)*/ - - if (ct->ct_flags & CT_ARRAY) { - return convert_array_from_object(data, ct, init); - } - if (ct->ct_flags & (CT_POINTER|CT_FUNCTIONPTR)) { - char *ptrdata; - CTypeDescrObject *ctinit; - - if (!CData_Check(init)) { - expected = "cdata pointer"; - goto cannot_convert; - } - ctinit = ((CDataObject *)init)->c_type; - if (!(ctinit->ct_flags & (CT_POINTER|CT_FUNCTIONPTR))) { - if (ctinit->ct_flags & CT_ARRAY) - ctinit = (CTypeDescrObject *)ctinit->ct_stuff; - else { - expected = "pointer or array"; - goto cannot_convert; - } - } - if (ctinit != ct) { - int combined_flags = ct->ct_flags | ctinit->ct_flags; - if (combined_flags & CT_IS_VOID_PTR) - ; /* accept "void *" as either source or target */ - else if (combined_flags & CT_IS_VOIDCHAR_PTR) { - /* for backward compatibility, accept "char *" as either - source of target. This is not what C does, though, - so emit a warning that will eventually turn into an - error. The warning is turned off if both types are - pointers to single bytes. */ - char *msg = (ct->ct_flags & CT_IS_VOIDCHAR_PTR ? - "implicit cast to 'char *' from a different pointer type: " - "will be forbidden in the future (check that the types " - "are as you expect; use an explicit ffi.cast() if they " - "are correct)" : - "implicit cast from 'char *' to a different pointer type: " - "will be forbidden in the future (check that the types " - "are as you expect; use an explicit ffi.cast() if they " - "are correct)"); - if ((ct->ct_flags & ctinit->ct_flags & CT_POINTER) && - ct->ct_itemdescr->ct_size == 1 && - ctinit->ct_itemdescr->ct_size == 1) { - /* no warning */ - } - else if (PyErr_WarnEx(PyExc_UserWarning, msg, 1)) - return -1; - } - else { - expected = "pointer to same type"; - goto cannot_convert; - } - } - ptrdata = ((CDataObject *)init)->c_data; - - *(char **)data = ptrdata; - return 0; - } - if (ct->ct_flags & CT_PRIMITIVE_SIGNED) { - PY_LONG_LONG value = _my_PyLong_AsLongLong(init); - if (value == -1 && PyErr_Occurred()) - return -1; - write_raw_integer_data(buf, value, ct->ct_size); - if (value != read_raw_signed_data(buf, ct->ct_size)) - goto overflow; - write_raw_integer_data(data, value, ct->ct_size); - return 0; - } - if (ct->ct_flags & CT_PRIMITIVE_UNSIGNED) { - unsigned PY_LONG_LONG value = _my_PyLong_AsUnsignedLongLong(init, 1); - if (value == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred()) - return -1; - if (ct->ct_flags & CT_IS_BOOL) { - if (value > 1ULL) /* value != 0 && value != 1 */ - goto overflow; - } - else { - write_raw_integer_data(buf, value, ct->ct_size); - if (value != read_raw_unsigned_data(buf, ct->ct_size)) - goto overflow; - } - write_raw_integer_data(data, value, ct->ct_size); - return 0; - } - if (ct->ct_flags & CT_PRIMITIVE_FLOAT) { - double value; - if ((ct->ct_flags & CT_IS_LONGDOUBLE) && - CData_Check(init) && - (((CDataObject *)init)->c_type->ct_flags & CT_IS_LONGDOUBLE)) { - long double lvalue; - char *initdata = ((CDataObject *)init)->c_data; - /*READ(initdata, sizeof(long double))*/ - lvalue = read_raw_longdouble_data(initdata); - write_raw_longdouble_data(data, lvalue); - return 0; - } - value = PyFloat_AsDouble(init); - if (value == -1.0 && PyErr_Occurred()) - return -1; - if (!(ct->ct_flags & CT_IS_LONGDOUBLE)) - write_raw_float_data(data, value, ct->ct_size); - else - write_raw_longdouble_data(data, (long double)value); - return 0; - } - if (ct->ct_flags & CT_PRIMITIVE_CHAR) { - switch (ct->ct_size) { - case sizeof(char): { - int res = _convert_to_char(init); - if (res < 0) - return -1; - data[0] = res; - return 0; - } - case 2: { - cffi_char16_t res = _convert_to_char16_t(init); - if (res == (cffi_char16_t)-1 && PyErr_Occurred()) - return -1; - *(cffi_char16_t *)data = res; - return 0; - } - case 4: { - cffi_char32_t res = _convert_to_char32_t(init); - if (res == (cffi_char32_t)-1 && PyErr_Occurred()) - return -1; - *(cffi_char32_t *)data = res; - return 0; - } - } - } - if (ct->ct_flags & (CT_STRUCT|CT_UNION)) { - - if (CData_Check(init)) { - if (((CDataObject *)init)->c_type == ct && ct->ct_size >= 0) { - memcpy(data, ((CDataObject *)init)->c_data, ct->ct_size); - return 0; - } - } - return convert_struct_from_object(data, ct, init, NULL); - } - if (ct->ct_flags & CT_PRIMITIVE_COMPLEX) { - Py_complex value = PyComplex_AsCComplex(init); - if (PyErr_Occurred()) - return -1; - write_raw_complex_data(data, value, ct->ct_size); - return 0; - } - PyErr_Format(PyExc_SystemError, - "convert_from_object: '%s'", ct->ct_name); - return -1; - - overflow: - return _convert_overflow(init, ct->ct_name); - - cannot_convert: - return _convert_error(init, ct, expected); -} - -static int -convert_from_object_bitfield(char *data, CFieldObject *cf, PyObject *init) -{ - CTypeDescrObject *ct = cf->cf_type; - PY_LONG_LONG fmin, fmax, value = PyLong_AsLongLong(init); - unsigned PY_LONG_LONG rawfielddata, rawvalue, rawmask; - if (value == -1 && PyErr_Occurred()) - return -1; - - if (ct->ct_flags & CT_PRIMITIVE_SIGNED) { - fmin = -(1LL << (cf->cf_bitsize-1)); - fmax = (1LL << (cf->cf_bitsize-1)) - 1LL; - if (fmax == 0) - fmax = 1; /* special case to let "int x:1" receive "1" */ - } - else { - fmin = 0LL; - fmax = (PY_LONG_LONG)((1ULL << cf->cf_bitsize) - 1ULL); - } - if (value < fmin || value > fmax) { - /* phew, PyErr_Format does not support "%lld" in Python 2.6 */ - PyObject *svalue = NULL, *sfmin = NULL, *sfmax = NULL; - PyObject *lfmin = NULL, *lfmax = NULL; - svalue = PyObject_Str(init); - if (svalue == NULL) goto skip; - lfmin = PyLong_FromLongLong(fmin); - if (lfmin == NULL) goto skip; - sfmin = PyObject_Str(lfmin); - if (sfmin == NULL) goto skip; - lfmax = PyLong_FromLongLong(fmax); - if (lfmax == NULL) goto skip; - sfmax = PyObject_Str(lfmax); - if (sfmax == NULL) goto skip; - PyErr_Format(PyExc_OverflowError, - "value %s outside the range allowed by the " - "bit field width: %s <= x <= %s", - PyText_AS_UTF8(svalue), - PyText_AS_UTF8(sfmin), - PyText_AS_UTF8(sfmax)); - skip: - Py_XDECREF(svalue); - Py_XDECREF(sfmin); - Py_XDECREF(sfmax); - Py_XDECREF(lfmin); - Py_XDECREF(lfmax); - return -1; - } - - rawmask = ((1ULL << cf->cf_bitsize) - 1ULL) << cf->cf_bitshift; - rawvalue = ((unsigned PY_LONG_LONG)value) << cf->cf_bitshift; - /*WRITE(data, ct->ct_size)*/ - rawfielddata = read_raw_unsigned_data(data, ct->ct_size); - rawfielddata = (rawfielddata & ~rawmask) | (rawvalue & rawmask); - write_raw_integer_data(data, rawfielddata, ct->ct_size); - return 0; -} - -static int -get_alignment(CTypeDescrObject *ct) -{ - int align; - retry: - if ((ct->ct_flags & (CT_PRIMITIVE_ANY|CT_STRUCT|CT_UNION)) && - !(ct->ct_flags & CT_IS_OPAQUE)) { - align = ct->ct_length; - if (align == -1 && (ct->ct_flags & CT_LAZY_FIELD_LIST)) { - force_lazy_struct(ct); - align = ct->ct_length; - } - } - else if (ct->ct_flags & (CT_POINTER|CT_FUNCTIONPTR)) { - struct aligncheck_ptr { char x; char *y; }; - align = offsetof(struct aligncheck_ptr, y); - } - else if (ct->ct_flags & CT_ARRAY) { - ct = ct->ct_itemdescr; - goto retry; - } - else { - PyErr_Format(PyExc_ValueError, "ctype '%s' is of unknown alignment", - ct->ct_name); - return -1; - } - - if ((align < 1) || (align & (align-1))) { - PyErr_Format(PyExc_SystemError, - "found for ctype '%s' bogus alignment '%d'", - ct->ct_name, align); - return -1; - } - return align; -} - -static void cdata_dealloc(CDataObject *cd) -{ - if (cd->c_weakreflist != NULL) - PyObject_ClearWeakRefs((PyObject *) cd); - - Py_DECREF(cd->c_type); -#ifndef CFFI_MEM_LEAK /* never release anything, tests only */ - Py_TYPE(cd)->tp_free((PyObject *)cd); -#endif -} - -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) - if (cd->c_type->ct_flags & (CT_PRIMITIVE_ANY | CT_STRUCT | CT_UNION)) { - assert(cd->c_type->ct_size >= 0); - memset(cd->c_data, 0xDD, cd->c_type->ct_size); - } - else if (cd->c_type->ct_flags & CT_ARRAY) { - Py_ssize_t x = get_array_length(cd); - assert(x >= 0); - x *= cd->c_type->ct_itemdescr->ct_size; - assert(x >= 0); - memset(cd->c_data, 0xDD, x); - } -#endif - cdata_dealloc(cd); -} - -static void cdataowninggc_dealloc(CDataObject *cd) -{ - PyObject_GC_UnTrack(cd); - - if (cd->c_type->ct_flags & CT_IS_VOID_PTR) { /* a handle */ - PyObject *x = ((CDataObject_own_structptr *)cd)->structobj; - Py_DECREF(x); - } - else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) { /* a callback */ - ffi_closure *closure = ((CDataObject_closure *)cd)->closure; - PyObject *args = (PyObject *)(closure->user_data); - Py_XDECREF(args); -#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_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 */ - PyObject *x = ((CDataObject_own_structptr *)cd)->structobj; - Py_VISIT(x); - } - else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) { /* a callback */ - ffi_closure *closure = ((CDataObject_closure *)cd)->closure; - PyObject *args = (PyObject *)(closure->user_data); - Py_VISIT(args); - } - 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; -} - -static int cdataowninggc_clear(CDataObject *cd) -{ - if (cd->c_type->ct_flags & CT_IS_VOID_PTR) { /* a handle */ - CDataObject_own_structptr *cd1 = (CDataObject_own_structptr *)cd; - PyObject *x = cd1->structobj; - Py_INCREF(Py_None); - cd1->structobj = Py_None; - Py_DECREF(x); - } - else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) { /* a callback */ - ffi_closure *closure = ((CDataObject_closure *)cd)->closure; - PyObject *args = (PyObject *)(closure->user_data); - closure->user_data = NULL; - Py_XDECREF(args); - } - return 0; -} - -static int cdatafrombuf_clear(CDataObject *cd) -{ - Py_buffer *view = ((CDataObject_frombuf *)cd)->bufferview; - PyBuffer_Release(view); - return 0; -} - -/* forward */ -static void _my_PyErr_WriteUnraisable(PyObject *t, PyObject *v, PyObject *tb, - char *objdescr, PyObject *obj, - char *extra_error_line); - - -static void gcp_finalize(PyObject *destructor, PyObject *origobj) -{ - /* NOTE: this decrements the reference count of the two arguments */ - - if (destructor != NULL) { - PyObject *result; - PyObject *error_type, *error_value, *error_traceback; - - /* Save the current exception */ - PyErr_Fetch(&error_type, &error_value, &error_traceback); - - result = PyObject_CallFunctionObjArgs(destructor, origobj, NULL); - if (result != NULL) { - Py_DECREF(result); - } - else { - PyObject *t, *v, *tb; - PyErr_Fetch(&t, &v, &tb); - /* Don't use error capture here, because it is very much - * like errors at __del__(), and these ones are not captured - * either */ - /* ecap = _cffi_start_error_capture(); */ - _my_PyErr_WriteUnraisable(t, v, tb, "From callback for ffi.gc ", - origobj, NULL); - /* _cffi_stop_error_capture(ecap); */ - } - Py_DECREF(destructor); - - /* Restore the saved exception */ - PyErr_Restore(error_type, error_value, error_traceback); - } - Py_XDECREF(origobj); -} - -static void cdatagcp_finalize(CDataObject_gcp *cd) -{ - PyObject *destructor = cd->destructor; - PyObject *origobj = cd->origobj; - cd->destructor = NULL; - cd->origobj = NULL; - gcp_finalize(destructor, origobj); -} - -static void cdatagcp_dealloc(CDataObject_gcp *cd) -{ - PyObject *destructor = cd->destructor; - PyObject *origobj = cd->origobj; - cdata_dealloc((CDataObject *)cd); - - gcp_finalize(destructor, origobj); -} - -static int cdatagcp_traverse(CDataObject_gcp *cd, visitproc visit, void *arg) -{ - Py_VISIT(cd->destructor); - Py_VISIT(cd->origobj); - return 0; -} - -static PyObject *cdata_float(CDataObject *cd); /*forward*/ - -static PyObject *convert_cdata_to_enum_string(CDataObject *cd, int both) -{ - PyObject *d_key, *d_value; - CTypeDescrObject *ct = cd->c_type; - - assert(ct->ct_flags & CT_IS_ENUM); - d_key = convert_to_object(cd->c_data, ct); - if (d_key == NULL) - return NULL; - - d_value = PyDict_GetItem(PyTuple_GET_ITEM(ct->ct_stuff, 1), d_key); - if (d_value != NULL) { - if (both) { - PyObject *o = PyObject_Str(d_key); - if (o == NULL) - d_value = NULL; - else { - d_value = PyText_FromFormat("%s: %s", - PyText_AS_UTF8(o), - PyText_AS_UTF8(d_value)); - Py_DECREF(o); - } - } - else - Py_INCREF(d_value); - } - else - d_value = PyObject_Str(d_key); - Py_DECREF(d_key); - return d_value; -} - -static PyObject *cdata_repr(CDataObject *cd) -{ - char *extra; - PyObject *result, *s; - - if (cd->c_type->ct_flags & CT_PRIMITIVE_ANY) { - if (cd->c_type->ct_flags & CT_IS_ENUM) { - s = convert_cdata_to_enum_string(cd, 1); - } - else if (cd->c_type->ct_flags & CT_IS_LONGDOUBLE) { - long double lvalue; - char buffer[128]; /* big enough */ - /*READ(cd->c_data, sizeof(long double)*/ - lvalue = read_raw_longdouble_data(cd->c_data); - sprintf(buffer, "%LE", lvalue); - s = PyText_FromString(buffer); - } - else { - PyObject *o = convert_to_object(cd->c_data, cd->c_type); - if (o == NULL) - return NULL; - s = PyObject_Repr(o); - Py_DECREF(o); - } - } - else if ((cd->c_type->ct_flags & CT_ARRAY) && cd->c_type->ct_length < 0) { - s = PyText_FromFormat("sliced length %zd", get_array_length(cd)); - } - else { - if (cd->c_data != NULL) { - s = PyText_FromFormat("%p", cd->c_data); - } - else - s = PyText_FromString("NULL"); - } - if (s == NULL) - return NULL; - /* it's slightly confusing to get "<cdata 'struct foo' 0x...>" because the - struct foo is not owned. Trying to make it clearer, write in this - case "<cdata 'struct foo &' 0x...>". */ - if (cd->c_type->ct_flags & (CT_STRUCT|CT_UNION)) - extra = " &"; - else - extra = ""; - result = PyText_FromFormat("<cdata '%s%s' %s>", - cd->c_type->ct_name, extra, - PyText_AsUTF8(s)); - Py_DECREF(s); - return result; -} - -static PyObject *_cdata_repr2(CDataObject *cd, char *text, PyObject *x) -{ - PyObject *res, *s = PyObject_Repr(x); - if (s == NULL) - return NULL; - res = PyText_FromFormat("<cdata '%s' %s %s>", - cd->c_type->ct_name, text, PyText_AsUTF8(s)); - Py_DECREF(s); - return res; -} - -static Py_ssize_t _cdata_var_byte_size(CDataObject *cd) -{ - /* If 'cd' is a 'struct foo' or 'struct foo *' allocated with - ffi.new(), and if the struct foo contains a varsize array, - then return the real allocated size. Otherwise, return -1. */ - if (!CDataOwn_Check(cd)) - return -1; - - if (cd->c_type->ct_flags & CT_IS_PTR_TO_OWNED) { - cd = (CDataObject *)((CDataObject_own_structptr *)cd)->structobj; - } - if (cd->c_type->ct_flags & CT_WITH_VAR_ARRAY) { - return ((CDataObject_own_length *)cd)->length; - } - 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); - if (size < 0) { - if (cd->c_type->ct_flags & CT_POINTER) - size = cd->c_type->ct_itemdescr->ct_size; - else if (cd->c_type->ct_flags & CT_ARRAY) - size = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size; - else - size = cd->c_type->ct_size; - } - return PyText_FromFormat("<cdata '%s' owning %zd bytes>", - cd->c_type->ct_name, size); -} - -static PyObject *cdataowninggc_repr(CDataObject *cd) -{ - if (cd->c_type->ct_flags & CT_IS_VOID_PTR) { /* a handle */ - PyObject *x = ((CDataObject_own_structptr *)cd)->structobj; - return _cdata_repr2(cd, "handle to", x); - } - else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) { /* a callback */ - ffi_closure *closure = ((CDataObject_closure *)cd)->closure; - PyObject *args = (PyObject *)closure->user_data; - if (args == NULL) - return cdata_repr(cd); - else - return _cdata_repr2(cd, "calling", PyTuple_GET_ITEM(args, 1)); - } - 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) -{ - if (cd->c_type->ct_flags & CT_PRIMITIVE_ANY) { - if (cd->c_type->ct_flags & (CT_PRIMITIVE_SIGNED | - CT_PRIMITIVE_UNSIGNED | - CT_PRIMITIVE_CHAR)) - return read_raw_unsigned_data(cd->c_data, cd->c_type->ct_size) != 0; - - if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) { - if (cd->c_type->ct_flags & CT_IS_LONGDOUBLE) - return read_raw_longdouble_data(cd->c_data) != 0.0; - return read_raw_float_data(cd->c_data, cd->c_type->ct_size) != 0.0; - } - if (cd->c_type->ct_flags & CT_PRIMITIVE_COMPLEX) { - Py_complex value = read_raw_complex_data(cd->c_data, - cd->c_type->ct_size); - return value.real != 0.0 || value.imag != 0.0; - } - } - return cd->c_data != NULL; -} - -static PyObject *cdata_int(CDataObject *cd) -{ - if ((cd->c_type->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_FITS_LONG)) - == (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_FITS_LONG)) { - /* this case is to handle enums, but also serves as a slight - performance improvement for some other primitive types */ - long value; - /*READ(cd->c_data, cd->c_type->ct_size)*/ - value = (long)read_raw_signed_data(cd->c_data, cd->c_type->ct_size); - return PyInt_FromLong(value); - } - if (cd->c_type->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_UNSIGNED)) { - 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)*/ - switch (cd->c_type->ct_size) { - case sizeof(char): - return PyInt_FromLong((unsigned char)cd->c_data[0]); - case 2: - return PyInt_FromLong((long)*(cffi_char16_t *)cd->c_data); - case 4: - if (cd->c_type->ct_flags & CT_IS_SIGNED_WCHAR) - return PyInt_FromLong((long)*(int32_t *)cd->c_data); - else if (sizeof(long) > 4) - return PyInt_FromLong(*(uint32_t *)cd->c_data); - else - return PyLong_FromUnsignedLong(*(uint32_t *)cd->c_data); - } - } - else if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) { - PyObject *o = cdata_float(cd); -#if PY_MAJOR_VERSION < 3 - PyObject *r = o ? PyNumber_Int(o) : NULL; -#else - PyObject *r = o ? PyNumber_Long(o) : NULL; -#endif - Py_XDECREF(o); - return r; - } - PyErr_Format(PyExc_TypeError, "int() not supported on cdata '%s'", - cd->c_type->ct_name); - return NULL; -} - -#if PY_MAJOR_VERSION < 3 -static PyObject *cdata_long(CDataObject *cd) -{ - PyObject *res = cdata_int(cd); - if (res != NULL && PyInt_CheckExact(res)) { - PyObject *o = PyLong_FromLong(PyInt_AS_LONG(res)); - Py_DECREF(res); - res = o; - } - return res; -} -#endif - -static PyObject *cdata_float(CDataObject *cd) -{ - if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) { - double value; - /*READ(cd->c_data, cd->c_type->ct_size)*/ - if (!(cd->c_type->ct_flags & CT_IS_LONGDOUBLE)) { - value = read_raw_float_data(cd->c_data, cd->c_type->ct_size); - } - else { - value = (double)read_raw_longdouble_data(cd->c_data); - } - return PyFloat_FromDouble(value); - } - PyErr_Format(PyExc_TypeError, "float() not supported on cdata '%s'", - cd->c_type->ct_name); - return NULL; -} - -static PyObject *cdata_richcompare(PyObject *v, PyObject *w, int op) -{ - int v_is_ptr, w_is_ptr; - PyObject *pyres; - - assert(CData_Check(v)); - - /* Comparisons involving a primitive cdata work differently than - * comparisons involving a struct/array/pointer. - * - * If v or w is a struct/array/pointer, then the other must be too - * (otherwise we return NotImplemented and leave the case to - * Python). If both are, then we compare the addresses. - * - * If v and/or w is a primitive cdata, then we convert the cdata(s) - * to regular Python objects and redo the comparison there. - */ - - v_is_ptr = !(((CDataObject *)v)->c_type->ct_flags & CT_PRIMITIVE_ANY); - w_is_ptr = CData_Check(w) && - !(((CDataObject *)w)->c_type->ct_flags & CT_PRIMITIVE_ANY); - - if (v_is_ptr && w_is_ptr) { - int res; - char *v_cdata = ((CDataObject *)v)->c_data; - char *w_cdata = ((CDataObject *)w)->c_data; - - switch (op) { - case Py_EQ: res = (v_cdata == w_cdata); break; - case Py_NE: res = (v_cdata != w_cdata); break; - case Py_LT: res = (v_cdata < w_cdata); break; - case Py_LE: res = (v_cdata <= w_cdata); break; - case Py_GT: res = (v_cdata > w_cdata); break; - case Py_GE: res = (v_cdata >= w_cdata); break; - default: res = -1; - } - pyres = res ? Py_True : Py_False; - } - else if (v_is_ptr || w_is_ptr) { - pyres = Py_NotImplemented; - } - else { - PyObject *aa[2]; - int i; - - aa[0] = v; Py_INCREF(v); - aa[1] = w; Py_INCREF(w); - pyres = NULL; - - for (i = 0; i < 2; i++) { - v = aa[i]; - if (!CData_Check(v)) - continue; - w = convert_to_object(((CDataObject *)v)->c_data, - ((CDataObject *)v)->c_type); - if (w == NULL) - goto error; - if (CData_Check(w)) { - Py_DECREF(w); - PyErr_Format(PyExc_NotImplementedError, - "cannot use <cdata '%s'> in a comparison", - ((CDataObject *)v)->c_type->ct_name); - goto error; - } - aa[i] = w; - Py_DECREF(v); - } - pyres = PyObject_RichCompare(aa[0], aa[1], op); - error: - Py_DECREF(aa[1]); - Py_DECREF(aa[0]); - return pyres; - } - - Py_INCREF(pyres); - return pyres; -} - -#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, - ((CDataObject *)v)->c_type); - if (vv == NULL) - return -1; - if (!CData_Check(vv)) { - Py_hash_t hash = PyObject_Hash(vv); - Py_DECREF(vv); - return hash; - } - Py_DECREF(vv); - } - return _Py_HashPointer(((CDataObject *)v)->c_data); -} - -static Py_ssize_t -cdata_length(CDataObject *cd) -{ - if (cd->c_type->ct_flags & CT_ARRAY) { - return get_array_length(cd); - } - PyErr_Format(PyExc_TypeError, "cdata of type '%s' has no len()", - cd->c_type->ct_name); - return -1; -} - -static char * -_cdata_get_indexed_ptr(CDataObject *cd, PyObject *key) -{ - Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); - if (i == -1 && PyErr_Occurred()) - return NULL; - - if (cd->c_type->ct_flags & CT_POINTER) { - if (CDataOwn_Check(cd)) { - if (i != 0) { - PyErr_Format(PyExc_IndexError, - "cdata '%s' can only be indexed by 0", - cd->c_type->ct_name); - return NULL; - } - } - else { - if (cd->c_data == NULL) { - PyErr_Format(PyExc_RuntimeError, - "cannot dereference null pointer from cdata '%s'", - cd->c_type->ct_name); - return NULL; - } - } - } - else if (cd->c_type->ct_flags & CT_ARRAY) { - if (i < 0) { - PyErr_SetString(PyExc_IndexError, - "negative index"); - return NULL; - } - if (i >= get_array_length(cd)) { - PyErr_Format(PyExc_IndexError, - "index too large for cdata '%s' (expected %zd < %zd)", - cd->c_type->ct_name, - i, get_array_length(cd)); - return NULL; - } - } - else { - PyErr_Format(PyExc_TypeError, "cdata of type '%s' cannot be indexed", - cd->c_type->ct_name); - return NULL; - } - return cd->c_data + i * cd->c_type->ct_itemdescr->ct_size; -} - -static PyObject * -new_array_type(CTypeDescrObject *ctptr, Py_ssize_t length); /* forward */ - -static CTypeDescrObject * -_cdata_getslicearg(CDataObject *cd, PySliceObject *slice, Py_ssize_t bounds[]) -{ - Py_ssize_t start, stop; - CTypeDescrObject *ct; - - start = PyInt_AsSsize_t(slice->start); - if (start == -1 && PyErr_Occurred()) { - if (slice->start == Py_None) - PyErr_SetString(PyExc_IndexError, "slice start must be specified"); - return NULL; - } - stop = PyInt_AsSsize_t(slice->stop); - if (stop == -1 && PyErr_Occurred()) { - if (slice->stop == Py_None) - PyErr_SetString(PyExc_IndexError, "slice stop must be specified"); - return NULL; - } - if (slice->step != Py_None) { - PyErr_SetString(PyExc_IndexError, "slice with step not supported"); - return NULL; - } - if (start > stop) { - PyErr_SetString(PyExc_IndexError, "slice start > stop"); - return NULL; - } - - ct = cd->c_type; - if (ct->ct_flags & CT_ARRAY) { - if (start < 0) { - PyErr_SetString(PyExc_IndexError, - "negative index"); - return NULL; - } - if (stop > get_array_length(cd)) { - PyErr_Format(PyExc_IndexError, - "index too large (expected %zd <= %zd)", - stop, get_array_length(cd)); - return NULL; - } - ct = (CTypeDescrObject *)ct->ct_stuff; - } - else if (!(ct->ct_flags & CT_POINTER)) { - PyErr_Format(PyExc_TypeError, "cdata of type '%s' cannot be indexed", - ct->ct_name); - return NULL; - } - - bounds[0] = start; - bounds[1] = stop - start; - return ct; -} - -static PyObject * -cdata_slice(CDataObject *cd, PySliceObject *slice) -{ - char *cdata; - Py_ssize_t bounds[2]; - CTypeDescrObject *ct = _cdata_getslicearg(cd, slice, bounds); - if (ct == NULL) - return NULL; - - if (ct->ct_stuff == NULL) { - ct->ct_stuff = new_array_type(ct, -1); - if (ct->ct_stuff == NULL) - return NULL; - } - ct = (CTypeDescrObject *)ct->ct_stuff; - - cdata = cd->c_data + ct->ct_itemdescr->ct_size * bounds[0]; - return new_sized_cdata(cdata, ct, bounds[1]); -} - -static int -cdata_ass_slice(CDataObject *cd, PySliceObject *slice, PyObject *v) -{ - Py_ssize_t bounds[2], i, length, itemsize; - PyObject *it, *item; - PyObject *(*iternext)(PyObject *); - char *cdata; - int err; - CTypeDescrObject *ct = _cdata_getslicearg(cd, slice, bounds); - if (ct == NULL) - return -1; - ct = ct->ct_itemdescr; - itemsize = ct->ct_size; - cdata = cd->c_data + itemsize * bounds[0]; - length = bounds[1]; - - if (CData_Check(v)) { - CTypeDescrObject *ctv = ((CDataObject *)v)->c_type; - if ((ctv->ct_flags & CT_ARRAY) && (ctv->ct_itemdescr == ct) && - (get_array_length((CDataObject *)v) == length)) { - /* fast path: copying from exactly the correct type */ - memmove(cdata, ((CDataObject *)v)->c_data, itemsize * length); - return 0; - } - } - - /* A fast path for <char[]>[0:N] = b"somestring" or bytearray, which - also adds support for Python 3: otherwise, you get integers while - enumerating the string, and you can't set them to characters :-/ - */ - if ((ct->ct_flags & CT_PRIMITIVE_CHAR) && itemsize == sizeof(char)) { - char *src; - Py_ssize_t srclen; - if (PyBytes_Check(v)) { - srclen = PyBytes_GET_SIZE(v); - src = PyBytes_AS_STRING(v); - } - else if (PyByteArray_Check(v)) { - srclen = PyByteArray_GET_SIZE(v); - src = PyByteArray_AS_STRING(v); - } - else - goto other_types; - - if (srclen != length) { - PyErr_Format(PyExc_ValueError, - "need a string of length %zd, got %zd", - length, srclen); - return -1; - } - memcpy(cdata, src, length); - return 0; - } - other_types: - - it = PyObject_GetIter(v); - if (it == NULL) - return -1; - iternext = *it->ob_type->tp_iternext; - - for (i = 0; i < length; i++) { - item = iternext(it); - if (item == NULL) { - if (!PyErr_Occurred()) - PyErr_Format(PyExc_ValueError, - "need %zd values to unpack, got %zd", - length, i); - goto error; - } - err = convert_from_object(cdata, ct, item); - Py_DECREF(item); - if (err < 0) - goto error; - - cdata += itemsize; - } - item = iternext(it); - if (item != NULL) { - Py_DECREF(item); - PyErr_Format(PyExc_ValueError, - "got more than %zd values to unpack", length); - } - error: - Py_DECREF(it); - return PyErr_Occurred() ? -1 : 0; -} - -static PyObject * -cdataowning_subscript(CDataObject *cd, PyObject *key) -{ - char *c; - if (PySlice_Check(key)) - return cdata_slice(cd, (PySliceObject *)key); - - c = _cdata_get_indexed_ptr(cd, key); - /* use 'mp_subscript' instead of 'sq_item' because we don't want - negative indexes to be corrected automatically */ - if (c == NULL && PyErr_Occurred()) - return NULL; - - if (cd->c_type->ct_flags & CT_IS_PTR_TO_OWNED) { - PyObject *res = ((CDataObject_own_structptr *)cd)->structobj; - Py_INCREF(res); - return res; - } - else { - return convert_to_object(c, cd->c_type->ct_itemdescr); - } -} - -static PyObject * -cdata_subscript(CDataObject *cd, PyObject *key) -{ - char *c; - if (PySlice_Check(key)) - return cdata_slice(cd, (PySliceObject *)key); - - c = _cdata_get_indexed_ptr(cd, key); - /* use 'mp_subscript' instead of 'sq_item' because we don't want - negative indexes to be corrected automatically */ - if (c == NULL && PyErr_Occurred()) - return NULL; - return convert_to_object(c, cd->c_type->ct_itemdescr); -} - -static int -cdata_ass_sub(CDataObject *cd, PyObject *key, PyObject *v) -{ - char *c; - CTypeDescrObject *ctitem; - if (PySlice_Check(key)) - return cdata_ass_slice(cd, (PySliceObject *)key, v); - - c = _cdata_get_indexed_ptr(cd, key); - ctitem = cd->c_type->ct_itemdescr; - /* use 'mp_ass_subscript' instead of 'sq_ass_item' because we don't want - negative indexes to be corrected automatically */ - if (c == NULL && PyErr_Occurred()) - return -1; - if (v == NULL) { - PyErr_SetString(PyExc_TypeError, - "'del x[n]' not supported for cdata objects"); - return -1; - } - return convert_from_object(c, ctitem, v); -} - -static PyObject * -_cdata_add_or_sub(PyObject *v, PyObject *w, int sign) -{ - Py_ssize_t i, itemsize; - CDataObject *cd; - CTypeDescrObject *ctptr; - - if (!CData_Check(v)) { - PyObject *swap; - assert(CData_Check(w)); - if (sign != 1) - goto not_implemented; - swap = v; - v = w; - w = swap; - } - - i = PyNumber_AsSsize_t(w, PyExc_OverflowError); - if (i == -1 && PyErr_Occurred()) - return NULL; - i *= sign; - - cd = (CDataObject *)v; - if (cd->c_type->ct_flags & CT_POINTER) - ctptr = cd->c_type; - else if (cd->c_type->ct_flags & CT_ARRAY) { - ctptr = (CTypeDescrObject *)cd->c_type->ct_stuff; - } - else { - PyErr_Format(PyExc_TypeError, "cannot add a cdata '%s' and a number", - cd->c_type->ct_name); - return NULL; - } - itemsize = ctptr->ct_itemdescr->ct_size; - if (itemsize < 0) { - if (ctptr->ct_flags & CT_IS_VOID_PTR) { - itemsize = 1; - } - else { - PyErr_Format(PyExc_TypeError, - "ctype '%s' points to items of unknown size", - cd->c_type->ct_name); - return NULL; - } - } - return new_simple_cdata(cd->c_data + i * itemsize, ctptr); - - not_implemented: - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; -} - -static PyObject * -cdata_add(PyObject *v, PyObject *w) -{ - return _cdata_add_or_sub(v, w, +1); -} - -static PyObject * -cdata_sub(PyObject *v, PyObject *w) -{ - if (CData_Check(v) && CData_Check(w)) { - CDataObject *cdv = (CDataObject *)v; - CDataObject *cdw = (CDataObject *)w; - CTypeDescrObject *ct = cdw->c_type; - Py_ssize_t diff, itemsize; - - if (ct->ct_flags & CT_ARRAY) /* ptr_to_T - array_of_T: ok */ - ct = (CTypeDescrObject *)ct->ct_stuff; - - if (ct != cdv->c_type || !(ct->ct_flags & CT_POINTER) || - (ct->ct_itemdescr->ct_size <= 0 && - !(ct->ct_flags & CT_IS_VOID_PTR))) { - PyErr_Format(PyExc_TypeError, - "cannot subtract cdata '%s' and cdata '%s'", - cdv->c_type->ct_name, ct->ct_name); - return NULL; - } - itemsize = ct->ct_itemdescr->ct_size; - diff = cdv->c_data - cdw->c_data; - if (itemsize > 1) { - if (diff % itemsize) { - PyErr_SetString(PyExc_ValueError, - "pointer subtraction: the distance between the two " - "pointers is not a multiple of the item size"); - return NULL; - } - diff = diff / itemsize; - } -#if PY_MAJOR_VERSION < 3 - return PyInt_FromSsize_t(diff); -#else - return PyLong_FromSsize_t(diff); -#endif - } - - return _cdata_add_or_sub(v, w, -1); -} - -static void -_cdata_attr_errmsg(char *errmsg, CDataObject *cd, PyObject *attr) -{ - const char *text; - if (!PyErr_ExceptionMatches(PyExc_AttributeError)) - return; - PyErr_Clear(); - text = PyText_AsUTF8(attr); - if (text == NULL) - return; - PyErr_Format(PyExc_AttributeError, errmsg, cd->c_type->ct_name, text); -} - -static PyObject * -cdata_getattro(CDataObject *cd, PyObject *attr) -{ - CFieldObject *cf; - CTypeDescrObject *ct = cd->c_type; - char *errmsg = "cdata '%s' has no attribute '%s'"; - PyObject *x; - - if (ct->ct_flags & CT_POINTER) - ct = ct->ct_itemdescr; - - if (ct->ct_flags & (CT_STRUCT|CT_UNION)) { - switch (force_lazy_struct(ct)) { - case 1: - cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, attr); - if (cf != NULL) { - /* read the field 'cf' */ - char *data = cd->c_data + cf->cf_offset; - Py_ssize_t array_len, size; - - if (cf->cf_bitshift == BS_REGULAR) { - return convert_to_object(data, cf->cf_type); - } - else if (cf->cf_bitshift != BS_EMPTY_ARRAY) { - return convert_to_object_bitfield(data, cf); - } - - /* variable-length array: */ - /* if reading variable length array from variable length - struct, calculate array type from allocated length */ - size = _cdata_var_byte_size(cd) - cf->cf_offset; - if (size >= 0) { - array_len = size / cf->cf_type->ct_itemdescr->ct_size; - return new_sized_cdata(data, cf->cf_type, array_len); - } - return new_simple_cdata(data, - (CTypeDescrObject *)cf->cf_type->ct_stuff); - } - errmsg = "cdata '%s' has no field '%s'"; - break; - case -1: - return NULL; - default: - errmsg = "cdata '%s' points to an opaque type: cannot read fields"; - break; - } - } - x = PyObject_GenericGetAttr((PyObject *)cd, attr); - if (x == NULL) - _cdata_attr_errmsg(errmsg, cd, attr); - return x; -} - -static int -cdata_setattro(CDataObject *cd, PyObject *attr, PyObject *value) -{ - CFieldObject *cf; - CTypeDescrObject *ct = cd->c_type; - char *errmsg = "cdata '%s' has no attribute '%s'"; - int x; - - if (ct->ct_flags & CT_POINTER) - ct = ct->ct_itemdescr; - - if (ct->ct_flags & (CT_STRUCT|CT_UNION)) { - switch (force_lazy_struct(ct)) { - case 1: - cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, attr); - if (cf != NULL) { - /* write the field 'cf' */ - if (value != NULL) { - return convert_field_from_object(cd->c_data, cf, value); - } - else { - PyErr_SetString(PyExc_AttributeError, - "cannot delete struct field"); - return -1; - } - } - errmsg = "cdata '%s' has no field '%s'"; - break; - case -1: - return -1; - default: - errmsg = "cdata '%s' points to an opaque type: cannot write fields"; - break; - } - } - x = PyObject_GenericSetAttr((PyObject *)cd, attr, value); - if (x < 0) - _cdata_attr_errmsg(errmsg, cd, attr); - return x; -} - -static PyObject * -convert_struct_to_owning_object(char *data, CTypeDescrObject *ct); /*forward*/ - -static cif_description_t * -fb_prepare_cif(PyObject *fargs, CTypeDescrObject *, Py_ssize_t, ffi_abi); - /*forward*/ - -static PyObject *new_primitive_type(const char *name); /*forward*/ - -static CTypeDescrObject *_get_ct_int(void) -{ - static CTypeDescrObject *ct_int = NULL; - if (ct_int == NULL) { - ct_int = (CTypeDescrObject *)new_primitive_type("int"); - } - return ct_int; -} - -static Py_ssize_t -_prepare_pointer_call_argument(CTypeDescrObject *ctptr, PyObject *init, - char **output_data) -{ - /* 'ctptr' is here a pointer type 'ITEM *'. Accept as argument an - initializer for an array 'ITEM[]'. This includes the case of - passing a Python byte string to a 'char *' argument. - - This function returns -1 if an error occurred, - 0 if conversion succeeded (into *output_data), - or N > 0 if conversion would require N bytes of storage. - */ - Py_ssize_t length, datasize; - CTypeDescrObject *ctitem; - - if (CData_Check(init)) - goto convert_default; - - ctitem = ctptr->ct_itemdescr; - /* XXX some code duplication, how to avoid it? */ - if (PyBytes_Check(init)) { - /* from a string: just returning the string here is fine. - We assume that the C code won't modify the 'char *' data. */ - if ((ctptr->ct_flags & CT_IS_VOIDCHAR_PTR) || - ((ctitem->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_UNSIGNED)) - && (ctitem->ct_size == sizeof(char)))) { -#if defined(CFFI_MEM_DEBUG) || defined(CFFI_MEM_LEAK) - length = PyBytes_GET_SIZE(init) + 1; -#else - *output_data = PyBytes_AS_STRING(init); - if (ctitem->ct_flags & CT_IS_BOOL) - if (must_be_array_of_zero_or_one(*output_data, - PyBytes_GET_SIZE(init)) < 0) - return -1; - return 0; -#endif - } - else - goto convert_default; - } - else if (PyList_Check(init) || PyTuple_Check(init)) { - length = PySequence_Fast_GET_SIZE(init); - } - else if (PyUnicode_Check(init)) { - /* from a unicode, we add the null terminator */ - if (ctitem->ct_size == 2) - length = _my_PyUnicode_SizeAsChar16(init); - else - length = _my_PyUnicode_SizeAsChar32(init); - length += 1; - } - else if ((ctitem->ct_flags & CT_IS_FILE) && PyFile_Check(init)) { - *output_data = (char *)PyFile_AsFile(init); - if (*output_data == NULL && PyErr_Occurred()) - return -1; - return 0; - } - else { - /* refuse to receive just an integer (and interpret it - as the array size) */ - goto convert_default; - } - - if (ctitem->ct_size <= 0) - goto convert_default; - datasize = MUL_WRAPAROUND(length, ctitem->ct_size); - if ((datasize / ctitem->ct_size) != length) { - PyErr_SetString(PyExc_OverflowError, - "array size would overflow a Py_ssize_t"); - return -1; - } - if (datasize <= 0) - datasize = 1; - return datasize; - - convert_default: - return convert_from_object((char *)output_data, ctptr, init); -} - -static PyObject* -cdata_call(CDataObject *cd, PyObject *args, PyObject *kwds) -{ - char *buffer; - void** buffer_array; - cif_description_t *cif_descr; - Py_ssize_t i, nargs, nargs_declared; - PyObject *signature, *res = NULL, *fvarargs; - 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"); - return NULL; - } - signature = cd->c_type->ct_stuff; - nargs = PyTuple_Size(args); - if (nargs < 0) - return NULL; - nargs_declared = PyTuple_GET_SIZE(signature) - 2; - fresult = (CTypeDescrObject *)PyTuple_GET_ITEM(signature, 1); - fvarargs = NULL; - buffer = NULL; - - cif_descr = (cif_description_t *)cd->c_type->ct_extra; - - if (cif_descr != NULL) { - /* regular case: this function does not take '...' arguments */ - if (nargs != nargs_declared) { - errormsg = "'%s' expects %zd arguments, got %zd"; - bad_number_of_arguments: - PyErr_Format(PyExc_TypeError, errormsg, - cd->c_type->ct_name, nargs_declared, nargs); - goto error; - } - } - else { - /* call of a variadic function */ - ffi_abi fabi; - if (nargs < nargs_declared) { - errormsg = "'%s' expects at least %zd arguments, got %zd"; - goto bad_number_of_arguments; - } - fvarargs = PyTuple_New(nargs); - if (fvarargs == NULL) - goto error; - for (i = 0; i < nargs_declared; i++) { - PyObject *o = PyTuple_GET_ITEM(signature, 2 + i); - Py_INCREF(o); - PyTuple_SET_ITEM(fvarargs, i, o); - } - for (i = nargs_declared; i < nargs; i++) { - PyObject *obj = PyTuple_GET_ITEM(args, i); - CTypeDescrObject *ct; - - if (CData_Check(obj)) { - ct = ((CDataObject *)obj)->c_type; - if (ct->ct_flags & (CT_PRIMITIVE_CHAR | CT_PRIMITIVE_UNSIGNED | - CT_PRIMITIVE_SIGNED)) { - if (ct->ct_size < (Py_ssize_t)sizeof(int)) { - ct = _get_ct_int(); - if (ct == NULL) - goto error; - } - } - else if (ct->ct_flags & CT_ARRAY) { - ct = (CTypeDescrObject *)ct->ct_stuff; - } - Py_INCREF(ct); - } - else { - PyErr_Format(PyExc_TypeError, - "argument %zd passed in the variadic part " - "needs to be a cdata object (got %.200s)", - i + 1, Py_TYPE(obj)->tp_name); - goto error; - } - PyTuple_SET_ITEM(fvarargs, i, (PyObject *)ct); - } -#if PY_MAJOR_VERSION < 3 - fabi = PyInt_AS_LONG(PyTuple_GET_ITEM(signature, 0)); -#else - fabi = PyLong_AS_LONG(PyTuple_GET_ITEM(signature, 0)); -#endif - cif_descr = fb_prepare_cif(fvarargs, fresult, nargs_declared, fabi); - if (cif_descr == NULL) - goto error; - } - - buffer = PyObject_Malloc(cif_descr->exchange_size); - if (buffer == NULL) { - PyErr_NoMemory(); - goto error; - } - - buffer_array = (void **)buffer; - - for (i=0; i<nargs; i++) { - CTypeDescrObject *argtype; - char *data = buffer + cif_descr->exchange_offset_arg[1 + i]; - PyObject *obj = PyTuple_GET_ITEM(args, i); - - buffer_array[i] = data; - - if (i < nargs_declared) - argtype = (CTypeDescrObject *)PyTuple_GET_ITEM(signature, 2 + i); - else - argtype = (CTypeDescrObject *)PyTuple_GET_ITEM(fvarargs, i); - - if (argtype->ct_flags & CT_POINTER) { - char *tmpbuf; - Py_ssize_t datasize = _prepare_pointer_call_argument( - argtype, obj, (char **)data); - if (datasize == 0) - ; /* successfully filled '*data' */ - else if (datasize < 0) - goto error; - else { - 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) - goto error; - } - } - else if (convert_from_object(data, argtype, obj) < 0) - goto error; - } - - resultdata = buffer + cif_descr->exchange_offset_arg[0]; - /*READ(cd->c_data, sizeof(void(*)(void)))*/ - - Py_BEGIN_ALLOW_THREADS - restore_errno(); - ffi_call(&cif_descr->cif, (void (*)(void))(cd->c_data), - resultdata, buffer_array); - save_errno(); - Py_END_ALLOW_THREADS - - if (fresult->ct_flags & (CT_PRIMITIVE_CHAR | CT_PRIMITIVE_SIGNED | - CT_PRIMITIVE_UNSIGNED)) { -#ifdef WORDS_BIGENDIAN - /* For results of precisely these types, libffi has a strange - rule that they will be returned as a whole 'ffi_arg' if they - are smaller. The difference only matters on big-endian. */ - if (fresult->ct_size < sizeof(ffi_arg)) - resultdata += (sizeof(ffi_arg) - fresult->ct_size); -#endif - res = convert_to_object(resultdata, fresult); - } - else if (fresult->ct_flags & CT_VOID) { - res = Py_None; - Py_INCREF(res); - } - else if (fresult->ct_flags & CT_STRUCT) { - res = convert_struct_to_owning_object(resultdata, fresult); - } - else { - res = convert_to_object(resultdata, fresult); - } - /* fall-through */ - - error: - while (freeme != NULL) { - void *p = (void *)freeme; - freeme = freeme->next; - PyObject_Free(p); - } - if (buffer) - PyObject_Free(buffer); - if (fvarargs != NULL) { - Py_DECREF(fvarargs); - if (cif_descr != NULL) /* but only if fvarargs != NULL, if variadic */ - PyObject_Free(cif_descr); - } - return res; -} - -static PyObject *cdata_dir(PyObject *cd, PyObject *noarg) -{ - CTypeDescrObject *ct = ((CDataObject *)cd)->c_type; - - /* replace the type 'pointer-to-t' with just 't' */ - if (ct->ct_flags & CT_POINTER) { - ct = ct->ct_itemdescr; - } - if ((ct->ct_flags & (CT_STRUCT | CT_UNION)) && - !(ct->ct_flags & CT_IS_OPAQUE)) { - - /* for non-opaque structs or unions */ - if (force_lazy_struct(ct) < 0) - return NULL; - return PyDict_Keys(ct->ct_stuff); - } - else { - return PyList_New(0); /* empty list for the other cases */ - } -} - -static PyObject *cdata_complex(PyObject *cd_, PyObject *noarg) -{ - CDataObject *cd = (CDataObject *)cd_; - - if (cd->c_type->ct_flags & CT_PRIMITIVE_COMPLEX) { - Py_complex value = read_raw_complex_data(cd->c_data, cd->c_type->ct_size); - PyObject *op = PyComplex_FromCComplex(value); - return op; - } - /* <cdata 'float'> or <cdata 'int'> cannot be directly converted by - calling complex(), just like <cdata 'int'> cannot be directly - converted by calling float() */ - - PyErr_Format(PyExc_TypeError, "complex() not supported on cdata '%s'", - cd->c_type->ct_name); - return NULL; -} - -static int explicit_release_case(PyObject *cd) -{ - CTypeDescrObject *ct = ((CDataObject *)cd)->c_type; - if (Py_TYPE(cd) == &CDataOwning_Type) { - if ((ct->ct_flags & (CT_POINTER | CT_ARRAY)) != 0) /* ffi.new() */ - return 0; - } - else if (Py_TYPE(cd) == &CDataFromBuf_Type) { - return 1; /* ffi.from_buffer() */ - } - else if (Py_TYPE(cd) == &CDataGCP_Type) { - return 2; /* ffi.gc() */ - } - PyErr_SetString(PyExc_ValueError, - "only 'cdata' object from ffi.new(), ffi.gc(), ffi.from_buffer() " - "or ffi.new_allocator()() can be used with the 'with' keyword or " - "ffi.release()"); - return -1; -} - -static PyObject *cdata_enter(PyObject *cd, PyObject *noarg) -{ - if (explicit_release_case(cd) < 0) /* only to check the ctype */ - return NULL; - Py_INCREF(cd); - return cd; -} - -static PyObject *cdata_exit(PyObject *cd, PyObject *args) -{ - /* 'args' ignored */ - CTypeDescrObject *ct; - Py_buffer *view; - switch (explicit_release_case(cd)) - { - case 0: /* ffi.new() */ - /* no effect on CPython: raw memory is allocated with the - same malloc() as the object itself, so it can't be - released independently. If we use a custom allocator, - then it's implemented with ffi.gc(). */ - ct = ((CDataObject *)cd)->c_type; - if (ct->ct_flags & CT_IS_PTR_TO_OWNED) { - 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 *") */ - cdatagcp_finalize((CDataObject_gcp *)x); - } - } - break; - - case 1: /* ffi.from_buffer() */ - view = ((CDataObject_frombuf *)cd)->bufferview; - PyBuffer_Release(view); - break; - - case 2: /* ffi.gc() or ffi.new_allocator()("not-struct-nor-union") */ - /* call the destructor immediately */ - cdatagcp_finalize((CDataObject_gcp *)cd); - break; - - default: - return NULL; - } - Py_INCREF(Py_None); - return Py_None; -} - -static PyObject *cdata_iter(CDataObject *); - -static PyNumberMethods CData_as_number = { - (binaryfunc)cdata_add, /*nb_add*/ - (binaryfunc)cdata_sub, /*nb_subtract*/ - 0, /*nb_multiply*/ -#if PY_MAJOR_VERSION < 3 - 0, /*nb_divide*/ -#endif - 0, /*nb_remainder*/ - 0, /*nb_divmod*/ - 0, /*nb_power*/ - 0, /*nb_negative*/ - 0, /*nb_positive*/ - 0, /*nb_absolute*/ - (inquiry)cdata_nonzero, /*nb_nonzero*/ - 0, /*nb_invert*/ - 0, /*nb_lshift*/ - 0, /*nb_rshift*/ - 0, /*nb_and*/ - 0, /*nb_xor*/ - 0, /*nb_or*/ -#if PY_MAJOR_VERSION < 3 - 0, /*nb_coerce*/ -#endif - (unaryfunc)cdata_int, /*nb_int*/ -#if PY_MAJOR_VERSION < 3 - (unaryfunc)cdata_long, /*nb_long*/ -#else - 0, -#endif - (unaryfunc)cdata_float, /*nb_float*/ - 0, /*nb_oct*/ - 0, /*nb_hex*/ -}; - -static PyMappingMethods CData_as_mapping = { - (lenfunc)cdata_length, /*mp_length*/ - (binaryfunc)cdata_subscript, /*mp_subscript*/ - (objobjargproc)cdata_ass_sub, /*mp_ass_subscript*/ -}; - -static PyMappingMethods CDataOwn_as_mapping = { - (lenfunc)cdata_length, /*mp_length*/ - (binaryfunc)cdataowning_subscript, /*mp_subscript*/ - (objobjargproc)cdata_ass_sub, /*mp_ass_subscript*/ -}; - -static PyMethodDef cdata_methods[] = { - {"__dir__", cdata_dir, METH_NOARGS}, - {"__complex__", cdata_complex, METH_NOARGS}, - {"__enter__", cdata_enter, METH_NOARGS}, - {"__exit__", cdata_exit, METH_VARARGS}, - {NULL, NULL} /* sentinel */ -}; - -static PyTypeObject CData_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_cffi_backend._CDataBase", - sizeof(CDataObject), - 0, - (destructor)cdata_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - (reprfunc)cdata_repr, /* tp_repr */ - &CData_as_number, /* tp_as_number */ - 0, /* tp_as_sequence */ - &CData_as_mapping, /* tp_as_mapping */ - 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 */ - "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 */ - offsetof(CDataObject, c_weakreflist), /* tp_weaklistoffset */ - (getiterfunc)cdata_iter, /* tp_iter */ - 0, /* tp_iternext */ - cdata_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* 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_Del, /* tp_free */ -}; - -static PyTypeObject CDataOwning_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_cffi_backend.__CDataOwn", - sizeof(CDataObject), - 0, - (destructor)cdataowning_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - (reprfunc)cdataowning_repr, /* tp_repr */ - 0, /* inherited */ /* tp_as_number */ - 0, /* tp_as_sequence */ - &CDataOwn_as_mapping, /* 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 */ - "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 */ - 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 */ - free, /* tp_free */ -}; - -static PyTypeObject CDataOwningGC_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_cffi_backend.__CDataOwnGC", - sizeof(CDataObject_own_structptr), - 0, - (destructor)cdataowninggc_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - (reprfunc)cdataowninggc_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)cdataowninggc_traverse, /* tp_traverse */ - (inquiry)cdataowninggc_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 */ - &CDataOwning_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 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", - sizeof(CDataObject_gcp), - 0, - (destructor)cdatagcp_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* inherited */ /* 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 */ -#ifdef Py_TPFLAGS_HAVE_FINALIZE - | Py_TPFLAGS_HAVE_FINALIZE -#endif - | 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)cdatagcp_traverse, /* tp_traverse */ - 0, /* 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 */ -#ifdef Py_TPFLAGS_HAVE_FINALIZE /* CPython >= 3.4 */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0, /* inherited */ /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* version_tag */ - (destructor)cdatagcp_finalize, /* tp_finalize */ -#endif -}; - -/************************************************************/ - -typedef struct { - PyObject_HEAD - char *di_next, *di_stop; - CDataObject *di_object; - CTypeDescrObject *di_itemtype; -} CDataIterObject; - -static PyObject * -cdataiter_next(CDataIterObject *it) -{ - char *result = it->di_next; - if (result != it->di_stop) { - it->di_next = result + it->di_itemtype->ct_size; - return convert_to_object(result, it->di_itemtype); - } - return NULL; -} - -static void -cdataiter_dealloc(CDataIterObject *it) -{ - Py_DECREF(it->di_object); - PyObject_Del(it); -} - -static PyTypeObject CDataIter_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_cffi_backend.__CData_iterator", /* tp_name */ - sizeof(CDataIterObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)cdataiter_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)cdataiter_next, /* tp_iternext */ -}; - -static PyObject * -cdata_iter(CDataObject *cd) -{ - CDataIterObject *it; - - if (!(cd->c_type->ct_flags & CT_ARRAY)) { - PyErr_Format(PyExc_TypeError, "cdata '%s' does not support iteration", - cd->c_type->ct_name); - return NULL; - } - - it = PyObject_New(CDataIterObject, &CDataIter_Type); - if (it == NULL) - return NULL; - - Py_INCREF(cd); - it->di_object = cd; - it->di_itemtype = cd->c_type->ct_itemdescr; - it->di_next = cd->c_data; - it->di_stop = cd->c_data + get_array_length(cd) * it->di_itemtype->ct_size; - return (PyObject *)it; -} - -/************************************************************/ - -static CDataObject *allocate_owning_object(Py_ssize_t size, - CTypeDescrObject *ct, - int dont_clear) -{ - /* note: objects with &CDataOwning_Type are always allocated with - either a plain malloc() or calloc(), and freed with free(). */ - CDataObject *cd; - if (dont_clear) - cd = malloc(size); - else - cd = calloc(size, 1); - if (PyObject_Init((PyObject *)cd, &CDataOwning_Type) == NULL) - return NULL; - - Py_INCREF(ct); - cd->c_type = ct; - cd->c_weakreflist = NULL; - return cd; -} - -static PyObject * -convert_struct_to_owning_object(char *data, CTypeDescrObject *ct) -{ - /* also accepts unions, for the API mode */ - CDataObject *cd; - Py_ssize_t dataoffset = offsetof(CDataObject_own_nolength, alignment); - Py_ssize_t datasize = ct->ct_size; - - if (datasize < 0) { - PyErr_SetString(PyExc_TypeError, - "return type is an opaque structure or union"); - return NULL; - } - if (ct->ct_flags & CT_WITH_VAR_ARRAY) { - PyErr_SetString(PyExc_TypeError, - "return type is a struct/union with a varsize array member"); - return NULL; - } - cd = allocate_owning_object(dataoffset + datasize, ct, /*dont_clear=*/1); - if (cd == NULL) - return NULL; - cd->c_data = ((char *)cd) + dataoffset; - - memcpy(cd->c_data, data, datasize); - return (PyObject *)cd; -} - -static CDataObject *allocate_gcp_object(CDataObject *origobj, - CTypeDescrObject *ct, - PyObject *destructor) -{ - CDataObject_gcp *cd = PyObject_GC_New(CDataObject_gcp, &CDataGCP_Type); - if (cd == NULL) - return NULL; - - Py_XINCREF(destructor); - Py_INCREF(origobj); - Py_INCREF(ct); - cd->head.c_data = origobj->c_data; - cd->head.c_type = ct; - cd->head.c_weakreflist = NULL; - cd->origobj = (PyObject *)origobj; - cd->destructor = destructor; - - PyObject_GC_Track(cd); - return (CDataObject *)cd; -} - -static CDataObject *allocate_with_allocator(Py_ssize_t basesize, - Py_ssize_t datasize, - CTypeDescrObject *ct, - const cffi_allocator_t *allocator) -{ - CDataObject *cd; - - if (allocator->ca_alloc == NULL) { - cd = allocate_owning_object(basesize + datasize, ct, - allocator->ca_dont_clear); - if (cd == NULL) - return NULL; - cd->c_data = ((char *)cd) + basesize; - } - else { - PyObject *res = PyObject_CallFunction(allocator->ca_alloc, "n", datasize); - if (res == NULL) - return NULL; - - if (!CData_Check(res)) { - PyErr_Format(PyExc_TypeError, - "alloc() must return a cdata object (got %.200s)", - Py_TYPE(res)->tp_name); - Py_DECREF(res); - return NULL; - } - cd = (CDataObject *)res; - if (!(cd->c_type->ct_flags & (CT_POINTER|CT_ARRAY))) { - PyErr_Format(PyExc_TypeError, - "alloc() must return a cdata pointer, not '%s'", - cd->c_type->ct_name); - Py_DECREF(res); - return NULL; - } - if (!cd->c_data) { - PyErr_SetString(PyExc_MemoryError, "alloc() returned NULL"); - Py_DECREF(res); - return NULL; - } - - cd = allocate_gcp_object(cd, ct, allocator->ca_free); - Py_DECREF(res); - if (!allocator->ca_dont_clear) - memset(cd->c_data, 0, datasize); - } - return cd; -} - -static PyObject *direct_newp(CTypeDescrObject *ct, PyObject *init, - const cffi_allocator_t *allocator) -{ - CTypeDescrObject *ctitem; - CDataObject *cd; - Py_ssize_t dataoffset, datasize, explicitlength; - - explicitlength = -1; - if (ct->ct_flags & CT_POINTER) { - dataoffset = offsetof(CDataObject_own_nolength, alignment); - ctitem = ct->ct_itemdescr; - datasize = ctitem->ct_size; - if (datasize < 0) { - PyErr_Format(PyExc_TypeError, - "cannot instantiate ctype '%s' of unknown size", - ctitem->ct_name); - return NULL; - } - if (ctitem->ct_flags & CT_PRIMITIVE_CHAR) - datasize *= 2; /* forcefully add another character: a null */ - - if (ctitem->ct_flags & (CT_STRUCT | CT_UNION)) { - if (force_lazy_struct(ctitem) < 0) /* for CT_WITH_VAR_ARRAY */ - return NULL; - - if (ctitem->ct_flags & CT_WITH_VAR_ARRAY) { - assert(ct->ct_flags & CT_IS_PTR_TO_OWNED); - dataoffset = offsetof(CDataObject_own_length, alignment); - - if (init != Py_None) { - Py_ssize_t optvarsize = datasize; - if (convert_struct_from_object(NULL, ctitem, init, - &optvarsize) < 0) - return NULL; - datasize = optvarsize; - } - } - } - } - else if (ct->ct_flags & CT_ARRAY) { - dataoffset = offsetof(CDataObject_own_nolength, alignment); - datasize = ct->ct_size; - if (datasize < 0) { - explicitlength = get_new_array_length(ct->ct_itemdescr, &init); - if (explicitlength < 0) - return NULL; - ctitem = ct->ct_itemdescr; - dataoffset = offsetof(CDataObject_own_length, alignment); - datasize = MUL_WRAPAROUND(explicitlength, ctitem->ct_size); - if (explicitlength > 0 && - (datasize / explicitlength) != ctitem->ct_size) { - PyErr_SetString(PyExc_OverflowError, - "array size would overflow a Py_ssize_t"); - return NULL; - } - } - } - else { - PyErr_Format(PyExc_TypeError, - "expected a pointer or array ctype, got '%s'", - ct->ct_name); - return NULL; - } - - if (ct->ct_flags & CT_IS_PTR_TO_OWNED) { - /* common case of ptr-to-struct (or ptr-to-union): for this case - we build two objects instead of one, with the memory-owning - one being really the struct (or union) and the returned one - having a strong reference to it */ - CDataObject *cds; - - cds = allocate_with_allocator(dataoffset, datasize, ct->ct_itemdescr, - allocator); - if (cds == NULL) - return NULL; - - cd = allocate_owning_object(sizeof(CDataObject_own_structptr), ct, - /*dont_clear=*/1); - if (cd == NULL) { - Py_DECREF(cds); - return NULL; - } - /* store the only reference to cds into cd */ - ((CDataObject_own_structptr *)cd)->structobj = (PyObject *)cds; - /* store information about the allocated size of the struct */ - if (dataoffset == offsetof(CDataObject_own_length, alignment)) { - ((CDataObject_own_length *)cds)->length = datasize; - } - assert(explicitlength < 0); - - cd->c_data = cds->c_data; - } - else { - cd = allocate_with_allocator(dataoffset, datasize, ct, allocator); - if (cd == NULL) - return NULL; - - if (explicitlength >= 0) - ((CDataObject_own_length*)cd)->length = explicitlength; - } - - if (init != Py_None) { - if (convert_from_object(cd->c_data, - (ct->ct_flags & CT_POINTER) ? ct->ct_itemdescr : ct, init) < 0) { - Py_DECREF(cd); - return NULL; - } - } - return (PyObject *)cd; -} - -static PyObject *b_newp(PyObject *self, PyObject *args) -{ - CTypeDescrObject *ct; - PyObject *init = Py_None; - if (!PyArg_ParseTuple(args, "O!|O:newp", &CTypeDescr_Type, &ct, &init)) - return NULL; - return direct_newp(ct, init, &default_allocator); -} - -static int -_my_PyObject_AsBool(PyObject *ob) -{ - /* convert and cast a Python object to a boolean. Accept an integer - or a float object, up to a CData 'long double'. */ - PyObject *io; - PyNumberMethods *nb; - int res; - -#if PY_MAJOR_VERSION < 3 - if (PyInt_Check(ob)) { - return PyInt_AS_LONG(ob) != 0; - } - else -#endif - if (PyLong_Check(ob)) { - return _PyLong_Sign(ob) != 0; - } - else if (PyFloat_Check(ob)) { - return PyFloat_AS_DOUBLE(ob) != 0.0; - } - else if (CData_Check(ob)) { - CDataObject *cd = (CDataObject *)ob; - if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) { - /*READ(cd->c_data, cd->c_type->ct_size)*/ - if (cd->c_type->ct_flags & CT_IS_LONGDOUBLE) { - /* 'long double' objects: return the answer directly */ - return read_raw_longdouble_data(cd->c_data) != 0.0; - } - else { - /* 'float'/'double' objects: return the answer directly */ - return read_raw_float_data(cd->c_data, - cd->c_type->ct_size) != 0.0; - } - } - } - nb = ob->ob_type->tp_as_number; - if (nb == NULL || (nb->nb_float == NULL && nb->nb_int == NULL)) { - PyErr_SetString(PyExc_TypeError, "integer/float expected"); - return -1; - } - if (nb->nb_float && !CData_Check(ob)) - io = (*nb->nb_float) (ob); - else - io = (*nb->nb_int) (ob); - if (io == NULL) - return -1; - - if (PyIntOrLong_Check(io) || PyFloat_Check(io)) { - res = _my_PyObject_AsBool(io); - } - else { - PyErr_SetString(PyExc_TypeError, "integer/float conversion failed"); - res = -1; - } - Py_DECREF(io); - return res; -} - -static CDataObject *_new_casted_primitive(CTypeDescrObject *ct) -{ - int dataoffset = offsetof(CDataObject_casted_primitive, alignment); - CDataObject *cd = (CDataObject *)PyObject_Malloc(dataoffset + ct->ct_size); - if (PyObject_Init((PyObject *)cd, &CData_Type) == NULL) - return NULL; - Py_INCREF(ct); - cd->c_type = ct; - cd->c_data = ((char*)cd) + dataoffset; - cd->c_weakreflist = NULL; - return cd; -} - -static CDataObject *cast_to_integer_or_char(CTypeDescrObject *ct, PyObject *ob) -{ - unsigned PY_LONG_LONG value; - CDataObject *cd; - - if (CData_Check(ob) && - ((CDataObject *)ob)->c_type->ct_flags & - (CT_POINTER|CT_FUNCTIONPTR|CT_ARRAY)) { - value = (Py_intptr_t)((CDataObject *)ob)->c_data; - } -#if PY_MAJOR_VERSION < 3 - else if (PyString_Check(ob)) { - if (PyString_GET_SIZE(ob) != 1) { - PyErr_Format(PyExc_TypeError, - "cannot cast string of length %zd to ctype '%s'", - PyString_GET_SIZE(ob), ct->ct_name); - return NULL; - } - value = (unsigned char)PyString_AS_STRING(ob)[0]; - } -#endif - else if (PyUnicode_Check(ob)) { - char err_buf[80]; - cffi_char32_t ordinal; - if (_my_PyUnicode_AsSingleChar32(ob, &ordinal, err_buf) < 0) { - PyErr_Format(PyExc_TypeError, - "cannot cast %s to ctype '%s'", err_buf, ct->ct_name); - return NULL; - } - /* the types char16_t and char32_t are both unsigned. However, - wchar_t might be signed. In theory it does not matter, - because 'ordinal' comes from a regular Python unicode. */ -#ifdef HAVE_WCHAR_H - if (ct->ct_flags & CT_IS_SIGNED_WCHAR) - value = (wchar_t)ordinal; - else -#endif - value = ordinal; - } - else if (PyBytes_Check(ob)) { - int res = _convert_to_char(ob); - if (res < 0) - return NULL; - value = (unsigned char)res; - } - else if (ct->ct_flags & CT_IS_BOOL) { - int res = _my_PyObject_AsBool(ob); - if (res < 0) - return NULL; - value = res; - } - else { - value = _my_PyLong_AsUnsignedLongLong(ob, 0); - if (value == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred()) - return NULL; - } - if (ct->ct_flags & CT_IS_BOOL) - value = !!value; - cd = _new_casted_primitive(ct); - if (cd != NULL) - write_raw_integer_data(cd->c_data, value, ct->ct_size); - return cd; -} - -/* returns -1 if cannot cast, 0 if we don't get a value, 1 if we do */ -static int check_bytes_for_float_compatible(PyObject *io, double *out_value) -{ - if (PyBytes_Check(io)) { - if (PyBytes_GET_SIZE(io) != 1) - goto error; - *out_value = (unsigned char)PyBytes_AS_STRING(io)[0]; - return 1; - } - else if (PyUnicode_Check(io)) { - char ignored[80]; - cffi_char32_t ordinal; - if (_my_PyUnicode_AsSingleChar32(io, &ordinal, ignored) < 0) - goto error; - /* the signness of the 32-bit version of wide chars should not - * matter here, because 'ordinal' comes from a normal Python - * unicode string */ - *out_value = ordinal; - return 1; - } - *out_value = 0; /* silence a gcc warning if this function is inlined */ - return 0; - - error: - Py_DECREF(io); - *out_value = 0; /* silence a gcc warning if this function is inlined */ - return -1; -} - -static PyObject *do_cast(CTypeDescrObject *ct, PyObject *ob) -{ - CDataObject *cd; - - if (ct->ct_flags & (CT_POINTER|CT_FUNCTIONPTR|CT_ARRAY) && - ct->ct_size >= 0) { - /* cast to a pointer, to a funcptr, or to an array. - Note that casting to an array is an extension to the C language, - which seems to be necessary in order to sanely get a - <cdata 'int[3]'> at some address. */ - unsigned PY_LONG_LONG value; - - if (CData_Check(ob)) { - CDataObject *cdsrc = (CDataObject *)ob; - if (cdsrc->c_type->ct_flags & - (CT_POINTER|CT_FUNCTIONPTR|CT_ARRAY)) { - return new_simple_cdata(cdsrc->c_data, ct); - } - } - if ((ct->ct_flags & CT_POINTER) && - (ct->ct_itemdescr->ct_flags & CT_IS_FILE) && - PyFile_Check(ob)) { - FILE *f = PyFile_AsFile(ob); - if (f == NULL && PyErr_Occurred()) - return NULL; - return new_simple_cdata((char *)f, ct); - } - value = _my_PyLong_AsUnsignedLongLong(ob, 0); - if (value == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred()) - return NULL; - return new_simple_cdata((char *)(Py_intptr_t)value, ct); - } - else if (ct->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_UNSIGNED - |CT_PRIMITIVE_CHAR)) { - /* cast to an integer type or a char */ - return (PyObject *)cast_to_integer_or_char(ct, ob); - } - else if (ct->ct_flags & CT_PRIMITIVE_FLOAT) { - /* cast to a float */ - double value; - PyObject *io; - int res; - - if (CData_Check(ob)) { - CDataObject *cdsrc = (CDataObject *)ob; - - if (!(cdsrc->c_type->ct_flags & CT_PRIMITIVE_ANY)) - goto cannot_cast; - io = convert_to_object(cdsrc->c_data, cdsrc->c_type); - if (io == NULL) - return NULL; - } - else { - io = ob; - Py_INCREF(io); - } - - res = check_bytes_for_float_compatible(io, &value); - if (res == -1) - goto cannot_cast; - if (res == 0) { - if ((ct->ct_flags & CT_IS_LONGDOUBLE) && - CData_Check(io) && - (((CDataObject *)io)->c_type->ct_flags & CT_IS_LONGDOUBLE)) { - long double lvalue; - char *data = ((CDataObject *)io)->c_data; - /*READ(data, sizeof(long double)*/ - lvalue = read_raw_longdouble_data(data); - Py_DECREF(io); - cd = _new_casted_primitive(ct); - if (cd != NULL) - write_raw_longdouble_data(cd->c_data, lvalue); - return (PyObject *)cd; - } - value = PyFloat_AsDouble(io); - } - Py_DECREF(io); - if (value == -1.0 && PyErr_Occurred()) - return NULL; - - cd = _new_casted_primitive(ct); - if (cd != NULL) { - if (!(ct->ct_flags & CT_IS_LONGDOUBLE)) - write_raw_float_data(cd->c_data, value, ct->ct_size); - else - write_raw_longdouble_data(cd->c_data, (long double)value); - } - return (PyObject *)cd; - } - else if (ct->ct_flags & CT_PRIMITIVE_COMPLEX) { - /* cast to a complex */ - Py_complex value; - PyObject *io; - int res; - - if (CData_Check(ob)) { - CDataObject *cdsrc = (CDataObject *)ob; - - if (!(cdsrc->c_type->ct_flags & CT_PRIMITIVE_ANY)) - goto cannot_cast; - io = convert_to_object(cdsrc->c_data, cdsrc->c_type); - if (io == NULL) - return NULL; - } - else { - io = ob; - Py_INCREF(io); - } - - res = check_bytes_for_float_compatible(io, &value.real); - if (res == -1) - goto cannot_cast; - if (res == 1) { - // got it from string - value.imag = 0.0; - } else { - value = PyComplex_AsCComplex(io); - } - Py_DECREF(io); - if (PyErr_Occurred()) { - return NULL; - } - cd = _new_casted_primitive(ct); - if (cd != NULL) { - write_raw_complex_data(cd->c_data, value, ct->ct_size); - } - return (PyObject *)cd; - } - else { - PyErr_Format(PyExc_TypeError, "cannot cast to ctype '%s'", - ct->ct_name); - return NULL; - } - - cannot_cast: - if (CData_Check(ob)) - PyErr_Format(PyExc_TypeError, "cannot cast ctype '%s' to ctype '%s'", - ((CDataObject *)ob)->c_type->ct_name, ct->ct_name); - else - PyErr_Format(PyExc_TypeError, - "cannot cast %.200s object to ctype '%s'", - Py_TYPE(ob)->tp_name, ct->ct_name); - return NULL; -} - -static PyObject *b_cast(PyObject *self, PyObject *args) -{ - CTypeDescrObject *ct; - PyObject *ob; - if (!PyArg_ParseTuple(args, "O!O:cast", &CTypeDescr_Type, &ct, &ob)) - return NULL; - - return do_cast(ct, ob); -} - -/************************************************************/ - -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 && dlobj->dl_auto_close) - dlclose(dlobj->dl_handle); - free(dlobj->dl_name); - PyObject_Del(dlobj); -} - -static PyObject *dl_repr(DynLibObject *dlobj) -{ - return PyText_FromFormat("<clibrary '%s'>", dlobj->dl_name); -} - -static int dl_check_closed(DynLibObject *dlobj) -{ - if (dlobj->dl_handle == NULL) - { - PyErr_Format(PyExc_ValueError, "library '%s' has already been closed", - dlobj->dl_name); - return -1; - } - return 0; -} - -static PyObject *dl_load_function(DynLibObject *dlobj, PyObject *args) -{ - CTypeDescrObject *ct; - char *funcname; - void *funcptr; - - if (!PyArg_ParseTuple(args, "O!s:load_function", - &CTypeDescr_Type, &ct, &funcname)) - return NULL; - - if (dl_check_closed(dlobj) < 0) - return NULL; - - if (!(ct->ct_flags & (CT_FUNCTIONPTR | CT_POINTER | CT_ARRAY))) { - PyErr_Format(PyExc_TypeError, - "function or pointer or array cdata expected, got '%s'", - ct->ct_name); - return NULL; - } - dlerror(); /* clear error condition */ - funcptr = dlsym(dlobj->dl_handle, funcname); - if (funcptr == NULL) { - const char *error = dlerror(); - PyErr_Format(PyExc_AttributeError, - "function/symbol '%s' not found in library '%s': %s", - funcname, dlobj->dl_name, error); - return NULL; - } - - if ((ct->ct_flags & CT_ARRAY) && ct->ct_length < 0) { - ct = (CTypeDescrObject *)ct->ct_stuff; - } - return new_simple_cdata(funcptr, ct); -} - -static PyObject *dl_read_variable(DynLibObject *dlobj, PyObject *args) -{ - CTypeDescrObject *ct; - char *varname; - char *data; - - if (!PyArg_ParseTuple(args, "O!s:read_variable", - &CTypeDescr_Type, &ct, &varname)) - return NULL; - - if (dl_check_closed(dlobj) < 0) - return NULL; - - dlerror(); /* clear error condition */ - data = dlsym(dlobj->dl_handle, varname); - if (data == NULL) { - const char *error = dlerror(); - if (error != NULL) { - PyErr_Format(PyExc_KeyError, - "variable '%s' not found in library '%s': %s", - varname, dlobj->dl_name, error); - return NULL; - } - } - return convert_to_object(data, ct); -} - -static PyObject *dl_write_variable(DynLibObject *dlobj, PyObject *args) -{ - CTypeDescrObject *ct; - PyObject *value; - char *varname; - char *data; - - if (!PyArg_ParseTuple(args, "O!sO:write_variable", - &CTypeDescr_Type, &ct, &varname, &value)) - return NULL; - - if (dl_check_closed(dlobj) < 0) - return NULL; - - dlerror(); /* clear error condition */ - data = dlsym(dlobj->dl_handle, varname); - if (data == NULL) { - const char *error = dlerror(); - PyErr_Format(PyExc_KeyError, - "variable '%s' not found in library '%s': %s", - varname, dlobj->dl_name, error); - return NULL; - } - if (convert_from_object(data, ct, value) < 0) - return NULL; - Py_INCREF(Py_None); - return Py_None; -} - -static PyObject *dl_close_lib(DynLibObject *dlobj, PyObject *no_args) -{ - if (dlobj->dl_handle != NULL) - { - dlclose(dlobj->dl_handle); - dlobj->dl_handle = NULL; - } - Py_INCREF(Py_None); - return Py_None; -} - -static PyMethodDef dl_methods[] = { - {"load_function", (PyCFunction)dl_load_function, METH_VARARGS}, - {"read_variable", (PyCFunction)dl_read_variable, METH_VARARGS}, - {"write_variable", (PyCFunction)dl_write_variable, METH_VARARGS}, - {"close_lib", (PyCFunction)dl_close_lib, METH_NOARGS}, - {NULL, NULL} /* sentinel */ -}; - -static PyTypeObject dl_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_cffi_backend.CLibrary", /* tp_name */ - sizeof(DynLibObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)dl_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - (reprfunc)dl_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - dl_methods, /* tp_methods */ -}; - -static void *b_do_dlopen(PyObject *args, const char **p_printable_filename, - 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 - the filename (maybe utf-8-encoded). '*p_temp' will be set either to NULL or - to a temporary object that must be freed after looking at printable_filename. - */ - void *handle; - 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; - if (!PyArg_ParseTuple(args, "|Oi:load_library", - &dummy, &flags)) - return NULL; - 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 - 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) - return NULL; - *p_temp = s; -#endif - *p_printable_filename = PyText_AsUTF8(s); - if (*p_printable_filename == NULL) - return NULL; - - 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(); -#endif - if (!PyArg_ParseTuple(args, "et|i:load_library", - Py_FileSystemDefaultEncoding, &filename_or_null, &flags)) - return NULL; -#if PY_MAJOR_VERSION < 3 - if (PyUnicode_Check(s)) - { - s = PyUnicode_AsUTF8String(s); - 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) { - 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: -#endif - if (handle == NULL) { - const char *error = dlerror(); - PyErr_Format(PyExc_OSError, "cannot load library '%s': %s", - *p_printable_filename, error); - return NULL; - } - return handle; -} - -static PyObject *b_load_library(PyObject *self, PyObject *args) -{ - const char *printable_filename; - PyObject *temp; - void *handle; - DynLibObject *dlobj = NULL; - int auto_close; - - handle = b_do_dlopen(args, &printable_filename, &temp, &auto_close); - if (handle == NULL) - goto error; - - dlobj = PyObject_New(DynLibObject, &dl_type); - if (dlobj == NULL) { - dlclose(handle); - goto error; - } - dlobj->dl_handle = handle; - dlobj->dl_name = strdup(printable_filename); - dlobj->dl_auto_close = auto_close; - - error: - Py_XDECREF(temp); - return (PyObject *)dlobj; -} - -/************************************************************/ - -static PyObject *get_unique_type(CTypeDescrObject *x, - const void *unique_key[], long keylength) -{ - /* Replace the CTypeDescrObject 'x' with a standardized one. - This either just returns x, or x is decrefed and a new reference - to the already-existing equivalent is returned. - - In this function, 'x' always contains a reference that must be - either decrefed or returned. - - Keys: - void ["void"] - primitive [&static_struct] - pointer [ctype] - array [ctype, length] - funcptr [ctresult, ellipsis+abi, num_args, ctargs...] - */ - PyObject *key, *y; - void *pkey; - - key = PyBytes_FromStringAndSize(NULL, keylength * sizeof(void *)); - if (key == NULL) - goto error; - - pkey = PyBytes_AS_STRING(key); - memcpy(pkey, unique_key, keylength * sizeof(void *)); - - y = PyDict_GetItem(unique_cache, key); - if (y != NULL) { - Py_DECREF(key); - Py_INCREF(y); - Py_DECREF(x); - return y; - } - if (PyDict_SetItem(unique_cache, key, (PyObject *)x) < 0) { - Py_DECREF(key); - goto error; - } - /* Haaaack for our reference count hack: gcmodule.c must not see this - dictionary. The problem is that any PyDict_SetItem() notices that - 'x' is tracked and re-tracks the unique_cache dictionary. So here - we re-untrack it again... */ - PyObject_GC_UnTrack(unique_cache); - - assert(x->ct_unique_key == NULL); - x->ct_unique_key = key; /* the key will be freed in ctypedescr_dealloc() */ - /* the 'value' in unique_cache doesn't count as 1, but don't use - Py_DECREF(x) here because it will confuse debug builds into thinking - there was an extra DECREF in total. */ - ((PyObject *)x)->ob_refcnt--; - return (PyObject *)x; - - error: - Py_DECREF(x); - return NULL; -} - -/* according to the C standard, these types should be equivalent to the - _Complex types for the purposes of storage (not arguments in calls!) */ -typedef float cffi_float_complex_t[2]; -typedef double cffi_double_complex_t[2]; - -static PyObject *new_primitive_type(const char *name) -{ -#define ENUM_PRIMITIVE_TYPES \ - EPTYPE(c, char, CT_PRIMITIVE_CHAR) \ - EPTYPE(s, short, CT_PRIMITIVE_SIGNED ) \ - EPTYPE(i, int, CT_PRIMITIVE_SIGNED ) \ - EPTYPE(l, long, CT_PRIMITIVE_SIGNED ) \ - EPTYPE(ll, long long, CT_PRIMITIVE_SIGNED ) \ - EPTYPE(sc, signed char, CT_PRIMITIVE_SIGNED ) \ - EPTYPE(uc, unsigned char, CT_PRIMITIVE_UNSIGNED ) \ - EPTYPE(us, unsigned short, CT_PRIMITIVE_UNSIGNED ) \ - EPTYPE(ui, unsigned int, CT_PRIMITIVE_UNSIGNED ) \ - EPTYPE(ul, unsigned long, CT_PRIMITIVE_UNSIGNED ) \ - EPTYPE(ull, unsigned long long, CT_PRIMITIVE_UNSIGNED ) \ - EPTYPE(f, float, CT_PRIMITIVE_FLOAT ) \ - EPTYPE(d, double, CT_PRIMITIVE_FLOAT ) \ - EPTYPE(ld, long double, CT_PRIMITIVE_FLOAT | CT_IS_LONGDOUBLE ) \ - EPTYPE2(fc, "float _Complex", cffi_float_complex_t, CT_PRIMITIVE_COMPLEX ) \ - EPTYPE2(dc, "double _Complex", cffi_double_complex_t, CT_PRIMITIVE_COMPLEX ) \ - ENUM_PRIMITIVE_TYPES_WCHAR \ - EPTYPE2(c16, "char16_t", cffi_char16_t, CT_PRIMITIVE_CHAR ) \ - EPTYPE2(c32, "char32_t", cffi_char32_t, CT_PRIMITIVE_CHAR ) \ - EPTYPE(b, _Bool, CT_PRIMITIVE_UNSIGNED | CT_IS_BOOL ) \ - /* the following types are not primitive in the C sense */ \ - EPTYPE(i8, int8_t, CT_PRIMITIVE_SIGNED) \ - EPTYPE(u8, uint8_t, CT_PRIMITIVE_UNSIGNED) \ - EPTYPE(i16, int16_t, CT_PRIMITIVE_SIGNED) \ - EPTYPE(u16, uint16_t, CT_PRIMITIVE_UNSIGNED) \ - EPTYPE(i32, int32_t, CT_PRIMITIVE_SIGNED) \ - EPTYPE(u32, uint32_t, CT_PRIMITIVE_UNSIGNED) \ - EPTYPE(i64, int64_t, CT_PRIMITIVE_SIGNED) \ - EPTYPE(u64, uint64_t, CT_PRIMITIVE_UNSIGNED) \ - EPTYPE(il8, int_least8_t, CT_PRIMITIVE_SIGNED) \ - EPTYPE(ul8, uint_least8_t, CT_PRIMITIVE_UNSIGNED) \ - EPTYPE(il16, int_least16_t, CT_PRIMITIVE_SIGNED) \ - EPTYPE(ul16, uint_least16_t, CT_PRIMITIVE_UNSIGNED) \ - EPTYPE(il32, int_least32_t, CT_PRIMITIVE_SIGNED) \ - EPTYPE(ul32, uint_least32_t, CT_PRIMITIVE_UNSIGNED) \ - EPTYPE(il64, int_least64_t, CT_PRIMITIVE_SIGNED) \ - EPTYPE(ul64, uint_least64_t, CT_PRIMITIVE_UNSIGNED) \ - EPTYPE(if8, int_fast8_t, CT_PRIMITIVE_SIGNED) \ - EPTYPE(uf8, uint_fast8_t, CT_PRIMITIVE_UNSIGNED) \ - EPTYPE(if16, int_fast16_t, CT_PRIMITIVE_SIGNED) \ - EPTYPE(uf16, uint_fast16_t, CT_PRIMITIVE_UNSIGNED) \ - EPTYPE(if32, int_fast32_t, CT_PRIMITIVE_SIGNED) \ - EPTYPE(uf32, uint_fast32_t, CT_PRIMITIVE_UNSIGNED) \ - EPTYPE(if64, int_fast64_t, CT_PRIMITIVE_SIGNED) \ - EPTYPE(uf64, uint_fast64_t, CT_PRIMITIVE_UNSIGNED) \ - EPTYPE(ip, intptr_t, CT_PRIMITIVE_SIGNED) \ - EPTYPE(up, uintptr_t, CT_PRIMITIVE_UNSIGNED) \ - EPTYPE(im, intmax_t, CT_PRIMITIVE_SIGNED) \ - EPTYPE(um, uintmax_t, CT_PRIMITIVE_UNSIGNED) \ - EPTYPE(pd, ptrdiff_t, CT_PRIMITIVE_SIGNED) \ - EPTYPE(sz, size_t, CT_PRIMITIVE_UNSIGNED) \ - EPTYPE2(ssz, "ssize_t", Py_ssize_t, CT_PRIMITIVE_SIGNED) - -#ifdef HAVE_WCHAR_H -# define ENUM_PRIMITIVE_TYPES_WCHAR \ - EPTYPE(wc, wchar_t, CT_PRIMITIVE_CHAR | \ - (((wchar_t)-1) > 0 ? 0 : CT_IS_SIGNED_WCHAR)) -#else -# define ENUM_PRIMITIVE_TYPES_WCHAR /* nothing */ -#endif - -#define EPTYPE(code, typename, flags) EPTYPE2(code, #typename, typename, flags) - -#define EPTYPE2(code, export_name, typename, flags) \ - struct aligncheck_##code { char x; typename y; }; - ENUM_PRIMITIVE_TYPES -#undef EPTYPE2 - - CTypeDescrObject *td; - static const struct descr_s { const char *name; int size, align, flags; } - types[] = { -#define EPTYPE2(code, export_name, typename, flags) \ - { export_name, \ - sizeof(typename), \ - offsetof(struct aligncheck_##code, y), \ - flags \ - }, - ENUM_PRIMITIVE_TYPES -#undef EPTYPE2 -#undef EPTYPE -#undef ENUM_PRIMITIVE_TYPES_WCHAR -#undef ENUM_PRIMITIVE_TYPES - { NULL } - }; - const struct descr_s *ptypes; - const void *unique_key[1]; - int name_size; - ffi_type *ffitype; - - for (ptypes=types; ; ptypes++) { - if (ptypes->name == NULL) { -#ifndef HAVE_WCHAR_H - if (strcmp(name, "wchar_t")) - PyErr_SetString(PyExc_NotImplementedError, name); - else -#endif - PyErr_SetString(PyExc_KeyError, name); - return NULL; - } - if (strcmp(name, ptypes->name) == 0) - break; - } - - if (ptypes->flags & CT_PRIMITIVE_SIGNED) { - switch (ptypes->size) { - case 1: ffitype = &ffi_type_sint8; break; - case 2: ffitype = &ffi_type_sint16; break; - case 4: ffitype = &ffi_type_sint32; break; - case 8: ffitype = &ffi_type_sint64; break; - default: goto bad_ffi_type; - } - } - else if (ptypes->flags & CT_PRIMITIVE_FLOAT) { - if (strcmp(ptypes->name, "float") == 0) - ffitype = &ffi_type_float; - else if (strcmp(ptypes->name, "double") == 0) - ffitype = &ffi_type_double; - else if (strcmp(ptypes->name, "long double") == 0) { - /* assume that if sizeof(double) == sizeof(long double), then - the two types are equivalent for C. libffi bugs on Win64 - if a function's return type is ffi_type_longdouble... */ - if (sizeof(double) == sizeof(long double)) - ffitype = &ffi_type_double; - else - ffitype = &ffi_type_longdouble; - } - else - goto bad_ffi_type; - } - else if (ptypes->flags & CT_PRIMITIVE_COMPLEX) { - /* As of March 2017, still no libffi support for complex. - It fails silently if we try to use ffi_type_complex_float - or ffi_type_complex_double. Better not use it at all. - */ - ffitype = NULL; - } - else { - switch (ptypes->size) { - case 1: ffitype = &ffi_type_uint8; break; - case 2: ffitype = &ffi_type_uint16; break; - case 4: ffitype = &ffi_type_uint32; break; - case 8: ffitype = &ffi_type_uint64; break; - default: goto bad_ffi_type; - } - } - - name_size = strlen(ptypes->name) + 1; - td = ctypedescr_new(name_size); - if (td == NULL) - return NULL; - - memcpy(td->ct_name, name, name_size); - td->ct_size = ptypes->size; - td->ct_length = ptypes->align; - td->ct_extra = ffitype; - td->ct_flags = ptypes->flags; - if (td->ct_flags & (CT_PRIMITIVE_SIGNED | CT_PRIMITIVE_CHAR)) { - if (td->ct_size <= (Py_ssize_t)sizeof(long)) - td->ct_flags |= CT_PRIMITIVE_FITS_LONG; - } - else if (td->ct_flags & CT_PRIMITIVE_UNSIGNED) { - if (td->ct_size < (Py_ssize_t)sizeof(long)) - td->ct_flags |= CT_PRIMITIVE_FITS_LONG; - } - td->ct_name_position = strlen(td->ct_name); - unique_key[0] = ptypes; - return get_unique_type(td, unique_key, 1); - - bad_ffi_type: - PyErr_Format(PyExc_NotImplementedError, - "primitive type '%s' has size %d; " - "the supported sizes are 1, 2, 4, 8", - name, (int)ptypes->size); - return NULL; -} - -static PyObject *b_new_primitive_type(PyObject *self, PyObject *args) -{ - char *name; - if (!PyArg_ParseTuple(args, "s:new_primitive_type", &name)) - return NULL; - return new_primitive_type(name); -} - -static PyObject *new_pointer_type(CTypeDescrObject *ctitem) -{ - CTypeDescrObject *td; - const char *extra; - const void *unique_key[1]; - - if (ctitem->ct_flags & CT_ARRAY) - extra = "(*)"; /* obscure case: see test_array_add */ - else - extra = " *"; - td = ctypedescr_new_on_top(ctitem, extra, 2); - if (td == NULL) - return NULL; - - td->ct_size = sizeof(void *); - td->ct_length = -1; - td->ct_flags = CT_POINTER; - if (ctitem->ct_flags & (CT_STRUCT|CT_UNION)) - td->ct_flags |= CT_IS_PTR_TO_OWNED; - if (ctitem->ct_flags & CT_VOID) - td->ct_flags |= CT_IS_VOID_PTR; - if ((ctitem->ct_flags & CT_VOID) || - ((ctitem->ct_flags & CT_PRIMITIVE_CHAR) && - ctitem->ct_size == sizeof(char))) - td->ct_flags |= CT_IS_VOIDCHAR_PTR; /* 'void *' or 'char *' only */ - unique_key[0] = ctitem; - return get_unique_type(td, unique_key, 1); -} - -static PyObject *b_new_pointer_type(PyObject *self, PyObject *args) -{ - CTypeDescrObject *ctitem; - if (!PyArg_ParseTuple(args, "O!:new_pointer_type", - &CTypeDescr_Type, &ctitem)) - return NULL; - return new_pointer_type(ctitem); -} - -static PyObject *b_new_array_type(PyObject *self, PyObject *args) -{ - PyObject *lengthobj; - Py_ssize_t length; - CTypeDescrObject *ctptr; - - if (!PyArg_ParseTuple(args, "O!O:new_array_type", - &CTypeDescr_Type, &ctptr, &lengthobj)) - return NULL; - - if (lengthobj == Py_None) { - length = -1; - } - else { - length = PyNumber_AsSsize_t(lengthobj, PyExc_OverflowError); - if (length < 0) { - if (!PyErr_Occurred()) - PyErr_SetString(PyExc_ValueError, "negative array length"); - return NULL; - } - } - return new_array_type(ctptr, length); -} - -static PyObject * -new_array_type(CTypeDescrObject *ctptr, Py_ssize_t length) -{ - CTypeDescrObject *td, *ctitem; - char extra_text[32]; - Py_ssize_t arraysize; - int flags = CT_ARRAY; - const void *unique_key[2]; - - if (!(ctptr->ct_flags & CT_POINTER)) { - PyErr_SetString(PyExc_TypeError, "first arg must be a pointer ctype"); - return NULL; - } - ctitem = ctptr->ct_itemdescr; - if (ctitem->ct_size < 0) { - PyErr_Format(PyExc_ValueError, "array item of unknown size: '%s'", - ctitem->ct_name); - return NULL; - } - - if (length < 0) { - sprintf(extra_text, "[]"); - length = -1; - arraysize = -1; - } - else { - sprintf(extra_text, "[%llu]", (unsigned PY_LONG_LONG)length); - arraysize = MUL_WRAPAROUND(length, ctitem->ct_size); - if (length > 0 && (arraysize / length) != ctitem->ct_size) { - PyErr_SetString(PyExc_OverflowError, - "array size would overflow a Py_ssize_t"); - return NULL; - } - } - td = ctypedescr_new_on_top(ctitem, extra_text, 0); - if (td == NULL) - return NULL; - - Py_INCREF(ctptr); - td->ct_stuff = (PyObject *)ctptr; - td->ct_size = arraysize; - td->ct_length = length; - td->ct_flags = flags; - unique_key[0] = ctptr; - unique_key[1] = (void *)length; - return get_unique_type(td, unique_key, 2); -} - -static PyObject *new_void_type(void) -{ - int name_size = strlen("void") + 1; - const void *unique_key[1]; - CTypeDescrObject *td = ctypedescr_new(name_size); - if (td == NULL) - return NULL; - - memcpy(td->ct_name, "void", name_size); - td->ct_size = -1; - td->ct_flags = CT_VOID | CT_IS_OPAQUE; - td->ct_name_position = strlen("void"); - unique_key[0] = "void"; - return get_unique_type(td, unique_key, 1); -} - -static PyObject *b_new_void_type(PyObject *self, PyObject *args) -{ - return new_void_type(); -} - -static PyObject *new_struct_or_union_type(const char *name, int flag) -{ - int namelen = strlen(name); - CTypeDescrObject *td = ctypedescr_new(namelen + 1); - if (td == NULL) - return NULL; - - td->ct_size = -1; - td->ct_length = -1; - td->ct_flags = flag | CT_IS_OPAQUE; - td->ct_extra = NULL; - memcpy(td->ct_name, name, namelen + 1); - td->ct_name_position = namelen; - return (PyObject *)td; -} - -static PyObject *b_new_struct_type(PyObject *self, PyObject *args) -{ - char *name; - int flag; - if (!PyArg_ParseTuple(args, "s:new_struct_type", &name)) - return NULL; - - flag = CT_STRUCT; - if (strcmp(name, "struct _IO_FILE") == 0 || strcmp(name, "FILE") == 0) - flag |= CT_IS_FILE; - return new_struct_or_union_type(name, flag); -} - -static PyObject *b_new_union_type(PyObject *self, PyObject *args) -{ - char *name; - if (!PyArg_ParseTuple(args, "s:new_union_type", &name)) - return NULL; - return new_struct_or_union_type(name, CT_UNION); -} - -static CFieldObject * -_add_field(PyObject *interned_fields, PyObject *fname, CTypeDescrObject *ftype, - Py_ssize_t offset, int bitshift, int fbitsize, int flags) -{ - int err; - Py_ssize_t prev_size; - CFieldObject *cf = PyObject_New(CFieldObject, &CField_Type); - if (cf == NULL) - return NULL; - - Py_INCREF(ftype); - cf->cf_type = ftype; - cf->cf_offset = offset; - cf->cf_bitshift = bitshift; - cf->cf_bitsize = fbitsize; - cf->cf_flags = flags; - - Py_INCREF(fname); - PyText_InternInPlace(&fname); - prev_size = PyDict_Size(interned_fields); - err = PyDict_SetItem(interned_fields, fname, (PyObject *)cf); - Py_DECREF(fname); - Py_DECREF(cf); - if (err < 0) - return NULL; - - if (PyDict_Size(interned_fields) != prev_size + 1) { - PyErr_Format(PyExc_KeyError, "duplicate field name '%s'", - PyText_AS_UTF8(fname)); - return NULL; - } - return cf; /* borrowed reference */ -} - -#define SF_MSVC_BITFIELDS 0x01 -#define SF_GCC_ARM_BITFIELDS 0x02 -#define SF_GCC_X86_BITFIELDS 0x10 - -#define SF_GCC_BIG_ENDIAN 0x04 -#define SF_GCC_LITTLE_ENDIAN 0x40 - -#define SF_PACKED 0x08 -#define SF_STD_FIELD_POS 0x80 - -#ifdef MS_WIN32 -# define SF_DEFAULT_PACKING 8 -#else -# define SF_DEFAULT_PACKING 0x40000000 /* a huge power of two */ -#endif - -static int complete_sflags(int sflags) -{ - /* add one of the SF_xxx_BITFIELDS flags if none is specified */ - if (!(sflags & (SF_MSVC_BITFIELDS | SF_GCC_ARM_BITFIELDS | - SF_GCC_X86_BITFIELDS))) { -#ifdef MS_WIN32 - sflags |= SF_MSVC_BITFIELDS; -#else -# 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; -# endif -#endif - } - /* add one of SF_GCC_xx_ENDIAN if none is specified */ - if (!(sflags & (SF_GCC_BIG_ENDIAN | SF_GCC_LITTLE_ENDIAN))) { - int _check_endian = 1; - if (*(char *)&_check_endian == 0) - sflags |= SF_GCC_BIG_ENDIAN; - else - sflags |= SF_GCC_LITTLE_ENDIAN; - } - return sflags; -} - -static int detect_custom_layout(CTypeDescrObject *ct, int sflags, - Py_ssize_t cdef_value, - Py_ssize_t compiler_value, - const char *msg1, const char *txt, - const char *msg2) -{ - if (compiler_value != cdef_value) { - 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 \"...;\" 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); - return -1; - } - ct->ct_flags |= CT_CUSTOM_FIELD_POS; - } - 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 byteoffset, i, nb_fields, byteoffsetmax, alignedsize; - int bitoffset; - Py_ssize_t byteoffsetorg; - Py_ssize_t totalsize = -1; - int totalalignment = -1; - CFieldObject **previous; - int prev_bitfield_size, prev_bitfield_free; - int sflags = 0, fflags; - int pack = 0; - - if (!PyArg_ParseTuple(args, "O!O!|Oniii:complete_struct_or_union", - &CTypeDescr_Type, &ct, - &PyList_Type, &fields, - &ignored, &totalsize, &totalalignment, &sflags, - &pack)) - return NULL; - - sflags = complete_sflags(sflags); - if (sflags & SF_PACKED) - pack = 1; - else if (pack <= 0) - pack = SF_DEFAULT_PACKING; - else - sflags |= SF_PACKED; - - if ((ct->ct_flags & (CT_STRUCT|CT_IS_OPAQUE)) == - (CT_STRUCT|CT_IS_OPAQUE)) { - is_union = 0; - } - else if ((ct->ct_flags & (CT_UNION|CT_IS_OPAQUE)) == - (CT_UNION|CT_IS_OPAQUE)) { - is_union = 1; - } - else { - PyErr_SetString(PyExc_TypeError, - "first arg must be a non-initialized struct or union ctype"); - return NULL; - } - ct->ct_flags &= ~(CT_CUSTOM_FIELD_POS | CT_WITH_PACKED_CHANGE); - - alignment = 1; - 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); - interned_fields = PyDict_New(); - if (interned_fields == NULL) - return NULL; - - previous = (CFieldObject **)&ct->ct_extra; - - for (i=0; i<nb_fields; i++) { - PyObject *fname; - CTypeDescrObject *ftype; - int fbitsize = -1, falign, falignorg, do_align; - Py_ssize_t foffset = -1; - - if (!PyArg_ParseTuple(PyList_GET_ITEM(fields, i), "O!O!|in:list item", - &PyText_Type, &fname, - &CTypeDescr_Type, &ftype, - &fbitsize, &foffset)) - goto error; - - if (ftype->ct_size < 0) { - if ((ftype->ct_flags & CT_ARRAY) && fbitsize < 0 - && (i == nb_fields - 1 || foffset != -1)) { - ct->ct_flags |= CT_WITH_VAR_ARRAY; - } - else { - PyErr_Format(PyExc_TypeError, - "field '%s.%s' has ctype '%s' of unknown size", - ct->ct_name, PyText_AS_UTF8(fname), - ftype->ct_name); - 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) - 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 */ - falignorg = get_alignment(ftype); - if (falignorg < 0) - goto error; - falign = (pack < falignorg) ? pack : falignorg; - - do_align = 1; - if (!(sflags & SF_GCC_ARM_BITFIELDS) && fbitsize >= 0) { - if (!(sflags & SF_MSVC_BITFIELDS)) { - /* GCC: anonymous bitfields (of any size) don't cause alignment */ - do_align = PyText_GetSize(fname) > 0; - } - else { - /* MSVC: zero-sized bitfields don't cause alignment */ - do_align = fbitsize > 0; - } - } - if (alignment < falign && do_align) - alignment = falign; - - fflags = (is_union && i > 0) ? BF_IGNORE_IN_CTOR : 0; - - if (fbitsize < 0) { - /* not a bitfield: common case */ - int bs_flag; - - if ((ftype->ct_flags & CT_ARRAY) && ftype->ct_length <= 0) - bs_flag = BS_EMPTY_ARRAY; - else - bs_flag = BS_REGULAR; - - /* align this field to its own 'falign' by inserting padding */ - - /* 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, byteoffset, foffset, - "wrong offset for field '", - PyText_AS_UTF8(fname), "'") < 0) - goto error; - byteoffset = foffset; - } - - if (PyText_GetSize(fname) == 0 && - ftype->ct_flags & (CT_STRUCT|CT_UNION)) { - /* a nested anonymous struct or union */ - CFieldObject *cfsrc = (CFieldObject *)ftype->ct_extra; - for (; cfsrc != NULL; cfsrc = cfsrc->cf_next) { - /* broken complexity in the call to get_field_name(), - but we'll assume you never do that with nested - anonymous structures with thousand of fields */ - *previous = _add_field(interned_fields, - get_field_name(ftype, cfsrc), - cfsrc->cf_type, - byteoffset + cfsrc->cf_offset, - cfsrc->cf_bitshift, - cfsrc->cf_bitsize, - cfsrc->cf_flags | fflags); - if (*previous == NULL) - goto error; - previous = &(*previous)->cf_next; - } - /* always forbid such structures from being passed by value */ - ct->ct_flags |= CT_CUSTOM_FIELD_POS; - } - else { - *previous = _add_field(interned_fields, fname, ftype, - byteoffset, bs_flag, -1, fflags); - if (*previous == NULL) - goto error; - previous = &(*previous)->cf_next; - } - if (ftype->ct_size >= 0) - byteoffset += ftype->ct_size; - prev_bitfield_size = 0; - } - else { - /* this is the case of a bitfield */ - Py_ssize_t field_offset_bytes; - int bits_already_occupied, bitshift; - - if (foffset >= 0) { - PyErr_Format(PyExc_TypeError, - "field '%s.%s' is a bitfield, " - "but a fixed offset is specified", - ct->ct_name, PyText_AS_UTF8(fname)); - goto error; - } - - if (!(ftype->ct_flags & (CT_PRIMITIVE_SIGNED | - CT_PRIMITIVE_UNSIGNED | - CT_PRIMITIVE_CHAR))) { - PyErr_Format(PyExc_TypeError, - "field '%s.%s' declared as '%s' cannot be a bit field", - ct->ct_name, PyText_AS_UTF8(fname), - ftype->ct_name); - goto error; - } - if (fbitsize > 8 * ftype->ct_size) { - PyErr_Format(PyExc_TypeError, - "bit field '%s.%s' is declared '%s:%d', which " - "exceeds the width of the type", - ct->ct_name, PyText_AS_UTF8(fname), - ftype->ct_name, fbitsize); - goto error; - } - - /* 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 = byteoffset; - field_offset_bytes &= ~(falign - 1); - - if (fbitsize == 0) { - if (PyText_GetSize(fname) > 0) { - PyErr_Format(PyExc_TypeError, - "field '%s.%s' is declared with :0", - ct->ct_name, PyText_AS_UTF8(fname)); - goto error; - } - if (!(sflags & SF_MSVC_BITFIELDS)) { - /* GCC's notion of "ftype :0;" */ - - /* pad byteoffset to a value aligned for "ftype" */ - if (ROUNDUP_BYTES(byteoffset, bitoffset) > field_offset_bytes) { - field_offset_bytes += falign; - assert(byteoffset < field_offset_bytes); - } - byteoffset = field_offset_bytes; - bitoffset = 0; - } - else { - /* MSVC's notion of "ftype :0;" */ - - /* Mostly ignored. It seems they only serve as - separator between other bitfields, to force them - into separate words. */ - } - prev_bitfield_size = 0; - } - else { - if (!(sflags & SF_MSVC_BITFIELDS)) { - /* GCC's algorithm */ - - /* 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 = (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 - allowed position */ - if ((sflags & SF_PACKED) && - (bits_already_occupied & 7)) { - PyErr_Format(PyExc_NotImplementedError, - "with 'packed', gcc would compile field " - "'%s.%s' to reuse some bits in the previous " - "field", ct->ct_name, PyText_AS_UTF8(fname)); - goto error; - } - field_offset_bytes += falign; - assert(byteoffset < field_offset_bytes); - byteoffset = field_offset_bytes; - bitoffset = 0; - bitshift = 0; - } - else { - bitshift = bits_already_occupied; - assert(bitshift >= 0); - } - bitoffset += fbitsize; - byteoffset += (bitoffset >> 3); - bitoffset &= 7; - } - else { - /* MSVC's algorithm */ - - /* A bitfield is considered as taking the full width - of their declared type. It can share some bits - with the previous field only if it was also a - bitfield and used a type of the same size. */ - if (prev_bitfield_size == ftype->ct_size && - prev_bitfield_free >= fbitsize) { - /* yes: reuse */ - bitshift = 8 * prev_bitfield_size - prev_bitfield_free; - } - else { - /* no: start a new full field */ - 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 = byteoffset - ftype->ct_size; - } - if (sflags & SF_GCC_BIG_ENDIAN) - bitshift = 8 * ftype->ct_size - fbitsize - bitshift; - - 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; - } - } - } - - 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. */ - alignedsize = (byteoffsetmax + alignment - 1) & ~(alignment-1); - if (alignedsize == 0) - alignedsize = 1; - - if (totalsize < 0) { - totalsize = alignedsize; - } - else { - if (detect_custom_layout(ct, sflags, alignedsize, - totalsize, "wrong total size", "", "") < 0) - goto error; - 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, byteoffsetmax); - goto error; - } - } - if (totalalignment < 0) { - totalalignment = alignment; - } - else { - if (detect_custom_layout(ct, sflags, alignment, totalalignment, - "wrong total alignment", "", "") < 0) - goto error; - } - - ct->ct_size = totalsize; - ct->ct_length = totalalignment; - ct->ct_stuff = interned_fields; - ct->ct_flags &= ~CT_IS_OPAQUE; - - Py_INCREF(Py_None); - return Py_None; - - error: - ct->ct_extra = NULL; - Py_DECREF(interned_fields); - return NULL; -} - -struct funcbuilder_s { - Py_ssize_t nb_bytes; - char *bufferp; - ffi_type **atypes; - ffi_type *rtype; - Py_ssize_t nargs; - CTypeDescrObject *fct; -}; - -static void *fb_alloc(struct funcbuilder_s *fb, Py_ssize_t size) -{ - if (fb->bufferp == NULL) { - fb->nb_bytes += size; - return NULL; - } - else { - char *result = fb->bufferp; - fb->bufferp += size; - return result; - } -} - -#define SUPPORTED_IN_API_MODE \ - " are only supported as %s if the function is " \ - "'API mode' and non-variadic (i.e. declared inside ffibuilder" \ - ".cdef()+ffibuilder.set_source() and not taking a final '...' " \ - "argument)" - -static ffi_type *fb_unsupported(CTypeDescrObject *ct, const char *place, - const char *detail) -{ - PyErr_Format(PyExc_NotImplementedError, - "ctype '%s' not supported as %s. %s. " - "Such structs" SUPPORTED_IN_API_MODE, - ct->ct_name, place, detail, place); - return NULL; -} - -static ffi_type *fb_fill_type(struct funcbuilder_s *fb, CTypeDescrObject *ct, - int is_result_type) -{ - const char *place = is_result_type ? "return value" : "argument"; - - if (ct->ct_flags & (CT_PRIMITIVE_ANY & ~CT_PRIMITIVE_COMPLEX)) { - return (ffi_type *)ct->ct_extra; - } - else if (ct->ct_flags & (CT_POINTER|CT_FUNCTIONPTR)) { - return &ffi_type_pointer; - } - else if ((ct->ct_flags & CT_VOID) && is_result_type) { - return &ffi_type_void; - } - - if (ct->ct_size <= 0) { - PyErr_Format(PyExc_TypeError, - ct->ct_size < 0 ? "ctype '%s' has incomplete type" - : "ctype '%s' has size 0", - ct->ct_name); - return NULL; - } - if (ct->ct_flags & CT_STRUCT) { - ffi_type *ffistruct, *ffifield; - ffi_type **elements; - Py_ssize_t i, n, nflat; - CFieldObject *cf; - - /* We can't pass a struct that was completed by verify(). - Issue: assume verify() is given "struct { long b; ...; }". - Then it will complete it in the same way whether it is actually - "struct { long a, b; }" or "struct { double a; long b; }". - But on 64-bit UNIX, these two structs are passed by value - differently: e.g. on x86-64, "b" ends up in register "rsi" in - the first case and "rdi" in the second case. - - Another reason for CT_CUSTOM_FIELD_POS would be anonymous - nested structures: we lost the information about having it - here, so better safe (and forbid it) than sorry (and maybe - crash). Note: it seems we only get in this case with - ffi.verify(). - */ - if (force_lazy_struct(ct) < 0) - return NULL; - if (ct->ct_flags & CT_CUSTOM_FIELD_POS) { - /* these NotImplementedErrors may be caught and ignored until - a real call is made to a function of this type */ - return fb_unsupported(ct, place, - "It is a struct declared with \"...;\", but the C " - "calling convention may depend on the missing fields; " - "or, it contains anonymous struct/unions"); - } - /* Another reason: __attribute__((packed)) is not supported by libffi. - */ - if (ct->ct_flags & CT_WITH_PACKED_CHANGE) { - return fb_unsupported(ct, place, - "It is a 'packed' structure, with a different layout than " - "expected by libffi"); - } - - n = PyDict_Size(ct->ct_stuff); - nflat = 0; - - /* walk the fields, expanding arrays into repetitions; first, - only count how many flattened fields there are */ - cf = (CFieldObject *)ct->ct_extra; - for (i=0; i<n; i++) { - Py_ssize_t flat; - CTypeDescrObject *ct1; - assert(cf != NULL); - if (cf->cf_bitshift >= 0) { - return fb_unsupported(ct, place, - "It is a struct with bit fields, which libffi does not " - "support"); - } - flat = 1; - ct1 = cf->cf_type; - while (ct1->ct_flags & CT_ARRAY) { - flat *= ct1->ct_length; - ct1 = ct1->ct_itemdescr; - } - if (flat <= 0) { - return fb_unsupported(ct, place, - "It is a struct with a zero-length array, which libffi " - "does not support"); - } - nflat += flat; - cf = cf->cf_next; - } - assert(cf == NULL); - - /* next, allocate and fill the flattened list */ - elements = fb_alloc(fb, (nflat + 1) * sizeof(ffi_type*)); - nflat = 0; - cf = (CFieldObject *)ct->ct_extra; - for (i=0; i<n; i++) { - Py_ssize_t j, flat = 1; - CTypeDescrObject *ct = cf->cf_type; - while (ct->ct_flags & CT_ARRAY) { - flat *= ct->ct_length; - ct = ct->ct_itemdescr; - } - ffifield = fb_fill_type(fb, ct, 0); - if (PyErr_Occurred()) - return NULL; - if (elements != NULL) { - for (j=0; j<flat; j++) - elements[nflat++] = ffifield; - } - cf = cf->cf_next; - } - - /* finally, allocate the FFI_TYPE_STRUCT */ - ffistruct = fb_alloc(fb, sizeof(ffi_type)); - if (ffistruct != NULL) { - elements[nflat] = NULL; - ffistruct->size = ct->ct_size; - ffistruct->alignment = ct->ct_length; - ffistruct->type = FFI_TYPE_STRUCT; - ffistruct->elements = elements; - } - return ffistruct; - } - else if (ct->ct_flags & CT_UNION) { - PyErr_Format(PyExc_NotImplementedError, - "ctype '%s' not supported as %s by libffi. " - "Unions" SUPPORTED_IN_API_MODE, - ct->ct_name, place, place); - return NULL; - } - else { - char *extra = ""; - if (ct->ct_flags & CT_PRIMITIVE_COMPLEX) - extra = " (the support for complex types inside libffi " - "is mostly missing at this point, so CFFI only " - "supports complex types as arguments or return " - "value in API-mode functions)"; - - PyErr_Format(PyExc_NotImplementedError, - "ctype '%s' (size %zd) not supported as %s%s", - ct->ct_name, ct->ct_size, place, extra); - return NULL; - } -} - -#define ALIGN_ARG(n) ((n) + 7) & ~7 - -static int fb_build(struct funcbuilder_s *fb, PyObject *fargs, - CTypeDescrObject *fresult) -{ - Py_ssize_t i, nargs = PyTuple_GET_SIZE(fargs); - Py_ssize_t exchange_offset; - cif_description_t *cif_descr; - - /* ffi buffer: start with a cif_description */ - cif_descr = fb_alloc(fb, sizeof(cif_description_t) + - nargs * sizeof(Py_ssize_t)); - - /* ffi buffer: next comes an array of 'ffi_type*', one per argument */ - fb->atypes = fb_alloc(fb, nargs * sizeof(ffi_type*)); - fb->nargs = nargs; - - /* ffi buffer: next comes the result type */ - fb->rtype = fb_fill_type(fb, fresult, 1); - if (PyErr_Occurred()) - return -1; - if (cif_descr != NULL) { - /* exchange data size */ - /* first, enough room for an array of 'nargs' pointers */ - exchange_offset = nargs * sizeof(void*); - exchange_offset = ALIGN_ARG(exchange_offset); - cif_descr->exchange_offset_arg[0] = exchange_offset; - /* then enough room for the result --- which means at least - sizeof(ffi_arg), according to the ffi docs */ - i = fb->rtype->size; - if (i < (Py_ssize_t)sizeof(ffi_arg)) - i = sizeof(ffi_arg); - exchange_offset += i; - } - else - exchange_offset = 0; /* not used */ - - /* loop over the arguments */ - for (i=0; i<nargs; i++) { - CTypeDescrObject *farg; - ffi_type *atype; - - farg = (CTypeDescrObject *)PyTuple_GET_ITEM(fargs, i); - /* convert arrays to pointers */ - if (farg->ct_flags & CT_ARRAY) - farg = (CTypeDescrObject *)farg->ct_stuff; - - /* ffi buffer: fill in the ffi for the i'th argument */ - assert(farg != NULL); - atype = fb_fill_type(fb, farg, 0); - if (PyErr_Occurred()) - return -1; - - if (fb->atypes != NULL) { - fb->atypes[i] = atype; - /* exchange data size */ - exchange_offset = ALIGN_ARG(exchange_offset); - cif_descr->exchange_offset_arg[1 + i] = exchange_offset; - exchange_offset += atype->size; - } - } - - if (cif_descr != NULL) { - /* exchange data size */ - /* we also align it to the next multiple of 8, in an attempt to - work around bugs(?) of libffi like #241 */ - cif_descr->exchange_size = ALIGN_ARG(exchange_offset); - } - return 0; -} - -#undef ALIGN_ARG - -static void fb_cat_name(struct funcbuilder_s *fb, const char *piece, - int piecelen) -{ - if (fb->bufferp == NULL) { - fb->nb_bytes += piecelen; - } - else { - memcpy(fb->bufferp, piece, piecelen); - fb->bufferp += piecelen; - } -} - -static int fb_build_name(struct funcbuilder_s *fb, const char *repl, - CTypeDescrObject **pfargs, Py_ssize_t nargs, - CTypeDescrObject *fresult, int ellipsis) -{ - Py_ssize_t i; - fb->nargs = nargs; - - /* name: the function type name we build here is, like in C, made - as follows: - - RESULT_TYPE_HEAD (*)(ARG_1_TYPE, ARG_2_TYPE, etc) RESULT_TYPE_TAIL - */ - fb_cat_name(fb, fresult->ct_name, fresult->ct_name_position); - if (repl[0] != '(' && - fresult->ct_name[fresult->ct_name_position - 1] != '*') - fb_cat_name(fb, " ", 1); /* add a space */ - fb_cat_name(fb, repl, strlen(repl)); - if (fb->fct) { - i = strlen(repl) - 1; /* between '(*' and ')' */ - assert(repl[i] == ')'); - fb->fct->ct_name_position = fresult->ct_name_position + i; - } - fb_cat_name(fb, "(", 1); - - /* loop over the arguments */ - for (i=0; i<nargs; i++) { - CTypeDescrObject *farg; - - farg = pfargs[i]; - if (!CTypeDescr_Check(farg)) { - PyErr_SetString(PyExc_TypeError, "expected a tuple of ctypes"); - return -1; - } - /* name: concatenate the name of the i'th argument's type */ - if (i > 0) - fb_cat_name(fb, ", ", 2); - fb_cat_name(fb, farg->ct_name, strlen(farg->ct_name)); - } - - /* name: add the '...' if needed */ - if (ellipsis) { - if (nargs > 0) - fb_cat_name(fb, ", ", 2); - fb_cat_name(fb, "...", 3); - } - - /* name: concatenate the tail of the result type */ - fb_cat_name(fb, ")", 1); - fb_cat_name(fb, fresult->ct_name + fresult->ct_name_position, - strlen(fresult->ct_name) - fresult->ct_name_position + 1); - return 0; -} - -static CTypeDescrObject *fb_prepare_ctype(struct funcbuilder_s *fb, - PyObject *fargs, - CTypeDescrObject *fresult, - int ellipsis, int fabi) -{ - CTypeDescrObject *fct, **pfargs; - Py_ssize_t nargs; - char *repl = "(*)"; - - fb->nb_bytes = 0; - fb->bufferp = NULL; - fb->fct = NULL; - - pfargs = (CTypeDescrObject **)&PyTuple_GET_ITEM(fargs, 0); - nargs = PyTuple_GET_SIZE(fargs); -#if defined(MS_WIN32) && !defined(_WIN64) - if (fabi == FFI_STDCALL) - repl = "(__stdcall *)"; -#endif - - /* compute the total size needed for the name */ - if (fb_build_name(fb, repl, pfargs, nargs, fresult, ellipsis) < 0) - return NULL; - - /* allocate the function type */ - fct = ctypedescr_new(fb->nb_bytes); - if (fct == NULL) - return NULL; - fb->fct = fct; - - /* call again fb_build_name() to really build the ct_name */ - fb->bufferp = fct->ct_name; - if (fb_build_name(fb, repl, pfargs, nargs, fresult, ellipsis) < 0) - goto error; - assert(fb->bufferp == fct->ct_name + fb->nb_bytes); - - fct->ct_extra = NULL; - fct->ct_size = sizeof(void(*)(void)); - fct->ct_flags = CT_FUNCTIONPTR; - return fct; - - error: - Py_DECREF(fct); - return NULL; -} - -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; - - /* compute the total size needed in the buffer for libffi */ - if (fb_build(&funcbuffer, fargs, fresult) < 0) - return NULL; - - /* allocate the buffer */ - buffer = PyObject_Malloc(funcbuffer.nb_bytes); - if (buffer == NULL) { - PyErr_NoMemory(); - return NULL; - } - - /* call again fb_build() to really build the libffi data structures */ - funcbuffer.bufferp = buffer; - if (fb_build(&funcbuffer, fargs, fresult) < 0) - goto error; - assert(funcbuffer.bufferp == buffer + funcbuffer.nb_bytes); - - cif_descr = (cif_description_t *)buffer; - - /* 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; - } - return cif_descr; - - error: - PyObject_Free(buffer); - return NULL; -} - -static PyObject *new_function_type(PyObject *fargs, /* tuple */ - CTypeDescrObject *fresult, - int ellipsis, int fabi) -{ - PyObject *fabiobj; - CTypeDescrObject *fct; - struct funcbuilder_s funcbuilder; - Py_ssize_t i; - const void **unique_key; - - if ((fresult->ct_size < 0 && !(fresult->ct_flags & CT_VOID)) || - (fresult->ct_flags & CT_ARRAY)) { - char *msg; - if (fresult->ct_flags & CT_IS_OPAQUE) - msg = "result type '%s' is opaque"; - else - msg = "invalid result type: '%s'"; - PyErr_Format(PyExc_TypeError, msg, fresult->ct_name); - return NULL; - } - - fct = fb_prepare_ctype(&funcbuilder, fargs, fresult, ellipsis, fabi); - if (fct == NULL) - return NULL; - - if (!ellipsis) { - /* Functions with '...' varargs are stored without a cif_descr - at all. The cif is computed on every call from the actual - types passed in. For all other functions, the cif_descr - is computed here. */ - cif_description_t *cif_descr; - - 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 - actual call */ - } - else - goto error; - } - - fct->ct_extra = (char *)cif_descr; - } - - /* build the signature, given by a tuple of ctype objects */ - fct->ct_stuff = PyTuple_New(2 + funcbuilder.nargs); - if (fct->ct_stuff == NULL) - goto error; - fabiobj = PyInt_FromLong(fabi); - if (fabiobj == NULL) - goto error; - PyTuple_SET_ITEM(fct->ct_stuff, 0, fabiobj); - - Py_INCREF(fresult); - PyTuple_SET_ITEM(fct->ct_stuff, 1, (PyObject *)fresult); - for (i=0; i<funcbuilder.nargs; i++) { - PyObject *o = PyTuple_GET_ITEM(fargs, i); - /* convert arrays into pointers */ - if (((CTypeDescrObject *)o)->ct_flags & CT_ARRAY) - o = ((CTypeDescrObject *)o)->ct_stuff; - Py_INCREF(o); - PyTuple_SET_ITEM(fct->ct_stuff, 2 + i, o); - } - - /* [ctresult, ellipsis+abi, num_args, ctargs...] */ - unique_key = alloca((3 + funcbuilder.nargs) * sizeof(void *)); - unique_key[0] = fresult; - unique_key[1] = (const void *)(Py_ssize_t)((fabi << 1) | !!ellipsis); - unique_key[2] = (const void *)(Py_ssize_t)(funcbuilder.nargs); - for (i=0; i<funcbuilder.nargs; i++) - unique_key[3 + i] = PyTuple_GET_ITEM(fct->ct_stuff, 2 + i); - return get_unique_type(fct, unique_key, 3 + funcbuilder.nargs); - - error: - Py_DECREF(fct); - return NULL; -} - -static PyObject *b_new_function_type(PyObject *self, PyObject *args) -{ - PyObject *fargs; - CTypeDescrObject *fresult; - int ellipsis = 0, fabi = FFI_DEFAULT_ABI; - - if (!PyArg_ParseTuple(args, "O!O!|ii:new_function_type", - &PyTuple_Type, &fargs, - &CTypeDescr_Type, &fresult, - &ellipsis, - &fabi)) - return NULL; - - return new_function_type(fargs, fresult, ellipsis, fabi); -} - -static int convert_from_object_fficallback(char *result, - CTypeDescrObject *ctype, - PyObject *pyobj, - int encode_result_for_libffi) -{ - /* work work work around a libffi irregularity: for integer return - types we have to fill at least a complete 'ffi_arg'-sized result - buffer. */ - if (ctype->ct_size < (Py_ssize_t)sizeof(ffi_arg)) { - if (ctype->ct_flags & CT_VOID) { - if (pyobj == Py_None) { - return 0; - } - else { - PyErr_SetString(PyExc_TypeError, - "callback with the return type 'void' must return None"); - return -1; - } - } - if (!encode_result_for_libffi) - goto skip; - if (ctype->ct_flags & CT_PRIMITIVE_SIGNED) { - PY_LONG_LONG value; - /* It's probably fine to always zero-extend, but you never - know: maybe some code somewhere expects a negative - 'short' result to be returned into EAX as a 32-bit - negative number. Better safe than sorry. This code - is about that case. Let's ignore this for enums. - */ - /* do a first conversion only to detect overflows. This - conversion produces stuff that is otherwise ignored. */ - if (convert_from_object(result, ctype, pyobj) < 0) - return -1; - /* manual inlining and tweaking of convert_from_object() - in order to write a whole 'ffi_arg'. */ - value = _my_PyLong_AsLongLong(pyobj); - if (value == -1 && PyErr_Occurred()) - return -1; - write_raw_integer_data(result, value, sizeof(ffi_arg)); - return 0; - } - else if (ctype->ct_flags & (CT_PRIMITIVE_CHAR | CT_PRIMITIVE_SIGNED | - CT_PRIMITIVE_UNSIGNED | - CT_POINTER | CT_FUNCTIONPTR)) { - /* zero extension: fill the '*result' with zeros, and (on big- - endian machines) correct the 'result' pointer to write to. - We also do that for pointers, even though we're normally not - in this branch because ctype->ct_size == sizeof(ffi_arg) for - pointers---except on some architectures like x32 (issue #372). - */ - memset(result, 0, sizeof(ffi_arg)); -#ifdef WORDS_BIGENDIAN - result += (sizeof(ffi_arg) - ctype->ct_size); -#endif - } - } - skip: - return convert_from_object(result, ctype, pyobj); -} - -static void _my_PyErr_WriteUnraisable(PyObject *t, PyObject *v, PyObject *tb, - char *objdescr, PyObject *obj, - 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 */ - PyErr_NormalizeException(&t, &v, &tb); - if (tb == NULL) { - tb = Py_None; - Py_INCREF(tb); - } - PyException_SetTraceback(v, tb); -#endif - f = PySys_GetObject("stderr"); - if (f != NULL) { - if (obj != NULL) { - PyFile_WriteString(objdescr, f); - PyFile_WriteObject(obj, f, 0); - PyFile_WriteString(":\n", f); - } - if (extra_error_line != NULL) - PyFile_WriteString(extra_error_line, f); - PyErr_Display(t, v, tb); - } - Py_XDECREF(t); - Py_XDECREF(v); - Py_XDECREF(tb); - -#endif -} - -static void general_invoke_callback(int decode_args_from_libffi, - void *result, char *args, void *userdata) -{ - PyObject *cb_args = (PyObject *)userdata; - CTypeDescrObject *ct = (CTypeDescrObject *)PyTuple_GET_ITEM(cb_args, 0); - PyObject *signature = ct->ct_stuff; - PyObject *py_ob = PyTuple_GET_ITEM(cb_args, 1); - PyObject *py_args = NULL; - PyObject *py_res = NULL; - PyObject *py_rawerr; - PyObject *onerror_cb; - Py_ssize_t i, n; - char *extra_error_line = NULL; - -#define SIGNATURE(i) ((CTypeDescrObject *)PyTuple_GET_ITEM(signature, i)) - - Py_INCREF(cb_args); - - n = PyTuple_GET_SIZE(signature) - 2; - py_args = PyTuple_New(n); - if (py_args == NULL) - goto error; - - for (i=0; i<n; i++) { - char *a_src; - PyObject *a; - CTypeDescrObject *a_ct = SIGNATURE(2 + i); - - if (decode_args_from_libffi) { - a_src = ((void **)args)[i]; - } - else { - a_src = args + i * 8; - if (a_ct->ct_flags & (CT_IS_LONGDOUBLE | CT_STRUCT | CT_UNION)) - a_src = *(char **)a_src; - } - a = convert_to_object(a_src, a_ct); - if (a == NULL) - goto error; - PyTuple_SET_ITEM(py_args, i, a); - } - - py_res = PyObject_Call(py_ob, py_args, NULL); - if (py_res == NULL) - 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: - Py_XDECREF(py_args); - Py_XDECREF(py_res); - Py_DECREF(cb_args); - return; - - error: - if (SIGNATURE(1)->ct_size > 0) { - py_rawerr = PyTuple_GET_ITEM(cb_args, 2); - memcpy(result, PyBytes_AS_STRING(py_rawerr), - PyBytes_GET_SIZE(py_rawerr)); - } - onerror_cb = PyTuple_GET_ITEM(cb_args, 3); - if (onerror_cb == Py_None) { - PyObject *ecap, *t, *v, *tb; - PyErr_Fetch(&t, &v, &tb); - ecap = _cffi_start_error_capture(); - _my_PyErr_WriteUnraisable(t, v, tb, "From cffi callback ", py_ob, - extra_error_line); - _cffi_stop_error_capture(ecap); - } - else { - PyObject *exc1, *val1, *tb1, *res1, *exc2, *val2, *tb2; - PyErr_Fetch(&exc1, &val1, &tb1); - PyErr_NormalizeException(&exc1, &val1, &tb1); - res1 = PyObject_CallFunctionObjArgs(onerror_cb, - exc1 ? exc1 : Py_None, - val1 ? val1 : Py_None, - tb1 ? tb1 : Py_None, - NULL); - if (res1 != NULL) { - if (res1 != Py_None) - convert_from_object_fficallback(result, SIGNATURE(1), res1, - decode_args_from_libffi); - Py_DECREF(res1); - } - if (!PyErr_Occurred()) { - Py_XDECREF(exc1); - Py_XDECREF(val1); - Py_XDECREF(tb1); - } - else { - /* double exception! print a double-traceback... */ - PyObject *ecap; - PyErr_Fetch(&exc2, &val2, &tb2); - ecap = _cffi_start_error_capture(); - _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); - } - } - goto done; - -#undef SIGNATURE -} - -static void invoke_callback(ffi_cif *cif, void *result, void **args, - void *userdata) -{ - save_errno(); - { - PyGILState_STATE state = gil_ensure(); - general_invoke_callback(1, result, (char *)args, userdata); - gil_release(state); - } - restore_errno(); -} - -static PyObject *prepare_callback_info_tuple(CTypeDescrObject *ct, - PyObject *ob, - PyObject *error_ob, - PyObject *onerror_ob, - int decode_args_from_libffi) -{ - CTypeDescrObject *ctresult; - PyObject *py_rawerr, *infotuple; - Py_ssize_t size; - - if (!(ct->ct_flags & CT_FUNCTIONPTR)) { - PyErr_Format(PyExc_TypeError, "expected a function ctype, got '%s'", - ct->ct_name); - return NULL; - } - if (!PyCallable_Check(ob)) { - PyErr_Format(PyExc_TypeError, - "expected a callable object, not %.200s", - Py_TYPE(ob)->tp_name); - return NULL; - } - if (onerror_ob != Py_None && !PyCallable_Check(onerror_ob)) { - PyErr_Format(PyExc_TypeError, - "expected a callable object for 'onerror', not %.200s", - Py_TYPE(onerror_ob)->tp_name); - return NULL; - } - - ctresult = (CTypeDescrObject *)PyTuple_GET_ITEM(ct->ct_stuff, 1); - size = ctresult->ct_size; - if (size < (Py_ssize_t)sizeof(ffi_arg)) - size = sizeof(ffi_arg); - py_rawerr = PyBytes_FromStringAndSize(NULL, size); - if (py_rawerr == NULL) - return NULL; - memset(PyBytes_AS_STRING(py_rawerr), 0, size); - if (error_ob != Py_None) { - if (convert_from_object_fficallback( - PyBytes_AS_STRING(py_rawerr), ctresult, error_ob, - decode_args_from_libffi) < 0) { - Py_DECREF(py_rawerr); - return NULL; - } - } - infotuple = Py_BuildValue("OOOO", ct, ob, py_rawerr, onerror_ob); - Py_DECREF(py_rawerr); - -#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. - 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; - CDataObject_closure *cd; - PyObject *ob, *error_ob = Py_None, *onerror_ob = Py_None; - 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, - &error_ob, &onerror_ob)) - return NULL; - - infotuple = prepare_callback_info_tuple(ct, ob, error_ob, onerror_ob, 1); - if (infotuple == NULL) - return NULL; - -#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); - if (cd == NULL) - goto error; - Py_INCREF(ct); - cd->head.c_type = ct; - cd->head.c_data = (char *)closure_exec; - cd->head.c_weakreflist = NULL; - closure->user_data = NULL; - cd->closure = closure; - - cif_descr = (cif_description_t *)ct->ct_extra; - if (cif_descr == NULL) { - PyErr_Format(PyExc_NotImplementedError, - "%s: callback with unsupported argument or " - "return type or with '...'", ct->ct_name); - goto error; - } - -#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 - 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 - 'ffi_closure' structure than the one we expect from ffi.h - (e.g. difference in details of the platform): a difference - in FFI_TRAMPOLINE_SIZE means that the 'user_data' field - ends up somewhere else, and so the test above fails. - */ - PyErr_SetString(PyExc_SystemError, - "ffi_prep_closure(): bad user_data (it seems that the " - "version of the libffi library seen at runtime is " - "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) { -#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) -{ - char *ename; - PyObject *enumerators, *enumvalues; - PyObject *dict1 = NULL, *dict2 = NULL, *combined = NULL, *tmpkey = NULL; - int name_size; - CTypeDescrObject *td, *basetd; - Py_ssize_t i, n; - - if (!PyArg_ParseTuple(args, "sO!O!O!:new_enum_type", - &ename, - &PyTuple_Type, &enumerators, - &PyTuple_Type, &enumvalues, - &CTypeDescr_Type, &basetd)) - return NULL; - - n = PyTuple_GET_SIZE(enumerators); - if (n != PyTuple_GET_SIZE(enumvalues)) { - PyErr_SetString(PyExc_ValueError, - "tuple args must have the same size"); - return NULL; - } - - if (!(basetd->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_UNSIGNED))) { - PyErr_SetString(PyExc_TypeError, - "expected a primitive signed or unsigned base type"); - return NULL; - } - - dict1 = PyDict_New(); - if (dict1 == NULL) - goto error; - dict2 = PyDict_New(); - if (dict2 == NULL) - goto error; - - for (i=n; --i >= 0; ) { - long long lvalue; - PyObject *value = PyTuple_GET_ITEM(enumvalues, i); - tmpkey = PyTuple_GET_ITEM(enumerators, i); - Py_INCREF(tmpkey); - if (!PyText_Check(tmpkey)) { -#if PY_MAJOR_VERSION < 3 - if (PyUnicode_Check(tmpkey)) { - const char *text = PyText_AsUTF8(tmpkey); - if (text == NULL) - goto error; - Py_DECREF(tmpkey); - tmpkey = PyString_FromString(text); - if (tmpkey == NULL) - goto error; - } - else -#endif - { - PyErr_SetString(PyExc_TypeError, - "enumerators must be a list of strings"); - goto error; - } - } - if (convert_from_object((char*)&lvalue, basetd, value) < 0) - goto error; /* out-of-range or badly typed 'value' */ - if (PyDict_SetItem(dict1, tmpkey, value) < 0) - goto error; - if (PyDict_SetItem(dict2, value, tmpkey) < 0) - goto error; - Py_DECREF(tmpkey); - tmpkey = NULL; - } - - combined = PyTuple_Pack(2, dict1, dict2); - if (combined == NULL) - goto error; - - Py_CLEAR(dict2); - Py_CLEAR(dict1); - - name_size = strlen(ename) + 1; - td = ctypedescr_new(name_size); - if (td == NULL) - goto error; - - memcpy(td->ct_name, ename, name_size); - td->ct_stuff = combined; - td->ct_size = basetd->ct_size; - td->ct_length = basetd->ct_length; /* alignment */ - td->ct_extra = basetd->ct_extra; /* ffi type */ - td->ct_flags = basetd->ct_flags | CT_IS_ENUM; - td->ct_name_position = name_size - 1; - return (PyObject *)td; - - error: - Py_XDECREF(tmpkey); - Py_XDECREF(combined); - Py_XDECREF(dict2); - Py_XDECREF(dict1); - return NULL; -} - -static PyObject *b_alignof(PyObject *self, PyObject *arg) -{ - int align; - if (!CTypeDescr_Check(arg)) { - PyErr_SetString(PyExc_TypeError, "expected a 'ctype' object"); - return NULL; - } - align = get_alignment((CTypeDescrObject *)arg); - if (align < 0) - return NULL; - return PyInt_FromLong(align); -} - -static Py_ssize_t direct_sizeof_cdata(CDataObject *cd) -{ - Py_ssize_t size; - if (cd->c_type->ct_flags & CT_ARRAY) - size = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size; - else { - size = -1; - if (cd->c_type->ct_flags & (CT_STRUCT | CT_UNION)) - size = _cdata_var_byte_size(cd); - if (size < 0) - size = cd->c_type->ct_size; - } - return size; -} - -static PyObject *b_sizeof(PyObject *self, PyObject *arg) -{ - Py_ssize_t size; - - if (CData_Check(arg)) { - size = direct_sizeof_cdata((CDataObject *)arg); - } - else if (CTypeDescr_Check(arg)) { - size = ((CTypeDescrObject *)arg)->ct_size; - if (size < 0) { - PyErr_Format(PyExc_ValueError, "ctype '%s' is of unknown size", - ((CTypeDescrObject *)arg)->ct_name); - return NULL; - } - } - else { - PyErr_SetString(PyExc_TypeError, - "expected a 'cdata' or 'ctype' object"); - return NULL; - } - return PyInt_FromSsize_t(size); -} - -static PyObject *b_typeof(PyObject *self, PyObject *arg) -{ - PyObject *res; - - if (!CData_Check(arg)) { - PyErr_SetString(PyExc_TypeError, "expected a 'cdata' object"); - return NULL; - } - res = (PyObject *)((CDataObject *)arg)->c_type; - Py_INCREF(res); - return res; -} - -static CTypeDescrObject *direct_typeoffsetof(CTypeDescrObject *ct, - PyObject *fieldname, - int following, Py_ssize_t *offset) -{ - /* Does not return a new reference! */ - CTypeDescrObject *res; - CFieldObject *cf; - - if (PyTextAny_Check(fieldname)) { - if (!following && (ct->ct_flags & CT_POINTER)) - ct = ct->ct_itemdescr; - if (!(ct->ct_flags & (CT_STRUCT|CT_UNION))) { - PyErr_SetString(PyExc_TypeError, - "with a field name argument, expected a " - "struct or union ctype"); - return NULL; - } - if (force_lazy_struct(ct) <= 0) { - if (!PyErr_Occurred()) - PyErr_SetString(PyExc_TypeError, "struct/union is opaque"); - return NULL; - } - cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, fieldname); - if (cf == NULL) { - PyErr_SetObject(PyExc_KeyError, fieldname); - return NULL; - } - if (cf->cf_bitshift >= 0) { - PyErr_SetString(PyExc_TypeError, "not supported for bitfields"); - return NULL; - } - res = cf->cf_type; - *offset = cf->cf_offset; - } - else { - Py_ssize_t index = PyInt_AsSsize_t(fieldname); - if (index < 0 && PyErr_Occurred()) { - PyErr_SetString(PyExc_TypeError, - "field name or array index expected"); - return NULL; - } - - if (!(ct->ct_flags & (CT_ARRAY|CT_POINTER)) || - ct->ct_itemdescr->ct_size < 0) { - PyErr_SetString(PyExc_TypeError, "with an integer argument, " - "expected an array ctype or a " - "pointer to non-opaque"); - return NULL; - } - res = ct->ct_itemdescr; - *offset = MUL_WRAPAROUND(index, ct->ct_itemdescr->ct_size); - if ((*offset / ct->ct_itemdescr->ct_size) != index) { - PyErr_SetString(PyExc_OverflowError, - "array offset would overflow a Py_ssize_t"); - return NULL; - } - } - return res; -} - -static PyObject *b_typeoffsetof(PyObject *self, PyObject *args) -{ - PyObject *res, *fieldname; - CTypeDescrObject *ct; - Py_ssize_t offset; - int following = 0; - - if (!PyArg_ParseTuple(args, "O!O|i:typeoffsetof", - &CTypeDescr_Type, &ct, &fieldname, &following)) - return NULL; - - res = (PyObject *)direct_typeoffsetof(ct, fieldname, following, &offset); - if (res == NULL) - return NULL; - - return Py_BuildValue("(On)", res, offset); -} - -static PyObject *b_rawaddressof(PyObject *self, PyObject *args) -{ - CTypeDescrObject *ct; - CDataObject *cd; - Py_ssize_t offset; - int accepted_flags; - - if (!PyArg_ParseTuple(args, "O!O!n:rawaddressof", - &CTypeDescr_Type, &ct, - &CData_Type, &cd, - &offset)) - return NULL; - - accepted_flags = CT_STRUCT | CT_UNION | CT_ARRAY | CT_POINTER; - if ((cd->c_type->ct_flags & accepted_flags) == 0) { - PyErr_SetString(PyExc_TypeError, - "expected a cdata struct/union/array/pointer object"); - return NULL; - } - if ((ct->ct_flags & CT_POINTER) == 0) { - PyErr_SetString(PyExc_TypeError, - "expected a pointer ctype"); - return NULL; - } - return new_simple_cdata(cd->c_data + offset, ct); -} - -static PyObject *b_getcname(PyObject *self, PyObject *args) -{ - CTypeDescrObject *ct; - char *replace_with, *p, *s; - Py_ssize_t namelen, replacelen; - - if (!PyArg_ParseTuple(args, "O!s:getcname", - &CTypeDescr_Type, &ct, &replace_with)) - return NULL; - - namelen = strlen(ct->ct_name); - replacelen = strlen(replace_with); - s = p = alloca(namelen + replacelen + 1); - memcpy(p, ct->ct_name, ct->ct_name_position); - p += ct->ct_name_position; - memcpy(p, replace_with, replacelen); - p += replacelen; - memcpy(p, ct->ct_name + ct->ct_name_position, - namelen - ct->ct_name_position); - - return PyText_FromStringAndSize(s, namelen + replacelen); -} - -static PyObject *b_string(PyObject *self, PyObject *args, PyObject *kwds) -{ - CDataObject *cd; - Py_ssize_t maxlen = -1; - static char *keywords[] = {"cdata", "maxlen", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|n:string", keywords, - &CData_Type, &cd, &maxlen)) - return NULL; - - if (cd->c_type->ct_itemdescr != NULL && - cd->c_type->ct_itemdescr->ct_flags & (CT_PRIMITIVE_CHAR | - CT_PRIMITIVE_SIGNED | - CT_PRIMITIVE_UNSIGNED) && - !(cd->c_type->ct_itemdescr->ct_flags & CT_IS_BOOL)) { - Py_ssize_t length = maxlen; - if (cd->c_data == NULL) { - PyObject *s = cdata_repr(cd); - if (s != NULL) { - PyErr_Format(PyExc_RuntimeError, - "cannot use string() on %s", - PyText_AS_UTF8(s)); - Py_DECREF(s); - } - return NULL; - } - if (length < 0 && cd->c_type->ct_flags & CT_ARRAY) { - length = get_array_length(cd); - } - if (cd->c_type->ct_itemdescr->ct_size == sizeof(char)) { - const char *start = cd->c_data; - if (length < 0) { - /*READ(start, 1)*/ - length = strlen(start); - /*READ(start, length)*/ - } - else { - const char *end; - /*READ(start, length)*/ - end = (const char *)memchr(start, 0, length); - if (end != NULL) - length = end - start; - } - return PyBytes_FromStringAndSize(start, length); - } - else if (cd->c_type->ct_itemdescr->ct_flags & CT_PRIMITIVE_CHAR) { - switch (cd->c_type->ct_itemdescr->ct_size) { - case 2: { - const cffi_char16_t *start = (cffi_char16_t *)cd->c_data; - if (length < 0) { - /*READ(start, 2)*/ - length = 0; - while (start[length]) - length++; - /*READ(start, 2 * length)*/ - } - else { - /*READ(start, 2 * length)*/ - maxlen = length; - length = 0; - while (length < maxlen && start[length]) - length++; - } - return _my_PyUnicode_FromChar16(start, length); - } - case 4: { - const cffi_char32_t *start = (cffi_char32_t *)cd->c_data; - if (length < 0) { - /*READ(start, 4)*/ - length = 0; - while (start[length]) - length++; - /*READ(start, 4 * length)*/ - } - else { - /*READ(start, 4 * length)*/ - maxlen = length; - length = 0; - while (length < maxlen && start[length]) - length++; - } - return _my_PyUnicode_FromChar32(start, length); - } - } - } - } - else if (cd->c_type->ct_flags & CT_IS_ENUM) { - return convert_cdata_to_enum_string(cd, 0); - } - else if (cd->c_type->ct_flags & CT_IS_BOOL) { - /* fall through to TypeError */ - } - else if (cd->c_type->ct_flags & (CT_PRIMITIVE_CHAR | - CT_PRIMITIVE_SIGNED | - CT_PRIMITIVE_UNSIGNED)) { - /*READ(cd->c_data, cd->c_type->ct_size)*/ - if (cd->c_type->ct_size == sizeof(char)) - return PyBytes_FromStringAndSize(cd->c_data, 1); - else if (cd->c_type->ct_flags & CT_PRIMITIVE_CHAR) { - switch (cd->c_type->ct_size) { - case 2: - return _my_PyUnicode_FromChar16((cffi_char16_t *)cd->c_data, 1); - case 4: - return _my_PyUnicode_FromChar32((cffi_char32_t *)cd->c_data, 1); - } - } - } - PyErr_Format(PyExc_TypeError, "string(): unexpected cdata '%s' argument", - cd->c_type->ct_name); - return NULL; -} - -static PyObject *b_unpack(PyObject *self, PyObject *args, PyObject *kwds) -{ - CDataObject *cd; - CTypeDescrObject *ctitem; - Py_ssize_t i, length, itemsize; - PyObject *result; - char *src; - int casenum; - static char *keywords[] = {"cdata", "length", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!n:unpack", keywords, - &CData_Type, &cd, &length)) - return NULL; - - if (!(cd->c_type->ct_flags & (CT_ARRAY|CT_POINTER))) { - PyErr_Format(PyExc_TypeError, - "expected a pointer or array, got '%s'", - cd->c_type->ct_name); - return NULL; - } - if (length < 0) { - PyErr_SetString(PyExc_ValueError, "'length' cannot be negative"); - return NULL; - } - if (cd->c_data == NULL) { - PyObject *s = cdata_repr(cd); - if (s != NULL) { - PyErr_Format(PyExc_RuntimeError, - "cannot use unpack() on %s", - PyText_AS_UTF8(s)); - Py_DECREF(s); - } - return NULL; - } - - /* byte- and unicode strings */ - ctitem = cd->c_type->ct_itemdescr; - if (ctitem->ct_flags & CT_PRIMITIVE_CHAR) { - switch (ctitem->ct_size) { - case sizeof(char): - return PyBytes_FromStringAndSize(cd->c_data, length); - case 2: - return _my_PyUnicode_FromChar16((cffi_char16_t *)cd->c_data,length); - case 4: - return _my_PyUnicode_FromChar32((cffi_char32_t *)cd->c_data,length); - } - } - - /* else, the result is a list. This implementation should be - equivalent to but much faster than '[p[i] for i in range(length)]'. - (Note that on PyPy, 'list(p[0:length])' should be equally fast, - but arguably, finding out that there *is* such an unexpected way - to write things down is the real problem.) - */ - result = PyList_New(length); - if (result == NULL) - return NULL; - - src = cd->c_data; - itemsize = ctitem->ct_size; - if (itemsize < 0) { - Py_DECREF(result); - PyErr_Format(PyExc_ValueError, "'%s' points to items of unknown size", - cd->c_type->ct_name); - return NULL; - } - - /* Determine some common fast-paths for the loop below. The case -1 - is the fall-back, which always gives the right answer. */ - -#define ALIGNMENT_CHECK(align) \ - (((align) & ((align) - 1)) == 0 && \ - (((uintptr_t)src) & ((align) - 1)) == 0) - - casenum = -1; - - if ((ctitem->ct_flags & CT_PRIMITIVE_ANY) && - ALIGNMENT_CHECK(ctitem->ct_length)) { - /* Source data is fully aligned; we can directly read without - memcpy(). The unaligned case is expected to be rare; in - this situation it is ok to fall back to the general - convert_to_object() in the loop. For now we also use this - fall-back for types that are too large. - */ - if (ctitem->ct_flags & CT_PRIMITIVE_SIGNED) { - if (itemsize == sizeof(long)) casenum = 3; - else if (itemsize == sizeof(int)) casenum = 2; - else if (itemsize == sizeof(short)) casenum = 1; - else if (itemsize == sizeof(signed char)) casenum = 0; - } - else if (ctitem->ct_flags & CT_PRIMITIVE_UNSIGNED) { - /* Note: we never pick case 6 if sizeof(int) == sizeof(long), - so that case 6 below can assume that the 'unsigned int' result - would always fit in a 'signed long'. */ - if (ctitem->ct_flags & CT_IS_BOOL) casenum = 11; - else if (itemsize == sizeof(unsigned long)) casenum = 7; - else if (itemsize == sizeof(unsigned int)) casenum = 6; - else if (itemsize == sizeof(unsigned short)) casenum = 5; - else if (itemsize == sizeof(unsigned char)) casenum = 4; - } - else if (ctitem->ct_flags & CT_PRIMITIVE_FLOAT) { - if (itemsize == sizeof(double)) casenum = 9; - else if (itemsize == sizeof(float)) casenum = 8; - } - } - else if (ctitem->ct_flags & (CT_POINTER | CT_FUNCTIONPTR)) { - casenum = 10; /* any pointer */ - } -#undef ALIGNMENT_CHECK - - for (i = 0; i < length; i++) { - PyObject *x; - switch (casenum) { - /* general case */ - default: x = convert_to_object(src, ctitem); break; - - /* special cases for performance only */ - case 0: x = PyInt_FromLong(*(signed char *)src); break; - case 1: x = PyInt_FromLong(*(short *)src); break; - case 2: x = PyInt_FromLong(*(int *)src); break; - case 3: x = PyInt_FromLong(*(long *)src); break; - case 4: x = PyInt_FromLong(*(unsigned char *)src); break; - case 5: x = PyInt_FromLong(*(unsigned short *)src); break; - case 6: x = PyInt_FromLong((long)*(unsigned int *)src); break; - case 7: x = PyLong_FromUnsignedLong(*(unsigned long *)src); break; - case 8: x = PyFloat_FromDouble(*(float *)src); break; - case 9: x = PyFloat_FromDouble(*(double *)src); break; - case 10: x = new_simple_cdata(*(char **)src, ctitem); break; - case 11: - switch (*(unsigned char *)src) { - case 0: x = Py_False; Py_INCREF(x); break; - case 1: x = Py_True; Py_INCREF(x); break; - default: x = convert_to_object(src, ctitem); /* error */ - } - break; - } - if (x == NULL) { - Py_DECREF(result); - return NULL; - } - PyList_SET_ITEM(result, i, x); - src += itemsize; - } - return result; -} - -static PyObject * -b_buffer_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - /* this is the constructor of the type implemented in minibuffer.h */ - CDataObject *cd; - Py_ssize_t size = -1; - static char *keywords[] = {"cdata", "size", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|n:buffer", keywords, - &CData_Type, &cd, &size)) - return NULL; - - if (size < 0) - size = _cdata_var_byte_size(cd); - - if (cd->c_type->ct_flags & CT_POINTER) { - if (size < 0) - size = cd->c_type->ct_itemdescr->ct_size; - } - else if (cd->c_type->ct_flags & CT_ARRAY) { - if (size < 0) - size = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size; - } - else { - PyErr_Format(PyExc_TypeError, - "expected a pointer or array cdata, got '%s'", - cd->c_type->ct_name); - return NULL; - } - if (size < 0) { - PyErr_Format(PyExc_TypeError, - "don't know the size pointed to by '%s'", - cd->c_type->ct_name); - return NULL; - } - /*WRITE(cd->c_data, size)*/ - return minibuffer_new(cd->c_data, size, (PyObject *)cd); -} - -static PyObject *b_get_errno(PyObject *self, PyObject *noarg) -{ - int err; - restore_errno_only(); - err = errno; - errno = 0; - return PyInt_FromLong(err); -} - -static PyObject *b_set_errno(PyObject *self, PyObject *arg) -{ - long ival = PyInt_AsLong(arg); - if (ival == -1 && PyErr_Occurred()) - return NULL; - else if (ival < INT_MIN || ival > INT_MAX) { - PyErr_SetString(PyExc_OverflowError, "errno value too large"); - return NULL; - } - errno = (int)ival; - save_errno_only(); - errno = 0; - Py_INCREF(Py_None); - return Py_None; -} - -static PyObject *newp_handle(CTypeDescrObject *ct_voidp, PyObject *x) -{ - CDataObject_own_structptr *cd; - cd = (CDataObject_own_structptr *)PyObject_GC_New(CDataObject_own_structptr, - &CDataOwningGC_Type); - if (cd == NULL) - return NULL; - Py_INCREF(ct_voidp); /* must be "void *" */ - cd->head.c_type = ct_voidp; - cd->head.c_data = (char *)cd; - cd->head.c_weakreflist = NULL; - Py_INCREF(x); - cd->structobj = x; - PyObject_GC_Track(cd); - return (PyObject *)cd; -} - -static PyObject *b_newp_handle(PyObject *self, PyObject *args) -{ - CTypeDescrObject *ct; - PyObject *x; - if (!PyArg_ParseTuple(args, "O!O", &CTypeDescr_Type, &ct, &x)) - return NULL; - - if (!(ct->ct_flags & CT_IS_VOID_PTR)) { - PyErr_Format(PyExc_TypeError, "needs 'void *', got '%s'", ct->ct_name); - return NULL; - } - - return newp_handle(ct, x); -} - -static PyObject *b_from_handle(PyObject *self, PyObject *arg) -{ - CTypeDescrObject *ct; - CDataObject_own_structptr *orgcd; - PyObject *x; - if (!CData_Check(arg)) { - PyErr_SetString(PyExc_TypeError, "expected a 'cdata' object"); - return NULL; - } - ct = ((CDataObject *)arg)->c_type; - if (!(ct->ct_flags & CT_IS_VOIDCHAR_PTR)) { - PyErr_Format(PyExc_TypeError, - "expected a 'cdata' object with a 'void *' out of " - "new_handle(), got '%s'", ct->ct_name); - return NULL; - } - orgcd = (CDataObject_own_structptr *)((CDataObject *)arg)->c_data; - if (!orgcd) { - PyErr_SetString(PyExc_RuntimeError, - "cannot use from_handle() on NULL pointer"); - return NULL; - } - if (Py_REFCNT(orgcd) <= 0 || Py_TYPE(orgcd) != &CDataOwningGC_Type) { - Py_FatalError("ffi.from_handle() detected that the address passed " - "points to garbage. If it is really the result of " - "ffi.new_handle(), then the Python object has already " - "been garbage collected"); - } - x = orgcd->structobj; - Py_INCREF(x); - return x; -} - -static int _my_PyObject_GetContiguousBuffer(PyObject *x, Py_buffer *view, - int writable_only) -{ -#if PY_MAJOR_VERSION < 3 - /* Some objects only support the buffer interface and CPython doesn't - translate it into the memoryview interface, mess. Hack a very - minimal content for 'view'. Don't care if the other fields are - uninitialized: we only call PyBuffer_Release(), which only reads - 'view->obj'. */ - PyBufferProcs *pb = x->ob_type->tp_as_buffer; - if (pb && !pb->bf_releasebuffer) { - /* we used to try all three in some vaguely sensible order, - i.e. first the write. But trying to call the write on a - read-only buffer fails with TypeError. So we use a less- - sensible order now. See test_from_buffer_more_cases. - - If 'writable_only', we only try bf_getwritebuffer. - */ - readbufferproc proc = NULL; - if (!writable_only) { - proc = (readbufferproc)pb->bf_getreadbuffer; - if (!proc) - proc = (readbufferproc)pb->bf_getcharbuffer; - } - if (!proc) - proc = (readbufferproc)pb->bf_getwritebuffer; - - if (proc && pb->bf_getsegcount) { - if ((*pb->bf_getsegcount)(x, NULL) != 1) { - PyErr_SetString(PyExc_TypeError, - "expected a single-segment buffer object"); - return -1; - } - view->len = (*proc)(x, 0, &view->buf); - if (view->len < 0) - return -1; - view->obj = x; - Py_INCREF(x); - return 0; - } - } -#endif - - if (PyObject_GetBuffer(x, view, writable_only ? PyBUF_WRITABLE - : PyBUF_SIMPLE) < 0) - return -1; - - if (!PyBuffer_IsContiguous(view, 'A')) { - PyBuffer_Release(view); - PyErr_SetString(PyExc_TypeError, "contiguous buffer expected"); - return -1; - } - return 0; -} - -static PyObject *direct_from_buffer(CTypeDescrObject *ct, PyObject *x, - int require_writable) -{ - CDataObject *cd; - Py_buffer *view; - Py_ssize_t arraylength, minimumlength = 0; - - 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; - } - - /* PyPy 5.7 can obtain buffers for string (python 2) - or bytes (python 3). from_buffer(u"foo") is disallowed. - */ - if (PyUnicode_Check(x)) { - PyErr_SetString(PyExc_TypeError, - "from_buffer() cannot return the address " - "of a unicode object"); - return NULL; - } - - view = PyObject_Malloc(sizeof(Py_buffer)); - if (view == NULL) { - PyErr_NoMemory(); - return NULL; - } - if (_my_PyObject_GetContiguousBuffer(x, view, require_writable) < 0) - goto error1; - - if (ct->ct_flags & CT_POINTER) - { - arraylength = view->len; /* number of bytes, not used so far */ - } - else { - /* 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 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_frombuf, - &CDataFromBuf_Type); - if (cd == NULL) - goto error2; - - Py_INCREF(ct); - cd->c_type = ct; - cd->c_data = view->buf; - cd->c_weakreflist = NULL; - ((CDataObject_frombuf *)cd)->length = arraylength; - ((CDataObject_frombuf *)cd)->bufferview = view; - PyObject_GC_Track(cd); - return (PyObject *)cd; - - error2: - PyBuffer_Release(view); - error1: - PyObject_Free(view); - return NULL; -} - -static PyObject *b_from_buffer(PyObject *self, PyObject *args) -{ - CTypeDescrObject *ct; - PyObject *x; - int require_writable = 0; - - if (!PyArg_ParseTuple(args, "O!O|i", &CTypeDescr_Type, &ct, &x, - &require_writable)) - return NULL; - - return direct_from_buffer(ct, x, require_writable); -} - -static int _fetch_as_buffer(PyObject *x, Py_buffer *view, int writable_only) -{ - if (CData_Check(x)) { - CTypeDescrObject *ct = ((CDataObject *)x)->c_type; - if (!(ct->ct_flags & (CT_POINTER|CT_ARRAY))) { - PyErr_Format(PyExc_TypeError, - "expected a pointer or array ctype, got '%s'", - ct->ct_name); - return -1; - } - view->buf = ((CDataObject *)x)->c_data; - view->obj = NULL; - return 0; - } - else { - return _my_PyObject_GetContiguousBuffer(x, view, writable_only); - } -} - -static PyObject *b_memmove(PyObject *self, PyObject *args, PyObject *kwds) -{ - PyObject *dest_obj, *src_obj; - Py_buffer dest_view, src_view; - Py_ssize_t n; - static char *keywords[] = {"dest", "src", "n", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOn", keywords, - &dest_obj, &src_obj, &n)) - return NULL; - if (n < 0) { - PyErr_SetString(PyExc_ValueError, "negative size"); - return NULL; - } - - if (_fetch_as_buffer(src_obj, &src_view, 0) < 0) { - return NULL; - } - if (_fetch_as_buffer(dest_obj, &dest_view, 1) < 0) { - PyBuffer_Release(&src_view); - return NULL; - } - - memmove(dest_view.buf, src_view.buf, n); - - PyBuffer_Release(&dest_view); - PyBuffer_Release(&src_view); - Py_INCREF(Py_None); - return Py_None; -} - -static PyObject *b__get_types(PyObject *self, PyObject *noarg) -{ - return PyTuple_Pack(2, (PyObject *)&CData_Type, - (PyObject *)&CTypeDescr_Type); -} - -/* forward, in commontypes.c */ -static PyObject *b__get_common_types(PyObject *self, PyObject *arg); - -static PyObject *b_gcp(PyObject *self, PyObject *args, PyObject *kwds) -{ - CDataObject *cd; - CDataObject *origobj; - PyObject *destructor; - Py_ssize_t ignored; /* for pypy */ - static char *keywords[] = {"cdata", "destructor", "size", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O|n:gc", keywords, - &CData_Type, &origobj, &destructor, - &ignored)) - return NULL; - - if (destructor == Py_None) { - if (!PyObject_TypeCheck(origobj, &CDataGCP_Type)) { - PyErr_SetString(PyExc_TypeError, - "Can remove destructor only on a object " - "previously returned by ffi.gc()"); - return NULL; - } - Py_CLEAR(((CDataObject_gcp *)origobj)->destructor); - Py_RETURN_NONE; - } - - cd = allocate_gcp_object(origobj, origobj->c_type, destructor); - return (PyObject *)cd; -} - -static PyObject *b_release(PyObject *self, PyObject *arg) -{ - if (!CData_Check(arg)) { - PyErr_SetString(PyExc_TypeError, "expected a 'cdata' object"); - return NULL; - } - return cdata_exit(arg, NULL); -} - -/************************************************************/ - -static char _testfunc0(char a, char b) -{ - return a + b; -} -static long _testfunc1(int a, long b) -{ - return (long)a + b; -} -static PY_LONG_LONG _testfunc2(PY_LONG_LONG a, PY_LONG_LONG b) -{ - return a + b; -} -static double _testfunc3(float a, double b) -{ - return a + b; -} -static float _testfunc4(float a, double b) -{ - return (float)(a + b); -} -static void _testfunc5(void) -{ - errno = errno + 15; -} -static int *_testfunc6(int *x) -{ - static int y; - y = *x - 1000; - return &y; -} -struct _testfunc7_s { unsigned char a1; short a2; }; -static short _testfunc7(struct _testfunc7_s inlined) -{ - return inlined.a1 + inlined.a2; -} -static int _testfunc9(int num, ...) -{ - va_list vargs; - int i, total = 0; - va_start(vargs, num); - for (i=0; i<num; i++) { - int value = va_arg(vargs, int); - if (value == 0) - value = -66666666; - total += value; - } - va_end(vargs); - return total; -} - -static struct _testfunc7_s _testfunc10(int n) -{ - struct _testfunc7_s result; - result.a1 = n; - result.a2 = n * n; - return result; -} - -struct _testfunc11_s { int a1, a2; }; -static struct _testfunc11_s _testfunc11(int n) -{ - struct _testfunc11_s result; - result.a1 = n; - result.a2 = n * n; - return result; -} - -struct _testfunc12_s { double a1; }; -static struct _testfunc12_s _testfunc12(int n) -{ - struct _testfunc12_s result; - result.a1 = n; - return result; -} - -struct _testfunc13_s { int a1, a2, a3; }; -static struct _testfunc13_s _testfunc13(int n) -{ - struct _testfunc13_s result; - result.a1 = n; - result.a2 = n * n; - result.a3 = n * n * n; - return result; -} - -struct _testfunc14_s { float a1; }; -static struct _testfunc14_s _testfunc14(int n) -{ - struct _testfunc14_s result; - result.a1 = (float)n; - return result; -} - -struct _testfunc15_s { float a1; int a2; }; -static struct _testfunc15_s _testfunc15(int n) -{ - struct _testfunc15_s result; - result.a1 = (float)n; - result.a2 = n * n; - return result; -} - -struct _testfunc16_s { float a1, a2; }; -static struct _testfunc16_s _testfunc16(int n) -{ - struct _testfunc16_s result; - result.a1 = (float)n; - result.a2 = -(float)n; - return result; -} - -struct _testfunc17_s { int a1; float a2; }; -static struct _testfunc17_s _testfunc17(int n) -{ - struct _testfunc17_s result; - result.a1 = n; - result.a2 = (float)n * (float)n; - return result; -} - -static int _testfunc18(struct _testfunc17_s *ptr) -{ - return ptr->a1 + (int)ptr->a2; -} - -static long double _testfunc19(long double x, int count) -{ - int i; - for (i=0; i<count; i++) { - x = 4*x - x*x; - } - return x; -} - -static short _testfunc20(struct _testfunc7_s *ptr) -{ - return ptr->a1 + ptr->a2; -} - -struct _testfunc21_s { int a, b, c, d, e, f, g, h, i, j; }; -static int _testfunc21(struct _testfunc21_s inlined) -{ - return ((inlined.a << 0) + - (inlined.b << 1) + - (inlined.c << 2) + - (inlined.d << 3) + - (inlined.e << 4) + - (inlined.f << 5) + - (inlined.g << 6) + - (inlined.h << 7) + - (inlined.i << 8) + - (inlined.j << 9)); -} - -struct _testfunc22_s { int a[10]; }; -static struct _testfunc22_s _testfunc22(struct _testfunc22_s s1, - struct _testfunc22_s s2) -{ - struct _testfunc22_s result; - int i; - for (i=0; i<10; i++) - result.a[i] = s1.a[i] - s2.a[i]; - return result; -} - -static int _testfunc23(char *p) -{ - if (p) - return 1000 * p[0]; - return -42; -} - -#if 0 /* libffi doesn't properly support complexes currently */ - /* also, MSVC might not support _Complex... */ - /* if this is enabled one day, remember to also add _Complex - * arguments in addition to return values. */ -static float _Complex _testfunc24(float a, float b) -{ - return a + I*2.0*b; -} -static double _Complex _testfunc25(double a, double b) -{ - return a + I*2.0*b; -} -#endif - -static PyObject *b__testfunc(PyObject *self, PyObject *args) -{ - /* for testing only */ - int i; - void *f; - if (!PyArg_ParseTuple(args, "i:_testfunc", &i)) - return NULL; - switch (i) { - case 0: f = &_testfunc0; break; - case 1: f = &_testfunc1; break; - case 2: f = &_testfunc2; break; - case 3: f = &_testfunc3; break; - case 4: f = &_testfunc4; break; - case 5: f = &_testfunc5; break; - case 6: f = &_testfunc6; break; - case 7: f = &_testfunc7; break; - case 8: f = stderr; break; - case 9: f = &_testfunc9; break; - case 10: f = &_testfunc10; break; - case 11: f = &_testfunc11; break; - case 12: f = &_testfunc12; break; - case 13: f = &_testfunc13; break; - case 14: f = &_testfunc14; break; - case 15: f = &_testfunc15; break; - case 16: f = &_testfunc16; break; - case 17: f = &_testfunc17; break; - case 18: f = &_testfunc18; break; - case 19: f = &_testfunc19; break; - case 20: f = &_testfunc20; break; - case 21: f = &_testfunc21; break; - case 22: f = &_testfunc22; break; - case 23: f = &_testfunc23; break; -#if 0 - case 24: f = &_testfunc24; break; - case 25: f = &_testfunc25; break; -#endif - default: - PyErr_SetNone(PyExc_ValueError); - return NULL; - } - return PyLong_FromVoidPtr(f); -} - -#if PY_MAJOR_VERSION < 3 -static Py_ssize_t _test_segcountproc(PyObject *o, Py_ssize_t *ignored) -{ - return 1; -} -static Py_ssize_t _test_getreadbuf(PyObject *o, Py_ssize_t i, void **r) -{ - static char buf[] = "RDB"; - *r = buf; - return 3; -} -static Py_ssize_t _test_getwritebuf(PyObject *o, Py_ssize_t i, void **r) -{ - static char buf[] = "WRB"; - *r = buf; - return 3; -} -static Py_ssize_t _test_getcharbuf(PyObject *o, Py_ssize_t i, char **r) -{ - static char buf[] = "CHB"; - *r = buf; - return 3; -} -#endif -static int _test_getbuf(PyObject *self, Py_buffer *view, int flags) -{ - static char buf[] = "GTB"; - return PyBuffer_FillInfo(view, self, buf, 3, /*readonly=*/0, flags); -} -static int _test_getbuf_ro(PyObject *self, Py_buffer *view, int flags) -{ - static char buf[] = "ROB"; - return PyBuffer_FillInfo(view, self, buf, 3, /*readonly=*/1, flags); -} - - -static PyObject *b__testbuff(PyObject *self, PyObject *args) -{ - /* for testing only */ - int methods; - PyTypeObject *obj; - if (!PyArg_ParseTuple(args, "O!i|_testbuff", &PyType_Type, &obj, &methods)) - return NULL; - - assert(obj->tp_as_buffer != NULL); - -#if PY_MAJOR_VERSION < 3 - obj->tp_as_buffer->bf_getsegcount = &_test_segcountproc; - obj->tp_flags |= Py_TPFLAGS_HAVE_GETCHARBUFFER; - obj->tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER; - if (methods & 1) obj->tp_as_buffer->bf_getreadbuffer = &_test_getreadbuf; - if (methods & 2) obj->tp_as_buffer->bf_getwritebuffer = &_test_getwritebuf; - if (methods & 4) obj->tp_as_buffer->bf_getcharbuffer = &_test_getcharbuf; -#endif - if (methods & 8) obj->tp_as_buffer->bf_getbuffer = &_test_getbuf; - if (methods & 16) obj->tp_as_buffer->bf_getbuffer = &_test_getbuf_ro; - - Py_INCREF(Py_None); - return Py_None; -} - -static PyObject *b_init_cffi_1_0_external_module(PyObject *, PyObject *); -/* forward, see cffi1_module.c */ - - -static PyMethodDef FFIBackendMethods[] = { - {"load_library", b_load_library, METH_VARARGS}, - {"new_primitive_type", b_new_primitive_type, METH_VARARGS}, - {"new_pointer_type", b_new_pointer_type, METH_VARARGS}, - {"new_array_type", b_new_array_type, METH_VARARGS}, - {"new_void_type", b_new_void_type, METH_NOARGS}, - {"new_struct_type", b_new_struct_type, METH_VARARGS}, - {"new_union_type", b_new_union_type, METH_VARARGS}, - {"complete_struct_or_union", b_complete_struct_or_union, METH_VARARGS}, - {"new_function_type", b_new_function_type, METH_VARARGS}, - {"new_enum_type", b_new_enum_type, METH_VARARGS}, - {"newp", b_newp, METH_VARARGS}, - {"cast", b_cast, METH_VARARGS}, - {"callback", b_callback, METH_VARARGS}, - {"alignof", b_alignof, METH_O}, - {"sizeof", b_sizeof, METH_O}, - {"typeof", b_typeof, METH_O}, - {"typeoffsetof", b_typeoffsetof, METH_VARARGS}, - {"rawaddressof", b_rawaddressof, METH_VARARGS}, - {"getcname", b_getcname, METH_VARARGS}, - {"string", (PyCFunction)b_string, METH_VARARGS | METH_KEYWORDS}, - {"unpack", (PyCFunction)b_unpack, METH_VARARGS | METH_KEYWORDS}, - {"get_errno", b_get_errno, METH_NOARGS}, - {"set_errno", b_set_errno, METH_O}, - {"newp_handle", b_newp_handle, METH_VARARGS}, - {"from_handle", b_from_handle, METH_O}, - {"from_buffer", b_from_buffer, METH_VARARGS}, - {"memmove", (PyCFunction)b_memmove, METH_VARARGS | METH_KEYWORDS}, - {"gcp", (PyCFunction)b_gcp, METH_VARARGS | METH_KEYWORDS}, - {"release", b_release, METH_O}, -#ifdef MS_WIN32 - {"getwinerror", (PyCFunction)b_getwinerror, METH_VARARGS | METH_KEYWORDS}, -#endif - {"_get_types", b__get_types, METH_NOARGS}, - {"_get_common_types", b__get_common_types, METH_O}, - {"_testfunc", b__testfunc, METH_VARARGS}, - {"_testbuff", b__testbuff, METH_VARARGS}, - {"_init_cffi_1_0_external_module", b_init_cffi_1_0_external_module, METH_O}, - {NULL, NULL} /* Sentinel */ -}; - -/************************************************************/ -/* Functions used by '_cffi_N.so', the generated modules */ - -#define _cffi_to_c_SIGNED_FN(RETURNTYPE, SIZE) \ -static RETURNTYPE _cffi_to_c_i##SIZE(PyObject *obj) { \ - PY_LONG_LONG tmp = _my_PyLong_AsLongLong(obj); \ - if ((tmp > (PY_LONG_LONG)((1ULL<<(SIZE-1)) - 1)) || \ - (tmp < (PY_LONG_LONG)(0ULL-(1ULL<<(SIZE-1))))) \ - if (!PyErr_Occurred()) \ - return (RETURNTYPE)_convert_overflow(obj, #SIZE "-bit int"); \ - return (RETURNTYPE)tmp; \ -} - -#define _cffi_to_c_UNSIGNED_FN(RETURNTYPE, SIZE) \ -static RETURNTYPE _cffi_to_c_u##SIZE(PyObject *obj) { \ - unsigned PY_LONG_LONG tmp = _my_PyLong_AsUnsignedLongLong(obj, 1); \ - if (tmp > ~(((unsigned PY_LONG_LONG)-2) << (SIZE-1))) \ - if (!PyErr_Occurred()) \ - return (RETURNTYPE)_convert_overflow(obj, \ - #SIZE "-bit unsigned int"); \ - return (RETURNTYPE)tmp; \ -} - -_cffi_to_c_SIGNED_FN(int, 8) -_cffi_to_c_SIGNED_FN(int, 16) -_cffi_to_c_SIGNED_FN(int, 32) -_cffi_to_c_SIGNED_FN(PY_LONG_LONG, 64) -_cffi_to_c_UNSIGNED_FN(int, 8) -_cffi_to_c_UNSIGNED_FN(int, 16) -_cffi_to_c_UNSIGNED_FN(unsigned int, 32) -_cffi_to_c_UNSIGNED_FN(unsigned PY_LONG_LONG, 64) - -static PyObject *_cffi_from_c_pointer(char *ptr, CTypeDescrObject *ct) -{ - return convert_to_object((char *)&ptr, ct); -} - -static char *_cffi_to_c_pointer(PyObject *obj, CTypeDescrObject *ct) -{ - char *result; - if (convert_from_object((char *)&result, ct, obj) < 0) { - if ((ct->ct_flags & CT_POINTER) && - (ct->ct_itemdescr->ct_flags & CT_IS_FILE) && - PyFile_Check(obj)) { - PyErr_Clear(); - return (char *)PyFile_AsFile(obj); - } - return NULL; - } - return result; -} - -static long double _cffi_to_c_long_double(PyObject *obj) -{ - if (CData_Check(obj) && - (((CDataObject *)obj)->c_type->ct_flags & CT_IS_LONGDOUBLE)) { - char *data = ((CDataObject *)obj)->c_data; - /*READ(data, sizeof(long double))*/ - return read_raw_longdouble_data(data); - } - else - return PyFloat_AsDouble(obj); -} - -static _Bool _cffi_to_c__Bool(PyObject *obj) -{ - PY_LONG_LONG tmp = _my_PyLong_AsLongLong(obj); - if (tmp == 0) - return 0; - else if (tmp == 1) - return 1; - else if (PyErr_Occurred()) - return (_Bool)-1; - else - return (_Bool)_convert_overflow(obj, "_Bool"); -} - -static PyObject *_cffi_get_struct_layout(Py_ssize_t nums[]) -{ - PyObject *result; - int count = 0; - while (nums[count] >= 0) - count++; - - result = PyList_New(count); - if (result == NULL) - return NULL; - - while (--count >= 0) { - PyObject *o = PyInt_FromSsize_t(nums[count]); - if (o == NULL) { - Py_DECREF(result); - return NULL; - } - PyList_SET_ITEM(result, count, o); - } - return result; -} - -static PyObject *_cffi_from_c_char(char x) { - return PyBytes_FromStringAndSize(&x, 1); -} - -/* backward-compatibility hack: instead of _cffi_to_c_char16_t() and - * _cffi_to_c_char32_t(), we have _cffi_to_c_wchar_t() handling whatever - * size is wchar_t, and _cffi_to_c_wchar3216_t() handling the opposite. - */ -#ifdef HAVE_WCHAR_H -typedef wchar_t cffi_wchar_t; -#else -typedef uint16_t cffi_wchar_t; /* random pick... */ -#endif - -static cffi_wchar_t _cffi_to_c_wchar_t(PyObject *init) -{ - if (sizeof(cffi_wchar_t) == 2) - return (cffi_wchar_t)_convert_to_char16_t(init); - else - return (cffi_wchar_t)_convert_to_char32_t(init); -} -static PyObject *_cffi_from_c_wchar_t(cffi_wchar_t x) { - if (sizeof(cffi_wchar_t) == 2) { - cffi_char16_t input = x; - return _my_PyUnicode_FromChar16(&input, 1); - } - else { - cffi_char32_t input = x; - return _my_PyUnicode_FromChar32(&input, 1); - } -} -static int _cffi_to_c_wchar3216_t(PyObject *init) -{ - if (sizeof(cffi_wchar_t) == 4) - return (int)_convert_to_char16_t(init); - else - return (int)_convert_to_char32_t(init); -} -static PyObject *_cffi_from_c_wchar3216_t(int x) { - if (sizeof(cffi_wchar_t) == 4) { - cffi_char16_t input = x; - return _my_PyUnicode_FromChar16(&input, 1); - } - else { - cffi_char32_t input = x; - return _my_PyUnicode_FromChar32(&input, 1); - } -} - -struct _cffi_externpy_s; /* forward declaration */ -static void cffi_call_python(struct _cffi_externpy_s *, char *args); - -static void *cffi_exports[] = { - NULL, - _cffi_to_c_i8, - _cffi_to_c_u8, - _cffi_to_c_i16, - _cffi_to_c_u16, - _cffi_to_c_i32, - _cffi_to_c_u32, - _cffi_to_c_i64, - _cffi_to_c_u64, - _convert_to_char, - _cffi_from_c_pointer, - _cffi_to_c_pointer, - _cffi_get_struct_layout, - restore_errno, - save_errno, - _cffi_from_c_char, - convert_to_object, - convert_from_object, - convert_struct_to_owning_object, - _cffi_to_c_wchar_t, - _cffi_from_c_wchar_t, - _cffi_to_c_long_double, - _cffi_to_c__Bool, - _prepare_pointer_call_argument, - convert_array_from_object, - cffi_call_python, - _cffi_to_c_wchar3216_t, - _cffi_from_c_wchar3216_t, -}; - -static struct { const char *name; int value; } all_dlopen_flags[] = { - { "RTLD_LAZY", RTLD_LAZY }, - { "RTLD_NOW", RTLD_NOW }, - { "RTLD_GLOBAL", RTLD_GLOBAL }, -#ifdef RTLD_LOCAL - { "RTLD_LOCAL", RTLD_LOCAL }, -#else - { "RTLD_LOCAL", 0 }, -#endif -#ifdef RTLD_NODELETE - { "RTLD_NODELETE", RTLD_NODELETE }, -#endif -#ifdef RTLD_NOLOAD - { "RTLD_NOLOAD", RTLD_NOLOAD }, -#endif -#ifdef RTLD_DEEPBIND - { "RTLD_DEEPBIND", RTLD_DEEPBIND }, -#endif - { NULL, 0 } -}; - - -/************************************************************/ - -#include "cffi1_module.c" - -/************************************************************/ - -#if PY_MAJOR_VERSION >= 3 -static struct PyModuleDef FFIBackendModuleDef = { - PyModuleDef_HEAD_INIT, - "_cffi_backend", - NULL, - -1, - FFIBackendMethods, - NULL, NULL, NULL, NULL -}; -#define INITERROR return NULL - -PyMODINIT_FUNC -PyInit__cffi_backend(void) -#else -#define INITERROR return - -PyMODINIT_FUNC -init_cffi_backend(void) -#endif -{ - 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) || - strncmp(PyText_AS_UTF8(v), PY_VERSION, 3) != 0) { - PyErr_Format(PyExc_ImportError, - "this module was compiled for Python %c%c%c", - PY_VERSION[0], PY_VERSION[1], PY_VERSION[2]); - INITERROR; - } - -#if PY_MAJOR_VERSION >= 3 - m = PyModule_Create(&FFIBackendModuleDef); -#else - m = Py_InitModule("_cffi_backend", FFIBackendMethods); -#endif - - if (m == NULL) - INITERROR; - - if (unique_cache == NULL) { - unique_cache = PyDict_New(); - if (unique_cache == NULL) - 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"); - if (v == NULL || PyDict_SetItemString(CData_Type.tp_dict, - "__module__", v) < 0) - INITERROR; - v = PyText_FromString("<cdata>"); - if (v == NULL || PyDict_SetItemString(CData_Type.tp_dict, - "__name__", v) < 0) - INITERROR; - init_done = 1; - } - - /* this is for backward compatibility only */ - v = PyCapsule_New((void *)cffi_exports, "cffi", NULL); - if (v == NULL || PyModule_AddObject(m, "_C_API", v) < 0) - INITERROR; - - v = PyText_FromString(CFFI_VERSION); - if (v == NULL || PyModule_AddObject(m, "__version__", v) < 0) - INITERROR; - - if (PyModule_AddIntConstant(m, "FFI_DEFAULT_ABI", FFI_DEFAULT_ABI) < 0 || -#if defined(MS_WIN32) && !defined(_WIN64) - PyModule_AddIntConstant(m, "FFI_STDCALL", FFI_STDCALL) < 0 || -#endif - PyModule_AddIntConstant(m, "FFI_CDECL", FFI_DEFAULT_ABI) < 0 || - -#ifdef MS_WIN32 -# ifdef _WIN64 - PyModule_AddIntConstant(m, "_WIN", 64) < 0 || /* win64 */ -# else - PyModule_AddIntConstant(m, "_WIN", 32) < 0 || /* win32 */ -# endif -#endif - 0) - INITERROR; - - for (i = 0; all_dlopen_flags[i].name != NULL; i++) { - if (PyModule_AddIntConstant(m, - all_dlopen_flags[i].name, - all_dlopen_flags[i].value) < 0) - INITERROR; - } - - init_cffi_tls(); - if (PyErr_Occurred()) - INITERROR; - init_cffi_tls_zombie(); - if (PyErr_Occurred()) - INITERROR; - - if (init_ffi_lib(m) < 0) - INITERROR; - -#if PY_MAJOR_VERSION >= 3 - if (init_file_emulator() < 0) - INITERROR; - return m; -#endif -} diff --git a/c/_cffi_backend.so b/c/_cffi_backend.so Binary files differdeleted file mode 100755 index 31f7528..0000000 --- a/c/_cffi_backend.so +++ /dev/null diff --git a/c/_dummy_file_cffi_backend.py b/c/_dummy_file_cffi_backend.py deleted file mode 100644 index e69de29..0000000 --- a/c/_dummy_file_cffi_backend.py +++ /dev/null diff --git a/c/_dummy_file_libffi.py b/c/_dummy_file_libffi.py deleted file mode 100644 index e69de29..0000000 --- a/c/_dummy_file_libffi.py +++ /dev/null diff --git a/c/call_python.c b/c/call_python.c deleted file mode 100644 index d3d2e17..0000000 --- a/c/call_python.c +++ /dev/null @@ -1,292 +0,0 @@ -#if PY_VERSION_HEX >= 0x03080000 -# 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. - Does not return a new reference. Returns NULL in case of - error, but without setting any exception. (If called late - during shutdown, we *can't* set an exception!) - */ - static PyObject *attr_name = NULL; - PyThreadState *tstate; - PyObject *d, *interpdict; - int err; - PyInterpreterState *interp; - - tstate = PyThreadState_GET(); - if (tstate == NULL) { - /* no thread state! */ - return 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; - } - - /* from there on, we know the (sub-)interpreter is still valid */ - - if (attr_name == NULL) { - attr_name = PyText_InternFromString("__cffi_backend_extern_py"); - if (attr_name == NULL) - goto error; - } - - d = PyDict_GetItem(interpdict, attr_name); - if (d == NULL) { - d = PyDict_New(); - if (d == NULL) - goto error; - err = PyDict_SetItem(interpdict, attr_name, d); - Py_DECREF(d); /* if successful, there is one ref left in interpdict */ - if (err < 0) - goto error; - } - return d; - - error: - PyErr_Clear(); /* typically a MemoryError */ - return NULL; -} - -static PyObject *_ffi_def_extern_decorator(PyObject *outer_args, PyObject *fn) -{ - const char *s; - PyObject *error, *onerror, *infotuple, *old1; - int index, err; - const struct _cffi_global_s *g; - struct _cffi_externpy_s *externpy; - CTypeDescrObject *ct; - FFIObject *ffi; - builder_c_t *types_builder; - PyObject *name = NULL; - PyObject *interpstate_dict; - PyObject *interpstate_key; - - if (!PyArg_ParseTuple(outer_args, "OzOO", &ffi, &s, &error, &onerror)) - return NULL; - - if (s == NULL) { - name = PyObject_GetAttrString(fn, "__name__"); - if (name == NULL) - return NULL; - s = PyText_AsUTF8(name); - if (s == NULL) { - Py_DECREF(name); - return NULL; - } - } - - types_builder = &ffi->types_builder; - index = search_in_globals(&types_builder->ctx, s, strlen(s)); - if (index < 0) - goto not_found; - g = &types_builder->ctx.globals[index]; - if (_CFFI_GETOP(g->type_op) != _CFFI_OP_EXTERN_PYTHON) - goto not_found; - Py_XDECREF(name); - - ct = realize_c_type(types_builder, types_builder->ctx.types, - _CFFI_GETARG(g->type_op)); - if (ct == NULL) - return NULL; - - infotuple = prepare_callback_info_tuple(ct, fn, error, onerror, 0); - Py_DECREF(ct); - if (infotuple == NULL) - return NULL; - - /* don't directly attach infotuple to externpy: in the presence of - subinterpreters, each time we switch to a different - subinterpreter and call the C function, it will notice the - change and look up infotuple from the interpstate_dict. - */ - interpstate_dict = _get_interpstate_dict(); - if (interpstate_dict == NULL) { - Py_DECREF(infotuple); - return PyErr_NoMemory(); - } - - externpy = (struct _cffi_externpy_s *)g->address; - interpstate_key = PyLong_FromVoidPtr((void *)externpy); - if (interpstate_key == NULL) { - Py_DECREF(infotuple); - return NULL; - } - - err = PyDict_SetItem(interpstate_dict, interpstate_key, infotuple); - Py_DECREF(interpstate_key); - Py_DECREF(infotuple); /* interpstate_dict owns the last ref */ - if (err < 0) - return NULL; - - /* force _update_cache_to_call_python() to be called the next time - the C function invokes cffi_call_python, to update the cache */ - old1 = externpy->reserved1; - externpy->reserved1 = Py_None; /* a non-NULL value */ - Py_INCREF(Py_None); - Py_XDECREF(old1); - - /* return the function object unmodified */ - Py_INCREF(fn); - return fn; - - not_found: - PyErr_Format(FFIError, "ffi.def_extern('%s'): no 'extern \"Python\"' " - "function with this name", s); - Py_XDECREF(name); - return NULL; -} - - -static int _update_cache_to_call_python(struct _cffi_externpy_s *externpy) -{ - PyObject *interpstate_dict, *interpstate_key, *infotuple, *old1, *new1; - PyObject *old2; - - interpstate_dict = _get_interpstate_dict(); - if (interpstate_dict == NULL) - return 4; /* oops, shutdown issue? */ - - interpstate_key = PyLong_FromVoidPtr((void *)externpy); - if (interpstate_key == NULL) - goto error; - - infotuple = PyDict_GetItem(interpstate_dict, interpstate_key); - Py_DECREF(interpstate_key); - if (infotuple == NULL) - return 3; /* no ffi.def_extern() from this subinterpreter */ - - new1 = _current_interp_key(); - Py_INCREF(new1); - Py_INCREF(infotuple); - old1 = (PyObject *)externpy->reserved1; - old2 = (PyObject *)externpy->reserved2; - externpy->reserved1 = new1; /* holds a reference */ - externpy->reserved2 = infotuple; /* holds a reference (issue #246) */ - Py_XDECREF(old1); - Py_XDECREF(old2); - - return 0; /* no error */ - - error: - PyErr_Clear(); - return 2; /* out of memory? */ -} - -#if (defined(WITH_THREAD) && !defined(_MSC_VER) && \ - !defined(__amd64__) && !defined(__x86_64__) && \ - !defined(__i386__) && !defined(__i386)) -# if defined(HAVE_SYNC_SYNCHRONIZE) -# define read_barrier() __sync_synchronize() -# elif defined(_AIX) -# define read_barrier() __lwsync() -# elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) -# include <mbarrier.h> -# define read_barrier() __compiler_barrier() -# elif defined(__hpux) -# define read_barrier() _Asm_mf() -# else -# define read_barrier() /* missing */ -# warning "no definition for read_barrier(), missing synchronization for\ - multi-thread initialization in embedded mode" -# endif -#else -# define read_barrier() (void)0 -#endif - -static void cffi_call_python(struct _cffi_externpy_s *externpy, char *args) -{ - /* Invoked by the helpers generated from extern "Python" in the cdef. - - 'externpy' is a static structure that describes which of the - extern "Python" functions is called. It has got fields 'name' and - 'type_index' describing the function, and more reserved fields - that are initially zero. These reserved fields are set up by - ffi.def_extern(), which invokes _ffi_def_extern_decorator() above. - - 'args' is a pointer to an array of 8-byte entries. Each entry - contains an argument. If an argument is less than 8 bytes, only - the part at the beginning of the entry is initialized. If an - argument is 'long double' or a struct/union, then it is passed - by reference. - - 'args' is also used as the place to write the result to - (directly, even if more than 8 bytes). In all cases, 'args' is - at least 8 bytes in size. - */ - int err = 0; - - /* This read barrier is needed for _embedding.h. It is paired - with the write_barrier() there. Without this barrier, we can - in theory see the following situation: the Python - initialization code already ran (in another thread), and the - '_cffi_call_python' function pointer directed execution here; - but any number of other data could still be seen as - uninitialized below. For example, 'externpy' would still - contain NULLs even though it was correctly set up, or - 'interpreter_lock' (the GIL inside CPython) would still be seen - as NULL, or 'autoInterpreterState' (used by - PyGILState_Ensure()) would be NULL or contain bogus fields. - */ - read_barrier(); - - save_errno(); - - /* We need the infotuple here. We could always go through - _update_cache_to_call_python(), but to avoid the extra dict - lookups, we cache in (reserved1, reserved2) the last seen pair - (interp->modules, infotuple). The first item in this tuple is - a random PyObject that identifies the subinterpreter. - */ - if (externpy->reserved1 == NULL) { - /* Not initialized! We didn't call @ffi.def_extern() on this - externpy object from any subinterpreter at all. */ - err = 1; - } - else { - PyGILState_STATE state = gil_ensure(); - 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. */ - err = _update_cache_to_call_python(externpy); - } - if (!err) { - general_invoke_callback(0, args, args, externpy->reserved2); - } - gil_release(state); - } - if (err) { - static const char *msg[] = { - "no code was attached to it yet with @ffi.def_extern()", - "got internal exception (out of memory?)", - "@ffi.def_extern() was not called in the current subinterpreter", - "got internal exception (shutdown issue?)", - }; - fprintf(stderr, "extern \"Python\": function %s() called, " - "but %s. Returning 0.\n", externpy->name, msg[err-1]); - memset(args, 0, externpy->size_of_result); - } - restore_errno(); -} diff --git a/c/cdlopen.c b/c/cdlopen.c deleted file mode 100644 index 0ed319b..0000000 --- a/c/cdlopen.c +++ /dev/null @@ -1,362 +0,0 @@ -/* ffi.dlopen() interface with dlopen()/dlsym()/dlclose() */ - -static void *cdlopen_fetch(PyObject *libname, void *libhandle, - const char *symbol) -{ - void *address; - - if (libhandle == NULL) { - PyErr_Format(FFIError, "library '%s' has been closed", - PyText_AS_UTF8(libname)); - return NULL; - } - - dlerror(); /* clear error condition */ - address = dlsym(libhandle, symbol); - if (address == NULL) { - const char *error = dlerror(); - PyErr_Format(FFIError, "symbol '%s' not found in library '%s': %s", - symbol, PyText_AS_UTF8(libname), error); - } - return address; -} - -static void cdlopen_close_ignore_errors(void *libhandle) -{ - if (libhandle != NULL) - dlclose(libhandle); -} - -static int cdlopen_close(PyObject *libname, void *libhandle) -{ - if (libhandle != NULL && dlclose(libhandle) != 0) { - const char *error = dlerror(); - PyErr_Format(FFIError, "closing library '%s': %s", - PyText_AS_UTF8(libname), error); - return -1; - } - return 0; -} - -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, &auto_close); - if (handle != NULL) - { - result = (PyObject *)lib_internal_new((FFIObject *)self, - modname, handle, auto_close); - } - Py_XDECREF(temp); - return result; -} - -static PyObject *ffi_dlclose(PyObject *self, PyObject *args) -{ - LibObject *lib; - void *libhandle; - if (!PyArg_ParseTuple(args, "O!", &Lib_Type, &lib)) - return NULL; - - libhandle = lib->l_libhandle; - if (libhandle != NULL) - { - lib->l_libhandle = NULL; - - /* Clear the dict to force further accesses to do cdlopen_fetch() - again, and fail because the library was closed. */ - PyDict_Clear(lib->l_dict); - - if (cdlopen_close(lib->l_libname, libhandle) < 0) - return NULL; - } - Py_INCREF(Py_None); - return Py_None; -} - - -static Py_ssize_t cdl_4bytes(char *src) -{ - /* read 4 bytes in little-endian order; return it as a signed integer */ - signed char *ssrc = (signed char *)src; - unsigned char *usrc = (unsigned char *)src; - return (ssrc[0] << 24) | (usrc[1] << 16) | (usrc[2] << 8) | usrc[3]; -} - -static _cffi_opcode_t cdl_opcode(char *src) -{ - return (_cffi_opcode_t)cdl_4bytes(src); -} - -typedef struct { - unsigned long long value; - int neg; -} cdl_intconst_t; - -static int _cdl_realize_global_int(struct _cffi_getconst_s *gc) -{ - /* The 'address' field of 'struct _cffi_global_s' is set to point - to this function in case ffiobj_init() sees constant integers. - This fishes around after the 'ctx->globals' array, which is - initialized to contain another array, this time of - 'cdl_intconst_t' structures. We get the nth one and it tells - us what to return. - */ - cdl_intconst_t *ic; - ic = (cdl_intconst_t *)(gc->ctx->globals + gc->ctx->num_globals); - ic += gc->gindex; - gc->value = ic->value; - return ic->neg; -} - -static int ffiobj_init(PyObject *self, PyObject *args, PyObject *kwds) -{ - FFIObject *ffi; - static char *keywords[] = {"module_name", "_version", "_types", - "_globals", "_struct_unions", "_enums", - "_typenames", "_includes", NULL}; - char *ffiname = "?", *types = NULL, *building = NULL; - Py_ssize_t version = -1; - Py_ssize_t types_len = 0; - PyObject *globals = NULL, *struct_unions = NULL, *enums = NULL; - PyObject *typenames = NULL, *includes = NULL; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "|sns#O!O!O!O!O!:FFI", keywords, - &ffiname, &version, &types, &types_len, - &PyTuple_Type, &globals, - &PyTuple_Type, &struct_unions, - &PyTuple_Type, &enums, - &PyTuple_Type, &typenames, - &PyTuple_Type, &includes)) - return -1; - - ffi = (FFIObject *)self; - if (ffi->ctx_is_nonempty) { - PyErr_SetString(PyExc_ValueError, - "cannot call FFI.__init__() more than once"); - return -1; - } - ffi->ctx_is_nonempty = 1; - - if (version == -1 && types_len == 0) - return 0; - if (version < CFFI_VERSION_MIN || version > CFFI_VERSION_MAX) { - PyErr_Format(PyExc_ImportError, - "cffi out-of-line Python module '%s' has unknown " - "version %p", ffiname, (void *)version); - return -1; - } - - if (types_len > 0) { - /* unpack a string of 4-byte entries into an array of _cffi_opcode_t */ - _cffi_opcode_t *ntypes; - Py_ssize_t i, n = types_len / 4; - - building = PyMem_Malloc(n * sizeof(_cffi_opcode_t)); - if (building == NULL) - goto error; - ntypes = (_cffi_opcode_t *)building; - - for (i = 0; i < n; i++) { - ntypes[i] = cdl_opcode(types); - types += 4; - } - ffi->types_builder.ctx.types = ntypes; - ffi->types_builder.ctx.num_types = n; - building = NULL; - } - - if (globals != NULL) { - /* unpack a tuple alternating strings and ints, each two together - describing one global_s entry with no specified address or size. - The int is only used with integer constants. */ - struct _cffi_global_s *nglobs; - cdl_intconst_t *nintconsts; - Py_ssize_t i, n = PyTuple_GET_SIZE(globals) / 2; - - i = n * (sizeof(struct _cffi_global_s) + sizeof(cdl_intconst_t)); - building = PyMem_Malloc(i); - if (building == NULL) - goto error; - memset(building, 0, i); - nglobs = (struct _cffi_global_s *)building; - nintconsts = (cdl_intconst_t *)(nglobs + n); - - for (i = 0; i < n; i++) { - char *g = PyBytes_AS_STRING(PyTuple_GET_ITEM(globals, i * 2)); - nglobs[i].type_op = cdl_opcode(g); g += 4; - nglobs[i].name = g; - if (_CFFI_GETOP(nglobs[i].type_op) == _CFFI_OP_CONSTANT_INT || - _CFFI_GETOP(nglobs[i].type_op) == _CFFI_OP_ENUM) { - PyObject *o = PyTuple_GET_ITEM(globals, i * 2 + 1); - nglobs[i].address = &_cdl_realize_global_int; -#if PY_MAJOR_VERSION < 3 - if (PyInt_Check(o)) { - nintconsts[i].neg = PyInt_AS_LONG(o) <= 0; - nintconsts[i].value = (long long)PyInt_AS_LONG(o); - } - else -#endif - { - nintconsts[i].neg = PyObject_RichCompareBool(o, Py_False, - Py_LE); - nintconsts[i].value = PyLong_AsUnsignedLongLongMask(o); - if (PyErr_Occurred()) - goto error; - } - } - } - ffi->types_builder.ctx.globals = nglobs; - ffi->types_builder.ctx.num_globals = n; - building = NULL; - } - - if (struct_unions != NULL) { - /* unpack a tuple of struct/unions, each described as a sub-tuple; - the item 0 of each sub-tuple describes the struct/union, and - the items 1..N-1 describe the fields, if any */ - struct _cffi_struct_union_s *nstructs; - struct _cffi_field_s *nfields; - Py_ssize_t i, n = PyTuple_GET_SIZE(struct_unions); - Py_ssize_t nf = 0; /* total number of fields */ - - for (i = 0; i < n; i++) { - nf += PyTuple_GET_SIZE(PyTuple_GET_ITEM(struct_unions, i)) - 1; - } - i = (n * sizeof(struct _cffi_struct_union_s) + - nf * sizeof(struct _cffi_field_s)); - building = PyMem_Malloc(i); - if (building == NULL) - goto error; - memset(building, 0, i); - nstructs = (struct _cffi_struct_union_s *)building; - nfields = (struct _cffi_field_s *)(nstructs + n); - nf = 0; - - for (i = 0; i < n; i++) { - /* 'desc' is the tuple of strings (desc_struct, desc_field_1, ..) */ - PyObject *desc = PyTuple_GET_ITEM(struct_unions, i); - Py_ssize_t j, nf1 = PyTuple_GET_SIZE(desc) - 1; - char *s = PyBytes_AS_STRING(PyTuple_GET_ITEM(desc, 0)); - /* 's' is the first string, describing the struct/union */ - nstructs[i].type_index = cdl_4bytes(s); s += 4; - nstructs[i].flags = cdl_4bytes(s); s += 4; - nstructs[i].name = s; - if (nstructs[i].flags & (_CFFI_F_OPAQUE | _CFFI_F_EXTERNAL)) { - nstructs[i].size = (size_t)-1; - nstructs[i].alignment = -1; - nstructs[i].first_field_index = -1; - nstructs[i].num_fields = 0; - assert(nf1 == 0); - } - else { - nstructs[i].size = (size_t)-2; - nstructs[i].alignment = -2; - nstructs[i].first_field_index = nf; - nstructs[i].num_fields = nf1; - } - for (j = 0; j < nf1; j++) { - char *f = PyBytes_AS_STRING(PyTuple_GET_ITEM(desc, j + 1)); - /* 'f' is one of the other strings beyond the first one, - describing one field each */ - nfields[nf].field_type_op = cdl_opcode(f); f += 4; - nfields[nf].field_offset = (size_t)-1; - if (_CFFI_GETOP(nfields[nf].field_type_op) != _CFFI_OP_NOOP) { - nfields[nf].field_size = cdl_4bytes(f); f += 4; - } - else { - nfields[nf].field_size = (size_t)-1; - } - nfields[nf].name = f; - nf++; - } - } - ffi->types_builder.ctx.struct_unions = nstructs; - ffi->types_builder.ctx.fields = nfields; - ffi->types_builder.ctx.num_struct_unions = n; - building = NULL; - } - - if (enums != NULL) { - /* unpack a tuple of strings, each of which describes one enum_s - entry */ - struct _cffi_enum_s *nenums; - Py_ssize_t i, n = PyTuple_GET_SIZE(enums); - - i = n * sizeof(struct _cffi_enum_s); - building = PyMem_Malloc(i); - if (building == NULL) - goto error; - memset(building, 0, i); - nenums = (struct _cffi_enum_s *)building; - - for (i = 0; i < n; i++) { - char *e = PyBytes_AS_STRING(PyTuple_GET_ITEM(enums, i)); - /* 'e' is a string describing the enum */ - nenums[i].type_index = cdl_4bytes(e); e += 4; - nenums[i].type_prim = cdl_4bytes(e); e += 4; - nenums[i].name = e; e += strlen(e) + 1; - nenums[i].enumerators = e; - } - ffi->types_builder.ctx.enums = nenums; - ffi->types_builder.ctx.num_enums = n; - building = NULL; - } - - if (typenames != NULL) { - /* unpack a tuple of strings, each of which describes one typename_s - entry */ - struct _cffi_typename_s *ntypenames; - Py_ssize_t i, n = PyTuple_GET_SIZE(typenames); - - i = n * sizeof(struct _cffi_typename_s); - building = PyMem_Malloc(i); - if (building == NULL) - goto error; - memset(building, 0, i); - ntypenames = (struct _cffi_typename_s *)building; - - for (i = 0; i < n; i++) { - char *t = PyBytes_AS_STRING(PyTuple_GET_ITEM(typenames, i)); - /* 't' is a string describing the typename */ - ntypenames[i].type_index = cdl_4bytes(t); t += 4; - ntypenames[i].name = t; - } - ffi->types_builder.ctx.typenames = ntypenames; - ffi->types_builder.ctx.num_typenames = n; - building = NULL; - } - - if (includes != NULL) { - PyObject *included_libs; - - included_libs = PyTuple_New(PyTuple_GET_SIZE(includes)); - if (included_libs == NULL) - return -1; - - Py_INCREF(includes); - ffi->types_builder.included_ffis = includes; - ffi->types_builder.included_libs = included_libs; - } - - /* Above, we took directly some "char *" strings out of the strings, - typically from somewhere inside tuples. Keep them alive by - incref'ing the whole input arguments. */ - Py_INCREF(args); - Py_XINCREF(kwds); - ffi->types_builder._keepalive1 = args; - ffi->types_builder._keepalive2 = kwds; - return 0; - - error: - if (building != NULL) - PyMem_Free(building); - if (!PyErr_Occurred()) - PyErr_NoMemory(); - return -1; -} diff --git a/c/cffi1_module.c b/c/cffi1_module.c deleted file mode 100644 index 06a84fe..0000000 --- a/c/cffi1_module.c +++ /dev/null @@ -1,216 +0,0 @@ - -#include "parse_c_type.c" -#include "realize_c_type.c" - -#define CFFI_VERSION_MIN 0x2601 -#define CFFI_VERSION_CHAR16CHAR32 0x2801 -#define CFFI_VERSION_MAX 0x28FF - -typedef struct FFIObject_s FFIObject; -typedef struct LibObject_s LibObject; - -static PyTypeObject FFI_Type; /* forward */ -static PyTypeObject Lib_Type; /* forward */ - -#include "ffi_obj.c" -#include "cglob.c" -#include "lib_obj.c" -#include "cdlopen.c" -#include "commontypes.c" -#include "call_python.c" - - -static int init_ffi_lib(PyObject *m) -{ - PyObject *x; - int i, res; - static char init_done = 0; - - if (!init_done) { - if (init_global_types_dict(FFI_Type.tp_dict) < 0) - return -1; - - FFIError = PyErr_NewException("ffi.error", NULL, NULL); - if (FFIError == NULL) - return -1; - if (PyDict_SetItemString(FFI_Type.tp_dict, "error", FFIError) < 0) - return -1; - if (PyDict_SetItemString(FFI_Type.tp_dict, "CType", - (PyObject *)&CTypeDescr_Type) < 0) - return -1; - if (PyDict_SetItemString(FFI_Type.tp_dict, "CData", - (PyObject *)&CData_Type) < 0) - return -1; - if (PyDict_SetItemString(FFI_Type.tp_dict, "buffer", - (PyObject *)&MiniBuffer_Type) < 0) - return -1; - - for (i = 0; all_dlopen_flags[i].name != NULL; i++) { - x = PyInt_FromLong(all_dlopen_flags[i].value); - if (x == NULL) - return -1; - res = PyDict_SetItemString(FFI_Type.tp_dict, - all_dlopen_flags[i].name, x); - Py_DECREF(x); - if (res < 0) - return -1; - } - init_done = 1; - } - return 0; -} - -static int make_included_tuples(char *module_name, - const char *const *ctx_includes, - PyObject **included_ffis, - PyObject **included_libs) -{ - Py_ssize_t num = 0; - const char *const *p_include; - - if (ctx_includes == NULL) - return 0; - - for (p_include = ctx_includes; *p_include; p_include++) { - num++; - } - *included_ffis = PyTuple_New(num); - *included_libs = PyTuple_New(num); - if (*included_ffis == NULL || *included_libs == NULL) - goto error; - - num = 0; - for (p_include = ctx_includes; *p_include; p_include++) { - PyObject *included_ffi, *included_lib; - PyObject *m = PyImport_ImportModule(*p_include); - if (m == NULL) - goto import_error; - - included_ffi = PyObject_GetAttrString(m, "ffi"); - PyTuple_SET_ITEM(*included_ffis, num, included_ffi); - - included_lib = (included_ffi == NULL) ? NULL : - PyObject_GetAttrString(m, "lib"); - PyTuple_SET_ITEM(*included_libs, num, included_lib); - - Py_DECREF(m); - if (included_lib == NULL) - goto import_error; - - if (!FFIObject_Check(included_ffi) || - !LibObject_Check(included_lib)) - goto import_error; - num++; - } - return 0; - - import_error: - PyErr_Format(PyExc_ImportError, - "while loading %.200s: failed to import ffi, lib from %.200s", - module_name, *p_include); - error: - Py_XDECREF(*included_ffis); *included_ffis = NULL; - Py_XDECREF(*included_libs); *included_libs = NULL; - return -1; -} - -static PyObject *_my_Py_InitModule(char *module_name) -{ -#if PY_MAJOR_VERSION >= 3 - struct PyModuleDef *module_def, local_module_def = { - PyModuleDef_HEAD_INIT, - module_name, - NULL, - -1, - NULL, NULL, NULL, NULL, NULL - }; - /* note: the 'module_def' is allocated dynamically and leaks, - but anyway the C extension module can never be unloaded */ - module_def = PyMem_Malloc(sizeof(struct PyModuleDef)); - if (module_def == NULL) - return PyErr_NoMemory(); - *module_def = local_module_def; - return PyModule_Create(module_def); -#else - return Py_InitModule(module_name, NULL); -#endif -} - -static PyObject *b_init_cffi_1_0_external_module(PyObject *self, PyObject *arg) -{ - PyObject *m, *modules_dict; - FFIObject *ffi; - LibObject *lib; - Py_ssize_t version, num_exports; - char *module_name, *exports, *module_name_with_lib; - void **raw; - const struct _cffi_type_context_s *ctx; - - raw = (void **)PyLong_AsVoidPtr(arg); - if (raw == NULL) - return NULL; - - module_name = (char *)raw[0]; - version = (Py_ssize_t)raw[1]; - exports = (char *)raw[2]; - ctx = (const struct _cffi_type_context_s *)raw[3]; - - if (version < CFFI_VERSION_MIN || version > CFFI_VERSION_MAX) { - if (!PyErr_Occurred()) - PyErr_Format(PyExc_ImportError, - "cffi extension module '%s' uses an unknown version tag %p. " - "This module might need a more recent version of cffi " - "than the one currently installed, which is %s", - module_name, (void *)version, CFFI_VERSION); - return NULL; - } - - /* initialize the exports array */ - num_exports = 25; - if (ctx->flags & 1) /* set to mean that 'extern "Python"' is used */ - num_exports = 26; - if (version >= CFFI_VERSION_CHAR16CHAR32) - num_exports = 28; - memcpy(exports, (char *)cffi_exports, num_exports * sizeof(void *)); - - /* make the module object */ - m = _my_Py_InitModule(module_name); - if (m == NULL) - return NULL; - - /* build the FFI and Lib object inside this new module */ - ffi = ffi_internal_new(&FFI_Type, ctx); - Py_XINCREF(ffi); /* make the ffi object really immortal */ - if (ffi == NULL || PyModule_AddObject(m, "ffi", (PyObject *)ffi) < 0) - return NULL; - - lib = lib_internal_new(ffi, module_name, NULL, 0); - if (lib == NULL || PyModule_AddObject(m, "lib", (PyObject *)lib) < 0) - return NULL; - - if (make_included_tuples(module_name, ctx->includes, - &ffi->types_builder.included_ffis, - &lib->l_types_builder->included_libs) < 0) - return NULL; - - /* add manually 'module_name.lib' in sys.modules: - see test_import_from_lib */ - modules_dict = PySys_GetObject("modules"); - if (!modules_dict) - return NULL; - module_name_with_lib = alloca(strlen(module_name) + 5); - strcpy(module_name_with_lib, module_name); - strcat(module_name_with_lib, ".lib"); - if (PyDict_SetItemString(modules_dict, module_name_with_lib, - (PyObject *)lib) < 0) - return NULL; - -#if PY_MAJOR_VERSION >= 3 - /* add manually 'module_name' in sys.modules: it seems that - Py_InitModule() is not enough to do that */ - if (PyDict_SetItemString(modules_dict, module_name, m) < 0) - return NULL; -#endif - - return m; -} diff --git a/c/cglob.c b/c/cglob.c deleted file mode 100644 index e97767c..0000000 --- a/c/cglob.c +++ /dev/null @@ -1,113 +0,0 @@ - -typedef void *(*gs_fetch_addr_fn)(void); - -typedef struct { - PyObject_HEAD - - PyObject *gs_name; - CTypeDescrObject *gs_type; - char *gs_data; - gs_fetch_addr_fn gs_fetch_addr; - -} GlobSupportObject; - -static void glob_support_dealloc(GlobSupportObject *gs) -{ - Py_DECREF(gs->gs_name); - Py_DECREF(gs->gs_type); - PyObject_Del(gs); -} - -static PyTypeObject GlobSupport_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_cffi_backend.__FFIGlobSupport", - sizeof(GlobSupportObject), - 0, - (destructor)glob_support_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ -}; - -#define GlobSupport_Check(ob) (Py_TYPE(ob) == &GlobSupport_Type) - -static PyObject *make_global_var(PyObject *name, CTypeDescrObject *type, - char *addr, gs_fetch_addr_fn fetch_addr) -{ - GlobSupportObject *gs = PyObject_New(GlobSupportObject, &GlobSupport_Type); - if (gs == NULL) - return NULL; - - Py_INCREF(name); - Py_INCREF(type); - gs->gs_name = name; - gs->gs_type = type; - gs->gs_data = addr; - gs->gs_fetch_addr = fetch_addr; - return (PyObject *)gs; -} - -static void *fetch_global_var_addr(GlobSupportObject *gs) -{ - void *data; - if (gs->gs_data != NULL) { - data = gs->gs_data; - } - else { - Py_BEGIN_ALLOW_THREADS - restore_errno(); - data = gs->gs_fetch_addr(); - save_errno(); - Py_END_ALLOW_THREADS - } - if (data == NULL) { - PyErr_Format(FFIError, "global variable '%s' is at address NULL", - PyText_AS_UTF8(gs->gs_name)); - return NULL; - } - return data; -} - -static PyObject *read_global_var(GlobSupportObject *gs) -{ - void *data = fetch_global_var_addr(gs); - if (data == NULL) - return NULL; - return convert_to_object(data, gs->gs_type); -} - -static int write_global_var(GlobSupportObject *gs, PyObject *obj) -{ - void *data = fetch_global_var_addr(gs); - if (data == NULL) - return -1; - return convert_from_object(data, gs->gs_type, obj); -} - -static PyObject *cg_addressof_global_var(GlobSupportObject *gs) -{ - void *data; - PyObject *x, *ptrtype = new_pointer_type(gs->gs_type); - if (ptrtype == NULL) - return NULL; - - data = fetch_global_var_addr(gs); - if (data != NULL) - x = new_simple_cdata(data, (CTypeDescrObject *)ptrtype); - else - x = NULL; - Py_DECREF(ptrtype); - return x; -} diff --git a/c/commontypes.c b/c/commontypes.c deleted file mode 100644 index a41c2fd..0000000 --- a/c/commontypes.c +++ /dev/null @@ -1,216 +0,0 @@ -/* This file must be kept in alphabetical order. See test_commontypes.py */ - -#define EQ(key, value) key "\0" value /* string concatenation */ -#ifdef _WIN64 -# define W32_64(X,Y) Y -# else -# define W32_64(X,Y) X -# endif - - -static const char *common_simple_types[] = { - -#ifdef MS_WIN32 /* Windows types */ - EQ("ATOM", "WORD"), - EQ("BOOL", "int"), - EQ("BOOLEAN", "BYTE"), - EQ("BYTE", "unsigned char"), - EQ("CCHAR", "char"), - EQ("CHAR", "char"), - EQ("COLORREF", "DWORD"), - EQ("DWORD", "unsigned long"), - EQ("DWORD32", "unsigned int"), - EQ("DWORD64", "unsigned long long"), - EQ("DWORDLONG", "ULONGLONG"), - EQ("DWORD_PTR", "ULONG_PTR"), -#endif - - EQ("FILE", "struct _IO_FILE"), - -#ifdef MS_WIN32 /* more Windows types */ - EQ("FLOAT", "float"), - EQ("HACCEL", "HANDLE"), - EQ("HALF_PTR", W32_64("short","int")), - EQ("HANDLE", "PVOID"), - EQ("HBITMAP", "HANDLE"), - EQ("HBRUSH", "HANDLE"), - EQ("HCOLORSPACE", "HANDLE"), - EQ("HCONV", "HANDLE"), - EQ("HCONVLIST", "HANDLE"), - EQ("HCURSOR", "HICON"), - EQ("HDC", "HANDLE"), - EQ("HDDEDATA", "HANDLE"), - EQ("HDESK", "HANDLE"), - EQ("HDROP", "HANDLE"), - EQ("HDWP", "HANDLE"), - EQ("HENHMETAFILE", "HANDLE"), - EQ("HFILE", "int"), - EQ("HFONT", "HANDLE"), - EQ("HGDIOBJ", "HANDLE"), - EQ("HGLOBAL", "HANDLE"), - EQ("HHOOK", "HANDLE"), - EQ("HICON", "HANDLE"), - EQ("HINSTANCE", "HANDLE"), - EQ("HKEY", "HANDLE"), - EQ("HKL", "HANDLE"), - EQ("HLOCAL", "HANDLE"), - EQ("HMENU", "HANDLE"), - EQ("HMETAFILE", "HANDLE"), - EQ("HMODULE", "HINSTANCE"), - EQ("HMONITOR", "HANDLE"), - EQ("HPALETTE", "HANDLE"), - EQ("HPEN", "HANDLE"), - EQ("HRESULT", "LONG"), - EQ("HRGN", "HANDLE"), - EQ("HRSRC", "HANDLE"), - EQ("HSZ", "HANDLE"), - EQ("HWND", "HANDLE"), - EQ("INT", "int"), - EQ("INT16", "short"), - EQ("INT32", "int"), - EQ("INT64", "long long"), - EQ("INT8", "signed char"), - EQ("INT_PTR", W32_64("int","long long")), - EQ("LANGID", "WORD"), - EQ("LCID", "DWORD"), - EQ("LCTYPE", "DWORD"), - EQ("LGRPID", "DWORD"), - EQ("LONG", "long"), - EQ("LONG32", "int"), - EQ("LONG64", "long long"), - EQ("LONGLONG", "long long"), - EQ("LONG_PTR", W32_64("long","long long")), - EQ("LPARAM", "LONG_PTR"), - EQ("LPBOOL", "BOOL *"), - EQ("LPBYTE", "BYTE *"), - EQ("LPCOLORREF", "DWORD *"), - EQ("LPCSTR", "const char *"), - EQ("LPCVOID", "const void *"), - EQ("LPCWSTR", "const WCHAR *"), - EQ("LPDWORD", "DWORD *"), - EQ("LPHANDLE", "HANDLE *"), - EQ("LPINT", "int *"), - EQ("LPLONG", "long *"), - EQ("LPSTR", "CHAR *"), - EQ("LPVOID", "void *"), - EQ("LPWORD", "WORD *"), - EQ("LPWSTR", "WCHAR *"), - EQ("LRESULT", "LONG_PTR"), - EQ("PBOOL", "BOOL *"), - EQ("PBOOLEAN", "BOOLEAN *"), - EQ("PBYTE", "BYTE *"), - EQ("PCHAR", "CHAR *"), - EQ("PCSTR", "const CHAR *"), - EQ("PCWSTR", "const WCHAR *"), - EQ("PDWORD", "DWORD *"), - EQ("PDWORD32", "DWORD32 *"), - EQ("PDWORD64", "DWORD64 *"), - EQ("PDWORDLONG", "DWORDLONG *"), - EQ("PDWORD_PTR", "DWORD_PTR *"), - EQ("PFLOAT", "FLOAT *"), - EQ("PHALF_PTR", "HALF_PTR *"), - EQ("PHANDLE", "HANDLE *"), - EQ("PHKEY", "HKEY *"), - EQ("PINT", "int *"), - EQ("PINT16", "INT16 *"), - EQ("PINT32", "INT32 *"), - EQ("PINT64", "INT64 *"), - EQ("PINT8", "INT8 *"), - EQ("PINT_PTR", "INT_PTR *"), - EQ("PLCID", "PDWORD"), - EQ("PLONG", "LONG *"), - EQ("PLONG32", "LONG32 *"), - EQ("PLONG64", "LONG64 *"), - EQ("PLONGLONG", "LONGLONG *"), - EQ("PLONG_PTR", "LONG_PTR *"), - EQ("PSHORT", "SHORT *"), - EQ("PSIZE_T", "SIZE_T *"), - EQ("PSSIZE_T", "SSIZE_T *"), - EQ("PSTR", "CHAR *"), - EQ("PUCHAR", "UCHAR *"), - EQ("PUHALF_PTR", "UHALF_PTR *"), - EQ("PUINT", "UINT *"), - EQ("PUINT16", "UINT16 *"), - EQ("PUINT32", "UINT32 *"), - EQ("PUINT64", "UINT64 *"), - EQ("PUINT8", "UINT8 *"), - EQ("PUINT_PTR", "UINT_PTR *"), - EQ("PULONG", "ULONG *"), - EQ("PULONG32", "ULONG32 *"), - EQ("PULONG64", "ULONG64 *"), - EQ("PULONGLONG", "ULONGLONG *"), - EQ("PULONG_PTR", "ULONG_PTR *"), - EQ("PUSHORT", "USHORT *"), - EQ("PVOID", "void *"), - EQ("PWCHAR", "WCHAR *"), - EQ("PWORD", "WORD *"), - EQ("PWSTR", "WCHAR *"), - EQ("QWORD", "unsigned long long"), - EQ("SC_HANDLE", "HANDLE"), - EQ("SC_LOCK", "LPVOID"), - EQ("SERVICE_STATUS_HANDLE", "HANDLE"), - EQ("SHORT", "short"), - EQ("SIZE_T", "ULONG_PTR"), - EQ("SSIZE_T", "LONG_PTR"), - EQ("UCHAR", "unsigned char"), - EQ("UHALF_PTR", W32_64("unsigned short","unsigned int")), - EQ("UINT", "unsigned int"), - EQ("UINT16", "unsigned short"), - EQ("UINT32", "unsigned int"), - EQ("UINT64", "unsigned long long"), - EQ("UINT8", "unsigned char"), - EQ("UINT_PTR", W32_64("unsigned int","unsigned long long")), - EQ("ULONG", "unsigned long"), - EQ("ULONG32", "unsigned int"), - EQ("ULONG64", "unsigned long long"), - EQ("ULONGLONG", "unsigned long long"), - EQ("ULONG_PTR", W32_64("unsigned long","unsigned long long")), - EQ("USHORT", "unsigned short"), - EQ("USN", "LONGLONG"), - EQ("VOID", "void"), - EQ("WCHAR", "wchar_t"), - EQ("WINSTA", "HANDLE"), - EQ("WORD", "unsigned short"), - EQ("WPARAM", "UINT_PTR"), -#endif - - EQ("bool", "_Bool"), -}; - - -#undef EQ -#undef W32_64 - -#define num_common_simple_types \ - (sizeof(common_simple_types) / sizeof(common_simple_types[0])) - - -static const char *get_common_type(const char *search, size_t search_len) -{ - const char *entry; - int index = search_sorted(common_simple_types, sizeof(const char *), - num_common_simple_types, search, search_len); - if (index < 0) - return NULL; - - entry = common_simple_types[index]; - return entry + strlen(entry) + 1; -} - -static PyObject *b__get_common_types(PyObject *self, PyObject *arg) -{ - int err; - size_t i; - for (i = 0; i < num_common_simple_types; i++) { - const char *s = common_simple_types[i]; - PyObject *o = PyText_FromString(s + strlen(s) + 1); - if (o == NULL) - return NULL; - err = PyDict_SetItemString(arg, s, o); - Py_DECREF(o); - if (err < 0) - return NULL; - } - Py_INCREF(Py_None); - return Py_None; -} diff --git a/c/ffi_obj.c b/c/ffi_obj.c deleted file mode 100644 index f154146..0000000 --- a/c/ffi_obj.c +++ /dev/null @@ -1,1221 +0,0 @@ - -/* An FFI object has methods like ffi.new(). It is also a container - for the type declarations (typedefs and structs) that you can use, - say in ffi.new(). - - CTypeDescrObjects are internally stored in the dict 'types_dict'. - The types_dict is lazily filled with CTypeDescrObjects made from - reading a _cffi_type_context_s structure. - - In "modern" mode, the FFI instance is made by the C extension - module originally created by recompile(). The _cffi_type_context_s - structure comes from global data in the C extension module. - - In "compatibility" mode, an FFI instance is created explicitly by - the user, and its _cffi_type_context_s is initially empty. You - need to call ffi.cdef() to add more information to it. -*/ - -#define FFI_COMPLEXITY_OUTPUT 1200 /* xxx should grow as needed */ - -#define FFIObject_Check(op) PyObject_TypeCheck(op, &FFI_Type) -#define LibObject_Check(ob) ((Py_TYPE(ob) == &Lib_Type)) - -struct FFIObject_s { - PyObject_HEAD - PyObject *gc_wrefs, *gc_wrefs_freelist; - PyObject *init_once_cache; - struct _cffi_parse_info_s info; - char ctx_is_static, ctx_is_nonempty; - builder_c_t types_builder; -}; - -static FFIObject *ffi_internal_new(PyTypeObject *ffitype, - const struct _cffi_type_context_s *static_ctx) -{ - static _cffi_opcode_t internal_output[FFI_COMPLEXITY_OUTPUT]; - - FFIObject *ffi; - if (static_ctx != NULL) { - ffi = (FFIObject *)PyObject_GC_New(FFIObject, ffitype); - /* we don't call PyObject_GC_Track() here: from _cffi_init_module() - it is not needed, because in this case the ffi object is immortal */ - } - else { - ffi = (FFIObject *)ffitype->tp_alloc(ffitype, 0); - } - if (ffi == NULL) - return NULL; - - if (init_builder_c(&ffi->types_builder, static_ctx) < 0) { - Py_DECREF(ffi); - return NULL; - } - ffi->gc_wrefs = NULL; - ffi->gc_wrefs_freelist = NULL; - ffi->init_once_cache = NULL; - ffi->info.ctx = &ffi->types_builder.ctx; - ffi->info.output = internal_output; - ffi->info.output_size = FFI_COMPLEXITY_OUTPUT; - ffi->ctx_is_static = (static_ctx != NULL); - ffi->ctx_is_nonempty = (static_ctx != NULL); - return ffi; -} - -static void ffi_dealloc(FFIObject *ffi) -{ - PyObject_GC_UnTrack(ffi); - Py_XDECREF(ffi->gc_wrefs); - Py_XDECREF(ffi->gc_wrefs_freelist); - Py_XDECREF(ffi->init_once_cache); - - free_builder_c(&ffi->types_builder, ffi->ctx_is_static); - - Py_TYPE(ffi)->tp_free((PyObject *)ffi); -} - -static int ffi_traverse(FFIObject *ffi, visitproc visit, void *arg) -{ - Py_VISIT(ffi->types_builder.types_dict); - Py_VISIT(ffi->types_builder.included_ffis); - Py_VISIT(ffi->types_builder.included_libs); - Py_VISIT(ffi->gc_wrefs); - return 0; -} - -static PyObject *ffiobj_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - /* user-facing initialization code, for explicit FFI() calls */ - return (PyObject *)ffi_internal_new(type, NULL); -} - -/* forward, declared in cdlopen.c because it's mostly useful for this case */ -static int ffiobj_init(PyObject *self, PyObject *args, PyObject *kwds); - -static PyObject *ffi_fetch_int_constant(FFIObject *ffi, const char *name, - int recursion) -{ - int index; - - index = search_in_globals(&ffi->types_builder.ctx, name, strlen(name)); - if (index >= 0) { - const struct _cffi_global_s *g; - g = &ffi->types_builder.ctx.globals[index]; - - switch (_CFFI_GETOP(g->type_op)) { - case _CFFI_OP_CONSTANT_INT: - case _CFFI_OP_ENUM: - return realize_global_int(&ffi->types_builder, index); - - default: - PyErr_Format(FFIError, - "function, global variable or non-integer constant " - "'%.200s' must be fetched from its original 'lib' " - "object", name); - return NULL; - } - } - - if (ffi->types_builder.included_ffis != NULL) { - Py_ssize_t i; - PyObject *included_ffis = ffi->types_builder.included_ffis; - - if (recursion > 100) { - PyErr_SetString(PyExc_RuntimeError, - "recursion overflow in ffi.include() delegations"); - return NULL; - } - - for (i = 0; i < PyTuple_GET_SIZE(included_ffis); i++) { - FFIObject *ffi1; - PyObject *x; - - ffi1 = (FFIObject *)PyTuple_GET_ITEM(included_ffis, i); - x = ffi_fetch_int_constant(ffi1, name, recursion + 1); - if (x != NULL || PyErr_Occurred()) - return x; - } - } - return NULL; /* no exception set, means "not found" */ -} - -#define ACCEPT_STRING 1 -#define ACCEPT_CTYPE 2 -#define ACCEPT_CDATA 4 -#define ACCEPT_ALL (ACCEPT_STRING | ACCEPT_CTYPE | ACCEPT_CDATA) -#define CONSIDER_FN_AS_FNPTR 8 - -static CTypeDescrObject *_ffi_bad_type(FFIObject *ffi, const char *input_text) -{ - size_t length = strlen(input_text); - char *extra; - - if (length > 500) { - extra = ""; - } - else { - char *p; - size_t i, num_spaces = ffi->info.error_location; - extra = alloca(length + num_spaces + 4); - p = extra; - *p++ = '\n'; - for (i = 0; i < length; i++) { - if (' ' <= input_text[i] && input_text[i] < 0x7f) - *p++ = input_text[i]; - else if (input_text[i] == '\t' || input_text[i] == '\n') - *p++ = ' '; - else - *p++ = '?'; - } - *p++ = '\n'; - memset(p, ' ', num_spaces); - p += num_spaces; - *p++ = '^'; - *p++ = 0; - } - PyErr_Format(FFIError, "%s%s", ffi->info.error_message, extra); - return NULL; -} - -static CTypeDescrObject *_ffi_type(FFIObject *ffi, PyObject *arg, - int accept) -{ - /* Returns the CTypeDescrObject from the user-supplied 'arg'. - Does not return a new reference! - */ - if ((accept & ACCEPT_STRING) && PyText_Check(arg)) { - PyObject *types_dict = ffi->types_builder.types_dict; - PyObject *x = PyDict_GetItem(types_dict, arg); - - if (x == NULL) { - const char *input_text = PyText_AS_UTF8(arg); - int err, index = parse_c_type(&ffi->info, input_text); - if (index < 0) - return _ffi_bad_type(ffi, input_text); - - x = realize_c_type_or_func(&ffi->types_builder, - ffi->info.output, index); - if (x == NULL) - return NULL; - - /* Cache under the name given by 'arg', in addition to the - fact that the same ct is probably already cached under - its standardized name. In a few cases, it is not, e.g. - if it is a primitive; for the purpose of this function, - the important point is the following line, which makes - sure that in any case the next _ffi_type() with the same - 'arg' will succeed early, in PyDict_GetItem() above. - */ - err = PyDict_SetItem(types_dict, arg, x); - Py_DECREF(x); /* we know it was written in types_dict (unless out - of mem), so there is at least that ref left */ - if (err < 0) - return NULL; - } - - if (CTypeDescr_Check(x)) - return (CTypeDescrObject *)x; - else if (accept & CONSIDER_FN_AS_FNPTR) - return unwrap_fn_as_fnptr(x); - else - return unexpected_fn_type(x); - } - else if ((accept & ACCEPT_CTYPE) && CTypeDescr_Check(arg)) { - return (CTypeDescrObject *)arg; - } - else if ((accept & ACCEPT_CDATA) && CData_Check(arg)) { - return ((CDataObject *)arg)->c_type; - } -#if PY_MAJOR_VERSION < 3 - else if (PyUnicode_Check(arg)) { - CTypeDescrObject *result; - arg = PyUnicode_AsASCIIString(arg); - if (arg == NULL) - return NULL; - result = _ffi_type(ffi, arg, accept); - Py_DECREF(arg); - return result; - } -#endif - else { - const char *m1 = (accept & ACCEPT_STRING) ? "string" : ""; - const char *m2 = (accept & ACCEPT_CTYPE) ? "ctype object" : ""; - const char *m3 = (accept & ACCEPT_CDATA) ? "cdata object" : ""; - const char *s12 = (*m1 && (*m2 || *m3)) ? " or " : ""; - const char *s23 = (*m2 && *m3) ? " or " : ""; - PyErr_Format(PyExc_TypeError, "expected a %s%s%s%s%s, got '%.200s'", - m1, s12, m2, s23, m3, - Py_TYPE(arg)->tp_name); - return NULL; - } -} - -PyDoc_STRVAR(ffi_sizeof_doc, -"Return the size in bytes of the argument.\n" -"It can be a string naming a C type, or a 'cdata' instance."); - -static PyObject *ffi_sizeof(FFIObject *self, PyObject *arg) -{ - Py_ssize_t size; - - if (CData_Check(arg)) { - size = direct_sizeof_cdata((CDataObject *)arg); - } - else { - CTypeDescrObject *ct = _ffi_type(self, arg, ACCEPT_ALL); - if (ct == NULL) - return NULL; - size = ct->ct_size; - if (size < 0) { - PyErr_Format(FFIError, "don't know the size of ctype '%s'", - ct->ct_name); - return NULL; - } - } - return PyInt_FromSsize_t(size); -} - -PyDoc_STRVAR(ffi_alignof_doc, -"Return the natural alignment size in bytes of the argument.\n" -"It can be a string naming a C type, or a 'cdata' instance."); - -static PyObject *ffi_alignof(FFIObject *self, PyObject *arg) -{ - int align; - CTypeDescrObject *ct = _ffi_type(self, arg, ACCEPT_ALL); - if (ct == NULL) - return NULL; - - align = get_alignment(ct); - if (align < 0) - return NULL; - return PyInt_FromLong(align); -} - -PyDoc_STRVAR(ffi_typeof_doc, -"Parse the C type given as a string and return the\n" -"corresponding <ctype> object.\n" -"It can also be used on 'cdata' instance to get its C type."); - -static PyObject *_cpyextfunc_type_index(PyObject *x); /* forward */ - -static PyObject *ffi_typeof(FFIObject *self, PyObject *arg) -{ - PyObject *x = (PyObject *)_ffi_type(self, arg, ACCEPT_STRING|ACCEPT_CDATA); - if (x != NULL) { - Py_INCREF(x); - } - else { - x = _cpyextfunc_type_index(arg); - } - return x; -} - -PyDoc_STRVAR(ffi_new_doc, -"Allocate an instance according to the specified C type and return a\n" -"pointer to it. The specified C type must be either a pointer or an\n" -"array: ``new('X *')`` allocates an X and returns a pointer to it,\n" -"whereas ``new('X[n]')`` allocates an array of n X'es and returns an\n" -"array referencing it (which works mostly like a pointer, like in C).\n" -"You can also use ``new('X[]', n)`` to allocate an array of a\n" -"non-constant length n.\n" -"\n" -"The memory is initialized following the rules of declaring a global\n" -"variable in C: by default it is zero-initialized, but an explicit\n" -"initializer can be given which can be used to fill all or part of the\n" -"memory.\n" -"\n" -"When the returned <cdata> object goes out of scope, the memory is\n" -"freed. In other words the returned <cdata> object has ownership of\n" -"the value of type 'cdecl' that it points to. This means that the raw\n" -"data can be used as long as this object is kept alive, but must not be\n" -"used for a longer time. Be careful about that when copying the\n" -"pointer to the memory somewhere else, e.g. into another structure."); - -static PyObject *_ffi_new(FFIObject *self, PyObject *args, PyObject *kwds, - const cffi_allocator_t *allocator) -{ - CTypeDescrObject *ct; - PyObject *arg, *init = Py_None; - static char *keywords[] = {"cdecl", "init", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:new", keywords, - &arg, &init)) - return NULL; - - ct = _ffi_type(self, arg, ACCEPT_STRING|ACCEPT_CTYPE); - if (ct == NULL) - return NULL; - - return direct_newp(ct, init, allocator); -} - -static PyObject *ffi_new(FFIObject *self, PyObject *args, PyObject *kwds) -{ - return _ffi_new(self, args, kwds, &default_allocator); -} - -static PyObject *_ffi_new_with_allocator(PyObject *allocator, PyObject *args, - PyObject *kwds) -{ - cffi_allocator_t alloc1; - PyObject *my_alloc, *my_free; - my_alloc = PyTuple_GET_ITEM(allocator, 1); - my_free = PyTuple_GET_ITEM(allocator, 2); - alloc1.ca_alloc = (my_alloc == Py_None ? NULL : my_alloc); - alloc1.ca_free = (my_free == Py_None ? NULL : my_free); - alloc1.ca_dont_clear = (PyTuple_GET_ITEM(allocator, 3) == Py_False); - - return _ffi_new((FFIObject *)PyTuple_GET_ITEM(allocator, 0), - args, kwds, &alloc1); -} - -PyDoc_STRVAR(ffi_new_allocator_doc, -"Return a new allocator, i.e. a function that behaves like ffi.new()\n" -"but uses the provided low-level 'alloc' and 'free' functions.\n" -"\n" -"'alloc' is called with the size as argument. If it returns NULL, a\n" -"MemoryError is raised. 'free' is called with the result of 'alloc'\n" -"as argument. Both can be either Python functions or directly C\n" -"functions. If 'free' is None, then no free function is called.\n" -"If both 'alloc' and 'free' are None, the default is used.\n" -"\n" -"If 'should_clear_after_alloc' is set to False, then the memory\n" -"returned by 'alloc' is assumed to be already cleared (or you are\n" -"fine with garbage); otherwise CFFI will clear it."); - -static PyObject *ffi_new_allocator(FFIObject *self, PyObject *args, - PyObject *kwds) -{ - PyObject *allocator, *result; - PyObject *my_alloc = Py_None, *my_free = Py_None; - int should_clear_after_alloc = 1; - static char *keywords[] = {"alloc", "free", "should_clear_after_alloc", - NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi:new_allocator", keywords, - &my_alloc, &my_free, - &should_clear_after_alloc)) - return NULL; - - if (my_alloc == Py_None && my_free != Py_None) { - PyErr_SetString(PyExc_TypeError, "cannot pass 'free' without 'alloc'"); - return NULL; - } - - allocator = PyTuple_Pack(4, - (PyObject *)self, - my_alloc, - my_free, - PyBool_FromLong(should_clear_after_alloc)); - if (allocator == NULL) - return NULL; - - { - static PyMethodDef md = {"allocator", - (PyCFunction)_ffi_new_with_allocator, - METH_VARARGS | METH_KEYWORDS}; - result = PyCFunction_New(&md, allocator); - } - Py_DECREF(allocator); - return result; -} - -PyDoc_STRVAR(ffi_cast_doc, -"Similar to a C cast: returns an instance of the named C\n" -"type initialized with the given 'source'. The source is\n" -"casted between integers or pointers of any type."); - -static PyObject *ffi_cast(FFIObject *self, PyObject *args) -{ - CTypeDescrObject *ct; - PyObject *ob, *arg; - if (!PyArg_ParseTuple(args, "OO:cast", &arg, &ob)) - return NULL; - - ct = _ffi_type(self, arg, ACCEPT_STRING|ACCEPT_CTYPE); - if (ct == NULL) - return NULL; - - return do_cast(ct, ob); -} - -PyDoc_STRVAR(ffi_string_doc, -"Return a Python string (or unicode string) from the 'cdata'. If\n" -"'cdata' is a pointer or array of characters or bytes, returns the\n" -"null-terminated string. The returned string extends until the first\n" -"null character, or at most 'maxlen' characters. If 'cdata' is an\n" -"array then 'maxlen' defaults to its length.\n" -"\n" -"If 'cdata' is a pointer or array of wchar_t, returns a unicode string\n" -"following the same rules.\n" -"\n" -"If 'cdata' is a single character or byte or a wchar_t, returns it as a\n" -"string or unicode string.\n" -"\n" -"If 'cdata' is an enum, returns the value of the enumerator as a\n" -"string, or 'NUMBER' if the value is out of range."); - -#define ffi_string b_string /* ffi_string() => b_string() - from _cffi_backend.c */ - -PyDoc_STRVAR(ffi_unpack_doc, -"Unpack an array of C data of the given length,\n" -"returning a Python string/unicode/list.\n" -"\n" -"If 'cdata' is a pointer to 'char', returns a byte string.\n" -"It does not stop at the first null. This is equivalent to:\n" -"ffi.buffer(cdata, length)[:]\n" -"\n" -"If 'cdata' is a pointer to 'wchar_t', returns a unicode string.\n" -"'length' is measured in wchar_t's; it is not the size in bytes.\n" -"\n" -"If 'cdata' is a pointer to anything else, returns a list of\n" -"'length' items. This is a faster equivalent to:\n" -"[cdata[i] for i in range(length)]"); - -#define ffi_unpack b_unpack /* ffi_unpack() => b_unpack() - from _cffi_backend.c */ - - -PyDoc_STRVAR(ffi_offsetof_doc, -"Return the offset of the named field inside the given structure or\n" -"array, which must be given as a C type name. You can give several\n" -"field names in case of nested structures. You can also give numeric\n" -"values which correspond to array items, in case of an array type."); - -static PyObject *ffi_offsetof(FFIObject *self, PyObject *args) -{ - PyObject *arg; - CTypeDescrObject *ct; - Py_ssize_t i, offset; - - if (PyTuple_Size(args) < 2) { - PyErr_SetString(PyExc_TypeError, - "offsetof() expects at least 2 arguments"); - return NULL; - } - - arg = PyTuple_GET_ITEM(args, 0); - ct = _ffi_type(self, arg, ACCEPT_STRING|ACCEPT_CTYPE); - if (ct == NULL) - return NULL; - - offset = 0; - for (i = 1; i < PyTuple_GET_SIZE(args); i++) { - Py_ssize_t ofs1; - ct = direct_typeoffsetof(ct, PyTuple_GET_ITEM(args, i), i > 1, &ofs1); - if (ct == NULL) - return NULL; - offset += ofs1; - } - return PyInt_FromSsize_t(offset); -} - -PyDoc_STRVAR(ffi_addressof_doc, -"Limited equivalent to the '&' operator in C:\n" -"\n" -"1. ffi.addressof(<cdata 'struct-or-union'>) returns a cdata that is a\n" -"pointer to this struct or union.\n" -"\n" -"2. ffi.addressof(<cdata>, field-or-index...) returns the address of a\n" -"field or array item inside the given structure or array, recursively\n" -"in case of nested structures or arrays.\n" -"\n" -"3. ffi.addressof(<library>, \"name\") returns the address of the named\n" -"function or global variable."); - -static PyObject *address_of_global_var(PyObject *args); /* forward */ - -static PyObject *ffi_addressof(FFIObject *self, PyObject *args) -{ - PyObject *arg, *z, *result; - CTypeDescrObject *ct; - Py_ssize_t i, offset = 0; - int accepted_flags; - - if (PyTuple_Size(args) < 1) { - PyErr_SetString(PyExc_TypeError, - "addressof() expects at least 1 argument"); - return NULL; - } - - arg = PyTuple_GET_ITEM(args, 0); - if (LibObject_Check(arg)) { - /* case 3 in the docstring */ - return address_of_global_var(args); - } - - ct = _ffi_type(self, arg, ACCEPT_CDATA); - if (ct == NULL) - return NULL; - - if (PyTuple_GET_SIZE(args) == 1) { - /* case 1 in the docstring */ - accepted_flags = CT_STRUCT | CT_UNION | CT_ARRAY; - if ((ct->ct_flags & accepted_flags) == 0) { - PyErr_SetString(PyExc_TypeError, - "expected a cdata struct/union/array object"); - return NULL; - } - } - else { - /* case 2 in the docstring */ - accepted_flags = CT_STRUCT | CT_UNION | CT_ARRAY | CT_POINTER; - if ((ct->ct_flags & accepted_flags) == 0) { - PyErr_SetString(PyExc_TypeError, - "expected a cdata struct/union/array/pointer object"); - return NULL; - } - for (i = 1; i < PyTuple_GET_SIZE(args); i++) { - Py_ssize_t ofs1; - ct = direct_typeoffsetof(ct, PyTuple_GET_ITEM(args, i), - i > 1, &ofs1); - if (ct == NULL) - return NULL; - offset += ofs1; - } - } - - z = new_pointer_type(ct); - if (z == NULL) - return NULL; - - result = new_simple_cdata(((CDataObject *)arg)->c_data + offset, - (CTypeDescrObject *)z); - Py_DECREF(z); - return result; -} - -static PyObject *_combine_type_name_l(CTypeDescrObject *ct, - size_t extra_text_len) -{ - size_t base_name_len; - PyObject *result; - char *p; - - base_name_len = strlen(ct->ct_name); - result = PyBytes_FromStringAndSize(NULL, base_name_len + extra_text_len); - if (result == NULL) - return NULL; - - p = PyBytes_AS_STRING(result); - memcpy(p, ct->ct_name, ct->ct_name_position); - p += ct->ct_name_position; - p += extra_text_len; - memcpy(p, ct->ct_name + ct->ct_name_position, - base_name_len - ct->ct_name_position); - return result; -} - -PyDoc_STRVAR(ffi_getctype_doc, -"Return a string giving the C type 'cdecl', which may be itself a\n" -"string or a <ctype> object. If 'replace_with' is given, it gives\n" -"extra text to append (or insert for more complicated C types), like a\n" -"variable name, or '*' to get actually the C type 'pointer-to-cdecl'."); - -static PyObject *ffi_getctype(FFIObject *self, PyObject *args, PyObject *kwds) -{ - PyObject *c_decl, *res; - char *p, *replace_with = ""; - int add_paren, add_space; - CTypeDescrObject *ct; - size_t replace_with_len; - static char *keywords[] = {"cdecl", "replace_with", NULL}; -#if PY_MAJOR_VERSION >= 3 - PyObject *u; -#endif - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|s:getctype", keywords, - &c_decl, &replace_with)) - return NULL; - - ct = _ffi_type(self, c_decl, ACCEPT_STRING|ACCEPT_CTYPE); - if (ct == NULL) - return NULL; - - while (replace_with[0] != 0 && isspace(replace_with[0])) - replace_with++; - replace_with_len = strlen(replace_with); - while (replace_with_len > 0 && isspace(replace_with[replace_with_len - 1])) - replace_with_len--; - - add_paren = (replace_with[0] == '*' && - ((ct->ct_flags & CT_ARRAY) != 0)); - add_space = (!add_paren && replace_with_len > 0 && - replace_with[0] != '[' && replace_with[0] != '('); - - res = _combine_type_name_l(ct, replace_with_len + add_space + 2*add_paren); - if (res == NULL) - return NULL; - - p = PyBytes_AS_STRING(res) + ct->ct_name_position; - if (add_paren) - *p++ = '('; - if (add_space) - *p++ = ' '; - memcpy(p, replace_with, replace_with_len); - if (add_paren) - p[replace_with_len] = ')'; - -#if PY_MAJOR_VERSION >= 3 - /* bytes -> unicode string */ - u = PyUnicode_DecodeLatin1(PyBytes_AS_STRING(res), - PyBytes_GET_SIZE(res), - NULL); - Py_DECREF(res); - res = u; -#endif - - return res; -} - -PyDoc_STRVAR(ffi_new_handle_doc, -"Return a non-NULL cdata of type 'void *' that contains an opaque\n" -"reference to the argument, which can be any Python object. To cast it\n" -"back to the original object, use from_handle(). You must keep alive\n" -"the cdata object returned by new_handle()!"); - -static PyObject *ffi_new_handle(FFIObject *self, PyObject *arg) -{ - /* g_ct_voidp is equal to <ctype 'void *'> */ - return newp_handle(g_ct_voidp, arg); -} - -PyDoc_STRVAR(ffi_from_handle_doc, -"Cast a 'void *' back to a Python object. Must be used *only* on the\n" -"pointers returned by new_handle(), and *only* as long as the exact\n" -"cdata object returned by new_handle() is still alive (somewhere else\n" -"in the program). Failure to follow these rules will crash."); - -#define ffi_from_handle b_from_handle /* ffi_from_handle => b_from_handle - from _cffi_backend.c */ - -PyDoc_STRVAR(ffi_from_buffer_doc, -"Return a <cdata 'char[]'> that points to the data of the given Python\n" -"object, which must support the buffer interface. Note that this is\n" -"not meant to be used on the built-in types str or unicode\n" -"(you can build 'char[]' arrays explicitly) but only on objects\n" -"containing large quantities of raw data in some other format, like\n" -"'array.array' or numpy arrays."); - -static PyObject *ffi_from_buffer(FFIObject *self, PyObject *args, - PyObject *kwds) -{ - PyObject *cdecl1, *python_buf = NULL; - CTypeDescrObject *ct; - int require_writable = 0; - static char *keywords[] = {"cdecl", "python_buffer", - "require_writable", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|Oi:from_buffer", keywords, - &cdecl1, &python_buf, &require_writable)) - return NULL; - - if (python_buf == NULL) { - python_buf = cdecl1; - ct = g_ct_chararray; - } - else { - ct = _ffi_type(self, cdecl1, ACCEPT_STRING|ACCEPT_CTYPE); - if (ct == NULL) - return NULL; - } - return direct_from_buffer(ct, python_buf, require_writable); -} - -PyDoc_STRVAR(ffi_gc_doc, -"Return a new cdata object that points to the same data.\n" -"Later, when this new cdata object is garbage-collected,\n" -"'destructor(old_cdata_object)' will be called.\n" -"\n" -"The optional 'size' gives an estimate of the size, used to\n" -"trigger the garbage collection more eagerly. So far only used\n" -"on PyPy. It tells the GC that the returned object keeps alive\n" -"roughly 'size' bytes of external memory."); - -#define ffi_gc b_gcp /* ffi_gc() => b_gcp() - from _cffi_backend.c */ - -PyDoc_STRVAR(ffi_def_extern_doc, -"A decorator. Attaches the decorated Python function to the C code\n" -"generated for the 'extern \"Python\"' function of the same name.\n" -"Calling the C function will then invoke the Python function.\n" -"\n" -"Optional arguments: 'name' is the name of the C function, if\n" -"different from the Python function; and 'error' and 'onerror'\n" -"handle what occurs if the Python function raises an exception\n" -"(see the docs for details)."); - -/* forward; see call_python.c */ -static PyObject *_ffi_def_extern_decorator(PyObject *, PyObject *); - -static PyObject *ffi_def_extern(FFIObject *self, PyObject *args, - PyObject *kwds) -{ - static PyMethodDef md = {"def_extern_decorator", - (PyCFunction)_ffi_def_extern_decorator, METH_O}; - PyObject *name = Py_None, *error = Py_None; - PyObject *res, *onerror = Py_None; - static char *keywords[] = {"name", "error", "onerror", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", keywords, - &name, &error, &onerror)) - return NULL; - - args = Py_BuildValue("(OOOO)", (PyObject *)self, name, error, onerror); - if (args == NULL) - return NULL; - - res = PyCFunction_New(&md, args); - Py_DECREF(args); - return res; -} - -PyDoc_STRVAR(ffi_callback_doc, -"Return a callback object or a decorator making such a callback object.\n" -"'cdecl' must name a C function pointer type. The callback invokes the\n" -"specified 'python_callable' (which may be provided either directly or\n" -"via a decorator). Important: the callback object must be manually\n" -"kept alive for as long as the callback may be invoked from the C code."); - -static PyObject *_ffi_callback_decorator(PyObject *outer_args, PyObject *fn) -{ - PyObject *res, *old; - - old = PyTuple_GET_ITEM(outer_args, 1); - PyTuple_SET_ITEM(outer_args, 1, fn); - res = b_callback(NULL, outer_args); - PyTuple_SET_ITEM(outer_args, 1, old); - return res; -} - -static PyObject *ffi_callback(FFIObject *self, PyObject *args, PyObject *kwds) -{ - PyObject *c_decl, *python_callable = Py_None, *error = Py_None; - PyObject *res, *onerror = Py_None; - static char *keywords[] = {"cdecl", "python_callable", "error", - "onerror", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOO", keywords, - &c_decl, &python_callable, &error, - &onerror)) - return NULL; - - c_decl = (PyObject *)_ffi_type(self, c_decl, ACCEPT_STRING | ACCEPT_CTYPE | - CONSIDER_FN_AS_FNPTR); - if (c_decl == NULL) - return NULL; - - args = Py_BuildValue("(OOOO)", c_decl, python_callable, error, onerror); - if (args == NULL) - return NULL; - - if (python_callable != Py_None) { - res = b_callback(NULL, args); - } - else { - static PyMethodDef md = {"callback_decorator", - (PyCFunction)_ffi_callback_decorator, METH_O}; - res = PyCFunction_New(&md, args); - } - Py_DECREF(args); - return res; -} - -#ifdef MS_WIN32 -PyDoc_STRVAR(ffi_getwinerror_doc, -"Return either the GetLastError() or the error number given by the\n" -"optional 'code' argument, as a tuple '(code, message)'."); - -#define ffi_getwinerror b_getwinerror /* ffi_getwinerror() => b_getwinerror() - from misc_win32.h */ -#endif - -PyDoc_STRVAR(ffi_errno_doc, "the value of 'errno' from/to the C calls"); - -static PyObject *ffi_get_errno(PyObject *self, void *closure) -{ - /* xxx maybe think about how to make the saved errno local - to an ffi instance */ - return b_get_errno(NULL, NULL); -} - -static int ffi_set_errno(PyObject *self, PyObject *newval, void *closure) -{ - PyObject *x = b_set_errno(NULL, newval); - if (x == NULL) - return -1; - Py_DECREF(x); - return 0; -} - -PyDoc_STRVAR(ffi_dlopen_doc, -"Load and return a dynamic library identified by 'name'. The standard\n" -"C library can be loaded by passing None.\n" -"\n" -"Note that functions and types declared with 'ffi.cdef()' are not\n" -"linked to a particular library, just like C headers. In the library\n" -"we only look for the actual (untyped) symbols at the time of their\n" -"first access."); - -PyDoc_STRVAR(ffi_dlclose_doc, -"Close a library obtained with ffi.dlopen(). After this call, access to\n" -"functions or variables from the library will fail (possibly with a\n" -"segmentation fault)."); - -static PyObject *ffi_dlopen(PyObject *self, PyObject *args); /* forward */ -static PyObject *ffi_dlclose(PyObject *self, PyObject *args); /* forward */ - -PyDoc_STRVAR(ffi_int_const_doc, -"Get the value of an integer constant.\n" -"\n" -"'ffi.integer_const(\"xxx\")' is equivalent to 'lib.xxx' if xxx names an\n" -"integer constant. The point of this function is limited to use cases\n" -"where you have an 'ffi' object but not any associated 'lib' object."); - -static PyObject *ffi_int_const(FFIObject *self, PyObject *args, PyObject *kwds) -{ - char *name; - PyObject *x; - static char *keywords[] = {"name", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", keywords, &name)) - return NULL; - - x = ffi_fetch_int_constant(self, name, 0); - - if (x == NULL && !PyErr_Occurred()) { - PyErr_Format(PyExc_AttributeError, - "integer constant '%.200s' not found", name); - } - return x; -} - -PyDoc_STRVAR(ffi_list_types_doc, -"Returns the user type names known to this FFI instance.\n" -"This returns a tuple containing three lists of names:\n" -"(typedef_names, names_of_structs, names_of_unions)"); - -static PyObject *ffi_list_types(FFIObject *self, PyObject *noargs) -{ - Py_ssize_t i, n1 = self->types_builder.ctx.num_typenames; - Py_ssize_t n23 = self->types_builder.ctx.num_struct_unions; - PyObject *o, *lst[3] = {NULL, NULL, NULL}, *result = NULL; - - lst[0] = PyList_New(n1); - if (lst[0] == NULL) - goto error; - lst[1] = PyList_New(0); - if (lst[1] == NULL) - goto error; - lst[2] = PyList_New(0); - if (lst[2] == NULL) - goto error; - - for (i = 0; i < n1; i++) { - o = PyText_FromString(self->types_builder.ctx.typenames[i].name); - if (o == NULL) - goto error; - PyList_SET_ITEM(lst[0], i, o); - } - - for (i = 0; i < n23; i++) { - const struct _cffi_struct_union_s *s; - int err, index; - - s = &self->types_builder.ctx.struct_unions[i]; - if (s->name[0] == '$') - continue; - - o = PyText_FromString(s->name); - if (o == NULL) - goto error; - index = (s->flags & _CFFI_F_UNION) ? 2 : 1; - err = PyList_Append(lst[index], o); - Py_DECREF(o); - if (err < 0) - goto error; - } - result = PyTuple_Pack(3, lst[0], lst[1], lst[2]); - /* fall-through */ - error: - Py_XDECREF(lst[2]); - Py_XDECREF(lst[1]); - Py_XDECREF(lst[0]); - return result; -} - -PyDoc_STRVAR(ffi_memmove_doc, -"ffi.memmove(dest, src, n) copies n bytes of memory from src to dest.\n" -"\n" -"Like the C function memmove(), the memory areas may overlap;\n" -"apart from that it behaves like the C function memcpy().\n" -"\n" -"'src' can be any cdata ptr or array, or any Python buffer object.\n" -"'dest' can be any cdata ptr or array, or a writable Python buffer\n" -"object. The size to copy, 'n', is always measured in bytes.\n" -"\n" -"Unlike other methods, this one supports all Python buffer including\n" -"byte strings and bytearrays---but it still does not support\n" -"non-contiguous buffers."); - -#define ffi_memmove b_memmove /* ffi_memmove() => b_memmove() - from _cffi_backend.c */ - -PyDoc_STRVAR(ffi_init_once_doc, -"init_once(function, tag): run function() once. More precisely,\n" -"'function()' is called the first time we see a given 'tag'.\n" -"\n" -"The return value of function() is remembered and returned by the current\n" -"and all future init_once() with the same tag. If init_once() is called\n" -"from multiple threads in parallel, all calls block until the execution\n" -"of function() is done. If function() raises an exception, it is\n" -"propagated and nothing is cached."); - -#if PY_MAJOR_VERSION < 3 -/* PyCapsule_New is redefined to be PyCObject_FromVoidPtr in _cffi_backend, - which gives 2.6 compatibility; but the destructor signature is different */ -static void _free_init_once_lock(void *lock) -{ - PyThread_free_lock((PyThread_type_lock)lock); -} -#else -static void _free_init_once_lock(PyObject *capsule) -{ - PyThread_type_lock lock; - lock = PyCapsule_GetPointer(capsule, "cffi_init_once_lock"); - if (lock != NULL) - PyThread_free_lock(lock); -} -#endif - -static PyObject *ffi_init_once(FFIObject *self, PyObject *args, PyObject *kwds) -{ - static char *keywords[] = {"func", "tag", NULL}; - PyObject *cache, *func, *tag, *tup, *res, *x, *lockobj; - PyThread_type_lock lock; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", keywords, &func, &tag)) - return NULL; - - /* a lot of fun with reference counting and error checking - in this function */ - - /* atomically get or create a new dict (no GIL release) */ - cache = self->init_once_cache; - if (cache == NULL) { - cache = PyDict_New(); - if (cache == NULL) - return NULL; - self->init_once_cache = cache; - } - - /* get the tuple from cache[tag], or make a new one: (False, lock) */ - tup = PyDict_GetItem(cache, tag); - if (tup == NULL) { - lock = PyThread_allocate_lock(); - if (lock == NULL) - return NULL; - x = PyCapsule_New(lock, "cffi_init_once_lock", _free_init_once_lock); - if (x == NULL) { - PyThread_free_lock(lock); - return NULL; - } - tup = PyTuple_Pack(2, Py_False, x); - Py_DECREF(x); - if (tup == NULL) - return NULL; - x = tup; - - /* Possible corner case if 'tag' is an object overriding __eq__ - in pure Python: the GIL may be released when we are running it. - We really need to call dict.setdefault(). */ - tup = PyObject_CallMethod(cache, "setdefault", "OO", tag, x); - Py_DECREF(x); - if (tup == NULL) - return NULL; - - Py_DECREF(tup); /* there is still a ref inside the dict */ - } - - res = PyTuple_GET_ITEM(tup, 1); - Py_INCREF(res); - - if (PyTuple_GET_ITEM(tup, 0) == Py_True) { - /* tup == (True, result): return the result. */ - return res; - } - - /* tup == (False, lock) */ - lockobj = res; - lock = (PyThread_type_lock)PyCapsule_GetPointer(lockobj, - "cffi_init_once_lock"); - if (lock == NULL) { - Py_DECREF(lockobj); - return NULL; - } - - Py_BEGIN_ALLOW_THREADS - PyThread_acquire_lock(lock, WAIT_LOCK); - Py_END_ALLOW_THREADS - - x = PyDict_GetItem(cache, tag); - if (x != NULL && PyTuple_GET_ITEM(x, 0) == Py_True) { - /* the real result was put in the dict while we were waiting - for PyThread_acquire_lock() above */ - res = PyTuple_GET_ITEM(x, 1); - Py_INCREF(res); - } - else { - res = PyObject_CallFunction(func, ""); - if (res != NULL) { - tup = PyTuple_Pack(2, Py_True, res); - if (tup == NULL || PyDict_SetItem(cache, tag, tup) < 0) { - Py_DECREF(res); - res = NULL; - } - Py_XDECREF(tup); - } - } - - PyThread_release_lock(lock); - Py_DECREF(lockobj); - return res; -} - -PyDoc_STRVAR(ffi_release_doc, -"Release now the resources held by a 'cdata' object from ffi.new(),\n" -"ffi.gc() or ffi.from_buffer(). The cdata object must not be used\n" -"afterwards.\n" -"\n" -"'ffi.release(cdata)' is equivalent to 'cdata.__exit__()'.\n" -"\n" -"Note that on CPython this method has no effect (so far) on objects\n" -"returned by ffi.new(), because the memory is allocated inline with the\n" -"cdata object and cannot be freed independently. It might be fixed in\n" -"future releases of cffi."); - -#define ffi_release b_release /* ffi_release() => b_release() - from _cffi_backend.c */ - - -#define METH_VKW (METH_VARARGS | METH_KEYWORDS) -static PyMethodDef ffi_methods[] = { - {"addressof", (PyCFunction)ffi_addressof, METH_VARARGS, ffi_addressof_doc}, - {"alignof", (PyCFunction)ffi_alignof, METH_O, ffi_alignof_doc}, - {"def_extern", (PyCFunction)ffi_def_extern, METH_VKW, ffi_def_extern_doc}, - {"callback", (PyCFunction)ffi_callback, METH_VKW, ffi_callback_doc}, - {"cast", (PyCFunction)ffi_cast, METH_VARARGS, ffi_cast_doc}, - {"dlclose", (PyCFunction)ffi_dlclose, METH_VARARGS, ffi_dlclose_doc}, - {"dlopen", (PyCFunction)ffi_dlopen, METH_VARARGS, ffi_dlopen_doc}, - {"from_buffer",(PyCFunction)ffi_from_buffer,METH_VKW, ffi_from_buffer_doc}, - {"from_handle",(PyCFunction)ffi_from_handle,METH_O, ffi_from_handle_doc}, - {"gc", (PyCFunction)ffi_gc, METH_VKW, ffi_gc_doc}, - {"getctype", (PyCFunction)ffi_getctype, METH_VKW, ffi_getctype_doc}, -#ifdef MS_WIN32 - {"getwinerror",(PyCFunction)ffi_getwinerror,METH_VKW, ffi_getwinerror_doc}, -#endif - {"init_once", (PyCFunction)ffi_init_once, METH_VKW, ffi_init_once_doc}, - {"integer_const",(PyCFunction)ffi_int_const,METH_VKW, ffi_int_const_doc}, - {"list_types", (PyCFunction)ffi_list_types, METH_NOARGS, ffi_list_types_doc}, - {"memmove", (PyCFunction)ffi_memmove, METH_VKW, ffi_memmove_doc}, - {"new", (PyCFunction)ffi_new, METH_VKW, ffi_new_doc}, -{"new_allocator",(PyCFunction)ffi_new_allocator,METH_VKW,ffi_new_allocator_doc}, - {"new_handle", (PyCFunction)ffi_new_handle, METH_O, ffi_new_handle_doc}, - {"offsetof", (PyCFunction)ffi_offsetof, METH_VARARGS, ffi_offsetof_doc}, - {"release", (PyCFunction)ffi_release, METH_O, ffi_release_doc}, - {"sizeof", (PyCFunction)ffi_sizeof, METH_O, ffi_sizeof_doc}, - {"string", (PyCFunction)ffi_string, METH_VKW, ffi_string_doc}, - {"typeof", (PyCFunction)ffi_typeof, METH_O, ffi_typeof_doc}, - {"unpack", (PyCFunction)ffi_unpack, METH_VKW, ffi_unpack_doc}, - {NULL} -}; - -static PyGetSetDef ffi_getsets[] = { - {"errno", ffi_get_errno, ffi_set_errno, ffi_errno_doc}, - {NULL} -}; - -static PyTypeObject FFI_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_cffi_backend.FFI", - sizeof(FFIObject), - 0, - (destructor)ffi_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - 0, /* tp_doc */ - (traverseproc)ffi_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - ffi_methods, /* tp_methods */ - 0, /* tp_members */ - ffi_getsets, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - ffiobj_init, /* tp_init */ - 0, /* tp_alloc */ - ffiobj_new, /* tp_new */ - PyObject_GC_Del, /* tp_free */ -}; - - -static PyObject * -_fetch_external_struct_or_union(const struct _cffi_struct_union_s *s, - PyObject *included_ffis, int recursion) -{ - Py_ssize_t i; - - if (included_ffis == NULL) - return NULL; - - if (recursion > 100) { - PyErr_SetString(PyExc_RuntimeError, - "recursion overflow in ffi.include() delegations"); - return NULL; - } - - for (i = 0; i < PyTuple_GET_SIZE(included_ffis); i++) { - FFIObject *ffi1; - const struct _cffi_struct_union_s *s1; - int sindex; - PyObject *x; - - ffi1 = (FFIObject *)PyTuple_GET_ITEM(included_ffis, i); - sindex = search_in_struct_unions(&ffi1->types_builder.ctx, s->name, - strlen(s->name)); - if (sindex < 0) /* not found at all */ - continue; - s1 = &ffi1->types_builder.ctx.struct_unions[sindex]; - if ((s1->flags & (_CFFI_F_EXTERNAL | _CFFI_F_UNION)) - == (s->flags & _CFFI_F_UNION)) { - /* s1 is not external, and the same kind (struct or union) as s */ - return _realize_c_struct_or_union(&ffi1->types_builder, sindex); - } - /* not found, look more recursively */ - x = _fetch_external_struct_or_union( - s, ffi1->types_builder.included_ffis, recursion + 1); - if (x != NULL || PyErr_Occurred()) - return x; /* either found, or got an error */ - } - return NULL; /* not found at all, leave without an error */ -} diff --git a/c/file_emulator.h b/c/file_emulator.h deleted file mode 100644 index 82a34c0..0000000 --- a/c/file_emulator.h +++ /dev/null @@ -1,93 +0,0 @@ - -/* Emulation of PyFile_Check() and PyFile_AsFile() for Python 3. */ - -static PyObject *PyIOBase_TypeObj; - -static int init_file_emulator(void) -{ - if (PyIOBase_TypeObj == NULL) { - PyObject *io = PyImport_ImportModule("_io"); - if (io == NULL) - return -1; - PyIOBase_TypeObj = PyObject_GetAttrString(io, "_IOBase"); - if (PyIOBase_TypeObj == NULL) - return -1; - } - return 0; -} - - -#define PyFile_Check(p) PyObject_IsInstance(p, PyIOBase_TypeObj) - - -static void _close_file_capsule(PyObject *ob_capsule) -{ - FILE *f = (FILE *)PyCapsule_GetPointer(ob_capsule, "FILE"); - if (f != NULL) - fclose(f); -} - - -static FILE *PyFile_AsFile(PyObject *ob_file) -{ - PyObject *ob, *ob_capsule = NULL, *ob_mode = NULL; - FILE *f; - int fd; - const char *mode; - - ob = PyObject_CallMethod(ob_file, "flush", NULL); - if (ob == NULL) - goto fail; - Py_DECREF(ob); - - ob_capsule = PyObject_GetAttrString(ob_file, "__cffi_FILE"); - if (ob_capsule == NULL) { - PyErr_Clear(); - - fd = PyObject_AsFileDescriptor(ob_file); - if (fd < 0) - goto fail; - - ob_mode = PyObject_GetAttrString(ob_file, "mode"); - if (ob_mode == NULL) - goto fail; - mode = PyText_AsUTF8(ob_mode); - if (mode == NULL) - goto fail; - - fd = dup(fd); - if (fd < 0) { - PyErr_SetFromErrno(PyExc_OSError); - goto fail; - } - - f = fdopen(fd, mode); - if (f == NULL) { - close(fd); - PyErr_SetFromErrno(PyExc_OSError); - goto fail; - } - setbuf(f, NULL); /* non-buffered */ - Py_DECREF(ob_mode); - ob_mode = NULL; - - ob_capsule = PyCapsule_New(f, "FILE", _close_file_capsule); - if (ob_capsule == NULL) { - fclose(f); - goto fail; - } - - if (PyObject_SetAttrString(ob_file, "__cffi_FILE", ob_capsule) < 0) - goto fail; - } - else { - f = PyCapsule_GetPointer(ob_capsule, "FILE"); - } - Py_DECREF(ob_capsule); /* assumes still at least one reference */ - return f; - - fail: - Py_XDECREF(ob_mode); - Py_XDECREF(ob_capsule); - return NULL; -} diff --git a/c/lib_obj.c b/c/lib_obj.c deleted file mode 100644 index 38bf3d5..0000000 --- a/c/lib_obj.c +++ /dev/null @@ -1,716 +0,0 @@ - -/* A Lib object is what is in the "lib" attribute of a C extension - module originally created by recompile(). - - A Lib object is special in the sense that it has a custom - __getattr__ which returns C globals, functions and constants. The - original idea was to raise AttributeError for anything else, even - attrs like '__class__', but it breaks various things; now, standard - attrs are returned, but in the unlikely case where a user cdef()s - the same name, then the standard attr is hidden (and the various - things like introspection might break). - - A Lib object has got a reference to the _cffi_type_context_s - structure, which is used to create lazily the objects returned by - __getattr__. -*/ - -struct CPyExtFunc_s { - PyMethodDef md; - void *direct_fn; - int type_index; - char doc[1]; -}; - -struct LibObject_s { - PyObject_HEAD - builder_c_t *l_types_builder; /* same as the one on the ffi object */ - PyObject *l_dict; /* content, built lazily */ - 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) -{ - PyObject *y; - LibObject *lo; - PyCFunctionObject *fo; - - if (!PyCFunction_Check(x)) - return NULL; - y = PyCFunction_GET_SELF(x); - if (!LibObject_Check(y)) - return NULL; - - fo = (PyCFunctionObject *)x; - lo = (LibObject *)y; - if (lo->l_libname != fo->m_module) - return NULL; - - return (struct CPyExtFunc_s *)(fo->m_ml); -} - -static PyObject *_cpyextfunc_type(LibObject *lib, struct CPyExtFunc_s *exf) -{ - PyObject *tuple, *result; - tuple = realize_c_type_or_func(lib->l_types_builder, - lib->l_types_builder->ctx.types, - exf->type_index); - if (tuple == NULL) - return NULL; - - /* 'tuple' is a tuple of length 1 containing the real CT_FUNCTIONPTR - object */ - result = PyTuple_GetItem(tuple, 0); - Py_XINCREF(result); - Py_DECREF(tuple); - return result; -} - -static PyObject *_cpyextfunc_type_index(PyObject *x) -{ - struct CPyExtFunc_s *exf; - LibObject *lib; - - assert(PyErr_Occurred()); - exf = _cpyextfunc_get(x); - if (exf == NULL) - return NULL; /* still the same exception is set */ - - PyErr_Clear(); - - lib = (LibObject *)PyCFunction_GET_SELF(x); - return _cpyextfunc_type(lib, exf); -} - -static void cdlopen_close_ignore_errors(void *libhandle); /* forward */ -static void *cdlopen_fetch(PyObject *libname, void *libhandle, - const char *symbol); - -static void lib_dealloc(LibObject *lib) -{ - PyObject_GC_UnTrack(lib); - 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); - PyObject_GC_Del(lib); -} - -static int lib_traverse(LibObject *lib, visitproc visit, void *arg) -{ - Py_VISIT(lib->l_dict); - Py_VISIT(lib->l_libname); - Py_VISIT(lib->l_ffi); - return 0; -} - -static PyObject *lib_repr(LibObject *lib) -{ - return PyText_FromFormat("<Lib object for '%.200s'>", - PyText_AS_UTF8(lib->l_libname)); -} - -static PyObject *lib_build_cpython_func(LibObject *lib, - const struct _cffi_global_s *g, - const char *s, int flags) -{ - /* First make sure the argument types and return type are really - built. The C extension code can then assume that they are, - by calling _cffi_type(). - */ - PyObject *result = NULL; - CTypeDescrObject **pfargs = NULL; - CTypeDescrObject *fresult; - Py_ssize_t nargs = 0; - struct CPyExtFunc_s *xfunc; - int i, type_index = _CFFI_GETARG(g->type_op); - _cffi_opcode_t *opcodes = lib->l_types_builder->ctx.types; - static const char *const format = ";\n\nCFFI C function from %s.lib"; - const char *libname = PyText_AS_UTF8(lib->l_libname); - struct funcbuilder_s funcbuilder; - - /* return type: */ - fresult = realize_c_func_return_type(lib->l_types_builder, opcodes, - type_index); - if (fresult == NULL) - goto error; - - /* argument types: */ - /* note that if the arguments are already built, they have a - pointer in the 'opcodes' array, and GETOP() returns a - random even value. But OP_FUNCTION_END is odd, so the - condition below still works correctly. */ - i = type_index + 1; - while (_CFFI_GETOP(opcodes[i]) != _CFFI_OP_FUNCTION_END) - i++; - pfargs = alloca(sizeof(CTypeDescrObject *) * (i - type_index - 1)); - i = type_index + 1; - while (_CFFI_GETOP(opcodes[i]) != _CFFI_OP_FUNCTION_END) { - CTypeDescrObject *ct = realize_c_type(lib->l_types_builder, opcodes, i); - if (ct == NULL) - goto error; - pfargs[nargs++] = ct; - i++; - } - - memset(&funcbuilder, 0, sizeof(funcbuilder)); - if (fb_build_name(&funcbuilder, g->name, pfargs, nargs, fresult, 0) < 0) - goto error; - - /* The few bytes of memory we allocate here appear to leak, but - this is not a real leak. Indeed, CPython never unloads its C - extension modules. There is only one PyMem_Malloc() per real - C function in a CFFI C extension module. That means that this - PyMem_Malloc() could also have been written with a static - global variable generated for each CPYTHON_BLTN defined in the - C extension, and the effect would be the same (but a bit more - complicated). - */ - xfunc = PyMem_Malloc(sizeof(struct CPyExtFunc_s) + - funcbuilder.nb_bytes + - strlen(format) + strlen(libname)); - if (xfunc == NULL) { - PyErr_NoMemory(); - goto error; - } - memset((char *)xfunc, 0, sizeof(struct CPyExtFunc_s)); - assert(g->address); - xfunc->md.ml_meth = (PyCFunction)g->address; - xfunc->md.ml_flags = flags; - xfunc->md.ml_name = g->name; - xfunc->md.ml_doc = xfunc->doc; - xfunc->direct_fn = g->size_or_direct_fn; - xfunc->type_index = type_index; - - /* build the docstring */ - funcbuilder.bufferp = xfunc->doc; - if (fb_build_name(&funcbuilder, g->name, pfargs, nargs, fresult, 0) < 0) - goto error; - sprintf(funcbuilder.bufferp - 1, format, libname); - /* done building the docstring */ - - result = PyCFunction_NewEx(&xfunc->md, (PyObject *)lib, lib->l_libname); - /* fall-through */ - error: - Py_XDECREF(fresult); - while (nargs > 0) { - --nargs; - Py_DECREF(pfargs[nargs]); - } - return result; -} - -static PyObject *lib_build_and_cache_attr(LibObject *lib, PyObject *name, - int recursion) -{ - /* does not return a new reference! */ - PyObject *x; - int index; - const struct _cffi_global_s *g; - CTypeDescrObject *ct; - builder_c_t *types_builder = lib->l_types_builder; - const char *s = PyText_AsUTF8(name); - if (s == NULL) - return NULL; - - index = search_in_globals(&types_builder->ctx, s, strlen(s)); - if (index < 0) { - - if (types_builder->included_libs != NULL) { - Py_ssize_t i; - PyObject *included_ffis = types_builder->included_ffis; - PyObject *included_libs = types_builder->included_libs; - - if (recursion > 100) { - PyErr_SetString(PyExc_RuntimeError, - "recursion overflow in ffi.include() delegations"); - return NULL; - } - - for (i = 0; i < PyTuple_GET_SIZE(included_libs); i++) { - LibObject *lib1; - - lib1 = (LibObject *)PyTuple_GET_ITEM(included_libs, i); - if (lib1 != NULL) { - x = PyDict_GetItem(lib1->l_dict, name); - if (x != NULL) { - Py_INCREF(x); - goto found; - } - x = lib_build_and_cache_attr(lib1, name, recursion + 1); - if (x != NULL) { - Py_INCREF(x); - goto found; - } - } - else { - FFIObject *ffi1; - - ffi1 = (FFIObject *)PyTuple_GetItem(included_ffis, i); - if (ffi1 == NULL) - return NULL; - x = ffi_fetch_int_constant(ffi1, s, recursion + 1); - if (x != NULL) - goto found; - } - if (PyErr_Occurred()) - return NULL; - } - } - - if (recursion > 0) - return NULL; /* no error set, continue looking elsewhere */ - - PyErr_Format(PyExc_AttributeError, - "cffi library '%.200s' has no function, constant " - "or global variable named '%.200s'", - PyText_AS_UTF8(lib->l_libname), s); - return NULL; - } - - g = &types_builder->ctx.globals[index]; - - switch (_CFFI_GETOP(g->type_op)) { - - case _CFFI_OP_CPYTHON_BLTN_V: - x = lib_build_cpython_func(lib, g, s, METH_VARARGS); - break; - - case _CFFI_OP_CPYTHON_BLTN_N: - x = lib_build_cpython_func(lib, g, s, METH_NOARGS); - break; - - case _CFFI_OP_CPYTHON_BLTN_O: - x = lib_build_cpython_func(lib, g, s, METH_O); - break; - - case _CFFI_OP_CONSTANT_INT: - case _CFFI_OP_ENUM: - { - /* a constant integer whose value, in an "unsigned long long", - is obtained by calling the function at g->address */ - x = realize_global_int(types_builder, index); - break; - } - - case _CFFI_OP_CONSTANT: - case _CFFI_OP_DLOPEN_CONST: - { - /* a constant which is not of integer type */ - char *data; - ct = realize_c_type(types_builder, types_builder->ctx.types, - _CFFI_GETARG(g->type_op)); - if (ct == NULL) - return NULL; - - if (ct->ct_size <= 0) { - PyErr_Format(FFIError, "constant '%s' is of type '%s', " - "whose size is not known", s, ct->ct_name); - return NULL; - } - if (g->address == NULL) { - /* for dlopen() style */ - assert(_CFFI_GETOP(g->type_op) == _CFFI_OP_DLOPEN_CONST); - data = cdlopen_fetch(lib->l_libname, lib->l_libhandle, s); - if (data == NULL) - return NULL; - } - else { - /* The few bytes of memory we allocate here appear to leak, but - this is not a real leak. Indeed, CPython never unloads its C - extension modules. There is only one PyMem_Malloc() per real - non-integer C constant in a CFFI C extension module. That - means that this PyMem_Malloc() could also have been written - with a static global variable generated for each OP_CONSTANT - defined in the C extension, and the effect would be the same - (but a bit more complicated). - - Note that we used to do alloca(), but see issue #198. We - could still do alloca(), or explicit PyMem_Free(), in some - cases; but there is no point and it only makes the remaining - less-common cases more suspicious. - */ - assert(_CFFI_GETOP(g->type_op) == _CFFI_OP_CONSTANT); - data = PyMem_Malloc(ct->ct_size); - if (data == NULL) { - PyErr_NoMemory(); - return NULL; - } - ((void(*)(char*))g->address)(data); - } - x = convert_to_object(data, ct); - Py_DECREF(ct); - break; - } - - case _CFFI_OP_GLOBAL_VAR: - { - /* global variable of the exact type specified here - (nowadays, only used by the ABI mode or backward - compatibility; see _CFFI_OP_GLOBAL_VAR_F for the API mode) - */ - Py_ssize_t g_size = (Py_ssize_t)g->size_or_direct_fn; - ct = realize_c_type(types_builder, types_builder->ctx.types, - _CFFI_GETARG(g->type_op)); - if (ct == NULL) - return NULL; - if (g_size != ct->ct_size && g_size != 0 && ct->ct_size > 0) { - PyErr_Format(FFIError, - "global variable '%.200s' should be %zd bytes " - "according to the cdef, but is actually %zd", - s, ct->ct_size, g_size); - x = NULL; - } - else { - void *address = g->address; - if (address == NULL) { - /* for dlopen() style */ - address = cdlopen_fetch(lib->l_libname, lib->l_libhandle, s); - if (address == NULL) - return NULL; - } - x = make_global_var(name, ct, address, NULL); - } - Py_DECREF(ct); - break; - } - - case _CFFI_OP_GLOBAL_VAR_F: - ct = realize_c_type(types_builder, types_builder->ctx.types, - _CFFI_GETARG(g->type_op)); - if (ct == NULL) - return NULL; - x = make_global_var(name, ct, NULL, (gs_fetch_addr_fn)g->address); - Py_DECREF(ct); - break; - - case _CFFI_OP_DLOPEN_FUNC: - { - /* For dlopen(): the function of the given 'name'. We use - dlsym() to get the address of something in the dynamic - library, which we interpret as being exactly a function of - the specified type. - */ - PyObject *ct1; - void *address = cdlopen_fetch(lib->l_libname, lib->l_libhandle, s); - if (address == NULL) - return NULL; - - ct1 = realize_c_type_or_func(types_builder, - types_builder->ctx.types, - _CFFI_GETARG(g->type_op)); - if (ct1 == NULL) - return NULL; - - assert(!CTypeDescr_Check(ct1)); /* must be a function */ - x = new_simple_cdata(address, unwrap_fn_as_fnptr(ct1)); - - Py_DECREF(ct1); - break; - } - - case _CFFI_OP_EXTERN_PYTHON: - /* for reading 'lib.bar' where bar is declared with extern "Python" */ - ct = realize_c_type(types_builder, types_builder->ctx.types, - _CFFI_GETARG(g->type_op)); - if (ct == NULL) - return NULL; - x = convert_to_object((char *)&g->size_or_direct_fn, ct); - Py_DECREF(ct); - break; - - default: - PyErr_Format(PyExc_NotImplementedError, "in lib_build_attr: op=%d", - (int)_CFFI_GETOP(g->type_op)); - return NULL; - } - - found: - if (x != NULL) { - int err = PyDict_SetItem(lib->l_dict, name, x); - Py_DECREF(x); - if (err < 0) /* else there is still one ref left in the dict */ - return NULL; - } - return x; -} - -#define LIB_GET_OR_CACHE_ADDR(x, lib, name, error) \ - do { \ - x = PyDict_GetItem(lib->l_dict, name); \ - if (x == NULL) { \ - x = lib_build_and_cache_attr(lib, name, 0); \ - if (x == NULL) { \ - error; \ - } \ - } \ - } while (0) - -static PyObject *_lib_dir1(LibObject *lib, int ignore_global_vars) -{ - const struct _cffi_global_s *g = lib->l_types_builder->ctx.globals; - int i, count = 0, total = lib->l_types_builder->ctx.num_globals; - PyObject *s, *lst = PyList_New(total); - if (lst == NULL) - return NULL; - - for (i = 0; i < total; i++) { - if (ignore_global_vars) { - int op = _CFFI_GETOP(g[i].type_op); - if (op == _CFFI_OP_GLOBAL_VAR || op == _CFFI_OP_GLOBAL_VAR_F) - continue; - } - s = PyText_FromString(g[i].name); - if (s == NULL) - goto error; - PyList_SET_ITEM(lst, count, s); - count++; - } - if (PyList_SetSlice(lst, count, total, NULL) < 0) - goto error; - return lst; - - error: - Py_DECREF(lst); - return NULL; -} - -static PyObject *_lib_dict(LibObject *lib) -{ - const struct _cffi_global_s *g = lib->l_types_builder->ctx.globals; - int i, total = lib->l_types_builder->ctx.num_globals; - PyObject *name, *x, *d = PyDict_New(); - if (d == NULL) - return NULL; - - for (i = 0; i < total; i++) { - name = PyText_FromString(g[i].name); - if (name == NULL) - goto error; - - LIB_GET_OR_CACHE_ADDR(x, lib, name, goto error); - - if (PyDict_SetItem(d, name, x) < 0) - goto error; - Py_DECREF(name); - } - return d; - - error: - Py_XDECREF(name); - Py_DECREF(d); - return NULL; -} - -static PyObject *lib_getattr(LibObject *lib, PyObject *name) -{ - const char *p; - PyObject *x; - LIB_GET_OR_CACHE_ADDR(x, lib, name, goto missing); - - if (GlobSupport_Check(x)) { - return read_global_var((GlobSupportObject *)x); - } - Py_INCREF(x); - return x; - - missing: - /*** ATTRIBUTEERROR IS SET HERE ***/ - p = PyText_AsUTF8(name); - if (p == NULL) - return NULL; - if (strcmp(p, "__all__") == 0) { - PyErr_Clear(); - return _lib_dir1(lib, 1); - } - if (strcmp(p, "__dict__") == 0) { - PyErr_Clear(); - return _lib_dict(lib); - } - if (strcmp(p, "__class__") == 0) { - PyErr_Clear(); - x = (PyObject *)&PyModule_Type; - /* ^^^ used to be Py_TYPE(lib). But HAAAAAACK! That makes - help() behave correctly. I couldn't find a more reasonable - way. Urgh. */ - Py_INCREF(x); - return x; - } - /* this hack is for Python 3.5, and also to give a more - module-like behavior */ - if (strcmp(p, "__name__") == 0) { - PyErr_Clear(); - return PyText_FromFormat("%s.lib", PyText_AS_UTF8(lib->l_libname)); - } -#if PY_MAJOR_VERSION >= 3 - if (strcmp(p, "__loader__") == 0 || strcmp(p, "__spec__") == 0) { - /* some more module-like behavior hacks */ - PyErr_Clear(); - Py_INCREF(Py_None); - return Py_None; - } -#endif - return NULL; -} - -static int lib_setattr(LibObject *lib, PyObject *name, PyObject *val) -{ - PyObject *x; - LIB_GET_OR_CACHE_ADDR(x, lib, name, return -1); - - if (val == NULL) { - PyErr_SetString(PyExc_AttributeError, "C attribute cannot be deleted"); - return -1; - } - - if (GlobSupport_Check(x)) { - return write_global_var((GlobSupportObject *)x, val); - } - - PyErr_Format(PyExc_AttributeError, - "cannot write to function or constant '%.200s'", - PyText_Check(name) ? PyText_AS_UTF8(name) : "?"); - return -1; -} - -static PyObject *lib_dir(PyObject *self, PyObject *noarg) -{ - return _lib_dir1((LibObject *)self, 0); -} - -static PyMethodDef lib_methods[] = { - {"__dir__", lib_dir, METH_NOARGS}, - {NULL, NULL} /* sentinel */ -}; - -static PyTypeObject Lib_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_cffi_backend.Lib", - sizeof(LibObject), - 0, - (destructor)lib_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - (reprfunc)lib_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - (getattrofunc)lib_getattr, /* tp_getattro */ - (setattrofunc)lib_setattr, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - 0, /* tp_doc */ - (traverseproc)lib_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - lib_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - offsetof(LibObject, l_dict), /* tp_dictoffset */ -}; - -static LibObject *lib_internal_new(FFIObject *ffi, const char *module_name, - void *dlopen_libhandle, int auto_close) -{ - LibObject *lib; - PyObject *libname, *dict; - - libname = PyText_FromString(module_name); - if (libname == NULL) - goto err1; - - dict = PyDict_New(); - if (dict == NULL) - goto err2; - - lib = (LibObject *)PyType_GenericAlloc(&Lib_Type, 0); - if (lib == NULL) - goto err3; - - lib->l_types_builder = &ffi->types_builder; - lib->l_dict = dict; - lib->l_libname = libname; - Py_INCREF(ffi); - lib->l_ffi = ffi; - lib->l_libhandle = dlopen_libhandle; - lib->l_auto_close = auto_close; - return lib; - - err3: - Py_DECREF(dict); - err2: - Py_DECREF(libname); - err1: - if (auto_close) - cdlopen_close_ignore_errors(dlopen_libhandle); - return NULL; -} - -static PyObject *address_of_global_var(PyObject *args) -{ - LibObject *lib; - PyObject *x, *o_varname; - char *varname; - - if (!PyArg_ParseTuple(args, "O!s", &Lib_Type, &lib, &varname)) - return NULL; - - /* rebuild a string from 'varname', to do typechecks and to force - a unicode back to a plain string (on python 2) */ - o_varname = PyText_FromString(varname); - if (o_varname == NULL) - return NULL; - - LIB_GET_OR_CACHE_ADDR(x, lib, o_varname, goto error); - Py_DECREF(o_varname); - if (GlobSupport_Check(x)) { - return cg_addressof_global_var((GlobSupportObject *)x); - } - else { - struct CPyExtFunc_s *exf = _cpyextfunc_get(x); - if (exf != NULL) { /* an OP_CPYTHON_BLTN: '&func' returns a cdata */ - PyObject *ct; - if (exf->direct_fn == NULL) { - Py_INCREF(x); /* backward compatibility */ - return x; - } - ct = _cpyextfunc_type(lib, exf); - if (ct == NULL) - return NULL; - x = new_simple_cdata(exf->direct_fn, (CTypeDescrObject *)ct); - Py_DECREF(ct); - return x; - } - if (CData_Check(x) && /* a constant functionptr cdata: 'f == &f' */ - (((CDataObject *)x)->c_type->ct_flags & CT_FUNCTIONPTR) != 0) { - Py_INCREF(x); - return x; - } - else { - PyErr_Format(PyExc_AttributeError, - "cannot take the address of the constant '%.200s'", - varname); - return NULL; - } - } - - error: - Py_DECREF(o_varname); - return NULL; -} diff --git a/c/libffi_arm64/README b/c/libffi_arm64/README deleted file mode 100644 index 3b8f133..0000000 --- a/c/libffi_arm64/README +++ /dev/null @@ -1,5 +0,0 @@ -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 differdeleted file mode 100644 index 4a8b84b..0000000 --- a/c/libffi_arm64/ffi.lib +++ /dev/null diff --git a/c/libffi_arm64/include/ffi.h b/c/libffi_arm64/include/ffi.h deleted file mode 100644 index d91c3e1..0000000 --- a/c/libffi_arm64/include/ffi.h +++ /dev/null @@ -1,515 +0,0 @@ -/* -----------------------------------------------------------------*-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 deleted file mode 100644 index 5768c29..0000000 --- a/c/libffi_arm64/include/fficonfig.h +++ /dev/null @@ -1,215 +0,0 @@ -/* 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 deleted file mode 100644 index ecb6d2d..0000000 --- a/c/libffi_arm64/include/ffitarget.h +++ /dev/null @@ -1,92 +0,0 @@ -/* 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_x86_x64/LICENSE b/c/libffi_x86_x64/LICENSE deleted file mode 100644 index f591795..0000000 --- a/c/libffi_x86_x64/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -libffi - Copyright (c) 1996-2003 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 CYGNUS SOLUTIONS 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. diff --git a/c/libffi_x86_x64/README b/c/libffi_x86_x64/README deleted file mode 100644 index 97a12cf..0000000 --- a/c/libffi_x86_x64/README +++ /dev/null @@ -1,502 +0,0 @@ -This directory contains the libffi package, which is not part of GCC but -shipped with GCC as convenience. - -Copied without changes from CPython 2.7 head (e04e1f253ed8). - -Status -====== - -libffi-2.00 has not been released yet! This is a development snapshot! - -libffi-1.20 was released on October 5, 1998. Check the libffi web -page for updates: <URL:http://sources.redhat.com/libffi/>. - - -What is libffi? -=============== - -Compilers for high level languages generate code that follow certain -conventions. These conventions are necessary, in part, for separate -compilation to work. One such convention is the "calling -convention". The "calling convention" is essentially a set of -assumptions made by the compiler about where function arguments will -be found on entry to a function. A "calling convention" also specifies -where the return value for a function is found. - -Some programs may not know at the time of compilation what arguments -are to be passed to a function. For instance, an interpreter may be -told at run-time about the number and types of arguments used to call -a given function. Libffi can be used in such programs to provide a -bridge from the interpreter program to compiled code. - -The libffi library provides a portable, high level programming -interface to various calling conventions. This allows a programmer to -call any function specified by a call interface description at run -time. - -Ffi stands for Foreign Function Interface. A foreign function -interface is the popular name for the interface that allows code -written in one language to call code written in another language. The -libffi library really only provides the lowest, machine dependent -layer of a fully featured foreign function interface. A layer must -exist above libffi that handles type conversions for values passed -between the two languages. - - -Supported Platforms and Prerequisites -===================================== - -Libffi has been ported to: - - SunOS 4.1.3 & Solaris 2.x (SPARC-V8, SPARC-V9) - - Irix 5.3 & 6.2 (System V/o32 & n32) - - Intel x86 - Linux (System V ABI) - - Alpha - Linux and OSF/1 - - m68k - Linux (System V ABI) - - PowerPC - Linux (System V ABI, Darwin, AIX) - - ARM - Linux (System V ABI) - -Libffi has been tested with the egcs 1.0.2 gcc compiler. Chances are -that other versions will work. Libffi has also been built and tested -with the SGI compiler tools. - -On PowerPC, the tests failed (see the note below). - -You must use GNU make to build libffi. SGI's make will not work. -Sun's probably won't either. - -If you port libffi to another platform, please let me know! I assume -that some will be easy (x86 NetBSD), and others will be more difficult -(HP). - - -Installing libffi -================= - -[Note: before actually performing any of these installation steps, - you may wish to read the "Platform Specific Notes" below.] - -First you must configure the distribution for your particular -system. Go to the directory you wish to build libffi in and run the -"configure" program found in the root directory of the libffi source -distribution. - -You may want to tell configure where to install the libffi library and -header files. To do that, use the --prefix configure switch. Libffi -will install under /usr/local by default. - -If you want to enable extra run-time debugging checks use the the ---enable-debug configure switch. This is useful when your program dies -mysteriously while using libffi. - -Another useful configure switch is --enable-purify-safety. Using this -will add some extra code which will suppress certain warnings when you -are using Purify with libffi. Only use this switch when using -Purify, as it will slow down the library. - -Configure has many other options. Use "configure --help" to see them all. - -Once configure has finished, type "make". Note that you must be using -GNU make. SGI's make will not work. Sun's probably won't either. -You can ftp GNU make from prep.ai.mit.edu:/pub/gnu. - -To ensure that libffi is working as advertised, type "make test". - -To install the library and header files, type "make install". - - -Using libffi -============ - - The Basics - ---------- - -Libffi assumes that you have a pointer to the function you wish to -call and that you know the number and types of arguments to pass it, -as well as the return type of the function. - -The first thing you must do is create an ffi_cif object that matches -the signature of the function you wish to call. The cif in ffi_cif -stands for Call InterFace. To prepare a call interface object, use the -following function: - -ffi_status ffi_prep_cif(ffi_cif *cif, ffi_abi abi, - unsigned int nargs, - ffi_type *rtype, ffi_type **atypes); - - CIF is a pointer to the call interface object you wish - to initialize. - - ABI is an enum that specifies the calling convention - to use for the call. FFI_DEFAULT_ABI defaults - to the system's native calling convention. Other - ABI's may be used with care. They are system - specific. - - NARGS is the number of arguments this function accepts. - libffi does not yet support vararg functions. - - RTYPE is a pointer to an ffi_type structure that represents - the return type of the function. Ffi_type objects - describe the types of values. libffi provides - ffi_type objects for many of the native C types: - signed int, unsigned int, signed char, unsigned char, - etc. There is also a pointer ffi_type object and - a void ffi_type. Use &ffi_type_void for functions that - don't return values. - - ATYPES is a vector of ffi_type pointers. ARGS must be NARGS long. - If NARGS is 0, this is ignored. - - -ffi_prep_cif will return a status code that you are responsible -for checking. It will be one of the following: - - FFI_OK - All is good. - - FFI_BAD_TYPEDEF - One of the ffi_type objects that ffi_prep_cif - came across is bad. - - -Before making the call, the VALUES vector should be initialized -with pointers to the appropriate argument values. - -To call the the function using the initialized ffi_cif, use the -ffi_call function: - -void ffi_call(ffi_cif *cif, void *fn, void *rvalue, void **avalues); - - CIF is a pointer to the ffi_cif initialized specifically - for this function. - - FN is a pointer to the function you want to call. - - RVALUE is a pointer to a chunk of memory that is to hold the - result of the function call. Currently, it must be - at least one word in size (except for the n32 version - under Irix 6.x, which must be a pointer to an 8 byte - aligned value (a long long). It must also be at least - word aligned (depending on the return type, and the - system's alignment requirements). If RTYPE is - &ffi_type_void, this is ignored. If RVALUE is NULL, - the return value is discarded. - - AVALUES is a vector of void* that point to the memory locations - holding the argument values for a call. - If NARGS is 0, this is ignored. - - -If you are expecting a return value from FN it will have been stored -at RVALUE. - - - - An Example - ---------- - -Here is a trivial example that calls puts() a few times. - - #include <stdio.h> - #include <ffi.h> - - int main() - { - ffi_cif cif; - ffi_type *args[1]; - void *values[1]; - char *s; - int rc; - - /* Initialize the argument info vectors */ - args[0] = &ffi_type_uint; - values[0] = &s; - - /* Initialize the cif */ - if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1, - &ffi_type_uint, args) == FFI_OK) - { - s = "Hello World!"; - ffi_call(&cif, puts, &rc, values); - /* rc now holds the result of the call to puts */ - - /* values holds a pointer to the function's arg, so to - call puts() again all we need to do is change the - value of s */ - s = "This is cool!"; - ffi_call(&cif, puts, &rc, values); - } - - return 0; - } - - - - Aggregate Types - --------------- - -Although libffi has no special support for unions or bit-fields, it is -perfectly happy passing structures back and forth. You must first -describe the structure to libffi by creating a new ffi_type object -for it. Here is the definition of ffi_type: - - typedef struct _ffi_type - { - unsigned size; - short alignment; - short type; - struct _ffi_type **elements; - } ffi_type; - -All structures must have type set to FFI_TYPE_STRUCT. You may set -size and alignment to 0. These will be calculated and reset to the -appropriate values by ffi_prep_cif(). - -elements is a NULL terminated array of pointers to ffi_type objects -that describe the type of the structure elements. These may, in turn, -be structure elements. - -The following example initializes a ffi_type object representing the -tm struct from Linux's time.h: - - struct tm { - int tm_sec; - int tm_min; - int tm_hour; - int tm_mday; - int tm_mon; - int tm_year; - int tm_wday; - int tm_yday; - int tm_isdst; - /* Those are for future use. */ - long int __tm_gmtoff__; - __const char *__tm_zone__; - }; - - { - ffi_type tm_type; - ffi_type *tm_type_elements[12]; - int i; - - tm_type.size = tm_type.alignment = 0; - tm_type.elements = &tm_type_elements; - - for (i = 0; i < 9; i++) - tm_type_elements[i] = &ffi_type_sint; - - tm_type_elements[9] = &ffi_type_slong; - tm_type_elements[10] = &ffi_type_pointer; - tm_type_elements[11] = NULL; - - /* tm_type can now be used to represent tm argument types and - return types for ffi_prep_cif() */ - } - - - -Platform Specific Notes -======================= - - Intel x86 - --------- - -There are no known problems with the x86 port. - - Sun SPARC - SunOS 4.1.3 & Solaris 2.x - ------------------------------------- - -You must use GNU Make to build libffi on Sun platforms. - - MIPS - Irix 5.3 & 6.x - --------------------- - -Irix 6.2 and better supports three different calling conventions: o32, -n32 and n64. Currently, libffi only supports both o32 and n32 under -Irix 6.x, but only o32 under Irix 5.3. Libffi will automatically be -configured for whichever calling convention it was built for. - -By default, the configure script will try to build libffi with the GNU -development tools. To build libffi with the SGI development tools, set -the environment variable CC to either "cc -32" or "cc -n32" before -running configure under Irix 6.x (depending on whether you want an o32 -or n32 library), or just "cc" for Irix 5.3. - -With the n32 calling convention, when returning structures smaller -than 16 bytes, be sure to provide an RVALUE that is 8 byte aligned. -Here's one way of forcing this: - - double struct_storage[2]; - my_small_struct *s = (my_small_struct *) struct_storage; - /* Use s for RVALUE */ - -If you don't do this you are liable to get spurious bus errors. - -"long long" values are not supported yet. - -You must use GNU Make to build libffi on SGI platforms. - - ARM - System V ABI - ------------------ - -The ARM port was performed on a NetWinder running ARM Linux ELF -(2.0.31) and gcc 2.8.1. - - - - PowerPC System V ABI - -------------------- - -There are two `System V ABI's which libffi implements for PowerPC. -They differ only in how small structures are returned from functions. - -In the FFI_SYSV version, structures that are 8 bytes or smaller are -returned in registers. This is what GCC does when it is configured -for solaris, and is what the System V ABI I have (dated September -1995) says. - -In the FFI_GCC_SYSV version, all structures are returned the same way: -by passing a pointer as the first argument to the function. This is -what GCC does when it is configured for linux or a generic sysv -target. - -EGCS 1.0.1 (and probably other versions of EGCS/GCC) also has a -inconsistency with the SysV ABI: When a procedure is called with many -floating-point arguments, some of them get put on the stack. They are -all supposed to be stored in double-precision format, even if they are -only single-precision, but EGCS stores single-precision arguments as -single-precision anyway. This causes one test to fail (the `many -arguments' test). - - -What's With The Crazy Comments? -=============================== - -You might notice a number of cryptic comments in the code, delimited -by /*@ and @*/. These are annotations read by the program LCLint, a -tool for statically checking C programs. You can read all about it at -<http://larch-www.lcs.mit.edu:8001/larch/lclint/index.html>. - - -History -======= - -1.20 Oct-5-98 - Raffaele Sena produces ARM port. - -1.19 Oct-5-98 - Fixed x86 long double and long long return support. - m68k bug fixes from Andreas Schwab. - Patch for DU assembler compatibility for the Alpha from Richard - Henderson. - -1.18 Apr-17-98 - Bug fixes and MIPS configuration changes. - -1.17 Feb-24-98 - Bug fixes and m68k port from Andreas Schwab. PowerPC port from - Geoffrey Keating. Various bug x86, Sparc and MIPS bug fixes. - -1.16 Feb-11-98 - Richard Henderson produces Alpha port. - -1.15 Dec-4-97 - Fixed an n32 ABI bug. New libtool, auto* support. - -1.14 May-13-97 - libtool is now used to generate shared and static libraries. - Fixed a minor portability problem reported by Russ McManus - <mcmanr@eq.gs.com>. - -1.13 Dec-2-96 - Added --enable-purify-safety to keep Purify from complaining - about certain low level code. - Sparc fix for calling functions with < 6 args. - Linux x86 a.out fix. - -1.12 Nov-22-96 - Added missing ffi_type_void, needed for supporting void return - types. Fixed test case for non MIPS machines. Cygnus Support - is now Cygnus Solutions. - -1.11 Oct-30-96 - Added notes about GNU make. - -1.10 Oct-29-96 - Added configuration fix for non GNU compilers. - -1.09 Oct-29-96 - Added --enable-debug configure switch. Clean-ups based on LCLint - feedback. ffi_mips.h is always installed. Many configuration - fixes. Fixed ffitest.c for sparc builds. - -1.08 Oct-15-96 - Fixed n32 problem. Many clean-ups. - -1.07 Oct-14-96 - Gordon Irlam rewrites v8.S again. Bug fixes. - -1.06 Oct-14-96 - Gordon Irlam improved the sparc port. - -1.05 Oct-14-96 - Interface changes based on feedback. - -1.04 Oct-11-96 - Sparc port complete (modulo struct passing bug). - -1.03 Oct-10-96 - Passing struct args, and returning struct values works for - all architectures/calling conventions. Expanded tests. - -1.02 Oct-9-96 - Added SGI n32 support. Fixed bugs in both o32 and Linux support. - Added "make test". - -1.01 Oct-8-96 - Fixed float passing bug in mips version. Restructured some - of the code. Builds cleanly with SGI tools. - -1.00 Oct-7-96 - First release. No public announcement. - - -Authors & Credits -================= - -libffi was written by Anthony Green <green@cygnus.com>. - -Portions of libffi were derived from Gianni Mariani's free gencall -library for Silicon Graphics machines. - -The closure mechanism was designed and implemented by Kresten Krab -Thorup. - -The Sparc port was derived from code contributed by the fine folks at -Visible Decisions Inc <http://www.vdi.com>. Further enhancements were -made by Gordon Irlam at Cygnus Solutions <http://www.cygnus.com>. - -The Alpha port was written by Richard Henderson at Cygnus Solutions. - -Andreas Schwab ported libffi to m68k Linux and provided a number of -bug fixes. - -Geoffrey Keating ported libffi to the PowerPC. - -Raffaele Sena ported libffi to the ARM. - -Jesper Skov and Andrew Haley both did more than their fair share of -stepping through the code and tracking down bugs. - -Thanks also to Tom Tromey for bug fixes and configuration help. - -Thanks to Jim Blandy, who provided some useful feedback on the libffi -interface. - -If you have a problem, or have found a bug, please send a note to -green@cygnus.com. diff --git a/c/libffi_x86_x64/README.ctypes b/c/libffi_x86_x64/README.ctypes deleted file mode 100644 index 17e8a40..0000000 --- a/c/libffi_x86_x64/README.ctypes +++ /dev/null @@ -1,7 +0,0 @@ -The purpose is to hack the libffi sources so that they can be compiled -with MSVC, and to extend them so that they have the features I need -for ctypes. - -I retrieved the libffi sources from the gcc cvs repository on -2004-01-27. Then I did 'configure' in a 'build' subdirectory on a x86 -linux system, and copied the files I found useful. diff --git a/c/libffi_x86_x64/ffi.c b/c/libffi_x86_x64/ffi.c deleted file mode 100644 index b9e324f..0000000 --- a/c/libffi_x86_x64/ffi.c +++ /dev/null @@ -1,495 +0,0 @@ -/* ----------------------------------------------------------------------- - ffi.c - Copyright (c) 1996, 1998, 1999, 2001 Red Hat, Inc. - Copyright (c) 2002 Ranjit Mathew - Copyright (c) 2002 Bo Thorsen - Copyright (c) 2002 Roger Sayle - - x86 Foreign Function Interface - - 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 CYGNUS SOLUTIONS 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. - ----------------------------------------------------------------------- */ - -#include <ffi.h> -#include <ffi_common.h> - -#include <stdlib.h> - -/* ffi_prep_args is called by the assembly routine once stack space - has been allocated for the function's arguments */ - -extern void Py_FatalError(const char *msg); - -/*@-exportheader@*/ -void ffi_prep_args(char *stack, extended_cif *ecif) -/*@=exportheader@*/ -{ - register unsigned int i; - register void **p_argv; - register char *argp; - register ffi_type **p_arg; - - argp = stack; - if (ecif->cif->flags == FFI_TYPE_STRUCT) - { - *(void **) argp = ecif->rvalue; - argp += sizeof(void *); - } - - p_argv = ecif->avalue; - - for (i = ecif->cif->nargs, p_arg = ecif->cif->arg_types; - i != 0; - i--, p_arg++) - { - size_t z; - - /* Align if necessary */ - if ((sizeof(void *) - 1) & (size_t) argp) - argp = (char *) ALIGN(argp, sizeof(void *)); - - z = (*p_arg)->size; - if (z < sizeof(int)) - { - z = sizeof(int); - switch ((*p_arg)->type) - { - case FFI_TYPE_SINT8: - *(signed int *) argp = (signed int)*(SINT8 *)(* p_argv); - break; - - case FFI_TYPE_UINT8: - *(unsigned int *) argp = (unsigned int)*(UINT8 *)(* p_argv); - break; - - case FFI_TYPE_SINT16: - *(signed int *) argp = (signed int)*(SINT16 *)(* p_argv); - break; - - case FFI_TYPE_UINT16: - *(unsigned int *) argp = (unsigned int)*(UINT16 *)(* p_argv); - break; - - case FFI_TYPE_SINT32: - *(signed int *) argp = (signed int)*(SINT32 *)(* p_argv); - break; - - case FFI_TYPE_UINT32: - *(unsigned int *) argp = (unsigned int)*(UINT32 *)(* p_argv); - break; - - case FFI_TYPE_STRUCT: - *(unsigned int *) argp = (unsigned int)*(UINT32 *)(* p_argv); - break; - - default: - FFI_ASSERT(0); - } - } -#ifdef _WIN64 - 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. */ - *(void **)argp = *p_argv; - z = 8; - } -#endif - else - { - memcpy(argp, *p_argv, z); - } - p_argv++; - argp += z; - } - - if (argp - stack > (long)ecif->cif->bytes) - { - Py_FatalError("FFI BUG: not enough stack space for arguments"); - } - return; -} - -/* Perform machine dependent cif processing */ -ffi_status ffi_prep_cif_machdep(ffi_cif *cif) -{ - /* Set the return type flag */ - switch (cif->rtype->type) - { - case FFI_TYPE_VOID: - case FFI_TYPE_SINT64: - case FFI_TYPE_FLOAT: - case FFI_TYPE_DOUBLE: - case FFI_TYPE_LONGDOUBLE: - cif->flags = (unsigned) cif->rtype->type; - break; - - case FFI_TYPE_STRUCT: - /* 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 == 1 || - cif->rtype->size == 2 || - cif->rtype->size == 4) - cif->flags = FFI_TYPE_INT; - else if (cif->rtype->size == 8) - cif->flags = FFI_TYPE_SINT64; - else - cif->flags = FFI_TYPE_STRUCT; - break; - - case FFI_TYPE_UINT64: -#ifdef _WIN64 - case FFI_TYPE_POINTER: -#endif - cif->flags = FFI_TYPE_SINT64; - break; - - default: - cif->flags = FFI_TYPE_INT; - break; - } - - return FFI_OK; -} - -#ifdef _WIN32 -extern int -ffi_call_x86(void (*)(char *, extended_cif *), - /*@out@*/ extended_cif *, - unsigned, unsigned, - /*@out@*/ unsigned *, - void (*fn)()); -#endif - -#ifdef _WIN64 -extern int -ffi_call_AMD64(void (*)(char *, extended_cif *), - /*@out@*/ extended_cif *, - unsigned, unsigned, - /*@out@*/ unsigned *, - void (*fn)()); -#endif - -int -ffi_call(/*@dependent@*/ ffi_cif *cif, - void (*fn)(), - /*@out@*/ void *rvalue, - /*@dependent@*/ void **avalue) -{ - extended_cif ecif; - - ecif.cif = cif; - ecif.avalue = avalue; - - /* If the return value is a struct and we don't have a return */ - /* value address then we need to make one */ - - if ((rvalue == NULL) && - (cif->flags == FFI_TYPE_STRUCT)) - { - /*@-sysunrecog@*/ - ecif.rvalue = alloca(cif->rtype->size); - /*@=sysunrecog@*/ - } - else - ecif.rvalue = rvalue; - - - switch (cif->abi) - { -#if !defined(_WIN64) - case FFI_SYSV: - case FFI_STDCALL: - return ffi_call_x86(ffi_prep_args, &ecif, cif->bytes, - cif->flags, ecif.rvalue, fn); - break; -#else - case FFI_SYSV: - /*@-usedef@*/ - return ffi_call_AMD64(ffi_prep_args, &ecif, cif->bytes, - cif->flags, ecif.rvalue, fn); - /*@=usedef@*/ - break; -#endif - - default: - FFI_ASSERT(0); - break; - } - return -1; /* theller: Hrm. */ -} - - -/** private members **/ - -static void ffi_prep_incoming_args_SYSV (char *stack, void **ret, - void** args, ffi_cif* cif); -/* This function is jumped to by the trampoline */ - -#ifdef _WIN64 -void * -#else -static void __fastcall -#endif -ffi_closure_SYSV (ffi_closure *closure, char *argp) -{ - // this is our return value storage - long double res; - - // our various things... - ffi_cif *cif; - void **arg_area; - unsigned short rtype; - void *resp = (void*)&res; - void *args = argp + sizeof(void *); - - cif = closure->cif; - arg_area = (void**) alloca (cif->nargs * sizeof (void*)); - - /* this call will initialize ARG_AREA, such that each - * element in that array points to the corresponding - * value on the stack; and if the function returns - * a structure, it will re-set RESP to point to the - * structure return address. */ - - ffi_prep_incoming_args_SYSV(args, (void**)&resp, arg_area, cif); - - (closure->fun) (cif, resp, arg_area, closure->user_data); - - rtype = cif->flags; - -#if defined(_WIN32) && !defined(_WIN64) -#ifdef _MSC_VER - /* now, do a generic return based on the value of rtype */ - if (rtype == FFI_TYPE_INT) - { - _asm mov eax, resp ; - _asm mov eax, [eax] ; - } - else if (rtype == FFI_TYPE_FLOAT) - { - _asm mov eax, resp ; - _asm fld DWORD PTR [eax] ; -// asm ("flds (%0)" : : "r" (resp) : "st" ); - } - 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_SINT64) - { - _asm mov edx, resp ; - _asm mov eax, [edx] ; - _asm mov edx, [edx + 4] ; -// asm ("movl 0(%0),%%eax;" -// "movl 4(%0),%%edx" -// : : "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) - { - asm ("movl (%0),%%eax" : : "r" (resp) : "eax"); - } - else if (rtype == FFI_TYPE_FLOAT) - { - asm ("flds (%0)" : : "r" (resp) : "st" ); - } - else if (rtype == FFI_TYPE_DOUBLE || rtype == FFI_TYPE_LONGDOUBLE) - { - asm ("fldl (%0)" : : "r" (resp) : "st", "st(1)" ); - } - else if (rtype == FFI_TYPE_SINT64) - { - asm ("movl 0(%0),%%eax;" - "movl 4(%0),%%edx" - : : "r"(resp) - : "eax", "edx"); - } - else if (rtype == FFI_TYPE_STRUCT) - { - asm ("movl %0,%%eax" : : "r" (resp) : "eax"); - } -#endif -#endif - -#ifdef _WIN64 - /* The result is returned in rax. This does the right thing for - 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 -} - -/*@-exportheader@*/ -static void -ffi_prep_incoming_args_SYSV(char *stack, void **rvalue, - void **avalue, ffi_cif *cif) -/*@=exportheader@*/ -{ - register unsigned int i; - register void **p_argv; - register char *argp; - register ffi_type **p_arg; - - argp = stack; - - if ( cif->flags == FFI_TYPE_STRUCT ) { - *rvalue = *(void **) argp; - argp += 4; - } - - p_argv = avalue; - - for (i = cif->nargs, p_arg = cif->arg_types; (i != 0); i--, p_arg++) - { - size_t z; - - /* Align if necessary */ - if ((sizeof(char *) - 1) & (size_t) argp) { - argp = (char *) ALIGN(argp, sizeof(char*)); - } - - z = (*p_arg)->size; - - /* because we're little endian, this is what it turns into. */ - -#ifdef _WIN64 - 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. */ - *p_argv = *((void**) argp); - z = 8; - } - else -#endif - *p_argv = (void*) argp; - - p_argv++; - argp += z; - } - - return; -} - -/* the cif must already be prep'ed */ -extern void ffi_closure_OUTER(); - -ffi_status -ffi_prep_closure_loc (ffi_closure* closure, - ffi_cif* cif, - void (*fun)(ffi_cif*,void*,void**,void*), - void *user_data, - void *codeloc) -{ - short bytes; - char *tramp; -#ifdef _WIN64 - int mask = 0; -#endif - FFI_ASSERT (cif->abi == FFI_SYSV); - - if (cif->abi == FFI_SYSV) - bytes = 0; -#if !defined(_WIN64) - else if (cif->abi == FFI_STDCALL) - bytes = cif->bytes; -#endif - else - return FFI_BAD_ABI; - - tramp = &closure->tramp[0]; - -#define BYTES(text) memcpy(tramp, text, sizeof(text)), tramp += sizeof(text)-1 -#define POINTER(x) *(void**)tramp = (void*)(x), tramp += sizeof(void*) -#define SHORT(x) *(short*)tramp = x, tramp += sizeof(short) -#define INT(x) *(int*)tramp = x, tramp += sizeof(int) - -#ifdef _WIN64 - if (cif->nargs >= 1 && - (cif->arg_types[0]->type == FFI_TYPE_FLOAT - || cif->arg_types[0]->type == FFI_TYPE_DOUBLE)) - mask |= 1; - if (cif->nargs >= 2 && - (cif->arg_types[1]->type == FFI_TYPE_FLOAT - || cif->arg_types[1]->type == FFI_TYPE_DOUBLE)) - mask |= 2; - if (cif->nargs >= 3 && - (cif->arg_types[2]->type == FFI_TYPE_FLOAT - || cif->arg_types[2]->type == FFI_TYPE_DOUBLE)) - mask |= 4; - if (cif->nargs >= 4 && - (cif->arg_types[3]->type == FFI_TYPE_FLOAT - || 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); - - /* 48 B8 -------- mov rax, closure */ - BYTES("\x48\xB8"); POINTER(closure); - - /* 49 BA -------- mov r10, ffi_closure_OUTER */ - BYTES("\x49\xBA"); POINTER(ffi_closure_OUTER); - - /* 41 FF E2 jmp r10 */ - BYTES("\x41\xFF\xE2"); - -#else - - /* mov ecx, closure */ - BYTES("\xb9"); POINTER(closure); - - /* mov edx, esp */ - BYTES("\x8b\xd4"); - - /* call ffi_closure_SYSV */ - BYTES("\xe8"); POINTER((char*)&ffi_closure_SYSV - (tramp + 4)); - - /* ret bytes */ - BYTES("\xc2"); - SHORT(bytes); - -#endif - - if (tramp - &closure->tramp[0] > FFI_TRAMPOLINE_SIZE) - Py_FatalError("FFI_TRAMPOLINE_SIZE too small in " __FILE__); - - closure->cif = cif; - closure->user_data = user_data; - closure->fun = fun; - return FFI_OK; -} diff --git a/c/libffi_x86_x64/ffi.h b/c/libffi_x86_x64/ffi.h deleted file mode 100644 index 97cdb59..0000000 --- a/c/libffi_x86_x64/ffi.h +++ /dev/null @@ -1,322 +0,0 @@ -/* -----------------------------------------------------------------*-C-*- - libffi 2.00-beta - Copyright (c) 1996-2003 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 CYGNUS SOLUTIONS 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. - - ----------------------------------------------------------------------- */ - -/* ------------------------------------------------------------------- - The basic API is described in the README file. - - The raw API is designed to bypass some of the argument packing - and unpacking on architectures for which it can be avoided. - - The closure API allows interpreted functions to be packaged up - inside a C function pointer, so that they can be called as C functions, - with no understanding on the client side that they are interpreted. - It can also be used in other cases in which it is necessary to package - up a user specified parameter and a function pointer as a single - function pointer. - - The closure API must be implemented in order to get its functionality, - e.g. for use by gij. Routines are provided to emulate the raw API - if the underlying platform doesn't allow faster implementation. - - More details on the raw and cloure 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. */ -//XXX #define X86 - -/* ---- System configuration information --------------------------------- */ - -#include <ffitarget.h> - -#ifndef LIBFFI_ASM - -#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. */ -#ifdef LONG_LONG_MAX -# define FFI_LONG_LONG_MAX LONG_LONG_MAX -#else -# ifdef LLONG_MAX -# define FFI_LONG_LONG_MAX LLONG_MAX -# else -# ifdef __GNUC__ -# define FFI_LONG_LONG_MAX __LONG_LONG_MAX__ -# endif -# ifdef _MSC_VER -# define FFI_LONG_LONG_MAX _I64_MAX -# endif -# endif -#endif - -#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 - -#define ffi_type_ulong ffi_type_uint64 -#define ffi_type_slong ffi_type_sint64 -#if LONG_MAX == 2147483647 -# if FFI_LONG_LONG_MAX != 9223372036854775807 - #error "no 64-bit data type supported" -# endif -#elif LONG_MAX != 9223372036854775807 - #error "long size not supported" -#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; - /*@null@*/ struct _ffi_type **elements; -} ffi_type; - -/* These are defined in types.c */ -extern ffi_type ffi_type_void; -extern ffi_type ffi_type_uint8; -extern ffi_type ffi_type_sint8; -extern ffi_type ffi_type_uint16; -extern ffi_type ffi_type_sint16; -extern ffi_type ffi_type_uint32; -extern ffi_type ffi_type_sint32; -extern ffi_type ffi_type_uint64; -extern ffi_type ffi_type_sint64; -extern ffi_type ffi_type_float; -extern ffi_type ffi_type_double; -extern ffi_type ffi_type_longdouble; -extern ffi_type ffi_type_pointer; - - -typedef enum { - FFI_OK = 0, - FFI_BAD_TYPEDEF, - FFI_BAD_ABI -} ffi_status; - -typedef unsigned FFI_TYPE; - -typedef struct { - ffi_abi abi; - unsigned nargs; - /*@dependent@*/ ffi_type **arg_types; - /*@dependent@*/ ffi_type *rtype; - unsigned bytes; - unsigned flags; -#ifdef FFI_EXTRA_CIF_FIELDS - FFI_EXTRA_CIF_FIELDS; -#endif -} ffi_cif; - -/* ---- Definitions for the raw API -------------------------------------- */ - -#ifdef _WIN64 -#define FFI_SIZEOF_ARG 8 -#else -#define FFI_SIZEOF_ARG 4 -#endif - -typedef union { - ffi_sarg sint; - ffi_arg uint; - float flt; - char data[FFI_SIZEOF_ARG]; - void* ptr; -} ffi_raw; - -void ffi_raw_call (/*@dependent@*/ ffi_cif *cif, - void (*fn)(), - /*@out@*/ void *rvalue, - /*@dependent@*/ ffi_raw *avalue); - -void ffi_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_raw *raw); -void ffi_raw_to_ptrarray (ffi_cif *cif, ffi_raw *raw, void **args); -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. */ - -void ffi_java_raw_call (/*@dependent@*/ ffi_cif *cif, - void (*fn)(), - /*@out@*/ void *rvalue, - /*@dependent@*/ ffi_raw *avalue); - -void ffi_java_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_raw *raw); -void ffi_java_raw_to_ptrarray (ffi_cif *cif, ffi_raw *raw, void **args); -size_t ffi_java_raw_size (ffi_cif *cif); - -/* ---- Definitions for closures ----------------------------------------- */ - -#if FFI_CLOSURES - -typedef struct { - char tramp[FFI_TRAMPOLINE_SIZE]; - ffi_cif *cif; - void (*fun)(ffi_cif*,void*,void**,void*); - void *user_data; -} ffi_closure; - -void ffi_closure_free(void *); -void *ffi_closure_alloc (size_t size, void **code); - -ffi_status -ffi_prep_closure_loc (ffi_closure*, - ffi_cif *, - void (*fun)(ffi_cif*,void*,void**,void*), - void *user_data, - void *codeloc); - -/* AR: for cffi we need the following API, and not the _loc version */ -#define ffi_prep_closure(a,b,c,d) ffi_prep_closure_loc(a,b,c,d,a) - -typedef struct { - char tramp[FFI_TRAMPOLINE_SIZE]; - - 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; - -ffi_status -ffi_prep_raw_closure (ffi_raw_closure*, - ffi_cif *cif, - void (*fun)(ffi_cif*,void*,ffi_raw*,void*), - void *user_data); - -ffi_status -ffi_prep_java_raw_closure (ffi_raw_closure*, - ffi_cif *cif, - void (*fun)(ffi_cif*,void*,ffi_raw*,void*), - void *user_data); - -#endif /* FFI_CLOSURES */ - -/* ---- Public interface definition -------------------------------------- */ - -ffi_status ffi_prep_cif(/*@out@*/ /*@partial@*/ ffi_cif *cif, - ffi_abi abi, - unsigned int nargs, - /*@dependent@*/ /*@out@*/ /*@partial@*/ ffi_type *rtype, - /*@dependent@*/ ffi_type **atypes); - -int -ffi_call(/*@dependent@*/ ffi_cif *cif, - void (*fn)(), - /*@out@*/ void *rvalue, - /*@dependent@*/ void **avalue); - -/* Useful for eliminating compiler warnings */ -#define FFI_FN(f) ((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 1 -#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 - -/* This should always refer to the last type code (for sanity checks) */ -#define FFI_TYPE_LAST FFI_TYPE_POINTER - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/c/libffi_x86_x64/ffi_common.h b/c/libffi_x86_x64/ffi_common.h deleted file mode 100644 index 43fb83b..0000000 --- a/c/libffi_x86_x64/ffi_common.h +++ /dev/null @@ -1,77 +0,0 @@ -/* ----------------------------------------------------------------------- - ffi_common.h - Copyright (c) 1996 Red Hat, Inc. - - Common internal definitions and macros. Only necessary for building - libffi. - ----------------------------------------------------------------------- */ - -#ifndef FFI_COMMON_H -#define FFI_COMMON_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include <fficonfig.h> -#include <malloc.h> - -/* Check for the existence of memcpy. */ -#if STDC_HEADERS -# include <string.h> -#else -# ifndef HAVE_MEMCPY -# define memcpy(d, s, n) bcopy ((s), (d), (n)) -# endif -#endif - -#if defined(FFI_DEBUG) -#include <stdio.h> -#endif - -#ifdef FFI_DEBUG -/*@exits@*/ void ffi_assert(/*@temp@*/ char *expr, /*@temp@*/ char *file, int line); -void ffi_stop_here(void); -void ffi_type_test(/*@temp@*/ /*@out@*/ ffi_type *a, /*@temp@*/ char *file, int line); - -#define FFI_ASSERT(x) ((x) ? (void)0 : ffi_assert(#x, __FILE__,__LINE__)) -#define FFI_ASSERT_AT(x, f, l) ((x) ? 0 : ffi_assert(#x, (f), (l))) -#define FFI_ASSERT_VALID_TYPE(x) ffi_type_test (x, __FILE__, __LINE__) -#else -#define FFI_ASSERT(x) -#define FFI_ASSERT_AT(x, f, l) -#define FFI_ASSERT_VALID_TYPE(x) -#endif - -#define ALIGN(v, a) (((((size_t) (v))-1) | ((a)-1))+1) - -/* Perform machine dependent cif processing */ -ffi_status ffi_prep_cif_machdep(ffi_cif *cif); - -/* Extended cif, used in callback from assembly routine */ -typedef struct -{ - /*@dependent@*/ ffi_cif *cif; - /*@dependent@*/ void *rvalue; - /*@dependent@*/ void **avalue; -} extended_cif; - -/* Terse sized type definitions. */ -typedef unsigned int UINT8 __attribute__((__mode__(__QI__))); -typedef signed int SINT8 __attribute__((__mode__(__QI__))); -typedef unsigned int UINT16 __attribute__((__mode__(__HI__))); -typedef signed int SINT16 __attribute__((__mode__(__HI__))); -typedef unsigned int UINT32 __attribute__((__mode__(__SI__))); -typedef signed int SINT32 __attribute__((__mode__(__SI__))); -typedef unsigned int UINT64 __attribute__((__mode__(__DI__))); -typedef signed int SINT64 __attribute__((__mode__(__DI__))); - -typedef float FLOAT32; - - -#ifdef __cplusplus -} -#endif - -#endif - - diff --git a/c/libffi_x86_x64/fficonfig.h b/c/libffi_x86_x64/fficonfig.h deleted file mode 100644 index c14f653..0000000 --- a/c/libffi_x86_x64/fficonfig.h +++ /dev/null @@ -1,96 +0,0 @@ -/* fficonfig.h. Originally created by configure, now hand_maintained for MSVC. */ - -/* fficonfig.h. Generated automatically by configure. */ -/* fficonfig.h.in. Generated automatically from configure.in by autoheader. */ - -/* Define this for MSVC, but not for mingw32! */ -#ifdef _MSC_VER -#define __attribute__(x) /* */ -#endif -#define alloca _alloca - -/*----------------------------------------------------------------*/ - -/* Define if using alloca.c. */ -/* #undef C_ALLOCA */ - -/* 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 if you have alloca, as a function or macro. */ -#define HAVE_ALLOCA 1 - -/* Define if you have <alloca.h> and it should be used (not on Ultrix). */ -/* #define HAVE_ALLOCA_H 1 */ - -/* 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 run-time. - 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 if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - -/* Define if you have the memcpy function. */ -#define HAVE_MEMCPY 1 - -/* Define if read-only mmap of a plain file works. */ -//#define HAVE_MMAP_FILE 1 - -/* Define if mmap of /dev/zero works. */ -//#define HAVE_MMAP_DEV_ZERO 1 - -/* Define if mmap with MAP_ANON(YMOUS) works. */ -//#define HAVE_MMAP_ANON 1 - -/* The number of bytes in type double */ -#define SIZEOF_DOUBLE 8 - -/* The number of bytes in type long double */ -#define SIZEOF_LONG_DOUBLE 12 - -/* Define if you have the long double type and it is bigger than a double */ -#define HAVE_LONG_DOUBLE 1 - -/* whether byteorder is bigendian */ -/* #undef WORDS_BIGENDIAN */ - -/* Define if the host machine stores words of multi-word integers in - big-endian order. */ -/* #undef HOST_WORDS_BIG_ENDIAN */ - -/* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */ -#define BYTEORDER 1234 - -/* Define if your assembler and linker support unaligned PC relative relocs. */ -/* #undef HAVE_AS_SPARC_UA_PCREL */ - -/* Define if your assembler supports .register. */ -/* #undef HAVE_AS_REGISTER_PSEUDO_OP */ - -/* Define if .eh_frame sections should be read-only. */ -/* #undef HAVE_RO_EH_FRAME */ - -/* Define to the flags needed for the .section .eh_frame directive. */ -/* #define EH_FRAME_FLAGS "aw" */ - -/* Define to the flags needed for the .section .eh_frame directive. */ -/* #define EH_FRAME_FLAGS "aw" */ - -/* Define this if you want extra debugging. */ -/* #undef FFI_DEBUG */ - -/* Define this is you do not want support for aggregate types. */ -/* #undef FFI_NO_STRUCTS */ - -/* Define this is you do not want support for the raw API. */ -/* #undef FFI_NO_RAW_API */ - -/* Define this if you are using Purify and want to suppress spurious messages. */ -/* #undef USING_PURIFY */ - diff --git a/c/libffi_x86_x64/ffitarget.h b/c/libffi_x86_x64/ffitarget.h deleted file mode 100644 index 85f5ee8..0000000 --- a/c/libffi_x86_x64/ffitarget.h +++ /dev/null @@ -1,85 +0,0 @@ -/* -----------------------------------------------------------------*-C-*- - ffitarget.h - Copyright (c) 1996-2003 Red Hat, Inc. - Target configuration macros for x86 and x86-64. - - 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 CYGNUS SOLUTIONS 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 - -/* ---- System specific configurations ----------------------------------- */ - -#if defined (X86_64) && defined (__i386__) -#undef X86_64 -#define X86 -#endif - -/* ---- Generic type definitions ----------------------------------------- */ - -#ifndef LIBFFI_ASM -#ifndef _WIN64 -typedef unsigned long ffi_arg; -#else -typedef unsigned __int64 ffi_arg; -#endif -typedef signed long ffi_sarg; - -typedef enum ffi_abi { - FFI_FIRST_ABI = 0, - - /* ---- Intel x86 Win32 ---------- */ - FFI_SYSV, -#ifndef _WIN64 - FFI_STDCALL, -#endif - /* TODO: Add fastcall support for the sake of completeness */ - FFI_DEFAULT_ABI = FFI_SYSV, - - /* ---- Intel x86 and AMD x86-64 - */ -/* #if !defined(X86_WIN32) && (defined(__i386__) || defined(__x86_64__)) */ -/* FFI_SYSV, */ -/* FFI_UNIX64,*/ /* Unix variants all use the same ABI for x86-64 */ -/* #ifdef __i386__ */ -/* FFI_DEFAULT_ABI = FFI_SYSV, */ -/* #else */ -/* FFI_DEFAULT_ABI = FFI_UNIX64, */ -/* #endif */ -/* #endif */ - - FFI_LAST_ABI = FFI_DEFAULT_ABI + 1 -} ffi_abi; -#endif - -/* ---- Definitions for closures ----------------------------------------- */ - -#define FFI_CLOSURES 1 - -#ifdef _WIN64 -#define FFI_TRAMPOLINE_SIZE 29 -#define FFI_NATIVE_RAW_API 0 -#else -#define FFI_TRAMPOLINE_SIZE 15 -#define FFI_NATIVE_RAW_API 1 /* x86 has native raw api support */ -#endif - -#endif - diff --git a/c/libffi_x86_x64/prep_cif.c b/c/libffi_x86_x64/prep_cif.c deleted file mode 100644 index df94a98..0000000 --- a/c/libffi_x86_x64/prep_cif.c +++ /dev/null @@ -1,184 +0,0 @@ -/* ----------------------------------------------------------------------- - prep_cif.c - Copyright (c) 1996, 1998 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 CYGNUS SOLUTIONS 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. - ----------------------------------------------------------------------- */ - -#include <ffi.h> -#include <ffi_common.h> -#include <stdlib.h> - - -/* Round up to FFI_SIZEOF_ARG. */ - -#define STACK_ARG_SIZE(x) ALIGN(x, FFI_SIZEOF_ARG) - -/* Perform machine independent initialization of aggregate type - specifications. */ - -static ffi_status initialize_aggregate(/*@out@*/ ffi_type *arg) -{ - ffi_type **ptr; - - FFI_ASSERT(arg != NULL); - - /*@-usedef@*/ - - FFI_ASSERT(arg->elements != NULL); - FFI_ASSERT(arg->size == 0); - FFI_ASSERT(arg->alignment == 0); - - ptr = &(arg->elements[0]); - - while ((*ptr) != NULL) - { - if (((*ptr)->size == 0) && (initialize_aggregate((*ptr)) != FFI_OK)) - return FFI_BAD_TYPEDEF; - - /* Perform a sanity check on the argument type */ - FFI_ASSERT_VALID_TYPE(*ptr); - - arg->size = ALIGN(arg->size, (*ptr)->alignment); - arg->size += (*ptr)->size; - - arg->alignment = (arg->alignment > (*ptr)->alignment) ? - arg->alignment : (*ptr)->alignment; - - ptr++; - } - - /* Structure size includes tail padding. This is important for - structures that fit in one register on ABIs like the PowerPC64 - Linux ABI that right justify small structs in a register. - It's also needed for nested structure layout, for example - struct A { long a; char b; }; struct B { struct A x; char y; }; - should find y at an offset of 2*sizeof(long) and result in a - total size of 3*sizeof(long). */ - arg->size = ALIGN (arg->size, arg->alignment); - - if (arg->size == 0) - return FFI_BAD_TYPEDEF; - else - return FFI_OK; - - /*@=usedef@*/ -} - -/* Perform machine independent ffi_cif preparation, then call - machine dependent routine. */ - -ffi_status ffi_prep_cif(/*@out@*/ /*@partial@*/ ffi_cif *cif, - ffi_abi abi, unsigned int nargs, - /*@dependent@*/ /*@out@*/ /*@partial@*/ ffi_type *rtype, - /*@dependent@*/ ffi_type **atypes) -{ - unsigned bytes = 0; - unsigned int i; - ffi_type **ptr; - - FFI_ASSERT(cif != NULL); - FFI_ASSERT((abi > FFI_FIRST_ABI) && (abi <= FFI_DEFAULT_ABI)); - - cif->abi = abi; - cif->arg_types = atypes; - cif->nargs = nargs; - cif->rtype = rtype; - - cif->flags = 0; - - /* Initialize the return type if necessary */ - /*@-usedef@*/ - if ((cif->rtype->size == 0) && (initialize_aggregate(cif->rtype) != FFI_OK)) - return FFI_BAD_TYPEDEF; - /*@=usedef@*/ - - /* Perform a sanity check on the return type */ - FFI_ASSERT_VALID_TYPE(cif->rtype); - - /* x86-64 and s390 stack space allocation is handled in prep_machdep. */ -#if !defined M68K && !defined __x86_64__ && !defined S390 - /* Make space for the return structure pointer */ - if (cif->rtype->type == FFI_TYPE_STRUCT -#ifdef _WIN32 - && (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) -#endif - ) - bytes = STACK_ARG_SIZE(sizeof(void*)); -#endif - - for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++) - { - - /* Initialize any uninitialized aggregate type definitions */ - if (((*ptr)->size == 0) && (initialize_aggregate((*ptr)) != FFI_OK)) - return FFI_BAD_TYPEDEF; - - /* Perform a sanity check on the argument type, do this - check after the initialization. */ - FFI_ASSERT_VALID_TYPE(*ptr); - -#if !defined __x86_64__ && !defined S390 -#ifdef SPARC - if (((*ptr)->type == FFI_TYPE_STRUCT - && ((*ptr)->size > 16 || cif->abi != FFI_V9)) - || ((*ptr)->type == FFI_TYPE_LONGDOUBLE - && cif->abi != FFI_V9)) - bytes += sizeof(void*); - else -#endif - { -#if !defined(_MSC_VER) && !defined(__MINGW32__) - /* Don't know if this is a libffi bug or not. At least on - Windows with MSVC, function call parameters are *not* - aligned in the same way as structure fields are, they are - only aligned in integer boundaries. - - This doesn't do any harm for cdecl functions and closures, - since the caller cleans up the stack, but it is wrong for - stdcall functions where the callee cleans. - */ - - /* Add any padding if necessary */ - if (((*ptr)->alignment - 1) & bytes) - bytes = ALIGN(bytes, (*ptr)->alignment); - -#endif - bytes += STACK_ARG_SIZE((*ptr)->size); - } -#endif - } - -#ifdef _WIN64 - /* Function call needs at least 40 bytes stack size, on win64 AMD64 */ - if (bytes < 40) - bytes = 40; -#endif - - cif->bytes = bytes; - - /* Perform machine dependent cif processing */ - return ffi_prep_cif_machdep(cif); -} diff --git a/c/libffi_x86_x64/types.c b/c/libffi_x86_x64/types.c deleted file mode 100644 index 4433ac2..0000000 --- a/c/libffi_x86_x64/types.c +++ /dev/null @@ -1,104 +0,0 @@ -/* ----------------------------------------------------------------------- - types.c - Copyright (c) 1996, 1998 Red Hat, Inc. - - Predefined ffi_types needed by libffi. - - 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 CYGNUS SOLUTIONS 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. - ----------------------------------------------------------------------- */ - -#include <ffi.h> -#include <ffi_common.h> - -/* Type definitions */ - -#define FFI_INTEGRAL_TYPEDEF(n, s, a, t) ffi_type ffi_type_##n = { s, a, t, NULL } -#define FFI_AGGREGATE_TYPEDEF(n, e) ffi_type ffi_type_##n = { 0, 0, FFI_TYPE_STRUCT, e } - -/* Size and alignment are fake here. They must not be 0. */ -FFI_INTEGRAL_TYPEDEF(void, 1, 1, FFI_TYPE_VOID); - -FFI_INTEGRAL_TYPEDEF(uint8, 1, 1, FFI_TYPE_UINT8); -FFI_INTEGRAL_TYPEDEF(sint8, 1, 1, FFI_TYPE_SINT8); -FFI_INTEGRAL_TYPEDEF(uint16, 2, 2, FFI_TYPE_UINT16); -FFI_INTEGRAL_TYPEDEF(sint16, 2, 2, FFI_TYPE_SINT16); -FFI_INTEGRAL_TYPEDEF(uint32, 4, 4, FFI_TYPE_UINT32); -FFI_INTEGRAL_TYPEDEF(sint32, 4, 4, FFI_TYPE_SINT32); -FFI_INTEGRAL_TYPEDEF(float, 4, 4, FFI_TYPE_FLOAT); - -#if defined ALPHA || defined SPARC64 || defined X86_64 || defined S390X \ - || defined IA64 || defined _WIN64 - -FFI_INTEGRAL_TYPEDEF(pointer, 8, 8, FFI_TYPE_POINTER); - -#else - -FFI_INTEGRAL_TYPEDEF(pointer, 4, 4, FFI_TYPE_POINTER); - -#endif - -#if defined X86 || defined X86_WIN32 || defined ARM || defined M68K - -FFI_INTEGRAL_TYPEDEF(uint64, 8, 4, FFI_TYPE_UINT64); -FFI_INTEGRAL_TYPEDEF(sint64, 8, 4, FFI_TYPE_SINT64); - -#elif defined SH - -FFI_INTEGRAL_TYPEDEF(uint64, 8, 4, FFI_TYPE_UINT64); -FFI_INTEGRAL_TYPEDEF(sint64, 8, 4, FFI_TYPE_SINT64); - -#else - -FFI_INTEGRAL_TYPEDEF(uint64, 8, 8, FFI_TYPE_UINT64); -FFI_INTEGRAL_TYPEDEF(sint64, 8, 8, FFI_TYPE_SINT64); - -#endif - - -#if defined X86 || defined X86_WIN32 || defined M68K - -FFI_INTEGRAL_TYPEDEF(double, 8, 4, FFI_TYPE_DOUBLE); -FFI_INTEGRAL_TYPEDEF(longdouble, 12, 4, FFI_TYPE_LONGDOUBLE); - -#elif defined ARM || defined SH || defined POWERPC_AIX || defined POWERPC_DARWIN - -FFI_INTEGRAL_TYPEDEF(double, 8, 4, FFI_TYPE_DOUBLE); -FFI_INTEGRAL_TYPEDEF(longdouble, 8, 4, FFI_TYPE_LONGDOUBLE); - -#elif defined SPARC - -FFI_INTEGRAL_TYPEDEF(double, 8, 8, FFI_TYPE_DOUBLE); -#ifdef SPARC64 -FFI_INTEGRAL_TYPEDEF(longdouble, 16, 16, FFI_TYPE_LONGDOUBLE); -#else -FFI_INTEGRAL_TYPEDEF(longdouble, 16, 8, FFI_TYPE_LONGDOUBLE); -#endif - -#elif defined X86_64 - -FFI_INTEGRAL_TYPEDEF(double, 8, 8, FFI_TYPE_DOUBLE); -FFI_INTEGRAL_TYPEDEF(longdouble, 16, 16, FFI_TYPE_LONGDOUBLE); - -#else - -FFI_INTEGRAL_TYPEDEF(double, 8, 8, FFI_TYPE_DOUBLE); -FFI_INTEGRAL_TYPEDEF(longdouble, 8, 8, FFI_TYPE_LONGDOUBLE); - -#endif - diff --git a/c/libffi_x86_x64/win32.c b/c/libffi_x86_x64/win32.c deleted file mode 100644 index d1149a8..0000000 --- a/c/libffi_x86_x64/win32.c +++ /dev/null @@ -1,162 +0,0 @@ -/* ----------------------------------------------------------------------- - win32.S - Copyright (c) 1996, 1998, 2001, 2002 Red Hat, Inc. - Copyright (c) 2001 John Beniton - Copyright (c) 2002 Ranjit Mathew - - - X86 Foreign Function Interface - - 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 CYGNUS SOLUTIONS 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. - ----------------------------------------------------------------------- */ - -/* theller: almost verbatim translation from gas syntax to MSVC inline - assembler code. */ - -/* theller: ffi_call_x86 now returns an integer - the difference of the stack - pointer before and after the function call. If everything is ok, zero is - returned. If stdcall functions are passed the wrong number of arguments, - the difference will be nonzero. */ - -#include <ffi.h> -#include <ffi_common.h> - -__declspec(naked) int -ffi_call_x86(void (* prepfunc)(char *, extended_cif *), /* 8 */ - extended_cif *ecif, /* 12 */ - unsigned bytes, /* 16 */ - unsigned flags, /* 20 */ - unsigned *rvalue, /* 24 */ - void (*fn)()) /* 28 */ -{ - _asm { - push ebp - mov ebp, esp - - push esi // NEW: this register must be preserved across function calls -// XXX SAVE ESP NOW! - mov esi, esp // save stack pointer before the call - -// Make room for all of the new args. - mov ecx, [ebp+16] - sub esp, ecx // sub esp, bytes - - mov eax, esp - -// Place all of the ffi_prep_args in position - push [ebp + 12] // ecif - push eax - call [ebp + 8] // prepfunc - -// Return stack to previous state and call the function - add esp, 8 -// FIXME: Align the stack to a 128-bit boundary to avoid -// potential performance hits. - call [ebp + 28] - -// Load ecif->cif->abi - mov ecx, [ebp + 12] - mov ecx, [ecx]ecif.cif - mov ecx, [ecx]ecif.cif.abi - - cmp ecx, FFI_STDCALL - je noclean -// STDCALL: Remove the space we pushed for the args - mov ecx, [ebp + 16] - add esp, ecx -// CDECL: Caller has already cleaned the stack -noclean: -// Check that esp has the same value as before! - sub esi, esp - -// Load %ecx with the return type code - mov ecx, [ebp + 20] - -// If the return value pointer is NULL, assume no return value. -/* - Intel asm is weird. We have to explicitely specify 'DWORD PTR' in the nexr instruction, - otherwise only one BYTE will be compared (instead of a DWORD)! - */ - cmp DWORD PTR [ebp + 24], 0 - jne sc_retint - -// Even if there is no space for the return value, we are -// obliged to handle floating-point values. - cmp ecx, FFI_TYPE_FLOAT - jne sc_noretval -// fstp %st(0) - fstp st(0) - - jmp sc_epilogue - -sc_retint: - cmp ecx, FFI_TYPE_INT - jne sc_retfloat -// # Load %ecx with the pointer to storage for the return value - mov ecx, [ebp + 24] - mov [ecx + 0], eax - jmp sc_epilogue - -sc_retfloat: - cmp ecx, FFI_TYPE_FLOAT - jne sc_retdouble -// Load %ecx with the pointer to storage for the return value - mov ecx, [ebp+24] -// fstps (%ecx) - fstp DWORD PTR [ecx] - jmp sc_epilogue - -sc_retdouble: - cmp ecx, FFI_TYPE_DOUBLE - jne sc_retlongdouble -// movl 24(%ebp),%ecx - mov ecx, [ebp+24] - fstp QWORD PTR [ecx] - jmp sc_epilogue - - jmp sc_retlongdouble // avoid warning about unused label -sc_retlongdouble: - cmp ecx, FFI_TYPE_LONGDOUBLE - jne sc_retint64 -// Load %ecx with the pointer to storage for the return value - mov ecx, [ebp+24] -// fstpt (%ecx) - fstp QWORD PTR [ecx] /* XXX ??? */ - jmp sc_epilogue - -sc_retint64: - cmp ecx, FFI_TYPE_SINT64 - jne sc_retstruct -// Load %ecx with the pointer to storage for the return value - mov ecx, [ebp+24] - mov [ecx+0], eax - mov [ecx+4], edx - -sc_retstruct: -// Nothing to do! - -sc_noretval: -sc_epilogue: - mov eax, esi - pop esi // NEW restore: must be preserved across function calls - mov esp, ebp - pop ebp - ret - } -} diff --git a/c/libffi_x86_x64/win64.asm b/c/libffi_x86_x64/win64.asm deleted file mode 100644 index 301188b..0000000 --- a/c/libffi_x86_x64/win64.asm +++ /dev/null @@ -1,156 +0,0 @@ -PUBLIC ffi_call_AMD64 - -EXTRN __chkstk:NEAR -EXTRN ffi_closure_SYSV:NEAR - -_TEXT SEGMENT - -;;; ffi_closure_OUTER will be called with these registers set: -;;; rax points to 'closure' -;;; r11 contains a bit mask that specifies which of the -;;; first four parameters are float or double -;;; -;;; It must move the parameters passed in registers to their stack location, -;;; call ffi_closure_SYSV for the actual work, then return the result. -;;; -ffi_closure_OUTER PROC FRAME - ;; save actual arguments to their stack space. - test r11, 1 - jne first_is_float - mov QWORD PTR [rsp+8], rcx - jmp second -first_is_float: - movlpd QWORD PTR [rsp+8], xmm0 - -second: - test r11, 2 - jne second_is_float - mov QWORD PTR [rsp+16], rdx - jmp third -second_is_float: - movlpd QWORD PTR [rsp+16], xmm1 - -third: - test r11, 4 - jne third_is_float - mov QWORD PTR [rsp+24], r8 - jmp forth -third_is_float: - movlpd QWORD PTR [rsp+24], xmm2 - -forth: - test r11, 8 - jne forth_is_float - mov QWORD PTR [rsp+32], r9 - jmp done -forth_is_float: - movlpd QWORD PTR [rsp+32], xmm3 - -done: -.ALLOCSTACK 40 - sub rsp, 40 -.ENDPROLOG - mov rcx, rax ; context is first parameter - mov rdx, rsp ; stack is second parameter - add rdx, 40 ; correct our own area - mov rax, ffi_closure_SYSV - call rax ; call the real closure function - ;; Here, code is missing that handles float return values - add rsp, 40 - movd xmm0, rax ; In case the closure returned a float. - ret 0 -ffi_closure_OUTER ENDP - - -;;; ffi_call_AMD64 - -stack$ = 0 -prepfunc$ = 32 -ecif$ = 40 -bytes$ = 48 -flags$ = 56 -rvalue$ = 64 -fn$ = 72 - -ffi_call_AMD64 PROC FRAME - - mov QWORD PTR [rsp+32], r9 - mov QWORD PTR [rsp+24], r8 - mov QWORD PTR [rsp+16], rdx - mov QWORD PTR [rsp+8], rcx -.PUSHREG rbp - push rbp -.ALLOCSTACK 48 - sub rsp, 48 ; 00000030H -.SETFRAME rbp, 32 - lea rbp, QWORD PTR [rsp+32] -.ENDPROLOG - - mov eax, DWORD PTR bytes$[rbp] - add rax, 15 - and rax, -16 - call __chkstk - sub rsp, rax - lea rax, QWORD PTR [rsp+32] - mov QWORD PTR stack$[rbp], rax - - mov rdx, QWORD PTR ecif$[rbp] - mov rcx, QWORD PTR stack$[rbp] - call QWORD PTR prepfunc$[rbp] - - mov rsp, QWORD PTR stack$[rbp] - - movlpd xmm3, QWORD PTR [rsp+24] - movd r9, xmm3 - - movlpd xmm2, QWORD PTR [rsp+16] - movd r8, xmm2 - - movlpd xmm1, QWORD PTR [rsp+8] - movd rdx, xmm1 - - movlpd xmm0, QWORD PTR [rsp] - movd rcx, xmm0 - - call QWORD PTR fn$[rbp] -ret_int$: - cmp DWORD PTR flags$[rbp], 1 ; FFI_TYPE_INT - jne ret_float$ - - mov rcx, QWORD PTR rvalue$[rbp] - mov DWORD PTR [rcx], eax - jmp SHORT ret_nothing$ - -ret_float$: - cmp DWORD PTR flags$[rbp], 2 ; FFI_TYPE_FLOAT - jne SHORT ret_double$ - - mov rax, QWORD PTR rvalue$[rbp] - movlpd QWORD PTR [rax], xmm0 - jmp SHORT ret_nothing$ - -ret_double$: - cmp DWORD PTR flags$[rbp], 3 ; FFI_TYPE_DOUBLE - jne SHORT ret_int64$ - - mov rax, QWORD PTR rvalue$[rbp] - movlpd QWORD PTR [rax], xmm0 - jmp SHORT ret_nothing$ - -ret_int64$: - cmp DWORD PTR flags$[rbp], 12 ; FFI_TYPE_SINT64 - jne ret_nothing$ - - mov rcx, QWORD PTR rvalue$[rbp] - mov QWORD PTR [rcx], rax - jmp SHORT ret_nothing$ - -ret_nothing$: - xor eax, eax - - lea rsp, QWORD PTR [rbp+16] - pop rbp - ret 0 -ffi_call_AMD64 ENDP -_TEXT ENDS -END diff --git a/c/libffi_x86_x64/win64.obj b/c/libffi_x86_x64/win64.obj Binary files differdeleted file mode 100644 index 38d3cd1..0000000 --- a/c/libffi_x86_x64/win64.obj +++ /dev/null diff --git a/c/malloc_closure.h b/c/malloc_closure.h deleted file mode 100644 index bebb93d..0000000 --- a/c/malloc_closure.h +++ /dev/null @@ -1,176 +0,0 @@ -/* - * This file is from CPython's Modules/_ctypes/malloc_closure.c - * and has received some edits. - */ - -#include <ffi.h> -#ifdef MS_WIN32 -#include <windows.h> -#else -#include <sys/mman.h> -#include <unistd.h> -# if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) -# define MAP_ANONYMOUS MAP_ANON -# endif -#endif - -/* On PaX enable kernels that have MPROTECT enable we can't use PROT_EXEC. - - This is, apparently, an undocumented change to ffi_prep_closure(): - depending on the Linux kernel we're running on, we must give it a - mmap that is either PROT_READ|PROT_WRITE|PROT_EXEC or only - PROT_READ|PROT_WRITE. In the latter case, just trying to obtain a - mmap with PROT_READ|PROT_WRITE|PROT_EXEC would kill our process(!), - but in that situation libffi is fine with only PROT_READ|PROT_WRITE. - There is nothing in the libffi API to know that, though, so we have - to guess by parsing /proc/self/status. "Meh." - */ -#ifdef __linux__ -#include <stdlib.h> - -static int emutramp_enabled = -1; - -static int -emutramp_enabled_check (void) -{ - char *buf = NULL; - size_t len = 0; - FILE *f; - int ret; - f = fopen ("/proc/self/status", "r"); - if (f == NULL) - return 0; - ret = 0; - - while (getline (&buf, &len, f) != -1) - if (!strncmp (buf, "PaX:", 4)) - { - char emutramp; - if (sscanf (buf, "%*s %*c%c", &emutramp) == 1) - ret = (emutramp == 'E'); - break; - } - free (buf); - fclose (f); - return ret; -} - -#define is_emutramp_enabled() (emutramp_enabled >= 0 ? emutramp_enabled \ - : (emutramp_enabled = emutramp_enabled_check ())) -#else -#define is_emutramp_enabled() 0 -#endif - - -/* 'allocate_num_pages' is dynamically adjusted starting from one - page. It grows by a factor of PAGE_ALLOCATION_GROWTH_RATE. This is - meant to handle both the common case of not needing a lot of pages, - and the rare case of needing many of them. Systems in general have a - limit of how many mmap'd blocks can be open. -*/ - -#define PAGE_ALLOCATION_GROWTH_RATE 1.3 - -static Py_ssize_t allocate_num_pages = 0; - -/* #define MALLOC_CLOSURE_DEBUG */ /* enable for some debugging output */ - -/******************************************************************/ - -union mmaped_block { - ffi_closure closure; - union mmaped_block *next; -}; - -static union mmaped_block *free_list = 0; -static Py_ssize_t _pagesize = 0; - -static void more_core(void) -{ - union mmaped_block *item; - Py_ssize_t count, i; - -/* determine the pagesize */ -#ifdef MS_WIN32 - if (!_pagesize) { - SYSTEM_INFO systeminfo; - GetSystemInfo(&systeminfo); - _pagesize = systeminfo.dwPageSize; - } -#else - if (!_pagesize) { -#ifdef _SC_PAGESIZE - _pagesize = sysconf(_SC_PAGESIZE); -#else - _pagesize = getpagesize(); -#endif - } -#endif - if (_pagesize <= 0) - _pagesize = 4096; - - /* bump 'allocate_num_pages' */ - allocate_num_pages = 1 + ( - (Py_ssize_t)(allocate_num_pages * PAGE_ALLOCATION_GROWTH_RATE)); - - /* calculate the number of mmaped_blocks to allocate */ - count = (allocate_num_pages * _pagesize) / sizeof(union mmaped_block); - - /* allocate a memory block */ -#ifdef MS_WIN32 - item = (union mmaped_block *)VirtualAlloc(NULL, - count * sizeof(union mmaped_block), - MEM_COMMIT, - PAGE_EXECUTE_READWRITE); - if (item == NULL) - return; -#else - { - int prot = PROT_READ | PROT_WRITE | PROT_EXEC; - if (is_emutramp_enabled ()) - prot &= ~PROT_EXEC; - item = (union mmaped_block *)mmap(NULL, - allocate_num_pages * _pagesize, - prot, - MAP_PRIVATE | MAP_ANONYMOUS, - -1, - 0); - if (item == (void *)MAP_FAILED) - return; - } -#endif - -#ifdef MALLOC_CLOSURE_DEBUG - printf("block at %p allocated (%ld bytes), %ld mmaped_blocks\n", - item, (long)(allocate_num_pages * _pagesize), (long)count); -#endif - /* put them into the free list */ - for (i = 0; i < count; ++i) { - item->next = free_list; - free_list = item; - ++item; - } -} - -/******************************************************************/ - -/* put the item back into the free list */ -static void cffi_closure_free(ffi_closure *p) -{ - union mmaped_block *item = (union mmaped_block *)p; - item->next = free_list; - free_list = item; -} - -/* return one item from the free list, allocating more if needed */ -static ffi_closure *cffi_closure_alloc(void) -{ - union mmaped_block *item; - if (!free_list) - more_core(); - if (!free_list) - return NULL; - item = free_list; - free_list = item->next; - return &item->closure; -} diff --git a/c/minibuffer.h b/c/minibuffer.h deleted file mode 100644 index f3f5ca1..0000000 --- a/c/minibuffer.h +++ /dev/null @@ -1,408 +0,0 @@ - -/* Implementation of a C object with the 'buffer' or 'memoryview' - * interface at C-level (as approriate for the version of Python we're - * compiling for), but only a minimal but *consistent* part of the - * 'buffer' interface at application level. - */ - -typedef struct { - PyObject_HEAD - char *mb_data; - Py_ssize_t mb_size; - PyObject *mb_keepalive; - PyObject *mb_weakreflist; /* weakref support */ -} MiniBufferObj; - -static Py_ssize_t mb_length(MiniBufferObj *self) -{ - return self->mb_size; -} - -static PyObject *mb_item(MiniBufferObj *self, Py_ssize_t idx) -{ - if (idx < 0 || idx >= self->mb_size ) { - PyErr_SetString(PyExc_IndexError, "buffer index out of range"); - return NULL; - } - return PyBytes_FromStringAndSize(self->mb_data + idx, 1); -} - -static PyObject *mb_slice(MiniBufferObj *self, - Py_ssize_t left, Py_ssize_t right) -{ - Py_ssize_t size = self->mb_size; - if (left < 0) left = 0; - if (right > size) right = size; - if (left > right) left = right; - return PyBytes_FromStringAndSize(self->mb_data + left, right - left); -} - -static int mb_ass_item(MiniBufferObj *self, Py_ssize_t idx, PyObject *other) -{ - if (idx < 0 || idx >= self->mb_size) { - PyErr_SetString(PyExc_IndexError, - "buffer assignment index out of range"); - return -1; - } - if (PyBytes_Check(other) && PyBytes_GET_SIZE(other) == 1) { - self->mb_data[idx] = PyBytes_AS_STRING(other)[0]; - return 0; - } - else { - PyErr_Format(PyExc_TypeError, - "must assign a "STR_OR_BYTES - " of length 1, not %.200s", Py_TYPE(other)->tp_name); - return -1; - } -} - -/* forward: from _cffi_backend.c */ -static int _fetch_as_buffer(PyObject *x, Py_buffer *view, int writable_only); - -static int mb_ass_slice(MiniBufferObj *self, - Py_ssize_t left, Py_ssize_t right, PyObject *other) -{ - Py_ssize_t count; - Py_ssize_t size = self->mb_size; - Py_buffer src_view; - - if (_fetch_as_buffer(other, &src_view, 0) < 0) - return -1; - - if (left < 0) left = 0; - if (right > size) right = size; - if (left > right) left = right; - - count = right - left; - if (count != src_view.len) { - PyBuffer_Release(&src_view); - PyErr_SetString(PyExc_ValueError, - "right operand length must match slice length"); - return -1; - } - memcpy(self->mb_data + left, src_view.buf, count); - PyBuffer_Release(&src_view); - return 0; -} - -#if PY_MAJOR_VERSION < 3 -static Py_ssize_t mb_getdata(MiniBufferObj *self, Py_ssize_t idx, void **pp) -{ - *pp = self->mb_data; - return self->mb_size; -} - -static Py_ssize_t mb_getsegcount(MiniBufferObj *self, Py_ssize_t *lenp) -{ - if (lenp) - *lenp = self->mb_size; - return 1; -} - -static PyObject *mb_str(MiniBufferObj *self) -{ - /* Python 2: we want str(buffer) to behave like buffer[:], because - that's what bytes(buffer) does on Python 3 and there is no way - we can prevent this. */ - return PyString_FromStringAndSize(self->mb_data, self->mb_size); -} -#endif - -static int mb_getbuf(MiniBufferObj *self, Py_buffer *view, int flags) -{ - return PyBuffer_FillInfo(view, (PyObject *)self, - self->mb_data, self->mb_size, - /*readonly=*/0, flags); -} - -static PySequenceMethods mb_as_sequence = { - (lenfunc)mb_length, /*sq_length*/ - (binaryfunc)0, /*sq_concat*/ - (ssizeargfunc)0, /*sq_repeat*/ - (ssizeargfunc)mb_item, /*sq_item*/ - (ssizessizeargfunc)mb_slice, /*sq_slice*/ - (ssizeobjargproc)mb_ass_item, /*sq_ass_item*/ - (ssizessizeobjargproc)mb_ass_slice, /*sq_ass_slice*/ -}; - -static PyBufferProcs mb_as_buffer = { -#if PY_MAJOR_VERSION < 3 - (readbufferproc)mb_getdata, - (writebufferproc)mb_getdata, - (segcountproc)mb_getsegcount, - (charbufferproc)mb_getdata, -#endif - (getbufferproc)mb_getbuf, - (releasebufferproc)0, -}; - -static void -mb_dealloc(MiniBufferObj *ob) -{ - PyObject_GC_UnTrack(ob); - if (ob->mb_weakreflist != NULL) - PyObject_ClearWeakRefs((PyObject *)ob); - Py_XDECREF(ob->mb_keepalive); - Py_TYPE(ob)->tp_free((PyObject *)ob); -} - -static int -mb_traverse(MiniBufferObj *ob, visitproc visit, void *arg) -{ - Py_VISIT(ob->mb_keepalive); - return 0; -} - -static int -mb_clear(MiniBufferObj *ob) -{ - Py_CLEAR(ob->mb_keepalive); - return 0; -} - -static PyObject * -mb_richcompare(PyObject *self, PyObject *other, int op) -{ - Py_ssize_t self_size, other_size; - Py_buffer self_bytes, other_bytes; - PyObject *res; - Py_ssize_t minsize; - int cmp, rc; - - /* Bytes can be compared to anything that supports the (binary) - buffer API. Except that a comparison with Unicode is always an - error, even if the comparison is for equality. */ - rc = PyObject_IsInstance(self, (PyObject*)&PyUnicode_Type); - if (!rc) - rc = PyObject_IsInstance(other, (PyObject*)&PyUnicode_Type); - if (rc < 0) - return NULL; - if (rc) { - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - - if (PyObject_GetBuffer(self, &self_bytes, PyBUF_SIMPLE) != 0) { - PyErr_Clear(); - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - - } - self_size = self_bytes.len; - - if (PyObject_GetBuffer(other, &other_bytes, PyBUF_SIMPLE) != 0) { - PyErr_Clear(); - PyBuffer_Release(&self_bytes); - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - - } - other_size = other_bytes.len; - - if (self_size != other_size && (op == Py_EQ || op == Py_NE)) { - /* Shortcut: if the lengths differ, the objects differ */ - cmp = (op == Py_NE); - } - else { - minsize = self_size; - if (other_size < minsize) - minsize = other_size; - - cmp = memcmp(self_bytes.buf, other_bytes.buf, minsize); - /* In ISO C, memcmp() guarantees to use unsigned bytes! */ - - if (cmp == 0) { - if (self_size < other_size) - cmp = -1; - else if (self_size > other_size) - cmp = 1; - } - - switch (op) { - case Py_LT: cmp = cmp < 0; break; - case Py_LE: cmp = cmp <= 0; break; - case Py_EQ: cmp = cmp == 0; break; - case Py_NE: cmp = cmp != 0; break; - case Py_GT: cmp = cmp > 0; break; - case Py_GE: cmp = cmp >= 0; break; - } - } - - res = cmp ? Py_True : Py_False; - PyBuffer_Release(&self_bytes); - PyBuffer_Release(&other_bytes); - Py_INCREF(res); - return res; -} - -#if PY_MAJOR_VERSION >= 3 -/* pfffffffffffff pages of copy-paste from listobject.c */ - -/* pfffffffffffff#2: the PySlice_GetIndicesEx() *macro* should not - be called, because C extension modules compiled with it differ - on ABI between 3.6.0, 3.6.1 and 3.6.2. */ -#if PY_VERSION_HEX < 0x03070000 && defined(PySlice_GetIndicesEx) && !defined(PYPY_VERSION) -#undef PySlice_GetIndicesEx -#endif - -static PyObject *mb_subscript(MiniBufferObj *self, PyObject *item) -{ - if (PyIndex_Check(item)) { - Py_ssize_t i; - i = PyNumber_AsSsize_t(item, PyExc_IndexError); - if (i == -1 && PyErr_Occurred()) - return NULL; - if (i < 0) - i += self->mb_size; - return mb_item(self, i); - } - else if (PySlice_Check(item)) { - Py_ssize_t start, stop, step, slicelength; - - if (PySlice_GetIndicesEx(item, self->mb_size, - &start, &stop, &step, &slicelength) < 0) - return NULL; - - if (step == 1) - return mb_slice(self, start, stop); - else { - PyErr_SetString(PyExc_TypeError, - "buffer doesn't support slicing with step != 1"); - return NULL; - } - } - else { - PyErr_Format(PyExc_TypeError, - "buffer indices must be integers, not %.200s", - item->ob_type->tp_name); - return NULL; - } -} -static int -mb_ass_subscript(MiniBufferObj* self, PyObject* item, PyObject* value) -{ - if (PyIndex_Check(item)) { - Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); - if (i == -1 && PyErr_Occurred()) - return -1; - if (i < 0) - i += self->mb_size; - return mb_ass_item(self, i, value); - } - else if (PySlice_Check(item)) { - Py_ssize_t start, stop, step, slicelength; - - if (PySlice_GetIndicesEx(item, self->mb_size, - &start, &stop, &step, &slicelength) < 0) { - return -1; - } - - if (step == 1) - return mb_ass_slice(self, start, stop, value); - else { - PyErr_SetString(PyExc_TypeError, - "buffer doesn't support slicing with step != 1"); - return -1; - } - } - else { - PyErr_Format(PyExc_TypeError, - "buffer indices must be integers, not %.200s", - item->ob_type->tp_name); - return -1; - } -} - -static PyMappingMethods mb_as_mapping = { - (lenfunc)mb_length, /*mp_length*/ - (binaryfunc)mb_subscript, /*mp_subscript*/ - (objobjargproc)mb_ass_subscript, /*mp_ass_subscript*/ -}; -#endif - -#if PY_MAJOR_VERSION >= 3 -# define MINIBUF_TPFLAGS 0 -#else -# define MINIBUF_TPFLAGS (Py_TPFLAGS_HAVE_GETCHARBUFFER | Py_TPFLAGS_HAVE_NEWBUFFER) -#endif - -PyDoc_STRVAR(ffi_buffer_doc, -"ffi.buffer(cdata[, byte_size]):\n" -"Return a read-write buffer object that references the raw C data\n" -"pointed to by the given 'cdata'. The 'cdata' must be a pointer or an\n" -"array. Can be passed to functions expecting a buffer, or directly\n" -"manipulated with:\n" -"\n" -" buf[:] get a copy of it in a regular string, or\n" -" buf[idx] as a single character\n" -" buf[:] = ...\n" -" buf[idx] = ... change the content"); - -static PyObject * /* forward, implemented in _cffi_backend.c */ -b_buffer_new(PyTypeObject *type, PyObject *args, PyObject *kwds); - - -static PyTypeObject MiniBuffer_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_cffi_backend.buffer", - sizeof(MiniBufferObj), - 0, - (destructor)mb_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - &mb_as_sequence, /* tp_as_sequence */ -#if PY_MAJOR_VERSION < 3 - 0, /* tp_as_mapping */ -#else - &mb_as_mapping, /* tp_as_mapping */ -#endif - 0, /* tp_hash */ - 0, /* tp_call */ -#if PY_MAJOR_VERSION < 3 - (reprfunc)mb_str, /* tp_str */ -#else - 0, /* tp_str */ -#endif - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - &mb_as_buffer, /* tp_as_buffer */ - (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - MINIBUF_TPFLAGS), /* tp_flags */ - ffi_buffer_doc, /* tp_doc */ - (traverseproc)mb_traverse, /* tp_traverse */ - (inquiry)mb_clear, /* tp_clear */ - (richcmpfunc)mb_richcompare, /* tp_richcompare */ - offsetof(MiniBufferObj, mb_weakreflist), /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - b_buffer_new, /* tp_new */ - 0, /* tp_free */ -}; - -static PyObject *minibuffer_new(char *data, Py_ssize_t size, - PyObject *keepalive) -{ - MiniBufferObj *ob = PyObject_GC_New(MiniBufferObj, &MiniBuffer_Type); - if (ob != NULL) { - ob->mb_data = data; - ob->mb_size = size; - ob->mb_keepalive = keepalive; Py_INCREF(keepalive); - ob->mb_weakreflist = NULL; - PyObject_GC_Track(ob); - } - return (PyObject *)ob; -} diff --git a/c/misc_thread_common.h b/c/misc_thread_common.h deleted file mode 100644 index 66e2835..0000000 --- a/c/misc_thread_common.h +++ /dev/null @@ -1,371 +0,0 @@ -#ifndef WITH_THREAD -# error "xxx no-thread configuration not tested, please report if you need that" -#endif -#include "pythread.h" - - -struct cffi_tls_s { - /* The current thread's ThreadCanaryObj. This is only non-null in - case cffi builds the thread state here. It remains null if this - thread had already a thread state provided by CPython. */ - struct thread_canary_s *local_thread_canary; - -#ifndef USE__THREAD - /* The saved errno. If the C compiler supports '__thread', then - we use that instead. */ - int saved_errno; -#endif - -#ifdef MS_WIN32 - /* The saved lasterror, on Windows. */ - int saved_lasterror; -#endif -}; - -static struct cffi_tls_s *get_cffi_tls(void); /* in misc_thread_posix.h - or misc_win32.h */ - - -/* We try to keep the PyThreadState around in a thread not started by - * Python but where cffi callbacks occur. If we didn't do that, then - * the standard logic in PyGILState_Ensure() and PyGILState_Release() - * would create a new PyThreadState and completely free it for every - * single call. For some applications, this is a huge slow-down. - * - * As shown by issue #362, it is quite messy to do. The current - * solution is to keep the PyThreadState alive by incrementing its - * 'gilstate_counter'. We detect thread shut-down, and we put the - * PyThreadState inside a list of zombies (we can't free it - * immediately because we don't have the GIL at that point in time). - * We also detect other pieces of code (notably Py_Finalize()) which - * clear and free PyThreadStates under our feet, using ThreadCanaryObj. - */ - -#define TLS_ZOM_LOCK() PyThread_acquire_lock(cffi_zombie_lock, WAIT_LOCK) -#define TLS_ZOM_UNLOCK() PyThread_release_lock(cffi_zombie_lock) -static PyThread_type_lock cffi_zombie_lock = NULL; - - -/* A 'canary' object is created in a thread when there is a callback - invoked, and that thread has no PyThreadState so far. It is an - object of reference count equal to 1, which is stored in the - PyThreadState->dict. Two things can occur then: - - 1. The PyThreadState can be forcefully cleared by Py_Finalize(). - Then thread_canary_dealloc() is called, and we have to cancel - the hacks we did to keep the PyThreadState alive. - - 2. The thread finishes. In that case, we put the canary in a list - of zombies, and at some convenient time later when we have the - GIL, we free all PyThreadStates in the zombie list. - - Some more fun comes from the fact that thread_canary_dealloc() can - be called at a point where the canary is in the zombie list already. - Also, the various pieces are freed at specific points in time, and - we must make sure not to access already-freed structures: - - - the struct cffi_tls_s is valid until the thread shuts down, and - then it is freed by cffi_thread_shutdown(). - - - the canary is a normal Python object, but we have a borrowed - reference to it from cffi_tls_s.local_thread_canary. - */ - -typedef struct thread_canary_s { - PyObject_HEAD - struct thread_canary_s *zombie_prev, *zombie_next; - PyThreadState *tstate; - struct cffi_tls_s *tls; -} ThreadCanaryObj; - -static PyTypeObject ThreadCanary_Type; /* forward */ -static ThreadCanaryObj cffi_zombie_head; - -static void -_thread_canary_detach_with_lock(ThreadCanaryObj *ob) -{ - /* must be called with both the GIL and TLS_ZOM_LOCK. */ - ThreadCanaryObj *p, *n; - p = ob->zombie_prev; - n = ob->zombie_next; - p->zombie_next = n; - n->zombie_prev = p; - ob->zombie_prev = NULL; - ob->zombie_next = NULL; -} - -static void -thread_canary_dealloc(ThreadCanaryObj *ob) -{ - /* this ThreadCanaryObj is being freed: if it is in the zombie - chained list, remove it. Thread-safety: 'zombie_next' amd - 'local_thread_canary' accesses need to be protected with - the TLS_ZOM_LOCK. - */ - TLS_ZOM_LOCK(); - if (ob->zombie_next != NULL) { - //fprintf(stderr, "thread_canary_dealloc(%p): ZOMBIE\n", ob); - _thread_canary_detach_with_lock(ob); - } - else { - //fprintf(stderr, "thread_canary_dealloc(%p): not a zombie\n", ob); - } - - if (ob->tls != NULL) { - //fprintf(stderr, "thread_canary_dealloc(%p): was local_thread_canary\n", ob); - assert(ob->tls->local_thread_canary == ob); - ob->tls->local_thread_canary = NULL; - } - TLS_ZOM_UNLOCK(); - - PyObject_Del((PyObject *)ob); -} - -static void -thread_canary_make_zombie(ThreadCanaryObj *ob) -{ - /* This must be called without the GIL, but with the TLS_ZOM_LOCK. - It must be called at most once for a given ThreadCanaryObj. */ - ThreadCanaryObj *last; - - //fprintf(stderr, "thread_canary_make_zombie(%p)\n", ob); - if (ob->zombie_next) - Py_FatalError("cffi: ThreadCanaryObj is already a zombie"); - last = cffi_zombie_head.zombie_prev; - ob->zombie_next = &cffi_zombie_head; - ob->zombie_prev = last; - last->zombie_next = ob; - cffi_zombie_head.zombie_prev = ob; -} - -static void -thread_canary_free_zombies(void) -{ - /* This must be called with the GIL. */ - if (cffi_zombie_head.zombie_next == &cffi_zombie_head) - return; /* fast path */ - - while (1) { - ThreadCanaryObj *ob; - PyThreadState *tstate = NULL; - - TLS_ZOM_LOCK(); - ob = cffi_zombie_head.zombie_next; - if (ob != &cffi_zombie_head) { - tstate = ob->tstate; - //fprintf(stderr, "thread_canary_free_zombie(%p) tstate=%p\n", ob, tstate); - _thread_canary_detach_with_lock(ob); - if (tstate == NULL) - Py_FatalError("cffi: invalid ThreadCanaryObj->tstate"); - } - TLS_ZOM_UNLOCK(); - - if (tstate == NULL) - break; - PyThreadState_Clear(tstate); /* calls thread_canary_dealloc on 'ob', - but now ob->zombie_next == NULL. */ - PyThreadState_Delete(tstate); - //fprintf(stderr, "thread_canary_free_zombie: cleared and deleted tstate=%p\n", tstate); - } - //fprintf(stderr, "thread_canary_free_zombie: end\n"); -} - -static void -thread_canary_register(PyThreadState *tstate) -{ - /* called with the GIL; 'tstate' is the current PyThreadState. */ - ThreadCanaryObj *canary; - PyObject *tdict; - struct cffi_tls_s *tls; - int err; - - /* first free the zombies, if any */ - thread_canary_free_zombies(); - - tls = get_cffi_tls(); - if (tls == NULL) - goto ignore_error; - - tdict = PyThreadState_GetDict(); - if (tdict == NULL) - goto ignore_error; - - canary = PyObject_New(ThreadCanaryObj, &ThreadCanary_Type); - //fprintf(stderr, "thread_canary_register(%p): tstate=%p tls=%p\n", canary, tstate, tls); - if (canary == NULL) - goto ignore_error; - canary->zombie_prev = NULL; - canary->zombie_next = NULL; - canary->tstate = tstate; - canary->tls = tls; - - err = PyDict_SetItemString(tdict, "cffi.thread.canary", (PyObject *)canary); - Py_DECREF(canary); - if (err < 0) - goto ignore_error; - - /* thread-safety: we have the GIL here, and 'tstate' is the one that - corresponds to our own thread. We are allocating a new 'canary' - and setting it up for our own thread, both in 'tdict' (which owns - the reference) and in 'tls->local_thread_canary' (which doesn't). */ - assert(Py_REFCNT(canary) == 1); - tls->local_thread_canary = canary; - tstate->gilstate_counter++; - /* ^^^ this means 'tstate' will never be automatically freed by - PyGILState_Release() */ - return; - - ignore_error: - PyErr_Clear(); -} - -static PyTypeObject ThreadCanary_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_cffi_backend.thread_canary", - sizeof(ThreadCanaryObj), - 0, - (destructor)thread_canary_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ -}; - -static void init_cffi_tls_zombie(void) -{ - cffi_zombie_head.zombie_next = &cffi_zombie_head; - cffi_zombie_head.zombie_prev = &cffi_zombie_head; - cffi_zombie_lock = PyThread_allocate_lock(); - if (cffi_zombie_lock == NULL) - PyErr_SetString(PyExc_SystemError, "can't allocate cffi_zombie_lock"); -} - -static void cffi_thread_shutdown(void *p) -{ - /* this function is called from misc_thread_posix or misc_win32 - when a thread is about to end. */ - struct cffi_tls_s *tls = (struct cffi_tls_s *)p; - - /* thread-safety: this field 'local_thread_canary' can be reset - to NULL in parallel, protected by TLS_ZOM_LOCK. */ - TLS_ZOM_LOCK(); - if (tls->local_thread_canary != NULL) { - tls->local_thread_canary->tls = NULL; - thread_canary_make_zombie(tls->local_thread_canary); - } - TLS_ZOM_UNLOCK(); - //fprintf(stderr, "thread_shutdown(%p)\n", tls); - free(tls); -} - -/* USE__THREAD is defined by setup.py if it finds that it is - syntactically valid to use "__thread" with this C compiler. */ -#ifdef USE__THREAD - -static __thread int cffi_saved_errno = 0; -static void save_errno_only(void) { cffi_saved_errno = errno; } -static void restore_errno_only(void) { errno = cffi_saved_errno; } - -#else - -static void save_errno_only(void) -{ - int saved = errno; - struct cffi_tls_s *tls = get_cffi_tls(); - if (tls != NULL) - tls->saved_errno = saved; -} - -static void restore_errno_only(void) -{ - struct cffi_tls_s *tls = get_cffi_tls(); - if (tls != NULL) - errno = tls->saved_errno; -} - -#endif - - -/* MESS. We can't use PyThreadState_GET(), because that calls - PyThreadState_Get() which fails an assert if the result is NULL. - - * in Python 2.7 and <= 3.4, the variable _PyThreadState_Current - is directly available, so use that. - - * in Python 3.5, the variable is available too, but it might be - the case that the headers don't define it (this changed in 3.5.1). - In case we're compiling with 3.5.x with x >= 1, we need to - manually define this variable. - - * in Python >= 3.6 there is _PyThreadState_UncheckedGet(). - It was added in 3.5.2 but should never be used in 3.5.x - because it is not available in 3.5.0 or 3.5.1. -*/ -#if PY_VERSION_HEX >= 0x03050100 && PY_VERSION_HEX < 0x03060000 -PyAPI_DATA(void *volatile) _PyThreadState_Current; -#endif - -static PyThreadState *get_current_ts(void) -{ -#if PY_VERSION_HEX >= 0x03060000 - return _PyThreadState_UncheckedGet(); -#elif defined(_Py_atomic_load_relaxed) - return (PyThreadState*)_Py_atomic_load_relaxed(&_PyThreadState_Current); -#else - return (PyThreadState*)_PyThreadState_Current; /* assume atomic read */ -#endif -} - -static PyGILState_STATE gil_ensure(void) -{ - /* Called at the start of a callback. Replacement for - PyGILState_Ensure(). - */ - PyGILState_STATE result; - PyThreadState *ts = PyGILState_GetThisThreadState(); - - if (ts != NULL) { - ts->gilstate_counter++; - if (ts != get_current_ts()) { - /* common case: 'ts' is our non-current thread state and - we have to make it current and acquire the GIL */ - PyEval_RestoreThread(ts); - return PyGILState_UNLOCKED; - } - else { - return PyGILState_LOCKED; - } - } - else { - /* no thread state here so far. */ - result = PyGILState_Ensure(); - assert(result == PyGILState_UNLOCKED); - - ts = PyGILState_GetThisThreadState(); - assert(ts != NULL); - assert(ts == get_current_ts()); - assert(ts->gilstate_counter >= 1); - - /* Use the ThreadCanary mechanism to keep 'ts' alive until the - thread really shuts down */ - thread_canary_register(ts); - - return result; - } -} - -static void gil_release(PyGILState_STATE oldstate) -{ - PyGILState_Release(oldstate); -} diff --git a/c/misc_thread_posix.h b/c/misc_thread_posix.h deleted file mode 100644 index bcc0177..0000000 --- a/c/misc_thread_posix.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - Logic for a better replacement of PyGILState_Ensure(). - - This version is ready to handle the case of a non-Python-started - thread in which we do a large number of calls to CFFI callbacks. If - we were to rely on PyGILState_Ensure() for that, we would constantly - be creating and destroying PyThreadStates---it is slow, and - PyThreadState_Delete() will actually walk the list of all thread - states, making it O(n). :-( - - This version only creates one PyThreadState object the first time we - see a given thread, and keep it alive until the thread is really - shut down, using a destructor on the tls key. -*/ - -#include <pthread.h> -#include "misc_thread_common.h" - - -static pthread_key_t cffi_tls_key; - -static void init_cffi_tls(void) -{ - if (pthread_key_create(&cffi_tls_key, &cffi_thread_shutdown) != 0) - PyErr_SetString(PyExc_OSError, "pthread_key_create() failed"); -} - -static struct cffi_tls_s *_make_cffi_tls(void) -{ - void *p = calloc(1, sizeof(struct cffi_tls_s)); - if (p == NULL) - return NULL; - if (pthread_setspecific(cffi_tls_key, p) != 0) { - free(p); - return NULL; - } - return p; -} - -static struct cffi_tls_s *get_cffi_tls(void) -{ - void *p = pthread_getspecific(cffi_tls_key); - if (p == NULL) - p = _make_cffi_tls(); - return (struct cffi_tls_s *)p; -} - -#define save_errno save_errno_only -#define restore_errno restore_errno_only diff --git a/c/misc_win32.h b/c/misc_win32.h deleted file mode 100644 index 156cf5d..0000000 --- a/c/misc_win32.h +++ /dev/null @@ -1,242 +0,0 @@ -#include <malloc.h> /* for alloca() */ - - -/************************************************************/ -/* errno and GetLastError support */ - -#include "misc_thread_common.h" - -static DWORD cffi_tls_index = TLS_OUT_OF_INDEXES; - -BOOL WINAPI DllMain(HINSTANCE hinstDLL, - DWORD reason_for_call, - LPVOID reserved) -{ - LPVOID p; - - switch (reason_for_call) { - - case DLL_THREAD_DETACH: - if (cffi_tls_index != TLS_OUT_OF_INDEXES) { - p = TlsGetValue(cffi_tls_index); - if (p != NULL) { - TlsSetValue(cffi_tls_index, NULL); - cffi_thread_shutdown(p); - } - } - break; - - default: - break; - } - return TRUE; -} - -static void init_cffi_tls(void) -{ - if (cffi_tls_index == TLS_OUT_OF_INDEXES) { - cffi_tls_index = TlsAlloc(); - if (cffi_tls_index == TLS_OUT_OF_INDEXES) - PyErr_SetString(PyExc_WindowsError, "TlsAlloc() failed"); - } -} - -static struct cffi_tls_s *get_cffi_tls(void) -{ - LPVOID p = TlsGetValue(cffi_tls_index); - - if (p == NULL) { - p = malloc(sizeof(struct cffi_tls_s)); - if (p == NULL) - return NULL; - memset(p, 0, sizeof(struct cffi_tls_s)); - TlsSetValue(cffi_tls_index, p); - } - return (struct cffi_tls_s *)p; -} - -#ifdef USE__THREAD -# error "unexpected USE__THREAD on Windows" -#endif - -static void save_errno(void) -{ - int current_err = errno; - int current_lasterr = GetLastError(); - struct cffi_tls_s *p = get_cffi_tls(); - if (p != NULL) { - p->saved_errno = current_err; - p->saved_lasterror = current_lasterr; - } - /* else: cannot report the error */ -} - -static void restore_errno(void) -{ - struct cffi_tls_s *p = get_cffi_tls(); - if (p != NULL) { - SetLastError(p->saved_lasterror); - errno = p->saved_errno; - } - /* else: cannot report the error */ -} - -/************************************************************/ - - -#if PY_MAJOR_VERSION >= 3 -static PyObject *b_getwinerror(PyObject *self, PyObject *args, PyObject *kwds) -{ - int err = -1; - int len; - WCHAR *s_buf = NULL; /* Free via LocalFree */ - PyObject *v, *message; - static char *keywords[] = {"code", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i", keywords, &err)) - return NULL; - - if (err == -1) { - struct cffi_tls_s *p = get_cffi_tls(); - if (p == NULL) - return PyErr_NoMemory(); - err = p->saved_lasterror; - } - - len = FormatMessageW( - /* Error API error */ - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, /* no message source */ - err, - MAKELANGID(LANG_NEUTRAL, - SUBLANG_DEFAULT), /* Default language */ - (LPWSTR) &s_buf, - 0, /* size not used */ - NULL); /* no args */ - if (len==0) { - /* Only seen this in out of mem situations */ - message = PyUnicode_FromFormat("Windows Error 0x%X", err); - } else { - /* remove trailing cr/lf and dots */ - while (len > 0 && (s_buf[len-1] <= L' ' || s_buf[len-1] == L'.')) - s_buf[--len] = L'\0'; - message = PyUnicode_FromWideChar(s_buf, len); - } - if (message != NULL) { - v = Py_BuildValue("(iO)", err, message); - Py_DECREF(message); - } - else - v = NULL; - LocalFree(s_buf); - return v; -} -#else -static PyObject *b_getwinerror(PyObject *self, PyObject *args, PyObject *kwds) -{ - int err = -1; - int len; - char *s; - char *s_buf = NULL; /* Free via LocalFree */ - char s_small_buf[40]; /* Room for "Windows Error 0xFFFFFFFFFFFFFFFF" */ - PyObject *v; - static char *keywords[] = {"code", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i", keywords, &err)) - return NULL; - - if (err == -1) { - struct cffi_tls_s *p = get_cffi_tls(); - if (p == NULL) - return PyErr_NoMemory(); - err = p->saved_lasterror; - } - - len = FormatMessage( - /* Error API error */ - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, /* no message source */ - err, - MAKELANGID(LANG_NEUTRAL, - SUBLANG_DEFAULT), /* Default language */ - (LPTSTR) &s_buf, - 0, /* size not used */ - NULL); /* no args */ - if (len==0) { - /* Only seen this in out of mem situations */ - sprintf(s_small_buf, "Windows Error 0x%X", err); - s = s_small_buf; - } else { - s = s_buf; - /* remove trailing cr/lf and dots */ - while (len > 0 && (s[len-1] <= ' ' || s[len-1] == '.')) - s[--len] = '\0'; - } - v = Py_BuildValue("(is)", err, s); - LocalFree(s_buf); - return v; -} -#endif - - -/************************************************************/ -/* Emulate dlopen()&co. from the Windows API */ - -#define RTLD_LAZY 0 -#define RTLD_NOW 0 -#define RTLD_GLOBAL 0 -#define RTLD_LOCAL 0 - -static void *dlopen(const char *filename, int flag) -{ - return (void *)LoadLibraryA(filename); -} - -static void *dlopenW(const wchar_t *filename) -{ - return (void *)LoadLibraryW(filename); -} - -static void *dlsym(void *handle, const char *symbol) -{ - void *address = GetProcAddress((HMODULE)handle, symbol); -#ifndef MS_WIN64 - if (!address) { - /* If 'symbol' is not found, then try '_symbol@N' for N in - (0, 4, 8, 12, ..., 124). Unlike ctypes, we try to do that - for any symbol, although in theory it should only be done - for __stdcall functions. - */ - int i; - char *mangled_name = alloca(1 + strlen(symbol) + 1 + 3 + 1); - if (!mangled_name) - return NULL; - for (i = 0; i < 32; i++) { - sprintf(mangled_name, "_%s@%d", symbol, i * 4); - address = GetProcAddress((HMODULE)handle, mangled_name); - if (address) - break; - } - } -#endif - return address; -} - -static int dlclose(void *handle) -{ - return FreeLibrary((HMODULE)handle) ? 0 : -1; -} - -static const char *dlerror(void) -{ - static char buf[32]; - DWORD dw = GetLastError(); - if (dw == 0) - return NULL; - sprintf(buf, "error 0x%x", (unsigned int)dw); - return buf; -} diff --git a/c/parse_c_type.c b/c/parse_c_type.c deleted file mode 100644 index 698ef64..0000000 --- a/c/parse_c_type.c +++ /dev/null @@ -1,847 +0,0 @@ -#include <stdlib.h> -#include <string.h> -#include <assert.h> -#include <errno.h> - -#define _CFFI_INTERNAL -#include "../cffi/parse_c_type.h" - - -enum token_e { - TOK_STAR='*', - TOK_OPEN_PAREN='(', - TOK_CLOSE_PAREN=')', - TOK_OPEN_BRACKET='[', - TOK_CLOSE_BRACKET=']', - TOK_COMMA=',', - - TOK_START=256, - TOK_END, - TOK_ERROR, - TOK_IDENTIFIER, - TOK_INTEGER, - TOK_DOTDOTDOT, - - /* keywords */ - TOK__BOOL, - TOK_CHAR, - TOK__COMPLEX, - TOK_CONST, - TOK_DOUBLE, - TOK_ENUM, - TOK_FLOAT, - //TOK__IMAGINARY, - TOK_INT, - TOK_LONG, - TOK_SHORT, - TOK_SIGNED, - TOK_STRUCT, - TOK_UNION, - TOK_UNSIGNED, - TOK_VOID, - TOK_VOLATILE, - - TOK_CDECL, - TOK_STDCALL, -}; - -typedef struct { - struct _cffi_parse_info_s *info; - const char *input, *p; - size_t size; // the next token is at 'p' and of length 'size' - enum token_e kind; - _cffi_opcode_t *output; - size_t output_index; -} token_t; - -static int is_space(char x) -{ - return (x == ' ' || x == '\f' || x == '\n' || x == '\r' || - x == '\t' || x == '\v'); -} - -static int is_ident_first(char x) -{ - return (('A' <= x && x <= 'Z') || ('a' <= x && x <= 'z') || x == '_' || - x == '$'); /* '$' in names is supported here, for the struct - names invented by cparser */ -} - -static int is_digit(char x) -{ - return ('0' <= x && x <= '9'); -} - -static int is_hex_digit(char x) -{ - return (('0' <= x && x <= '9') || - ('A' <= x && x <= 'F') || - ('a' <= x && x <= 'f')); -} - -static int is_ident_next(char x) -{ - return (is_ident_first(x) || is_digit(x)); -} - -static char get_following_char(token_t *tok) -{ - const char *p = tok->p + tok->size; - if (tok->kind == TOK_ERROR) - return 0; - while (is_space(*p)) - p++; - return *p; -} - -static int number_of_commas(token_t *tok) -{ - const char *p = tok->p; - int result = 0; - int nesting = 0; - while (1) { - switch (*p++) { - case ',': result += !nesting; break; - case '(': nesting++; break; - case ')': if ((--nesting) < 0) return result; break; - case 0: return result; - default: break; - } - } -} - -static void next_token(token_t *tok) -{ - const char *p = tok->p + tok->size; - if (tok->kind == TOK_ERROR) - return; - while (!is_ident_first(*p)) { - if (is_space(*p)) { - p++; - } - else if (is_digit(*p)) { - tok->kind = TOK_INTEGER; - tok->p = p; - tok->size = 1; - if (p[1] == 'x' || p[1] == 'X') - tok->size = 2; - while (is_hex_digit(p[tok->size])) - tok->size++; - return; - } - else if (p[0] == '.' && p[1] == '.' && p[2] == '.') { - tok->kind = TOK_DOTDOTDOT; - tok->p = p; - tok->size = 3; - return; - } - else if (*p) { - tok->kind = *p; - tok->p = p; - tok->size = 1; - return; - } - else { - tok->kind = TOK_END; - tok->p = p; - tok->size = 0; - return; - } - } - tok->kind = TOK_IDENTIFIER; - tok->p = p; - tok->size = 1; - while (is_ident_next(p[tok->size])) - tok->size++; - - switch (*p) { - case '_': - if (tok->size == 5 && !memcmp(p, "_Bool", 5)) tok->kind = TOK__BOOL; - if (tok->size == 7 && !memcmp(p,"__cdecl",7)) tok->kind = TOK_CDECL; - if (tok->size == 9 && !memcmp(p,"__stdcall",9))tok->kind = TOK_STDCALL; - if (tok->size == 8 && !memcmp(p,"_Complex",8)) tok->kind = TOK__COMPLEX; - break; - case 'c': - if (tok->size == 4 && !memcmp(p, "char", 4)) tok->kind = TOK_CHAR; - if (tok->size == 5 && !memcmp(p, "const", 5)) tok->kind = TOK_CONST; - break; - case 'd': - if (tok->size == 6 && !memcmp(p, "double", 6)) tok->kind = TOK_DOUBLE; - break; - case 'e': - if (tok->size == 4 && !memcmp(p, "enum", 4)) tok->kind = TOK_ENUM; - break; - case 'f': - if (tok->size == 5 && !memcmp(p, "float", 5)) tok->kind = TOK_FLOAT; - break; - case 'i': - if (tok->size == 3 && !memcmp(p, "int", 3)) tok->kind = TOK_INT; - break; - case 'l': - if (tok->size == 4 && !memcmp(p, "long", 4)) tok->kind = TOK_LONG; - break; - case 's': - if (tok->size == 5 && !memcmp(p, "short", 5)) tok->kind = TOK_SHORT; - if (tok->size == 6 && !memcmp(p, "signed", 6)) tok->kind = TOK_SIGNED; - if (tok->size == 6 && !memcmp(p, "struct", 6)) tok->kind = TOK_STRUCT; - break; - case 'u': - if (tok->size == 5 && !memcmp(p, "union", 5)) tok->kind = TOK_UNION; - if (tok->size == 8 && !memcmp(p,"unsigned",8)) tok->kind = TOK_UNSIGNED; - break; - case 'v': - if (tok->size == 4 && !memcmp(p, "void", 4)) tok->kind = TOK_VOID; - if (tok->size == 8 && !memcmp(p,"volatile",8)) tok->kind = TOK_VOLATILE; - break; - } -} - -static int parse_error(token_t *tok, const char *msg) -{ - if (tok->kind != TOK_ERROR) { - tok->kind = TOK_ERROR; - tok->info->error_location = tok->p - tok->input; - tok->info->error_message = msg; - } - return -1; -} - -static int write_ds(token_t *tok, _cffi_opcode_t ds) -{ - size_t index = tok->output_index; - if (index >= tok->info->output_size) { - parse_error(tok, "internal type complexity limit reached"); - return -1; - } - tok->output[index] = ds; - tok->output_index = index + 1; - return index; -} - -#define MAX_SSIZE_T (((size_t)-1) >> 1) - -static int parse_complete(token_t *tok); -static const char *get_common_type(const char *search, size_t search_len); -static int parse_common_type_replacement(token_t *tok, const char *replacement); - -static int parse_sequel(token_t *tok, int outer) -{ - /* Emit opcodes for the "sequel", which is the optional part of a - type declaration that follows the type name, i.e. everything - with '*', '[ ]', '( )'. Returns the entry point index pointing - the innermost opcode (the one that corresponds to the complete - type). The 'outer' argument is the index of the opcode outside - this "sequel". - */ - int check_for_grouping, abi=0; - _cffi_opcode_t result, *p_current; - - header: - switch (tok->kind) { - case TOK_STAR: - outer = write_ds(tok, _CFFI_OP(_CFFI_OP_POINTER, outer)); - next_token(tok); - goto header; - case TOK_CONST: - /* ignored for now */ - next_token(tok); - goto header; - case TOK_VOLATILE: - /* ignored for now */ - next_token(tok); - goto header; - case TOK_CDECL: - case TOK_STDCALL: - /* must be in a function; checked below */ - abi = tok->kind; - next_token(tok); - goto header; - default: - break; - } - - check_for_grouping = 1; - if (tok->kind == TOK_IDENTIFIER) { - next_token(tok); /* skip a potential variable name */ - check_for_grouping = 0; - } - - result = 0; - p_current = &result; - - while (tok->kind == TOK_OPEN_PAREN) { - next_token(tok); - - if (tok->kind == TOK_CDECL || tok->kind == TOK_STDCALL) { - abi = tok->kind; - next_token(tok); - } - - if ((check_for_grouping--) == 1 && (tok->kind == TOK_STAR || - tok->kind == TOK_CONST || - tok->kind == TOK_VOLATILE || - tok->kind == TOK_OPEN_BRACKET)) { - /* just parentheses for grouping. Use a OP_NOOP to simplify */ - int x; - assert(p_current == &result); - x = tok->output_index; - p_current = tok->output + x; - - write_ds(tok, _CFFI_OP(_CFFI_OP_NOOP, 0)); - - x = parse_sequel(tok, x); - result = _CFFI_OP(_CFFI_GETOP(0), x); - } - else { - /* function type */ - int arg_total, base_index, arg_next, flags=0; - - if (abi == TOK_STDCALL) { - flags = 2; - /* note that an ellipsis below will overwrite this flags, - which is the goal: variadic functions are always cdecl */ - } - abi = 0; - - if (tok->kind == TOK_VOID && get_following_char(tok) == ')') { - next_token(tok); - } - - /* (over-)estimate 'arg_total'. May return 1 when it is really 0 */ - arg_total = number_of_commas(tok) + 1; - - *p_current = _CFFI_OP(_CFFI_GETOP(*p_current), tok->output_index); - p_current = tok->output + tok->output_index; - - base_index = write_ds(tok, _CFFI_OP(_CFFI_OP_FUNCTION, 0)); - if (base_index < 0) - return -1; - /* reserve (arg_total + 1) slots for the arguments and the - final FUNCTION_END */ - for (arg_next = 0; arg_next <= arg_total; arg_next++) - if (write_ds(tok, _CFFI_OP(0, 0)) < 0) - return -1; - - arg_next = base_index + 1; - - if (tok->kind != TOK_CLOSE_PAREN) { - while (1) { - int arg; - _cffi_opcode_t oarg; - - if (tok->kind == TOK_DOTDOTDOT) { - flags = 1; /* ellipsis */ - next_token(tok); - break; - } - arg = parse_complete(tok); - switch (_CFFI_GETOP(tok->output[arg])) { - case _CFFI_OP_ARRAY: - case _CFFI_OP_OPEN_ARRAY: - arg = _CFFI_GETARG(tok->output[arg]); - /* fall-through */ - case _CFFI_OP_FUNCTION: - oarg = _CFFI_OP(_CFFI_OP_POINTER, arg); - break; - default: - oarg = _CFFI_OP(_CFFI_OP_NOOP, arg); - break; - } - assert(arg_next - base_index <= arg_total); - tok->output[arg_next++] = oarg; - if (tok->kind != TOK_COMMA) - break; - next_token(tok); - } - } - tok->output[arg_next] = _CFFI_OP(_CFFI_OP_FUNCTION_END, flags); - } - - if (tok->kind != TOK_CLOSE_PAREN) - return parse_error(tok, "expected ')'"); - next_token(tok); - } - - if (abi != 0) - return parse_error(tok, "expected '('"); - - while (tok->kind == TOK_OPEN_BRACKET) { - *p_current = _CFFI_OP(_CFFI_GETOP(*p_current), tok->output_index); - p_current = tok->output + tok->output_index; - - next_token(tok); - if (tok->kind != TOK_CLOSE_BRACKET) { - size_t length; - int gindex; - char *endptr; - - switch (tok->kind) { - - case TOK_INTEGER: - errno = 0; - if (sizeof(length) > sizeof(unsigned long)) { -#ifdef MS_WIN32 -# ifdef _WIN64 - length = _strtoui64(tok->p, &endptr, 0); -# else - abort(); /* unreachable */ -# endif -#else - length = strtoull(tok->p, &endptr, 0); -#endif - } - else - length = strtoul(tok->p, &endptr, 0); - if (endptr != tok->p + tok->size) - return parse_error(tok, "invalid number"); - if (errno == ERANGE || length > MAX_SSIZE_T) - return parse_error(tok, "number too large"); - break; - - case TOK_IDENTIFIER: - gindex = search_in_globals(tok->info->ctx, tok->p, tok->size); - if (gindex >= 0) { - const struct _cffi_global_s *g; - g = &tok->info->ctx->globals[gindex]; - if (_CFFI_GETOP(g->type_op) == _CFFI_OP_CONSTANT_INT || - _CFFI_GETOP(g->type_op) == _CFFI_OP_ENUM) { - int neg; - struct _cffi_getconst_s gc; - gc.ctx = tok->info->ctx; - gc.gindex = gindex; - neg = ((int(*)(struct _cffi_getconst_s*))g->address) - (&gc); - if (neg == 0 && gc.value > MAX_SSIZE_T) - return parse_error(tok, - "integer constant too large"); - if (neg == 0 || gc.value == 0) { - length = (size_t)gc.value; - break; - } - if (neg != 1) - return parse_error(tok, "disagreement about" - " this constant's value"); - } - } - /* fall-through to the default case */ - default: - return parse_error(tok, "expected a positive integer constant"); - } - - next_token(tok); - - write_ds(tok, _CFFI_OP(_CFFI_OP_ARRAY, 0)); - write_ds(tok, (_cffi_opcode_t)length); - } - else - write_ds(tok, _CFFI_OP(_CFFI_OP_OPEN_ARRAY, 0)); - - if (tok->kind != TOK_CLOSE_BRACKET) - return parse_error(tok, "expected ']'"); - next_token(tok); - } - - *p_current = _CFFI_OP(_CFFI_GETOP(*p_current), outer); - return _CFFI_GETARG(result); -} - -static int search_sorted(const char *const *base, - size_t item_size, int array_len, - const char *search, size_t search_len) -{ - int left = 0, right = array_len; - const char *baseptr = (const char *)base; - - while (left < right) { - int middle = (left + right) / 2; - const char *src = *(const char *const *)(baseptr + middle * item_size); - int diff = strncmp(src, search, search_len); - if (diff == 0 && src[search_len] == '\0') - return middle; - else if (diff >= 0) - right = middle; - else - left = middle + 1; - } - return -1; -} - -#define MAKE_SEARCH_FUNC(FIELD) \ - static \ - int search_in_##FIELD(const struct _cffi_type_context_s *ctx, \ - const char *search, size_t search_len) \ - { \ - return search_sorted(&ctx->FIELD->name, sizeof(*ctx->FIELD), \ - ctx->num_##FIELD, search, search_len); \ - } - -MAKE_SEARCH_FUNC(globals) -MAKE_SEARCH_FUNC(struct_unions) -MAKE_SEARCH_FUNC(typenames) -MAKE_SEARCH_FUNC(enums) - -#undef MAKE_SEARCH_FUNC - - -static -int search_standard_typename(const char *p, size_t size) -{ - if (size < 6 || p[size-2] != '_' || p[size-1] != 't') - return -1; - - switch (p[4]) { - - case '1': - if (size == 8 && !memcmp(p, "uint16", 6)) return _CFFI_PRIM_UINT16; - if (size == 8 && !memcmp(p, "char16", 6)) return _CFFI_PRIM_CHAR16; - break; - - case '2': - if (size == 7 && !memcmp(p, "int32", 5)) return _CFFI_PRIM_INT32; - break; - - case '3': - if (size == 8 && !memcmp(p, "uint32", 6)) return _CFFI_PRIM_UINT32; - if (size == 8 && !memcmp(p, "char32", 6)) return _CFFI_PRIM_CHAR32; - break; - - case '4': - if (size == 7 && !memcmp(p, "int64", 5)) return _CFFI_PRIM_INT64; - break; - - case '6': - if (size == 8 && !memcmp(p, "uint64", 6)) return _CFFI_PRIM_UINT64; - if (size == 7 && !memcmp(p, "int16", 5)) return _CFFI_PRIM_INT16; - break; - - case '8': - if (size == 7 && !memcmp(p, "uint8", 5)) return _CFFI_PRIM_UINT8; - break; - - case 'a': - if (size == 8 && !memcmp(p, "intmax", 6)) return _CFFI_PRIM_INTMAX; - break; - - case 'e': - if (size == 7 && !memcmp(p, "ssize", 5)) return _CFFI_PRIM_SSIZE; - break; - - case 'f': - if (size == 11 && !memcmp(p, "int_fast8", 9)) return _CFFI_PRIM_INT_FAST8; - if (size == 12 && !memcmp(p, "int_fast16", 10)) return _CFFI_PRIM_INT_FAST16; - if (size == 12 && !memcmp(p, "int_fast32", 10)) return _CFFI_PRIM_INT_FAST32; - if (size == 12 && !memcmp(p, "int_fast64", 10)) return _CFFI_PRIM_INT_FAST64; - break; - - case 'i': - if (size == 9 && !memcmp(p, "ptrdiff", 7)) return _CFFI_PRIM_PTRDIFF; - break; - - case 'l': - if (size == 12 && !memcmp(p, "int_least8", 10)) return _CFFI_PRIM_INT_LEAST8; - if (size == 13 && !memcmp(p, "int_least16", 11)) return _CFFI_PRIM_INT_LEAST16; - if (size == 13 && !memcmp(p, "int_least32", 11)) return _CFFI_PRIM_INT_LEAST32; - if (size == 13 && !memcmp(p, "int_least64", 11)) return _CFFI_PRIM_INT_LEAST64; - break; - - case 'm': - if (size == 9 && !memcmp(p, "uintmax", 7)) return _CFFI_PRIM_UINTMAX; - break; - - case 'p': - if (size == 9 && !memcmp(p, "uintptr", 7)) return _CFFI_PRIM_UINTPTR; - break; - - case 'r': - if (size == 7 && !memcmp(p, "wchar", 5)) return _CFFI_PRIM_WCHAR; - break; - - case 't': - if (size == 8 && !memcmp(p, "intptr", 6)) return _CFFI_PRIM_INTPTR; - break; - - case '_': - if (size == 6 && !memcmp(p, "size", 4)) return _CFFI_PRIM_SIZE; - if (size == 6 && !memcmp(p, "int8", 4)) return _CFFI_PRIM_INT8; - if (size >= 12) { - switch (p[10]) { - case '1': - if (size == 14 && !memcmp(p, "uint_least16", 12)) return _CFFI_PRIM_UINT_LEAST16; - break; - case '2': - if (size == 13 && !memcmp(p, "uint_fast32", 11)) return _CFFI_PRIM_UINT_FAST32; - break; - case '3': - if (size == 14 && !memcmp(p, "uint_least32", 12)) return _CFFI_PRIM_UINT_LEAST32; - break; - case '4': - if (size == 13 && !memcmp(p, "uint_fast64", 11)) return _CFFI_PRIM_UINT_FAST64; - break; - case '6': - if (size == 14 && !memcmp(p, "uint_least64", 12)) return _CFFI_PRIM_UINT_LEAST64; - if (size == 13 && !memcmp(p, "uint_fast16", 11)) return _CFFI_PRIM_UINT_FAST16; - break; - case '8': - if (size == 13 && !memcmp(p, "uint_least8", 11)) return _CFFI_PRIM_UINT_LEAST8; - break; - case '_': - if (size == 12 && !memcmp(p, "uint_fast8", 10)) return _CFFI_PRIM_UINT_FAST8; - break; - default: - break; - } - } - break; - - default: - break; - } - return -1; -} - - -static int parse_complete(token_t *tok) -{ - unsigned int t0; - _cffi_opcode_t t1; - _cffi_opcode_t t1complex; - int modifiers_length, modifiers_sign; - - qualifiers: - switch (tok->kind) { - case TOK_CONST: - /* ignored for now */ - next_token(tok); - goto qualifiers; - case TOK_VOLATILE: - /* ignored for now */ - next_token(tok); - goto qualifiers; - default: - ; - } - - modifiers_length = 0; - modifiers_sign = 0; - modifiers: - switch (tok->kind) { - - case TOK_SHORT: - if (modifiers_length != 0) - return parse_error(tok, "'short' after another 'short' or 'long'"); - modifiers_length--; - next_token(tok); - goto modifiers; - - case TOK_LONG: - if (modifiers_length < 0) - return parse_error(tok, "'long' after 'short'"); - if (modifiers_length >= 2) - return parse_error(tok, "'long long long' is too long"); - modifiers_length++; - next_token(tok); - goto modifiers; - - case TOK_SIGNED: - if (modifiers_sign) - return parse_error(tok, "multiple 'signed' or 'unsigned'"); - modifiers_sign++; - next_token(tok); - goto modifiers; - - case TOK_UNSIGNED: - if (modifiers_sign) - return parse_error(tok, "multiple 'signed' or 'unsigned'"); - modifiers_sign--; - next_token(tok); - goto modifiers; - - default: - break; - } - - t1complex = 0; - - if (modifiers_length || modifiers_sign) { - - switch (tok->kind) { - - case TOK_VOID: - case TOK__BOOL: - case TOK_FLOAT: - case TOK_STRUCT: - case TOK_UNION: - case TOK_ENUM: - case TOK__COMPLEX: - return parse_error(tok, "invalid combination of types"); - - case TOK_DOUBLE: - if (modifiers_sign != 0 || modifiers_length != 1) - return parse_error(tok, "invalid combination of types"); - next_token(tok); - t0 = _CFFI_PRIM_LONGDOUBLE; - break; - - case TOK_CHAR: - if (modifiers_length != 0) - return parse_error(tok, "invalid combination of types"); - modifiers_length = -2; - /* fall-through */ - case TOK_INT: - next_token(tok); - /* fall-through */ - default: - if (modifiers_sign >= 0) - switch (modifiers_length) { - case -2: t0 = _CFFI_PRIM_SCHAR; break; - case -1: t0 = _CFFI_PRIM_SHORT; break; - case 1: t0 = _CFFI_PRIM_LONG; break; - case 2: t0 = _CFFI_PRIM_LONGLONG; break; - default: t0 = _CFFI_PRIM_INT; break; - } - else - switch (modifiers_length) { - case -2: t0 = _CFFI_PRIM_UCHAR; break; - case -1: t0 = _CFFI_PRIM_USHORT; break; - case 1: t0 = _CFFI_PRIM_ULONG; break; - case 2: t0 = _CFFI_PRIM_ULONGLONG; break; - default: t0 = _CFFI_PRIM_UINT; break; - } - } - t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, t0); - } - else { - switch (tok->kind) { - case TOK_INT: - t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_INT); - break; - case TOK_CHAR: - t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_CHAR); - break; - case TOK_VOID: - t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_VOID); - break; - case TOK__BOOL: - t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_BOOL); - break; - case TOK_FLOAT: - t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_FLOAT); - t1complex = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_FLOATCOMPLEX); - break; - case TOK_DOUBLE: - t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_DOUBLE); - t1complex = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_DOUBLECOMPLEX); - break; - case TOK_IDENTIFIER: - { - const char *replacement; - int n = search_in_typenames(tok->info->ctx, tok->p, tok->size); - if (n >= 0) { - t1 = _CFFI_OP(_CFFI_OP_TYPENAME, n); - break; - } - n = search_standard_typename(tok->p, tok->size); - if (n >= 0) { - t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, n); - break; - } - replacement = get_common_type(tok->p, tok->size); - if (replacement != NULL) { - n = parse_common_type_replacement(tok, replacement); - if (n < 0) - return parse_error(tok, "internal error, please report!"); - t1 = _CFFI_OP(_CFFI_OP_NOOP, n); - break; - } - return parse_error(tok, "undefined type name"); - } - case TOK_STRUCT: - case TOK_UNION: - { - int n, kind = tok->kind; - next_token(tok); - if (tok->kind != TOK_IDENTIFIER) - return parse_error(tok, "struct or union name expected"); - - n = search_in_struct_unions(tok->info->ctx, tok->p, tok->size); - if (n < 0) { - if (kind == TOK_STRUCT && tok->size == 8 && - !memcmp(tok->p, "_IO_FILE", 8)) - n = _CFFI__IO_FILE_STRUCT; - else - return parse_error(tok, "undefined struct/union name"); - } - else if (((tok->info->ctx->struct_unions[n].flags & _CFFI_F_UNION) - != 0) ^ (kind == TOK_UNION)) - return parse_error(tok, "wrong kind of tag: struct vs union"); - - t1 = _CFFI_OP(_CFFI_OP_STRUCT_UNION, n); - break; - } - case TOK_ENUM: - { - int n; - next_token(tok); - if (tok->kind != TOK_IDENTIFIER) - return parse_error(tok, "enum name expected"); - - n = search_in_enums(tok->info->ctx, tok->p, tok->size); - if (n < 0) - return parse_error(tok, "undefined enum name"); - - t1 = _CFFI_OP(_CFFI_OP_ENUM, n); - break; - } - default: - return parse_error(tok, "identifier expected"); - } - next_token(tok); - } - if (tok->kind == TOK__COMPLEX) - { - if (t1complex == 0) - return parse_error(tok, "_Complex type combination unsupported"); - t1 = t1complex; - next_token(tok); - } - - return parse_sequel(tok, write_ds(tok, t1)); -} - - -static -int parse_c_type_from(struct _cffi_parse_info_s *info, size_t *output_index, - const char *input) -{ - int result; - token_t token; - - token.info = info; - token.kind = TOK_START; - token.input = input; - token.p = input; - token.size = 0; - token.output = info->output; - token.output_index = *output_index; - - next_token(&token); - result = parse_complete(&token); - - *output_index = token.output_index; - if (token.kind != TOK_END) - return parse_error(&token, "unexpected symbol"); - return result; -} - -static -int parse_c_type(struct _cffi_parse_info_s *info, const char *input) -{ - size_t output_index = 0; - return parse_c_type_from(info, &output_index, input); -} - -static -int parse_common_type_replacement(token_t *tok, const char *replacement) -{ - return parse_c_type_from(tok->info, &tok->output_index, replacement); -} diff --git a/c/realize_c_type.c b/c/realize_c_type.c deleted file mode 100644 index 82629b7..0000000 --- a/c/realize_c_type.c +++ /dev/null @@ -1,820 +0,0 @@ - -typedef struct { - struct _cffi_type_context_s ctx; /* inlined substructure */ - PyObject *types_dict; - PyObject *included_ffis; - PyObject *included_libs; - PyObject *_keepalive1; - PyObject *_keepalive2; -} builder_c_t; - - -static PyObject *all_primitives[_CFFI__NUM_PRIM]; -static CTypeDescrObject *g_ct_voidp, *g_ct_chararray; - -static PyObject *build_primitive_type(int num); /* forward */ - -#define primitive_in_range(num) ((num) >= 0 && (num) < _CFFI__NUM_PRIM) -#define get_primitive_type(num) \ - ((primitive_in_range(num) && all_primitives[num] != NULL) ? \ - all_primitives[num] : build_primitive_type(num)) - -static int init_global_types_dict(PyObject *ffi_type_dict) -{ - int err; - PyObject *ct_void, *ct_char, *ct2, *pnull; - /* XXX some leaks in case these functions fail, but well, - MemoryErrors during importing an extension module are kind - of bad anyway */ - - ct_void = get_primitive_type(_CFFI_PRIM_VOID); // 'void' - if (ct_void == NULL) - return -1; - - ct2 = new_pointer_type((CTypeDescrObject *)ct_void); // 'void *' - if (ct2 == NULL) - return -1; - g_ct_voidp = (CTypeDescrObject *)ct2; - - ct_char = get_primitive_type(_CFFI_PRIM_CHAR); // 'char' - if (ct_char == NULL) - return -1; - - ct2 = new_pointer_type((CTypeDescrObject *)ct_char); // 'char *' - if (ct2 == NULL) - return -1; - - ct2 = new_array_type((CTypeDescrObject *)ct2, -1); // 'char[]' - if (ct2 == NULL) - return -1; - g_ct_chararray = (CTypeDescrObject *)ct2; - - pnull = new_simple_cdata(NULL, g_ct_voidp); - if (pnull == NULL) - return -1; - err = PyDict_SetItemString(ffi_type_dict, "NULL", pnull); - Py_DECREF(pnull); - return err; -} - -static void free_builder_c(builder_c_t *builder, int ctx_is_static) -{ - if (!ctx_is_static) { - size_t i; - const void *mem[] = {builder->ctx.types, - builder->ctx.globals, - builder->ctx.struct_unions, - //builder->ctx.fields: allocated with struct_unions - builder->ctx.enums, - builder->ctx.typenames}; - for (i = 0; i < sizeof(mem) / sizeof(*mem); i++) { - if (mem[i] != NULL) - PyMem_Free((void *)mem[i]); - } - } - Py_XDECREF(builder->included_ffis); - Py_XDECREF(builder->included_libs); - Py_XDECREF(builder->types_dict); - Py_XDECREF(builder->_keepalive1); - Py_XDECREF(builder->_keepalive2); -} - -static int init_builder_c(builder_c_t *builder, - const struct _cffi_type_context_s *ctx) -{ - PyObject *ldict = PyDict_New(); - if (ldict == NULL) - return -1; - - if (ctx) - builder->ctx = *ctx; - else - memset(&builder->ctx, 0, sizeof(builder->ctx)); - - builder->types_dict = ldict; - builder->included_ffis = NULL; - builder->included_libs = NULL; - builder->_keepalive1 = NULL; - builder->_keepalive2 = NULL; - return 0; -} - -static PyObject *build_primitive_type(int num) -{ - /* XXX too many translations between here and new_primitive_type() */ - static const char *primitive_name[] = { - NULL, - "_Bool", - "char", - "signed char", - "unsigned char", - "short", - "unsigned short", - "int", - "unsigned int", - "long", - "unsigned long", - "long long", - "unsigned long long", - "float", - "double", - "long double", - "wchar_t", - "int8_t", - "uint8_t", - "int16_t", - "uint16_t", - "int32_t", - "uint32_t", - "int64_t", - "uint64_t", - "intptr_t", - "uintptr_t", - "ptrdiff_t", - "size_t", - "ssize_t", - "int_least8_t", - "uint_least8_t", - "int_least16_t", - "uint_least16_t", - "int_least32_t", - "uint_least32_t", - "int_least64_t", - "uint_least64_t", - "int_fast8_t", - "uint_fast8_t", - "int_fast16_t", - "uint_fast16_t", - "int_fast32_t", - "uint_fast32_t", - "int_fast64_t", - "uint_fast64_t", - "intmax_t", - "uintmax_t", - "float _Complex", - "double _Complex", - "char16_t", - "char32_t", - }; - PyObject *x; - - assert(sizeof(primitive_name) == sizeof(*primitive_name) * _CFFI__NUM_PRIM); - if (num == _CFFI_PRIM_VOID) { - x = new_void_type(); - } - else if (primitive_in_range(num) && primitive_name[num] != NULL) { - x = new_primitive_type(primitive_name[num]); - } - else if (num == _CFFI__UNKNOWN_PRIM) { - PyErr_SetString(FFIError, "primitive integer type with an unexpected " - "size (or not an integer type at all)"); - return NULL; - } - else if (num == _CFFI__UNKNOWN_FLOAT_PRIM) { - PyErr_SetString(FFIError, "primitive floating-point type with an " - "unexpected size (or not a float type at all)"); - return NULL; - } - else if (num == _CFFI__UNKNOWN_LONG_DOUBLE) { - PyErr_SetString(FFIError, "primitive floating-point type is " - "'long double', not supported for now with " - "the syntax 'typedef double... xxx;'"); - return NULL; - } - else { - PyErr_Format(PyExc_NotImplementedError, "prim=%d", num); - return NULL; - } - - all_primitives[num] = x; - return x; -} - -static PyObject *realize_global_int(builder_c_t *builder, int gindex) -{ - int neg; - char got[64]; - unsigned long long value; - struct _cffi_getconst_s gc; - const struct _cffi_global_s *g = &builder->ctx.globals[gindex]; - gc.ctx = &builder->ctx; - gc.gindex = gindex; - /* note: we cast g->address to this function type; we do the same - in parse_c_type:parse_sequel() too. Note that the called function - may be declared simply with "unsigned long long *" as argument, - which is fine as it is the first field in _cffi_getconst_s. */ - assert(&gc.value == (unsigned long long *)&gc); - neg = ((int(*)(struct _cffi_getconst_s *))g->address)(&gc); - value = gc.value; - - switch (neg) { - - case 0: - if (value <= (unsigned long long)LONG_MAX) - return PyInt_FromLong((long)value); - else - return PyLong_FromUnsignedLongLong(value); - - case 1: - if ((long long)value >= (long long)LONG_MIN) - return PyInt_FromLong((long)value); - else - return PyLong_FromLongLong((long long)value); - - default: - break; - } - if (neg == 2) - sprintf(got, "%llu (0x%llx)", value, value); - else - sprintf(got, "%lld", (long long)value); - PyErr_Format(FFIError, "the C compiler says '%.200s' is equal to %s, " - "but the cdef disagrees", g->name, got); - return NULL; -} - -static CTypeDescrObject * -unwrap_fn_as_fnptr(PyObject *x) -{ - assert(PyTuple_Check(x)); - return (CTypeDescrObject *)PyTuple_GET_ITEM(x, 0); -} - -static CTypeDescrObject * -unexpected_fn_type(PyObject *x) -{ - CTypeDescrObject *ct = unwrap_fn_as_fnptr(x); - char *text1 = ct->ct_name; - char *text2 = text1 + ct->ct_name_position + 1; - assert(text2[-3] == '('); - text2[-3] = '\0'; - PyErr_Format(FFIError, "the type '%s%s' is a function type, not a " - "pointer-to-function type", text1, text2); - text2[-3] = '('; - return NULL; -} - -static PyObject * -realize_c_type_or_func(builder_c_t *builder, - _cffi_opcode_t opcodes[], int index); /* forward */ - - -/* Interpret an opcodes[] array. If opcodes == ctx->types, store all - the intermediate types back in the opcodes[]. Returns a new - reference. -*/ -static CTypeDescrObject * -realize_c_type(builder_c_t *builder, _cffi_opcode_t opcodes[], int index) -{ - PyObject *x = realize_c_type_or_func(builder, opcodes, index); - if (x == NULL || CTypeDescr_Check(x)) - return (CTypeDescrObject *)x; - else { - unexpected_fn_type(x); - Py_DECREF(x); - return NULL; - } -} - -static void _realize_name(char *target, const char *prefix, const char *srcname) -{ - /* "xyz" => "struct xyz" - "$xyz" => "xyz" - "$1" => "struct $1" - */ - if (srcname[0] == '$' && srcname[1] != '$' && - !('0' <= srcname[1] && srcname[1] <= '9')) { - strcpy(target, &srcname[1]); - } - else { - strcpy(target, prefix); - strcat(target, srcname); - } -} - -static void _unrealize_name(char *target, const char *srcname) -{ - /* reverse of _realize_name() */ - if (strncmp(srcname, "struct ", 7) == 0) { - strcpy(target, &srcname[7]); - } - else if (strncmp(srcname, "union ", 6) == 0) { - strcpy(target, &srcname[6]); - } - else if (strncmp(srcname, "enum ", 5) == 0) { - strcpy(target, &srcname[5]); - } - else { - strcpy(target, "$"); - strcat(target, srcname); - } -} - -static PyObject * /* forward */ -_fetch_external_struct_or_union(const struct _cffi_struct_union_s *s, - PyObject *included_ffis, int recursion); - -static PyObject * -_realize_c_struct_or_union(builder_c_t *builder, int sindex) -{ - PyObject *x; - _cffi_opcode_t op2; - const struct _cffi_struct_union_s *s; - - if (sindex == _CFFI__IO_FILE_STRUCT) { - /* returns a single global cached opaque type */ - static PyObject *file_struct = NULL; - if (file_struct == NULL) - file_struct = new_struct_or_union_type("FILE", - CT_STRUCT | CT_IS_FILE); - Py_XINCREF(file_struct); - return file_struct; - } - - s = &builder->ctx.struct_unions[sindex]; - op2 = builder->ctx.types[s->type_index]; - if ((((uintptr_t)op2) & 1) == 0) { - x = (PyObject *)op2; /* found already in the "primary" slot */ - Py_INCREF(x); - } - else { - CTypeDescrObject *ct = NULL; - - if (!(s->flags & _CFFI_F_EXTERNAL)) { - int flags = (s->flags & _CFFI_F_UNION) ? CT_UNION : CT_STRUCT; - char *name = alloca(8 + strlen(s->name)); - _realize_name(name, - (s->flags & _CFFI_F_UNION) ? "union " : "struct ", - s->name); - if (strcmp(name, "struct _IO_FILE") == 0) - x = _realize_c_struct_or_union(builder, _CFFI__IO_FILE_STRUCT); - else - x = new_struct_or_union_type(name, flags); - if (x == NULL) - return NULL; - - if (!(s->flags & _CFFI_F_OPAQUE)) { - assert(s->first_field_index >= 0); - ct = (CTypeDescrObject *)x; - ct->ct_size = (Py_ssize_t)s->size; - ct->ct_length = s->alignment; /* may be -1 */ - ct->ct_flags &= ~CT_IS_OPAQUE; - ct->ct_flags |= CT_LAZY_FIELD_LIST; - ct->ct_extra = builder; - } - else - assert(s->first_field_index < 0); - } - else { - assert(s->first_field_index < 0); - x = _fetch_external_struct_or_union(s, builder->included_ffis, 0); - if (x == NULL) { - if (!PyErr_Occurred()) - PyErr_Format(FFIError, "'%s %.200s' should come from " - "ffi.include() but was not found", - (s->flags & _CFFI_F_UNION) ? "union" - : "struct", s->name); - return NULL; - } - if (!(s->flags & _CFFI_F_OPAQUE)) { - if (((CTypeDescrObject *)x)->ct_flags & CT_IS_OPAQUE) { - const char *prefix = (s->flags & _CFFI_F_UNION) ? "union" - : "struct"; - PyErr_Format(PyExc_NotImplementedError, - "'%s %.200s' is opaque in the ffi.include(), " - "but no longer in the ffi doing the include " - "(workaround: don't use ffi.include() but " - "duplicate the declarations of everything " - "using %s %.200s)", - prefix, s->name, prefix, s->name); - Py_DECREF(x); - return NULL; - } - } - } - - /* Update the "primary" OP_STRUCT_UNION slot */ - assert((((uintptr_t)x) & 1) == 0); - assert(builder->ctx.types[s->type_index] == op2); - Py_INCREF(x); - builder->ctx.types[s->type_index] = x; - - if (ct != NULL && s->size == (size_t)-2) { - /* oops, this struct is unnamed and we couldn't generate - a C expression to get its size. We have to rely on - complete_struct_or_union() to compute it now. */ - if (do_realize_lazy_struct(ct) < 0) { - builder->ctx.types[s->type_index] = op2; - return NULL; - } - } - } - return x; -} - -static PyObject * -realize_c_type_or_func_now(builder_c_t *builder, _cffi_opcode_t op, - _cffi_opcode_t opcodes[], int index) -{ - PyObject *x, *y, *z; - Py_ssize_t length = -1; - - switch (_CFFI_GETOP(op)) { - - case _CFFI_OP_PRIMITIVE: - x = get_primitive_type(_CFFI_GETARG(op)); - Py_XINCREF(x); - break; - - case _CFFI_OP_POINTER: - y = realize_c_type_or_func(builder, opcodes, _CFFI_GETARG(op)); - if (y == NULL) - return NULL; - if (CTypeDescr_Check(y)) { - x = new_pointer_type((CTypeDescrObject *)y); - } - else { - assert(PyTuple_Check(y)); /* from _CFFI_OP_FUNCTION */ - x = PyTuple_GET_ITEM(y, 0); - Py_INCREF(x); - } - Py_DECREF(y); - break; - - case _CFFI_OP_ARRAY: - length = (Py_ssize_t)opcodes[index + 1]; - /* fall-through */ - case _CFFI_OP_OPEN_ARRAY: - y = (PyObject *)realize_c_type(builder, opcodes, _CFFI_GETARG(op)); - if (y == NULL) - return NULL; - z = new_pointer_type((CTypeDescrObject *)y); - Py_DECREF(y); - if (z == NULL) - return NULL; - x = new_array_type((CTypeDescrObject *)z, length); - Py_DECREF(z); - break; - - case _CFFI_OP_STRUCT_UNION: - x = _realize_c_struct_or_union(builder, _CFFI_GETARG(op)); - break; - - case _CFFI_OP_ENUM: - { - const struct _cffi_enum_s *e; - _cffi_opcode_t op2; - - e = &builder->ctx.enums[_CFFI_GETARG(op)]; - op2 = builder->ctx.types[e->type_index]; - if ((((uintptr_t)op2) & 1) == 0) { - x = (PyObject *)op2; - Py_INCREF(x); - } - else { - PyObject *enumerators = NULL, *enumvalues = NULL, *tmp; - Py_ssize_t i, j, n = 0; - const char *p; - int gindex; - PyObject *args; - PyObject *basetd = get_primitive_type(e->type_prim); - if (basetd == NULL) - return NULL; - - if (*e->enumerators != '\0') { - n++; - for (p = e->enumerators; *p != '\0'; p++) - n += (*p == ','); - } - enumerators = PyTuple_New(n); - if (enumerators == NULL) - return NULL; - - enumvalues = PyTuple_New(n); - if (enumvalues == NULL) { - Py_DECREF(enumerators); - return NULL; - } - - p = e->enumerators; - for (i = 0; i < n; i++) { - j = 0; - while (p[j] != ',' && p[j] != '\0') - j++; - tmp = PyText_FromStringAndSize(p, j); - if (tmp == NULL) - break; - PyTuple_SET_ITEM(enumerators, i, tmp); - - gindex = search_in_globals(&builder->ctx, p, j); - assert(gindex >= 0); - assert(builder->ctx.globals[gindex].type_op == - _CFFI_OP(_CFFI_OP_ENUM, -1)); - - tmp = realize_global_int(builder, gindex); - if (tmp == NULL) - break; - PyTuple_SET_ITEM(enumvalues, i, tmp); - - p += j + 1; - } - - args = NULL; - if (!PyErr_Occurred()) { - char *name = alloca(6 + strlen(e->name)); - _realize_name(name, "enum ", e->name); - args = Py_BuildValue("(sOOO)", name, enumerators, - enumvalues, basetd); - } - Py_DECREF(enumerators); - Py_DECREF(enumvalues); - if (args == NULL) - return NULL; - - x = b_new_enum_type(NULL, args); - Py_DECREF(args); - if (x == NULL) - return NULL; - - /* Update the "primary" _CFFI_OP_ENUM slot, which - may be the same or a different slot than the "current" one */ - assert((((uintptr_t)x) & 1) == 0); - assert(builder->ctx.types[e->type_index] == op2); - Py_INCREF(x); - builder->ctx.types[e->type_index] = x; - - /* Done, leave without updating the "current" slot because - it may be done already above. If not, never mind, the - next call to realize_c_type() will do it. */ - return x; - } - break; - } - - case _CFFI_OP_FUNCTION: - { - PyObject *fargs; - int i, base_index, num_args, ellipsis, abi; - - y = (PyObject *)realize_c_type(builder, opcodes, _CFFI_GETARG(op)); - if (y == NULL) - return NULL; - - base_index = index + 1; - num_args = 0; - /* note that if the arguments are already built, they have a - pointer in the 'opcodes' array, and GETOP() returns a - random even value. But OP_FUNCTION_END is odd, so the - condition below still works correctly. */ - while (_CFFI_GETOP(opcodes[base_index + num_args]) != - _CFFI_OP_FUNCTION_END) - num_args++; - - ellipsis = _CFFI_GETARG(opcodes[base_index + num_args]) & 0x01; - abi = _CFFI_GETARG(opcodes[base_index + num_args]) & 0xFE; - switch (abi) { - case 0: - abi = FFI_DEFAULT_ABI; - break; - case 2: -#if defined(MS_WIN32) && !defined(_WIN64) - abi = FFI_STDCALL; -#else - abi = FFI_DEFAULT_ABI; -#endif - break; - default: - PyErr_Format(FFIError, "abi number %d not supported", abi); - Py_DECREF(y); - return NULL; - } - - fargs = PyTuple_New(num_args); - if (fargs == NULL) { - Py_DECREF(y); - return NULL; - } - - for (i = 0; i < num_args; i++) { - z = (PyObject *)realize_c_type(builder, opcodes, base_index + i); - if (z == NULL) { - Py_DECREF(fargs); - Py_DECREF(y); - return NULL; - } - PyTuple_SET_ITEM(fargs, i, z); - } - - z = new_function_type(fargs, (CTypeDescrObject *)y, ellipsis, abi); - Py_DECREF(fargs); - Py_DECREF(y); - if (z == NULL) - return NULL; - - x = PyTuple_Pack(1, z); /* hack: hide the CT_FUNCTIONPTR. it will - be revealed again by the OP_POINTER */ - Py_DECREF(z); - break; - } - - case _CFFI_OP_NOOP: - x = realize_c_type_or_func(builder, opcodes, _CFFI_GETARG(op)); - break; - - case _CFFI_OP_TYPENAME: - { - /* essential: the TYPENAME opcode resolves the type index looked - up in the 'ctx->typenames' array, but it does so in 'ctx->types' - instead of in 'opcodes'! */ - int type_index = builder->ctx.typenames[_CFFI_GETARG(op)].type_index; - x = realize_c_type_or_func(builder, builder->ctx.types, type_index); - break; - } - - default: - PyErr_Format(PyExc_NotImplementedError, "op=%d", (int)_CFFI_GETOP(op)); - 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); - Py_INCREF(x); - opcodes[index] = x; - } - return x; -} - -static CTypeDescrObject * -realize_c_func_return_type(builder_c_t *builder, - _cffi_opcode_t opcodes[], int index) -{ - PyObject *x; - _cffi_opcode_t op = opcodes[index]; - - if ((((uintptr_t)op) & 1) == 0) { - /* already built: assert that it is a function and fish - for the return type */ - x = (PyObject *)op; - assert(PyTuple_Check(x)); /* from _CFFI_OP_FUNCTION */ - x = PyTuple_GET_ITEM(x, 0); - assert(CTypeDescr_Check(x)); - assert(((CTypeDescrObject *)x)->ct_flags & CT_FUNCTIONPTR); - x = PyTuple_GET_ITEM(((CTypeDescrObject *)x)->ct_stuff, 1); - assert(CTypeDescr_Check(x)); - Py_INCREF(x); - return (CTypeDescrObject *)x; - } - else { - assert(_CFFI_GETOP(op) == _CFFI_OP_FUNCTION); - return realize_c_type(builder, opcodes, _CFFI_GETARG(opcodes[index])); - } -} - -static int do_realize_lazy_struct(CTypeDescrObject *ct) -{ - /* This is called by force_lazy_struct() in _cffi_backend.c */ - assert(ct->ct_flags & (CT_STRUCT | CT_UNION)); - - if (ct->ct_flags & CT_LAZY_FIELD_LIST) { - builder_c_t *builder; - char *p; - int n, i, sflags; - const struct _cffi_struct_union_s *s; - const struct _cffi_field_s *fld; - PyObject *fields, *args, *res; - - assert(!(ct->ct_flags & CT_IS_OPAQUE)); - - builder = ct->ct_extra; - assert(builder != NULL); - - p = alloca(2 + strlen(ct->ct_name)); - _unrealize_name(p, ct->ct_name); - - n = search_in_struct_unions(&builder->ctx, p, strlen(p)); - if (n < 0) - Py_FatalError("lost a struct/union!"); - - s = &builder->ctx.struct_unions[n]; - fld = &builder->ctx.fields[s->first_field_index]; - - /* XXX painfully build all the Python objects that are the args - to b_complete_struct_or_union() */ - - fields = PyList_New(s->num_fields); - if (fields == NULL) - return -1; - - for (i = 0; i < s->num_fields; i++, fld++) { - _cffi_opcode_t op = fld->field_type_op; - int fbitsize = -1; - PyObject *f; - CTypeDescrObject *ctf; - - switch (_CFFI_GETOP(op)) { - - case _CFFI_OP_BITFIELD: - assert(fld->field_size >= 0); - fbitsize = (int)fld->field_size; - /* fall-through */ - case _CFFI_OP_NOOP: - ctf = realize_c_type(builder, builder->ctx.types, - _CFFI_GETARG(op)); - break; - - default: - Py_DECREF(fields); - PyErr_Format(PyExc_NotImplementedError, "field op=%d", - (int)_CFFI_GETOP(op)); - return -1; - } - - if (ctf != NULL && fld->field_offset == (size_t)-1) { - /* unnamed struct, with field positions and sizes entirely - determined by complete_struct_or_union() and not checked. - Or, bitfields (field_size >= 0), similarly not checked. */ - assert(fld->field_size == (size_t)-1 || fbitsize >= 0); - } - else if (ctf == NULL || detect_custom_layout(ct, SF_STD_FIELD_POS, - ctf->ct_size, fld->field_size, - "wrong size for field '", - fld->name, "'") < 0) { - Py_DECREF(fields); - return -1; - } - - f = Py_BuildValue("(sOin)", fld->name, ctf, - fbitsize, (Py_ssize_t)fld->field_offset); - if (f == NULL) { - Py_DECREF(fields); - return -1; - } - PyList_SET_ITEM(fields, i, f); - } - - sflags = 0; - if (s->flags & _CFFI_F_CHECK_FIELDS) - sflags |= SF_STD_FIELD_POS; - if (s->flags & _CFFI_F_PACKED) - sflags |= SF_PACKED; - - args = Py_BuildValue("(OOOnii)", ct, fields, Py_None, - (Py_ssize_t)s->size, - s->alignment, - sflags); - Py_DECREF(fields); - if (args == NULL) - return -1; - - ct->ct_extra = NULL; - ct->ct_flags |= CT_IS_OPAQUE; - res = b_complete_struct_or_union(NULL, args); - ct->ct_flags &= ~CT_IS_OPAQUE; - Py_DECREF(args); - - if (res == NULL) { - ct->ct_extra = builder; - return -1; - } - - assert(ct->ct_stuff != NULL); - ct->ct_flags &= ~CT_LAZY_FIELD_LIST; - Py_DECREF(res); - return 1; - } - else { - assert(ct->ct_flags & CT_IS_OPAQUE); - return 0; - } -} diff --git a/c/test_c.py b/c/test_c.py deleted file mode 100644 index 654584d..0000000 --- a/c/test_c.py +++ /dev/null @@ -1,4575 +0,0 @@ -import py -import pytest - -def _setup_path(): - import os, sys - sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) -_setup_path() -from _cffi_backend import * -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.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,): - type_or_class = "type" - mandatory_b_prefix = '' - mandatory_u_prefix = 'u' - bytechr = chr - bitem2bchr = lambda x: x - class U(object): - def __add__(self, other): - return eval('u'+repr(other).replace(r'\\u', r'\u') - .replace(r'\\U', r'\U')) - u = U() - str2bytes = str - strict_compare = False -else: - type_or_class = "class" - long = int - unicode = str - unichr = chr - mandatory_b_prefix = 'b' - mandatory_u_prefix = '' - bytechr = lambda n: bytes([n]) - bitem2bchr = bytechr - u = "" - str2bytes = lambda s: bytes(s, "ascii") - strict_compare = True - -def size_of_int(): - BInt = new_primitive_type("int") - return sizeof(BInt) - -def size_of_long(): - BLong = new_primitive_type("long") - return sizeof(BLong) - -def size_of_ptr(): - BInt = new_primitive_type("int") - BPtr = new_pointer_type(BInt) - return sizeof(BPtr) - - -def find_and_load_library(name, flags=RTLD_NOW): - import ctypes.util - if name is None: - path = None - else: - path = ctypes.util.find_library(name) - if path is None and name == 'c': - assert sys.platform == 'win32' - 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(): - x = find_and_load_library('c') - assert repr(x).startswith("<clibrary '") - x = find_and_load_library('c', RTLD_NOW | RTLD_GLOBAL) - assert repr(x).startswith("<clibrary '") - x = find_and_load_library('c', RTLD_LAZY) - assert repr(x).startswith("<clibrary '") - -def test_all_rtld_symbols(): - import sys - FFI_DEFAULT_ABI # these symbols must be defined - FFI_CDECL - RTLD_LAZY - RTLD_NOW - RTLD_GLOBAL - RTLD_LOCAL - if sys.platform.startswith("linux"): - RTLD_NODELETE - RTLD_NOLOAD - RTLD_DEEPBIND - -def test_new_primitive_type(): - py.test.raises(KeyError, new_primitive_type, "foo") - p = new_primitive_type("signed char") - assert repr(p) == "<ctype 'signed char'>" - -def check_dir(p, expected): - got = [name for name in dir(p) if not name.startswith('_')] - assert got == sorted(expected) - -def test_inspect_primitive_type(): - p = new_primitive_type("signed char") - assert p.kind == "primitive" - assert p.cname == "signed char" - check_dir(p, ['cname', 'kind']) - -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._CDataBase'>" % type_or_class - assert int(x) == -65 - x = cast(p, -66 + (1<<199)*256) - assert repr(x) == "<cdata 'signed char' -66>" - assert int(x) == -66 - assert (x == cast(p, -66)) is True - assert (x != cast(p, -66)) is False - q = new_primitive_type("short") - assert (x == cast(q, -66)) is True - assert (x != cast(q, -66)) is False - -def test_sizeof_type(): - py.test.raises(TypeError, sizeof, 42.5) - p = new_primitive_type("short") - assert sizeof(p) == 2 - -def test_integer_types(): - for name in ['signed char', 'short', 'int', 'long', 'long long']: - p = new_primitive_type(name) - size = sizeof(p) - min = -(1 << (8*size-1)) - max = (1 << (8*size-1)) - 1 - assert int(cast(p, min)) == min - assert int(cast(p, max)) == max - assert int(cast(p, min - 1)) == max - assert int(cast(p, max + 1)) == min - py.test.raises(TypeError, cast, p, None) - assert long(cast(p, min - 1)) == max - assert int(cast(p, b'\x08')) == 8 - assert int(cast(p, u+'\x08')) == 8 - for name in ['char', 'short', 'int', 'long', 'long long']: - p = new_primitive_type('unsigned ' + name) - size = sizeof(p) - max = (1 << (8*size)) - 1 - assert int(cast(p, 0)) == 0 - assert int(cast(p, max)) == max - assert int(cast(p, -1)) == max - assert int(cast(p, max + 1)) == 0 - assert long(cast(p, -1)) == max - assert int(cast(p, b'\xFE')) == 254 - assert int(cast(p, u+'\xFE')) == 254 - -def test_no_float_on_int_types(): - p = new_primitive_type('long') - py.test.raises(TypeError, float, cast(p, 42)) - py.test.raises(TypeError, complex, cast(p, 42)) - -def test_float_types(): - INF = 1E200 * 1E200 - for name in ["float", "double"]: - p = new_primitive_type(name) - assert bool(cast(p, 0)) is False # since 1.7 - assert bool(cast(p, -0.0)) is False # since 1.7 - assert bool(cast(p, 1e-42)) is True - assert bool(cast(p, -1e-42)) is True - assert bool(cast(p, INF)) - assert bool(cast(p, -INF)) - assert bool(cast(p, float("nan"))) - assert int(cast(p, -150)) == -150 - assert int(cast(p, 61.91)) == 61 - assert long(cast(p, 61.91)) == 61 - assert type(int(cast(p, 61.91))) is int - assert type(int(cast(p, 1E22))) is long - assert type(long(cast(p, 61.91))) is long - assert type(long(cast(p, 1E22))) is long - py.test.raises(OverflowError, int, cast(p, INF)) - py.test.raises(OverflowError, int, cast(p, -INF)) - assert float(cast(p, 1.25)) == 1.25 - assert float(cast(p, INF)) == INF - assert float(cast(p, -INF)) == -INF - if name == "float": - assert float(cast(p, 1.1)) != 1.1 # rounding error - assert float(cast(p, 1E200)) == INF # limited range - - assert cast(p, -1.1) == cast(p, -1.1) - assert repr(float(cast(p, -0.0))) == '-0.0' - assert float(cast(p, b'\x09')) == 9.0 - assert float(cast(p, u+'\x09')) == 9.0 - assert float(cast(p, True)) == 1.0 - py.test.raises(TypeError, cast, p, None) - -def test_complex_types(): - INF = 1E200 * 1E200 - for name in ["float", "double"]: - p = new_primitive_type(name + " _Complex") - assert bool(cast(p, 0)) is False - assert bool(cast(p, INF)) - assert bool(cast(p, -INF)) - assert bool(cast(p, 0j)) is False - assert bool(cast(p, INF*1j)) - assert bool(cast(p, -INF*1j)) - # "can't convert complex to float", like CPython's "float(0j)" - py.test.raises(TypeError, int, cast(p, -150)) - py.test.raises(TypeError, long, cast(p, -150)) - py.test.raises(TypeError, float, cast(p, -150)) - assert complex(cast(p, 1.25)) == 1.25 - assert complex(cast(p, 1.25j)) == 1.25j - assert complex(cast(p, complex(0,INF))) == complex(0,INF) - assert complex(cast(p, -INF)) == -INF - if name == "float": - assert complex(cast(p, 1.1j)) != 1.1j # rounding error - assert complex(cast(p, 1E200+3j)) == INF+3j # limited range - assert complex(cast(p, complex(3,1E200))) == complex(3,INF) # limited range - - assert cast(p, -1.1j) == cast(p, -1.1j) - assert repr(complex(cast(p, -0.0)).real) == '-0.0' - #assert repr(complex(cast(p, -0j))) == '-0j' # http://bugs.python.org/issue29602 - assert complex(cast(p, b'\x09')) == 9.0 + 0j - assert complex(cast(p, u+'\x09')) == 9.0 + 0j - assert complex(cast(p, True)) == 1.0 + 0j - py.test.raises(TypeError, cast, p, None) - # - py.test.raises(TypeError, cast, new_primitive_type(name), 1+0j) - # - for basetype in ["char", "int", "uint64_t", "float", - "double", "long double"]: - baseobj = cast(new_primitive_type(basetype), 65) - py.test.raises(TypeError, complex, baseobj) - # - BArray = new_array_type(new_pointer_type(p), 10) - x = newp(BArray, None) - x[5] = 12.34 + 56.78j - assert type(x[5]) is complex - assert abs(x[5] - (12.34 + 56.78j)) < 1e-5 - assert (x[5] == 12.34 + 56.78j) == (name == "double") # rounding error - # - class Foo: - def __complex__(self): - return 2 + 3j - assert complex(Foo()) == 2 + 3j - assert complex(cast(p, Foo())) == 2 + 3j - py.test.raises(TypeError, cast, new_primitive_type("int"), 1+0j) - -def test_character_type(): - p = new_primitive_type("char") - assert bool(cast(p, 'A')) is True - assert bool(cast(p, '\x00')) is False # since 1.7 - assert cast(p, '\x00') == cast(p, -17*256) - assert int(cast(p, 'A')) == 65 - assert long(cast(p, 'A')) == 65 - assert type(int(cast(p, 'A'))) is int - assert type(long(cast(p, 'A'))) is long - assert str(cast(p, 'A')) == repr(cast(p, 'A')) - assert repr(cast(p, 'A')) == "<cdata 'char' %s'A'>" % mandatory_b_prefix - assert repr(cast(p, 255)) == r"<cdata 'char' %s'\xff'>" % mandatory_b_prefix - assert repr(cast(p, 0)) == r"<cdata 'char' %s'\x00'>" % mandatory_b_prefix - -def test_pointer_type(): - p = new_primitive_type("int") - assert repr(p) == "<ctype 'int'>" - p = new_pointer_type(p) - assert repr(p) == "<ctype 'int *'>" - p = new_pointer_type(p) - assert repr(p) == "<ctype 'int * *'>" - p = new_pointer_type(p) - assert repr(p) == "<ctype 'int * * *'>" - -def test_inspect_pointer_type(): - p1 = new_primitive_type("int") - p2 = new_pointer_type(p1) - assert p2.kind == "pointer" - assert p2.cname == "int *" - assert p2.item is p1 - check_dir(p2, ['cname', 'kind', 'item']) - p3 = new_pointer_type(p2) - assert p3.item is p2 - -def test_pointer_to_int(): - BInt = new_primitive_type("int") - py.test.raises(TypeError, newp, BInt) - py.test.raises(TypeError, newp, BInt, None) - BPtr = new_pointer_type(BInt) - p = newp(BPtr) - assert repr(p) == "<cdata 'int *' owning %d bytes>" % size_of_int() - p = newp(BPtr, None) - assert repr(p) == "<cdata 'int *' owning %d bytes>" % size_of_int() - p = newp(BPtr, 5000) - assert repr(p) == "<cdata 'int *' owning %d bytes>" % size_of_int() - q = cast(BPtr, p) - assert repr(q).startswith("<cdata 'int *' 0x") - assert p == q - assert hash(p) == hash(q) - e = py.test.raises(TypeError, newp, new_array_type(BPtr, None), None) - assert str(e.value) == ( - "expected new array length or list/tuple/str, not NoneType") - -def test_pointer_bool(): - BInt = new_primitive_type("int") - BPtr = new_pointer_type(BInt) - p = cast(BPtr, 0) - assert bool(p) is False - p = cast(BPtr, 42) - assert bool(p) is True - -def test_pointer_to_pointer(): - BInt = new_primitive_type("int") - BPtr = new_pointer_type(BInt) - BPtrPtr = new_pointer_type(BPtr) - p = newp(BPtrPtr, None) - assert repr(p) == "<cdata 'int * *' owning %d bytes>" % size_of_ptr() - -def test_reading_pointer_to_int(): - BInt = new_primitive_type("int") - BPtr = new_pointer_type(BInt) - p = newp(BPtr, None) - assert p[0] == 0 - p = newp(BPtr, 5000) - assert p[0] == 5000 - with pytest.raises(IndexError): - p[1] - with pytest.raises(IndexError): - p[-1] - -def test_reading_pointer_to_float(): - BFloat = new_primitive_type("float") - py.test.raises(TypeError, newp, BFloat, None) - BPtr = new_pointer_type(BFloat) - p = newp(BPtr, None) - assert p[0] == 0.0 and type(p[0]) is float - p = newp(BPtr, 1.25) - assert p[0] == 1.25 and type(p[0]) is float - p = newp(BPtr, 1.1) - assert p[0] != 1.1 and abs(p[0] - 1.1) < 1E-5 # rounding errors - -def test_cast_float_to_int(): - for type in ["int", "unsigned int", "long", "unsigned long", - "long long", "unsigned long long"]: - p = new_primitive_type(type) - assert int(cast(p, 4.2)) == 4 - py.test.raises(TypeError, newp, new_pointer_type(p), 4.2) - -def test_newp_integer_types(): - for name in ['signed char', 'short', 'int', 'long', 'long long']: - p = new_primitive_type(name) - pp = new_pointer_type(p) - size = sizeof(p) - min = -(1 << (8*size-1)) - max = (1 << (8*size-1)) - 1 - assert newp(pp, min)[0] == min - assert newp(pp, max)[0] == max - py.test.raises(OverflowError, newp, pp, min - 2 ** 32) - py.test.raises(OverflowError, newp, pp, min - 2 ** 64) - py.test.raises(OverflowError, newp, pp, max + 2 ** 32) - py.test.raises(OverflowError, newp, pp, max + 2 ** 64) - py.test.raises(OverflowError, newp, pp, min - 1) - py.test.raises(OverflowError, newp, pp, max + 1) - py.test.raises(OverflowError, newp, pp, min - 1 - 2 ** 32) - py.test.raises(OverflowError, newp, pp, min - 1 - 2 ** 64) - py.test.raises(OverflowError, newp, pp, max + 1) - py.test.raises(OverflowError, newp, pp, max + 1 + 2 ** 32) - py.test.raises(OverflowError, newp, pp, max + 1 + 2 ** 64) - py.test.raises(TypeError, newp, pp, 1.0) - for name in ['char', 'short', 'int', 'long', 'long long']: - p = new_primitive_type('unsigned ' + name) - pp = new_pointer_type(p) - size = sizeof(p) - max = (1 << (8*size)) - 1 - assert newp(pp, 0)[0] == 0 - assert newp(pp, max)[0] == max - py.test.raises(OverflowError, newp, pp, -1) - py.test.raises(OverflowError, newp, pp, max + 1) - -def test_reading_pointer_to_char(): - BChar = new_primitive_type("char") - py.test.raises(TypeError, newp, BChar, None) - BPtr = new_pointer_type(BChar) - p = newp(BPtr, None) - assert p[0] == b'\x00' - p = newp(BPtr, b'A') - assert p[0] == b'A' - py.test.raises(TypeError, newp, BPtr, 65) - py.test.raises(TypeError, newp, BPtr, b"foo") - py.test.raises(TypeError, newp, BPtr, u+"foo") - c = cast(BChar, b'A') - assert str(c) == repr(c) - assert int(c) == ord(b'A') - py.test.raises(TypeError, cast, BChar, b'foo') - py.test.raises(TypeError, cast, BChar, u+'foo') - e = py.test.raises(TypeError, newp, new_array_type(BPtr, None), 12.3) - assert str(e.value) == ( - "expected new array length or list/tuple/str, not float") - -def test_reading_pointer_to_pointer(): - BVoidP = new_pointer_type(new_void_type()) - BCharP = new_pointer_type(new_primitive_type("char")) - BInt = new_primitive_type("int") - BIntPtr = new_pointer_type(BInt) - BIntPtrPtr = new_pointer_type(BIntPtr) - q = newp(BIntPtr, 42) - assert q[0] == 42 - p = newp(BIntPtrPtr, None) - assert p[0] is not None - assert p[0] == cast(BVoidP, 0) - assert p[0] == cast(BCharP, 0) - assert p[0] != None - assert repr(p[0]) == "<cdata 'int *' NULL>" - p[0] = q - assert p[0] != cast(BVoidP, 0) - assert p[0] != cast(BCharP, 0) - assert p[0][0] == 42 - q[0] += 1 - assert p[0][0] == 43 - p = newp(BIntPtrPtr, q) - assert p[0][0] == 43 - -def test_load_standard_library(): - if sys.platform == "win32": - py.test.raises(OSError, find_and_load_library, None) - return - x = find_and_load_library(None) - BVoidP = new_pointer_type(new_void_type()) - assert x.load_function(BVoidP, 'strcpy') - py.test.raises(AttributeError, x.load_function, - BVoidP, 'xxx_this_function_does_not_exist') - # the next one is from 'libm', not 'libc', but we assume - # that it is already loaded too, so it should work - assert x.load_function(BVoidP, 'sqrt') - # - x.close_lib() - py.test.raises(ValueError, x.load_function, BVoidP, 'sqrt') - x.close_lib() - -def test_no_len_on_nonarray(): - p = new_primitive_type("int") - py.test.raises(TypeError, len, cast(p, 42)) - -def test_cmp_none(): - p = new_primitive_type("int") - x = cast(p, 42) - assert (x == None) is False - assert (x != None) is True - assert (x == ["hello"]) is False - assert (x != ["hello"]) is True - y = cast(p, 0) - assert (y == None) is False - -def test_invalid_indexing(): - p = new_primitive_type("int") - x = cast(p, 42) - with pytest.raises(TypeError): - x[0] - -def test_default_str(): - BChar = new_primitive_type("char") - x = cast(BChar, 42) - assert str(x) == repr(x) - BInt = new_primitive_type("int") - x = cast(BInt, 42) - assert str(x) == repr(x) - BArray = new_array_type(new_pointer_type(BInt), 10) - x = newp(BArray, None) - assert str(x) == repr(x) - -def test_default_unicode(): - BInt = new_primitive_type("int") - x = cast(BInt, 42) - assert unicode(x) == unicode(repr(x)) - BArray = new_array_type(new_pointer_type(BInt), 10) - x = newp(BArray, None) - assert unicode(x) == unicode(repr(x)) - -def test_cast_from_cdataint(): - BInt = new_primitive_type("int") - x = cast(BInt, 0) - y = cast(new_pointer_type(BInt), x) - assert bool(y) is False - # - x = cast(BInt, 42) - y = cast(BInt, x) - assert int(y) == 42 - y = cast(new_primitive_type("char"), x) - assert int(y) == 42 - y = cast(new_primitive_type("float"), x) - assert float(y) == 42.0 - # - z = cast(BInt, 42.5) - assert int(z) == 42 - z = cast(BInt, y) - assert int(z) == 42 - -def test_void_type(): - p = new_void_type() - assert p.kind == "void" - assert p.cname == "void" - check_dir(p, ['kind', 'cname']) - -def test_array_type(): - p = new_primitive_type("int") - assert repr(p) == "<ctype 'int'>" - # - py.test.raises(TypeError, new_array_type, new_pointer_type(p), "foo") - py.test.raises(ValueError, new_array_type, new_pointer_type(p), -42) - # - p1 = new_array_type(new_pointer_type(p), None) - assert repr(p1) == "<ctype 'int[]'>" - py.test.raises(ValueError, new_array_type, new_pointer_type(p1), 42) - # - p1 = new_array_type(new_pointer_type(p), 42) - p2 = new_array_type(new_pointer_type(p1), 25) - assert repr(p2) == "<ctype 'int[25][42]'>" - p2 = new_array_type(new_pointer_type(p1), None) - assert repr(p2) == "<ctype 'int[][42]'>" - # - py.test.raises(OverflowError, - new_array_type, new_pointer_type(p), sys.maxsize+1) - py.test.raises(OverflowError, - new_array_type, new_pointer_type(p), sys.maxsize // 3) - -def test_inspect_array_type(): - p = new_primitive_type("int") - p1 = new_array_type(new_pointer_type(p), None) - assert p1.kind == "array" - assert p1.cname == "int[]" - assert p1.item is p - assert p1.length is None - check_dir(p1, ['cname', 'kind', 'item', 'length']) - p1 = new_array_type(new_pointer_type(p), 42) - assert p1.kind == "array" - assert p1.cname == "int[42]" - assert p1.item is p - assert p1.length == 42 - check_dir(p1, ['cname', 'kind', 'item', 'length']) - -def test_array_instance(): - LENGTH = 1423 - p = new_primitive_type("int") - p1 = new_array_type(new_pointer_type(p), LENGTH) - a = newp(p1, None) - assert repr(a) == "<cdata 'int[%d]' owning %d bytes>" % ( - LENGTH, LENGTH * size_of_int()) - assert len(a) == LENGTH - for i in range(LENGTH): - assert a[i] == 0 - 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 - 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) - -def test_array_of_unknown_length_instance(): - p = new_primitive_type("int") - p1 = new_array_type(new_pointer_type(p), None) - py.test.raises(TypeError, newp, p1, None) - py.test.raises(ValueError, newp, p1, -42) - a = newp(p1, 42) - assert len(a) == 42 - for i in range(42): - a[i] -= i - for i in range(42): - assert a[i] == -i - 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") - p1 = new_array_type(new_pointer_type(p), None) - a = newp(p1, list(range(42))) - assert len(a) == 42 - a = newp(p1, tuple(range(142))) - assert len(a) == 142 - -def test_array_initializer(): - p = new_primitive_type("int") - p1 = new_array_type(new_pointer_type(p), None) - a = newp(p1, list(range(100, 142))) - for i in range(42): - assert a[i] == 100 + i - # - p2 = new_array_type(new_pointer_type(p), 43) - a = newp(p2, tuple(range(100, 142))) - for i in range(42): - assert a[i] == 100 + i - assert a[42] == 0 # extra uninitialized item - -def test_array_add(): - p = new_primitive_type("int") - p1 = new_array_type(new_pointer_type(p), 5) # int[5] - p2 = new_array_type(new_pointer_type(p1), 3) # int[3][5] - a = newp(p2, [list(range(n, n+5)) for n in [100, 200, 300]]) - assert repr(a) == "<cdata 'int[3][5]' owning %d bytes>" % ( - 3*5*size_of_int(),) - assert repr(a + 0).startswith("<cdata 'int(*)[5]' 0x") - assert 0 + a == a + 0 != 1 + a == a + 1 - assert repr(a[0]).startswith("<cdata 'int[5]' 0x") - assert repr((a + 0)[0]).startswith("<cdata 'int[5]' 0x") - assert repr(a[0] + 0).startswith("<cdata 'int *' 0x") - assert type(a[0][0]) is int - assert type((a[0] + 0)[0]) is int - -def test_array_sub(): - BInt = new_primitive_type("int") - BArray = new_array_type(new_pointer_type(BInt), 5) # int[5] - a = newp(BArray, None) - p = a + 1 - assert p - a == 1 - assert p - (a+0) == 1 - assert a == (p - 1) - BPtr = new_pointer_type(new_primitive_type("short")) - q = newp(BPtr, None) - 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(): - BInt = new_primitive_type("int") - BIntPtr = new_pointer_type(BInt) - a = cast(BIntPtr, 1240) - for bi in range(1430, 1438): - b = cast(BIntPtr, bi) - if ((bi - 1240) % size_of_int()) == 0: - assert b - a == (bi - 1240) // size_of_int() - assert a - b == (1240 - bi) // size_of_int() - else: - with pytest.raises(ValueError): - b - a - with pytest.raises(ValueError): - a - b - -def test_cast_primitive_from_cdata(): - p = new_primitive_type("int") - n = cast(p, cast(p, -42)) - assert int(n) == -42 - # - p = new_primitive_type("unsigned int") - n = cast(p, cast(p, 42)) - assert int(n) == 42 - # - p = new_primitive_type("long long") - n = cast(p, cast(p, -(1<<60))) - assert int(n) == -(1<<60) - # - p = new_primitive_type("unsigned long long") - n = cast(p, cast(p, 1<<63)) - assert int(n) == 1<<63 - # - p = new_primitive_type("float") - n = cast(p, cast(p, 42.5)) - assert float(n) == 42.5 - # - p = new_primitive_type("char") - n = cast(p, cast(p, "A")) - assert int(n) == ord("A") - -def test_new_primitive_from_cdata(): - p = new_primitive_type("int") - p1 = new_pointer_type(p) - n = newp(p1, cast(p, -42)) - assert n[0] == -42 - # - p = new_primitive_type("unsigned int") - p1 = new_pointer_type(p) - n = newp(p1, cast(p, 42)) - assert n[0] == 42 - # - p = new_primitive_type("float") - p1 = new_pointer_type(p) - n = newp(p1, cast(p, 42.5)) - assert n[0] == 42.5 - # - p = new_primitive_type("char") - p1 = new_pointer_type(p) - n = newp(p1, cast(p, "A")) - assert n[0] == b"A" - -def test_cast_between_pointers(): - BIntP = new_pointer_type(new_primitive_type("int")) - BIntA = new_array_type(BIntP, None) - a = newp(BIntA, [40, 41, 42, 43, 44]) - BShortP = new_pointer_type(new_primitive_type("short")) - b = cast(BShortP, a) - c = cast(BIntP, b) - assert c[3] == 43 - BLongLong = new_primitive_type("long long") - d = cast(BLongLong, c) - e = cast(BIntP, d) - assert e[3] == 43 - f = cast(BIntP, int(d)) - assert f[3] == 43 - # - b = cast(BShortP, 0) - assert not b - c = cast(BIntP, b) - assert not c - assert int(cast(BLongLong, c)) == 0 - -def test_alignof(): - BInt = new_primitive_type("int") - assert alignof(BInt) == sizeof(BInt) - BPtr = new_pointer_type(BInt) - assert alignof(BPtr) == sizeof(BPtr) - BArray = new_array_type(BPtr, None) - assert alignof(BArray) == alignof(BInt) - -def test_new_struct_type(): - BStruct = new_struct_type("foo") - assert repr(BStruct) == "<ctype 'foo'>" - BStruct = new_struct_type("struct foo") - assert repr(BStruct) == "<ctype 'struct foo'>" - BPtr = new_pointer_type(BStruct) - assert repr(BPtr) == "<ctype 'struct foo *'>" - py.test.raises(ValueError, sizeof, BStruct) - py.test.raises(ValueError, alignof, BStruct) - -def test_new_union_type(): - BUnion = new_union_type("union foo") - assert repr(BUnion) == "<ctype 'union foo'>" - BPtr = new_pointer_type(BUnion) - assert repr(BPtr) == "<ctype 'union foo *'>" - -def test_complete_struct(): - BLong = new_primitive_type("long") - BChar = new_primitive_type("char") - BShort = new_primitive_type("short") - BStruct = new_struct_type("struct foo") - assert BStruct.kind == "struct" - assert BStruct.cname == "struct foo" - assert BStruct.fields is None - check_dir(BStruct, ['cname', 'kind', 'fields']) - # - complete_struct_or_union(BStruct, [('a1', BLong, -1), - ('a2', BChar, -1), - ('a3', BShort, -1)]) - d = BStruct.fields - assert len(d) == 3 - assert d[0][0] == 'a1' - assert d[0][1].type is BLong - assert d[0][1].offset == 0 - assert d[0][1].bitshift == -1 - assert d[0][1].bitsize == -1 - assert d[1][0] == 'a2' - assert d[1][1].type is BChar - assert d[1][1].offset == sizeof(BLong) - assert d[1][1].bitshift == -1 - assert d[1][1].bitsize == -1 - assert d[2][0] == 'a3' - assert d[2][1].type is BShort - assert d[2][1].offset == sizeof(BLong) + sizeof(BShort) - assert d[2][1].bitshift == -1 - assert d[2][1].bitsize == -1 - assert sizeof(BStruct) == 2 * sizeof(BLong) - assert alignof(BStruct) == alignof(BLong) - -def test_complete_union(): - BLong = new_primitive_type("long") - BChar = new_primitive_type("char") - BUnion = new_union_type("union foo") - assert BUnion.kind == "union" - assert BUnion.cname == "union foo" - assert BUnion.fields is None - complete_struct_or_union(BUnion, [('a1', BLong, -1), - ('a2', BChar, -1)]) - d = BUnion.fields - assert len(d) == 2 - assert d[0][0] == 'a1' - assert d[0][1].type is BLong - assert d[0][1].offset == 0 - assert d[1][0] == 'a2' - assert d[1][1].type is BChar - assert d[1][1].offset == 0 - assert sizeof(BUnion) == sizeof(BLong) - assert alignof(BUnion) == alignof(BLong) - -def test_struct_instance(): - BInt = new_primitive_type("int") - BStruct = new_struct_type("struct foo") - BStructPtr = new_pointer_type(BStruct) - p = cast(BStructPtr, 42) - with pytest.raises(AttributeError) as e: - p.a1 # opaque - assert str(e.value) == ("cdata 'struct foo *' points to an opaque type: " - "cannot read fields") - 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") - - complete_struct_or_union(BStruct, [('a1', BInt, -1), - ('a2', BInt, -1)]) - p = newp(BStructPtr, None) - s = p[0] - assert s.a1 == 0 - s.a2 = 123 - assert s.a1 == 0 - assert s.a2 == 123 - with pytest.raises(OverflowError): - s.a1 = sys.maxsize+1 - assert s.a1 == 0 - with pytest.raises(AttributeError) as e: - p.foobar - assert str(e.value) == "cdata 'struct foo *' has no field 'foobar'" - with pytest.raises(AttributeError) as e: - p.foobar = 42 - assert str(e.value) == "cdata 'struct foo *' has no field 'foobar'" - with pytest.raises(AttributeError) as e: - s.foobar - assert str(e.value) == "cdata 'struct foo' has no field 'foobar'" - with pytest.raises(AttributeError) as e: - s.foobar = 42 - assert str(e.value) == "cdata 'struct foo' has no field 'foobar'" - j = cast(BInt, 42) - with pytest.raises(AttributeError) as e: - j.foobar - assert str(e.value) == "cdata 'int' has no attribute 'foobar'" - 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) - with pytest.raises(AttributeError) as e: - j.foobar - assert str(e.value) == "cdata 'int *' has no attribute 'foobar'" - 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) - with pytest.raises(AttributeError) as e: - pp.a1 - assert str(e.value) == "cdata 'struct foo * *' has no attribute 'a1'" - with pytest.raises(AttributeError) as e: - pp.a1 = 42 - assert str(e.value) == "cdata 'struct foo * *' has no attribute 'a1'" - -def test_union_instance(): - BInt = new_primitive_type("int") - BUInt = new_primitive_type("unsigned int") - BUnion = new_union_type("union bar") - complete_struct_or_union(BUnion, [('a1', BInt, -1), ('a2', BUInt, -1)]) - p = newp(new_pointer_type(BUnion), [-42]) - bigval = -42 + (1 << (8*size_of_int())) - assert p.a1 == -42 - assert p.a2 == bigval - p = newp(new_pointer_type(BUnion), {'a2': bigval}) - assert p.a1 == -42 - assert p.a2 == bigval - py.test.raises(OverflowError, newp, new_pointer_type(BUnion), - {'a1': bigval}) - p = newp(new_pointer_type(BUnion), []) - assert p.a1 == p.a2 == 0 - -def test_struct_pointer(): - BInt = new_primitive_type("int") - BStruct = new_struct_type("struct foo") - BStructPtr = new_pointer_type(BStruct) - complete_struct_or_union(BStruct, [('a1', BInt, -1), - ('a2', BInt, -1)]) - p = newp(BStructPtr, None) - assert p.a1 == 0 # read/write via the pointer (C equivalent: '->') - p.a2 = 123 - assert p.a1 == 0 - assert p.a2 == 123 - -def test_struct_init_list(): - BVoidP = new_pointer_type(new_void_type()) - BInt = new_primitive_type("int") - BIntPtr = new_pointer_type(BInt) - BStruct = new_struct_type("struct foo") - BStructPtr = new_pointer_type(BStruct) - complete_struct_or_union(BStruct, [('a1', BInt, -1), - ('a2', BInt, -1), - ('a3', BInt, -1), - ('p4', BIntPtr, -1)]) - s = newp(BStructPtr, [123, 456]) - assert s.a1 == 123 - assert s.a2 == 456 - assert s.a3 == 0 - assert s.p4 == cast(BVoidP, 0) - assert s.p4 != 0 - # - s = newp(BStructPtr, {'a2': 41122, 'a3': -123}) - assert s.a1 == 0 - assert s.a2 == 41122 - assert s.a3 == -123 - assert s.p4 == cast(BVoidP, 0) - # - py.test.raises(KeyError, newp, BStructPtr, {'foobar': 0}) - # - p = newp(BIntPtr, 14141) - s = newp(BStructPtr, [12, 34, 56, p]) - assert s.p4 == p - assert s.p4 - # - s = newp(BStructPtr, [12, 34, 56, cast(BVoidP, 0)]) - assert s.p4 == cast(BVoidP, 0) - assert not s.p4 - # - py.test.raises(TypeError, newp, BStructPtr, [12, 34, 56, None]) - -def test_array_in_struct(): - BInt = new_primitive_type("int") - BStruct = new_struct_type("struct foo") - BArrayInt5 = new_array_type(new_pointer_type(BInt), 5) - complete_struct_or_union(BStruct, [('a1', BArrayInt5, -1)]) - s = newp(new_pointer_type(BStruct), [[20, 24, 27, 29, 30]]) - assert s.a1[2] == 27 - assert repr(s.a1).startswith("<cdata 'int[5]' 0x") - -def test_offsetof(): - def offsetof(BType, fieldname): - return typeoffsetof(BType, fieldname)[1] - BInt = new_primitive_type("int") - BStruct = new_struct_type("struct foo") - py.test.raises(TypeError, offsetof, BInt, "abc") - py.test.raises(TypeError, offsetof, BStruct, "abc") - complete_struct_or_union(BStruct, [('abc', BInt, -1), ('def', BInt, -1)]) - assert offsetof(BStruct, 'abc') == 0 - assert offsetof(BStruct, 'def') == size_of_int() - py.test.raises(KeyError, offsetof, BStruct, "ghi") - assert offsetof(new_pointer_type(BStruct), "def") == size_of_int() - -def test_function_type(): - BInt = new_primitive_type("int") - BFunc = new_function_type((BInt, BInt), BInt, False) - assert repr(BFunc) == "<ctype 'int(*)(int, int)'>" - BFunc2 = new_function_type((), BFunc, False) - assert repr(BFunc2) == "<ctype 'int(*(*)())(int, int)'>" - -def test_inspect_function_type(): - BInt = new_primitive_type("int") - BFunc = new_function_type((BInt, BInt), BInt, False) - assert BFunc.kind == "function" - assert BFunc.cname == "int(*)(int, int)" - assert BFunc.args == (BInt, BInt) - assert BFunc.result is BInt - assert BFunc.ellipsis is False - assert BFunc.abi == FFI_DEFAULT_ABI - -def test_function_type_taking_struct(): - BChar = new_primitive_type("char") - BShort = new_primitive_type("short") - BStruct = new_struct_type("struct foo") - complete_struct_or_union(BStruct, [('a1', BChar, -1), - ('a2', BShort, -1)]) - BFunc = new_function_type((BStruct,), BShort, False) - assert repr(BFunc) == "<ctype 'short(*)(struct foo)'>" - -def test_function_void_result(): - BVoid = new_void_type() - BInt = new_primitive_type("int") - BFunc = new_function_type((BInt, BInt), BVoid, False) - assert repr(BFunc) == "<ctype 'void(*)(int, int)'>" - -def test_function_void_arg(): - BVoid = new_void_type() - BInt = new_primitive_type("int") - py.test.raises(TypeError, new_function_type, (BVoid,), BInt, False) - -def test_call_function_0(): - BSignedChar = new_primitive_type("signed char") - BFunc0 = new_function_type((BSignedChar, BSignedChar), BSignedChar, False) - f = cast(BFunc0, _testfunc(0)) - assert f(40, 2) == 42 - assert f(-100, -100) == -200 + 256 - py.test.raises(OverflowError, f, 128, 0) - py.test.raises(OverflowError, f, 0, 128) - -def test_call_function_0_pretend_bool_result(): - BSignedChar = new_primitive_type("signed char") - BBool = new_primitive_type("_Bool") - BFunc0 = new_function_type((BSignedChar, BSignedChar), BBool, False) - f = cast(BFunc0, _testfunc(0)) - assert f(40, -39) is True - assert f(40, -40) is False - py.test.raises(ValueError, f, 40, 2) - -def test_call_function_1(): - BInt = new_primitive_type("int") - BLong = new_primitive_type("long") - BFunc1 = new_function_type((BInt, BLong), BLong, False) - f = cast(BFunc1, _testfunc(1)) - assert f(40, 2) == 42 - assert f(-100, -100) == -200 - int_max = (1 << (8*size_of_int()-1)) - 1 - long_max = (1 << (8*size_of_long()-1)) - 1 - if int_max == long_max: - assert f(int_max, 1) == - int_max - 1 - else: - assert f(int_max, 1) == int_max + 1 - -def test_call_function_2(): - BLongLong = new_primitive_type("long long") - BFunc2 = new_function_type((BLongLong, BLongLong), BLongLong, False) - f = cast(BFunc2, _testfunc(2)) - longlong_max = (1 << (8*sizeof(BLongLong)-1)) - 1 - assert f(longlong_max - 42, 42) == longlong_max - assert f(43, longlong_max - 42) == - longlong_max - 1 - -def test_call_function_3(): - BFloat = new_primitive_type("float") - BDouble = new_primitive_type("double") - BFunc3 = new_function_type((BFloat, BDouble), BDouble, False) - f = cast(BFunc3, _testfunc(3)) - assert f(1.25, 5.1) == 1.25 + 5.1 # exact - res = f(1.3, 5.1) - assert res != 6.4 and abs(res - 6.4) < 1E-5 # inexact - -def test_call_function_4(): - BFloat = new_primitive_type("float") - BDouble = new_primitive_type("double") - BFunc4 = new_function_type((BFloat, BDouble), BFloat, False) - f = cast(BFunc4, _testfunc(4)) - res = f(1.25, 5.1) - assert res != 6.35 and abs(res - 6.35) < 1E-5 # inexact - -def test_call_function_5(): - BVoid = new_void_type() - BFunc5 = new_function_type((), BVoid, False) - f = cast(BFunc5, _testfunc(5)) - f() # did not crash - -def test_call_function_6(): - BInt = new_primitive_type("int") - BIntPtr = new_pointer_type(BInt) - BFunc6 = new_function_type((BIntPtr,), BIntPtr, False) - f = cast(BFunc6, _testfunc(6)) - x = newp(BIntPtr, 42) - res = f(x) - assert typeof(res) is BIntPtr - assert res[0] == 42 - 1000 - # - BIntArray = new_array_type(BIntPtr, None) - BFunc6bis = new_function_type((BIntArray,), BIntPtr, False) - f = cast(BFunc6bis, _testfunc(6)) - # - res = f([142]) - assert typeof(res) is BIntPtr - assert res[0] == 142 - 1000 - # - res = f((143,)) - assert typeof(res) is BIntPtr - assert res[0] == 143 - 1000 - # - x = newp(BIntArray, [242]) - res = f(x) - assert typeof(res) is BIntPtr - assert res[0] == 242 - 1000 - # - py.test.raises(TypeError, f, 123456) - py.test.raises(TypeError, f, "foo") - py.test.raises(TypeError, f, u+"bar") - -def test_call_function_7(): - BChar = new_primitive_type("char") - BShort = new_primitive_type("short") - BStruct = new_struct_type("struct foo") - BStructPtr = new_pointer_type(BStruct) - complete_struct_or_union(BStruct, [('a1', BChar, -1), - ('a2', BShort, -1)]) - BFunc7 = new_function_type((BStruct,), BShort, False) - f = cast(BFunc7, _testfunc(7)) - res = f({'a1': b'A', 'a2': -4042}) - assert res == -4042 + ord(b'A') - # - x = newp(BStructPtr, {'a1': b'A', 'a2': -4042}) - res = f(x[0]) - assert res == -4042 + ord(b'A') - -def test_call_function_20(): - BChar = new_primitive_type("char") - BShort = new_primitive_type("short") - BStruct = new_struct_type("struct foo") - BStructPtr = new_pointer_type(BStruct) - complete_struct_or_union(BStruct, [('a1', BChar, -1), - ('a2', BShort, -1)]) - BFunc20 = new_function_type((BStructPtr,), BShort, False) - f = cast(BFunc20, _testfunc(20)) - x = newp(BStructPtr, {'a1': b'A', 'a2': -4042}) - # can't pass a 'struct foo' - py.test.raises(TypeError, f, x[0]) - -def test_call_function_21(): - BInt = new_primitive_type("int") - BStruct = new_struct_type("struct foo") - complete_struct_or_union(BStruct, [('a', BInt, -1), - ('b', BInt, -1), - ('c', BInt, -1), - ('d', BInt, -1), - ('e', BInt, -1), - ('f', BInt, -1), - ('g', BInt, -1), - ('h', BInt, -1), - ('i', BInt, -1), - ('j', BInt, -1)]) - BFunc21 = new_function_type((BStruct,), BInt, False) - f = cast(BFunc21, _testfunc(21)) - res = f(list(range(13, 3, -1))) - lst = [(n << i) for (i, n) in enumerate(range(13, 3, -1))] - assert res == sum(lst) - -def test_call_function_22(): - BInt = new_primitive_type("int") - BArray10 = new_array_type(new_pointer_type(BInt), 10) - BStruct = new_struct_type("struct foo") - BStructP = new_pointer_type(BStruct) - complete_struct_or_union(BStruct, [('a', BArray10, -1)]) - BFunc22 = new_function_type((BStruct, BStruct), BStruct, False) - f = cast(BFunc22, _testfunc(22)) - p1 = newp(BStructP, {'a': list(range(100, 110))}) - p2 = newp(BStructP, {'a': list(range(1000, 1100, 10))}) - res = f(p1[0], p2[0]) - for i in range(10): - assert res.a[i] == p1.a[i] - p2.a[i] - -def test_call_function_23(): - BVoid = new_void_type() # declaring the function as int(void*) - BVoidP = new_pointer_type(BVoid) - BInt = new_primitive_type("int") - BFunc23 = new_function_type((BVoidP,), BInt, False) - f = cast(BFunc23, _testfunc(23)) - res = f(b"foo") - assert res == 1000 * ord(b'f') - res = f(cast(BVoidP, 0)) # NULL - assert res == -42 - py.test.raises(TypeError, f, None) - py.test.raises(TypeError, f, 0) - py.test.raises(TypeError, f, 0.0) - -def test_call_function_23_bis(): - # declaring the function as int(unsigned char*) - BUChar = new_primitive_type("unsigned char") - BUCharP = new_pointer_type(BUChar) - BInt = new_primitive_type("int") - BFunc23 = new_function_type((BUCharP,), BInt, False) - f = cast(BFunc23, _testfunc(23)) - res = f(b"foo") - assert res == 1000 * ord(b'f') - -def test_call_function_23_bool_array(): - # declaring the function as int(_Bool*) - BBool = new_primitive_type("_Bool") - BBoolP = new_pointer_type(BBool) - BInt = new_primitive_type("int") - BFunc23 = new_function_type((BBoolP,), BInt, False) - f = cast(BFunc23, _testfunc(23)) - res = f(b"\x01\x01") - assert res == 1000 - py.test.raises(ValueError, f, b"\x02\x02") - -def test_cannot_pass_struct_with_array_of_length_0(): - BInt = new_primitive_type("int") - BArray0 = new_array_type(new_pointer_type(BInt), 0) - BStruct = new_struct_type("struct foo") - BStructP = new_pointer_type(BStruct) - complete_struct_or_union(BStruct, [('a', BArray0)]) - BFunc = new_function_type((BStruct,), BInt, False) - py.test.raises(NotImplementedError, cast(BFunc, 123), cast(BStructP, 123)) - BFunc2 = new_function_type((BInt,), BStruct, False) - py.test.raises(NotImplementedError, cast(BFunc2, 123), 123) - -def test_call_function_9(): - BInt = new_primitive_type("int") - BFunc9 = new_function_type((BInt,), BInt, True) # vararg - f = cast(BFunc9, _testfunc(9)) - assert f(0) == 0 - assert f(1, cast(BInt, 42)) == 42 - assert f(2, cast(BInt, 40), cast(BInt, 2)) == 42 - py.test.raises(TypeError, f, 1, 42) - py.test.raises(TypeError, f, 2, None) - # promotion of chars and shorts to ints - BSChar = new_primitive_type("signed char") - BUChar = new_primitive_type("unsigned char") - BSShort = new_primitive_type("short") - assert f(3, cast(BSChar, -3), cast(BUChar, 200), cast(BSShort, -5)) == 192 - -def test_call_function_24(): - BFloat = new_primitive_type("float") - BFloatComplex = new_primitive_type("float _Complex") - BFunc3 = new_function_type((BFloat, BFloat), BFloatComplex, False) - if 0: # libffi returning nonsense silently, so logic disabled for now - f = cast(BFunc3, _testfunc(24)) - result = f(1.25, 5.1) - assert type(result) == complex - assert result.real == 1.25 # exact - assert (result.imag != 2*5.1) and (abs(result.imag - 2*5.1) < 1e-5) # inexact - else: - f = cast(BFunc3, _testfunc(9)) - py.test.raises(NotImplementedError, f, 12.3, 34.5) - -def test_call_function_25(): - BDouble = new_primitive_type("double") - BDoubleComplex = new_primitive_type("double _Complex") - BFunc3 = new_function_type((BDouble, BDouble), BDoubleComplex, False) - if 0: # libffi returning nonsense silently, so logic disabled for now - f = cast(BFunc3, _testfunc(25)) - result = f(1.25, 5.1) - assert type(result) == complex - assert result.real == 1.25 # exact - assert (result.imag != 2*5.1) and (abs(result.imag - 2*5.1) < 1e-10) # inexact - else: - f = cast(BFunc3, _testfunc(9)) - py.test.raises(NotImplementedError, f, 12.3, 34.5) - -def test_cannot_call_with_a_autocompleted_struct(): - BSChar = new_primitive_type("signed char") - BDouble = new_primitive_type("double") - BStruct = new_struct_type("struct foo") - BStructPtr = new_pointer_type(BStruct) - complete_struct_or_union(BStruct, [('c', BDouble, -1, 8), - ('a', BSChar, -1, 2), - ('b', BSChar, -1, 0)]) - BFunc = new_function_type((BStruct,), BDouble) # internally not callable - dummy_func = cast(BFunc, 42) - e = py.test.raises(NotImplementedError, dummy_func, "?") - msg = ("ctype 'struct foo' not supported as argument. It is a struct " - 'declared with "...;", but the C calling convention may depend ' - "on the missing fields; or, it contains anonymous struct/unions. " - "Such structs are only supported as argument if the function is " - "'API mode' and non-variadic (i.e. declared inside ffibuilder." - "cdef()+ffibuilder.set_source() and not taking a final '...' " - "argument)") - assert str(e.value) == msg - -def test_new_charp(): - BChar = new_primitive_type("char") - BCharP = new_pointer_type(BChar) - BCharA = new_array_type(BCharP, None) - x = newp(BCharA, 42) - assert len(x) == 42 - x = newp(BCharA, b"foobar") - assert len(x) == 7 - -def test_load_and_call_function(): - BChar = new_primitive_type("char") - BCharP = new_pointer_type(BChar) - BLong = new_primitive_type("long") - BFunc = new_function_type((BCharP,), BLong, False) - ll = find_and_load_library('c') - strlen = ll.load_function(BFunc, "strlen") - input = newp(new_array_type(BCharP, None), b"foobar") - assert strlen(input) == 6 - # - assert strlen(b"foobarbaz") == 9 - # - BVoidP = new_pointer_type(new_void_type()) - strlenaddr = ll.load_function(BVoidP, "strlen") - assert strlenaddr == cast(BVoidP, strlen) - -def test_read_variable(): - ## FIXME: this test assumes glibc specific behavior, it's not compliant with C standard - ## https://bugs.pypy.org/issue1643 - if not sys.platform.startswith("linux"): - py.test.skip("untested") - BVoidP = new_pointer_type(new_void_type()) - ll = find_and_load_library('c') - stderr = ll.read_variable(BVoidP, "stderr") - assert stderr == cast(BVoidP, _testfunc(8)) - # - ll.close_lib() - py.test.raises(ValueError, ll.read_variable, BVoidP, "stderr") - -def test_read_variable_as_unknown_length_array(): - ## FIXME: this test assumes glibc specific behavior, it's not compliant with C standard - ## https://bugs.pypy.org/issue1643 - if not sys.platform.startswith("linux"): - py.test.skip("untested") - BCharP = new_pointer_type(new_primitive_type("char")) - BArray = new_array_type(BCharP, None) - ll = find_and_load_library('c') - stderr = ll.read_variable(BArray, "stderr") - assert repr(stderr).startswith("<cdata 'char *' 0x") - # ^^ and not 'char[]', which is basically not allowed and would crash - -def test_write_variable(): - ## FIXME: this test assumes glibc specific behavior, it's not compliant with C standard - ## https://bugs.pypy.org/issue1643 - if not sys.platform.startswith("linux"): - py.test.skip("untested") - BVoidP = new_pointer_type(new_void_type()) - ll = find_and_load_library('c') - stderr = ll.read_variable(BVoidP, "stderr") - ll.write_variable(BVoidP, "stderr", cast(BVoidP, 0)) - assert ll.read_variable(BVoidP, "stderr") is not None - assert not ll.read_variable(BVoidP, "stderr") - ll.write_variable(BVoidP, "stderr", stderr) - assert ll.read_variable(BVoidP, "stderr") == stderr - # - ll.close_lib() - py.test.raises(ValueError, ll.write_variable, BVoidP, "stderr", stderr) - -def test_callback(): - BInt = new_primitive_type("int") - def make_callback(): - def cb(n): - return n + 1 - BFunc = new_function_type((BInt,), BInt, False) - return callback(BFunc, cb, 42) # 'cb' and 'BFunc' go out of scope - f = make_callback() - assert f(-142) == -141 - assert repr(f).startswith( - "<cdata 'int(*)(int)' calling <function ") - assert "cb at 0x" in repr(f) - e = py.test.raises(TypeError, f) - assert str(e.value) == "'int(*)(int)' expects 1 arguments, got 0" - -def test_callback_exception(): - try: - import cStringIO - except ImportError: - import io as cStringIO # Python 3 - import linecache - def matches(istr, ipattern, ipattern38): - if sys.version_info >= (3, 8): - ipattern = ipattern38 - str, pattern = istr, ipattern - while '$' in pattern: - i = pattern.index('$') - assert str[:i] == pattern[:i] - j = str.find(pattern[i+1], i) - assert i + 1 <= j <= str.find('\n', i) - str = str[j:] - pattern = pattern[i+1:] - assert str == pattern - return True - def check_value(x): - if x == 10000: - raise ValueError(42) - def Zcb1(x): - check_value(x) - return x * 3 - BShort = new_primitive_type("short") - BFunc = new_function_type((BShort,), BShort, False) - f = callback(BFunc, Zcb1, -42) - # - seen = [] - oops_result = None - def oops(*args): - seen.append(args) - return oops_result - ff = callback(BFunc, Zcb1, -42, oops) - # - orig_stderr = sys.stderr - orig_getline = linecache.getline - 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 - assert matches(sys.stderr.getvalue(), """\ -From cffi callback <function$Zcb1 at 0x$>: -Traceback (most recent call last): - File "$", line $, in Zcb1 - $ - 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 - assert f(bigvalue) == -42 - assert matches(sys.stderr.getvalue(), """\ -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 - assert len(seen) == 0 - assert ff(bigvalue) == -42 - assert sys.stderr.getvalue() == "" - assert len(seen) == 1 - exc, val, tb = seen[0] - assert exc is OverflowError - assert str(val) == "integer 60000 does not fit 'short'" - # - sys.stderr = cStringIO.StringIO() - bigvalue = 20000 - del seen[:] - oops_result = 81 - assert ff(bigvalue) == 81 - oops_result = None - assert sys.stderr.getvalue() == "" - assert len(seen) == 1 - exc, val, tb = seen[0] - assert exc is OverflowError - assert str(val) == "integer 60000 does not fit 'short'" - # - sys.stderr = cStringIO.StringIO() - bigvalue = 20000 - del seen[:] - oops_result = "xy" # not None and not an int! - assert ff(bigvalue) == -42 - oops_result = None - assert matches(sys.stderr.getvalue(), """\ -From cffi callback <function$Zcb1 at 0x$>: -Trying to convert the result back to C: -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: -OverflowError: integer 60000 does not fit 'short' - -During the call to 'onerror', another exception occurred: - -Traceback (most recent call last): - File "$", line $, in oops - $ -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 - linecache.getline = orig_getline - -def test_callback_return_type(): - for rettype in ["signed char", "short", "int", "long", "long long", - "unsigned char", "unsigned short", "unsigned int", - "unsigned long", "unsigned long long"]: - BRet = new_primitive_type(rettype) - def cb(n): - return n + 1 - BFunc = new_function_type((BRet,), BRet) - f = callback(BFunc, cb, 42) - assert f(41) == 42 - if rettype.startswith("unsigned "): - min = 0 - max = (1 << (8*sizeof(BRet))) - 1 - else: - min = -(1 << (8*sizeof(BRet)-1)) - max = (1 << (8*sizeof(BRet)-1)) - 1 - assert f(min) == min + 1 - assert f(max - 1) == max - assert f(max) == 42 - -def test_a_lot_of_callbacks(): - BIGNUM = 10000 - if 'PY_DOT_PY' in globals(): BIGNUM = 100 # tests on py.py - # - BInt = new_primitive_type("int") - BFunc = new_function_type((BInt,), BInt, False) - def make_callback(m): - def cb(n): - return n + m - 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): - assert f(-142) == -142 + i - -def test_callback_receiving_tiny_struct(): - BSChar = new_primitive_type("signed char") - BInt = new_primitive_type("int") - BStruct = new_struct_type("struct foo") - BStructPtr = new_pointer_type(BStruct) - complete_struct_or_union(BStruct, [('a', BSChar, -1), - ('b', BSChar, -1)]) - def cb(s): - return s.a + 10 * s.b - BFunc = new_function_type((BStruct,), BInt) - f = callback(BFunc, cb) - p = newp(BStructPtr, [-2, -4]) - n = f(p[0]) - assert n == -42 - -def test_callback_returning_tiny_struct(): - BSChar = new_primitive_type("signed char") - BInt = new_primitive_type("int") - BStruct = new_struct_type("struct foo") - BStructPtr = new_pointer_type(BStruct) - complete_struct_or_union(BStruct, [('a', BSChar, -1), - ('b', BSChar, -1)]) - def cb(n): - return newp(BStructPtr, [-n, -3*n])[0] - BFunc = new_function_type((BInt,), BStruct) - f = callback(BFunc, cb) - s = f(10) - assert typeof(s) is BStruct - assert repr(s) == "<cdata 'struct foo' owning 2 bytes>" - assert s.a == -10 - assert s.b == -30 - -def test_callback_receiving_struct(): - BSChar = new_primitive_type("signed char") - BInt = new_primitive_type("int") - BDouble = new_primitive_type("double") - BStruct = new_struct_type("struct foo") - BStructPtr = new_pointer_type(BStruct) - complete_struct_or_union(BStruct, [('a', BSChar, -1), - ('b', BDouble, -1)]) - def cb(s): - return s.a + int(s.b) - BFunc = new_function_type((BStruct,), BInt) - f = callback(BFunc, cb) - p = newp(BStructPtr, [-2, 44.444]) - n = f(p[0]) - assert n == 42 - -def test_callback_returning_struct(): - BSChar = new_primitive_type("signed char") - BInt = new_primitive_type("int") - BDouble = new_primitive_type("double") - BStruct = new_struct_type("struct foo") - BStructPtr = new_pointer_type(BStruct) - complete_struct_or_union(BStruct, [('a', BSChar, -1), - ('b', BDouble, -1)]) - def cb(n): - return newp(BStructPtr, [-n, 1E-42])[0] - BFunc = new_function_type((BInt,), BStruct) - f = callback(BFunc, cb) - s = f(10) - assert typeof(s) is BStruct - assert repr(s) in ["<cdata 'struct foo' owning 12 bytes>", - "<cdata 'struct foo' owning 16 bytes>"] - assert s.a == -10 - assert s.b == 1E-42 - -def test_callback_receiving_big_struct(): - BInt = new_primitive_type("int") - BStruct = new_struct_type("struct foo") - BStructPtr = new_pointer_type(BStruct) - complete_struct_or_union(BStruct, [('a', BInt, -1), - ('b', BInt, -1), - ('c', BInt, -1), - ('d', BInt, -1), - ('e', BInt, -1), - ('f', BInt, -1), - ('g', BInt, -1), - ('h', BInt, -1), - ('i', BInt, -1), - ('j', BInt, -1)]) - def cb(s): - for i, name in enumerate("abcdefghij"): - assert getattr(s, name) == 13 - i - return 42 - BFunc = new_function_type((BStruct,), BInt) - f = callback(BFunc, cb) - p = newp(BStructPtr, list(range(13, 3, -1))) - n = f(p[0]) - assert n == 42 - -def test_callback_returning_big_struct(): - BInt = new_primitive_type("int") - BStruct = new_struct_type("struct foo") - BStructPtr = new_pointer_type(BStruct) - complete_struct_or_union(BStruct, [('a', BInt, -1), - ('b', BInt, -1), - ('c', BInt, -1), - ('d', BInt, -1), - ('e', BInt, -1), - ('f', BInt, -1), - ('g', BInt, -1), - ('h', BInt, -1), - ('i', BInt, -1), - ('j', BInt, -1)]) - def cb(): - return newp(BStructPtr, list(range(13, 3, -1)))[0] - BFunc = new_function_type((), BStruct) - f = callback(BFunc, cb) - s = f() - assert typeof(s) is BStruct - assert repr(s) in ["<cdata 'struct foo' owning 40 bytes>", - "<cdata 'struct foo' owning 80 bytes>"] - for i, name in enumerate("abcdefghij"): - assert getattr(s, name) == 13 - i - -def test_callback_returning_void(): - BVoid = new_void_type() - BFunc = new_function_type((), BVoid, False) - def cb(): - seen.append(42) - f = callback(BFunc, cb) - seen = [] - f() - assert seen == [42] - py.test.raises(TypeError, callback, BFunc, cb, -42) - -def test_enum_type(): - BUInt = new_primitive_type("unsigned int") - BEnum = new_enum_type("foo", (), (), BUInt) - assert repr(BEnum) == "<ctype 'foo'>" - assert BEnum.kind == "enum" - assert BEnum.cname == "foo" - assert BEnum.elements == {} - # - BInt = new_primitive_type("int") - BEnum = new_enum_type("enum foo", ('def', 'c', 'ab'), (0, 1, -20), BInt) - assert BEnum.kind == "enum" - assert BEnum.cname == "enum foo" - assert BEnum.elements == {-20: 'ab', 0: 'def', 1: 'c'} - # 'elements' is not the real dict, but merely a copy - BEnum.elements[2] = '??' - assert BEnum.elements == {-20: 'ab', 0: 'def', 1: 'c'} - # - BEnum = new_enum_type("enum bar", ('ab', 'cd'), (5, 5), BUInt) - assert BEnum.elements == {5: 'ab'} - assert BEnum.relements == {'ab': 5, 'cd': 5} - -def test_cast_to_enum(): - BInt = new_primitive_type("int") - BEnum = new_enum_type("enum foo", ('def', 'c', 'ab'), (0, 1, -20), BInt) - assert sizeof(BEnum) == sizeof(BInt) - e = cast(BEnum, 0) - assert repr(e) == "<cdata 'enum foo' 0: def>" - assert repr(cast(BEnum, -42)) == "<cdata 'enum foo' -42>" - assert repr(cast(BEnum, -20)) == "<cdata 'enum foo' -20: ab>" - assert string(e) == 'def' - assert string(cast(BEnum, -20)) == 'ab' - assert int(cast(BEnum, 1)) == 1 - assert int(cast(BEnum, 0)) == 0 - assert int(cast(BEnum, -242 + 2**128)) == -242 - assert string(cast(BEnum, -242 + 2**128)) == '-242' - # - BUInt = new_primitive_type("unsigned int") - BEnum = new_enum_type("enum bar", ('def', 'c', 'ab'), (0, 1, 20), BUInt) - e = cast(BEnum, -1) - assert repr(e) == "<cdata 'enum bar' 4294967295>" # unsigned int - # - BLong = new_primitive_type("long") - BEnum = new_enum_type("enum baz", (), (), BLong) - assert sizeof(BEnum) == sizeof(BLong) - e = cast(BEnum, -1) - assert repr(e) == "<cdata 'enum baz' -1>" - -def test_enum_with_non_injective_mapping(): - BInt = new_primitive_type("int") - BEnum = new_enum_type("enum foo", ('ab', 'cd'), (7, 7), BInt) - e = cast(BEnum, 7) - assert repr(e) == "<cdata 'enum foo' 7: ab>" - assert string(e) == 'ab' - -def test_enum_in_struct(): - BInt = new_primitive_type("int") - BEnum = new_enum_type("enum foo", ('def', 'c', 'ab'), (0, 1, -20), BInt) - BStruct = new_struct_type("struct bar") - BStructPtr = new_pointer_type(BStruct) - complete_struct_or_union(BStruct, [('a1', BEnum, -1)]) - p = newp(BStructPtr, [-20]) - assert p.a1 == -20 - p = newp(BStructPtr, [12]) - assert p.a1 == 12 - e = py.test.raises(TypeError, newp, BStructPtr, [None]) - msg = str(e.value) - 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 - 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' - assert type(string(cast(BEnum2, 5))) is str - -def test_enum_overflow(): - max_uint = 2 ** (size_of_int()*8) - 1 - max_int = max_uint // 2 - max_ulong = 2 ** (size_of_long()*8) - 1 - max_long = max_ulong // 2 - for BPrimitive in [new_primitive_type("int"), - new_primitive_type("unsigned int"), - new_primitive_type("long"), - new_primitive_type("unsigned long")]: - for x in [max_uint, max_int, max_ulong, max_long]: - for testcase in [x, x+1, -x-1, -x-2]: - if int(cast(BPrimitive, testcase)) == testcase: - # fits - BEnum = new_enum_type("foo", ("AA",), (testcase,), - BPrimitive) - assert int(cast(BEnum, testcase)) == testcase - else: - # overflows - py.test.raises(OverflowError, new_enum_type, - "foo", ("AA",), (testcase,), BPrimitive) - -def test_callback_returning_enum(): - BInt = new_primitive_type("int") - BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, -20), BInt) - def cb(n): - if n & 1: - return cast(BEnum, n) - else: - return n - BFunc = new_function_type((BInt,), BEnum) - f = callback(BFunc, cb) - assert f(0) == 0 - assert f(1) == 1 - assert f(-20) == -20 - assert f(20) == 20 - assert f(21) == 21 - -def test_callback_returning_enum_unsigned(): - BInt = new_primitive_type("int") - BUInt = new_primitive_type("unsigned int") - BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, 20), BUInt) - def cb(n): - if n & 1: - return cast(BEnum, n) - else: - return n - BFunc = new_function_type((BInt,), BEnum) - f = callback(BFunc, cb) - assert f(0) == 0 - assert f(1) == 1 - assert f(-21) == 2**32 - 21 - assert f(20) == 20 - assert f(21) == 21 - -def test_callback_returning_char(): - BInt = new_primitive_type("int") - BChar = new_primitive_type("char") - def cb(n): - return bytechr(n) - BFunc = new_function_type((BInt,), BChar) - f = callback(BFunc, cb) - assert f(0) == b'\x00' - assert f(255) == b'\xFF' - -def _hacked_pypy_uni4(): - pyuni4 = {1: True, 2: False}[len(u+'\U00012345')] - return 'PY_DOT_PY' in globals() and not pyuni4 - -def test_callback_returning_wchar_t(): - BInt = new_primitive_type("int") - BWChar = new_primitive_type("wchar_t") - def cb(n): - if n == -1: - return u+'\U00012345' - if n == -2: - raise ValueError - return unichr(n) - BFunc = new_function_type((BInt,), BWChar) - f = callback(BFunc, cb) - assert f(0) == unichr(0) - assert f(255) == unichr(255) - assert f(0x1234) == u+'\u1234' - if sizeof(BWChar) == 4 and not _hacked_pypy_uni4(): - assert f(-1) == u+'\U00012345' - assert f(-2) == u+'\x00' # and an exception printed to stderr - -def test_struct_with_bitfields(): - BLong = new_primitive_type("long") - BStruct = new_struct_type("struct foo") - LONGBITS = 8 * sizeof(BLong) - complete_struct_or_union(BStruct, [('a1', BLong, 1), - ('a2', BLong, 2), - ('a3', BLong, 3), - ('a4', BLong, LONGBITS - 5)]) - d = BStruct.fields - assert d[0][1].offset == d[1][1].offset == d[2][1].offset == 0 - assert d[3][1].offset == sizeof(BLong) - def f(m, r): - if sys.byteorder == 'little': - return r - else: - return LONGBITS - m - r - assert d[0][1].bitshift == f(1, 0) - assert d[0][1].bitsize == 1 - assert d[1][1].bitshift == f(2, 1) - assert d[1][1].bitsize == 2 - assert d[2][1].bitshift == f(3, 3) - assert d[2][1].bitsize == 3 - assert d[3][1].bitshift == f(LONGBITS - 5, 0) - assert d[3][1].bitsize == LONGBITS - 5 - assert sizeof(BStruct) == 2 * sizeof(BLong) - assert alignof(BStruct) == alignof(BLong) - -def test_bitfield_instance(): - BInt = new_primitive_type("int") - BUnsignedInt = new_primitive_type("unsigned int") - BStruct = new_struct_type("struct foo") - complete_struct_or_union(BStruct, [('a1', BInt, 1), - ('a2', BUnsignedInt, 2), - ('a3', BInt, 3)]) - p = newp(new_pointer_type(BStruct), None) - p.a1 = -1 - assert p.a1 == -1 - p.a1 = 0 - with pytest.raises(OverflowError): - p.a1 = 2 - assert p.a1 == 0 - # - p.a1 = -1 - p.a2 = 3 - p.a3 = -4 - 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 - # - # special case for convenience: "int x:1", while normally signed, - # allows also setting the value "1" (it still gets read back as -1) - p.a1 = 1 - assert p.a1 == -1 - 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") - -def test_bitfield_instance_init(): - BInt = new_primitive_type("int") - BStruct = new_struct_type("struct foo") - complete_struct_or_union(BStruct, [('a1', BInt, 1)]) - p = newp(new_pointer_type(BStruct), [-1]) - assert p.a1 == -1 - p = newp(new_pointer_type(BStruct), {'a1': -1}) - assert p.a1 == -1 - # - BUnion = new_union_type("union bar") - complete_struct_or_union(BUnion, [('a1', BInt, 1)]) - p = newp(new_pointer_type(BUnion), [-1]) - assert p.a1 == -1 - -def test_weakref(): - import _weakref - BInt = new_primitive_type("int") - BPtr = new_pointer_type(BInt) - rlist = [_weakref.ref(BInt), - _weakref.ref(newp(BPtr, 42)), - _weakref.ref(cast(BPtr, 42)), - _weakref.ref(cast(BInt, 42)), - _weakref.ref(buffer(newp(BPtr, 42))), - ] - for i in range(5): - import gc; gc.collect() - if [r() for r in rlist] == [None for r in rlist]: - break - -def test_no_inheritance(): - BInt = new_primitive_type("int") - try: - class foo(type(BInt)): pass - except TypeError: - pass - else: - raise AssertionError - x = cast(BInt, 42) - try: - class foo(type(x)): pass - except TypeError: - pass - else: - raise AssertionError - -def test_assign_string(): - BChar = new_primitive_type("char") - BArray1 = new_array_type(new_pointer_type(BChar), 5) - BArray2 = new_array_type(new_pointer_type(BArray1), 5) - a = newp(BArray2, [b"abc", b"de", b"ghij"]) - assert string(a[1]) == b"de" - assert string(a[2]) == b"ghij" - a[2] = b"." - assert string(a[2]) == b"." - a[2] = b"12345" - assert string(a[2]) == b"12345" - 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) - with pytest.raises(TypeError): - x + 1 - with pytest.raises(TypeError): - x - 1 - -def test_void_errors(): - py.test.raises(ValueError, alignof, new_void_type()) - py.test.raises(TypeError, newp, new_pointer_type(new_void_type()), None) - -def test_too_many_items(): - BChar = new_primitive_type("char") - BArray = new_array_type(new_pointer_type(BChar), 5) - py.test.raises(IndexError, newp, BArray, tuple(b'123456')) - py.test.raises(IndexError, newp, BArray, list(b'123456')) - py.test.raises(IndexError, newp, BArray, b'123456') - BStruct = new_struct_type("struct foo") - complete_struct_or_union(BStruct, []) - py.test.raises(TypeError, newp, new_pointer_type(BStruct), b'') - py.test.raises(ValueError, newp, new_pointer_type(BStruct), [b'1']) - -def test_more_type_errors(): - BInt = new_primitive_type("int") - BChar = new_primitive_type("char") - BArray = new_array_type(new_pointer_type(BChar), 5) - py.test.raises(TypeError, newp, BArray, 12.34) - BArray = new_array_type(new_pointer_type(BInt), 5) - py.test.raises(TypeError, newp, BArray, 12.34) - BFloat = new_primitive_type("float") - py.test.raises(TypeError, cast, BFloat, newp(BArray, None)) - -def test_more_overflow_errors(): - BUInt = new_primitive_type("unsigned int") - py.test.raises(OverflowError, newp, new_pointer_type(BUInt), -1) - py.test.raises(OverflowError, newp, new_pointer_type(BUInt), 2**32) - -def test_newp_copying(): - """Test that we can do newp(<type>, <cdata of the given type>) for most - types, including same-type arrays. - """ - BInt = new_primitive_type("int") - p = newp(new_pointer_type(BInt), cast(BInt, 42)) - assert p[0] == 42 - # - BUInt = new_primitive_type("unsigned int") - p = newp(new_pointer_type(BUInt), cast(BUInt, 42)) - assert p[0] == 42 - # - BChar = new_primitive_type("char") - p = newp(new_pointer_type(BChar), cast(BChar, '!')) - assert p[0] == b'!' - # - BFloat = new_primitive_type("float") - p = newp(new_pointer_type(BFloat), cast(BFloat, 12.25)) - assert p[0] == 12.25 - # - BStruct = new_struct_type("struct foo_s") - BStructPtr = new_pointer_type(BStruct) - complete_struct_or_union(BStruct, [('a1', BInt, -1)]) - s1 = newp(BStructPtr, [42]) - p1 = newp(new_pointer_type(BStructPtr), s1) - assert p1[0] == s1 - # - BArray = new_array_type(new_pointer_type(BInt), None) - a1 = newp(BArray, [1, 2, 3, 4]) - py.test.raises(TypeError, newp, BArray, a1) - BArray6 = new_array_type(new_pointer_type(BInt), 6) - a1 = newp(BArray6, [10, 20, 30]) - a2 = newp(BArray6, a1) - assert list(a2) == [10, 20, 30, 0, 0, 0] - # - s1 = newp(BStructPtr, [42]) - s2 = newp(BStructPtr, s1[0]) - assert s2.a1 == 42 - # - BUnion = new_union_type("union foo_u") - BUnionPtr = new_pointer_type(BUnion) - complete_struct_or_union(BUnion, [('a1', BInt, -1)]) - u1 = newp(BUnionPtr, [42]) - u2 = newp(BUnionPtr, u1[0]) - assert u2.a1 == 42 - # - BFunc = new_function_type((BInt,), BUInt) - p1 = cast(BFunc, 42) - p2 = newp(new_pointer_type(BFunc), p1) - assert p2[0] == p1 - -def test_string(): - BChar = new_primitive_type("char") - assert string(cast(BChar, 42)) == b'*' - assert string(cast(BChar, 0)) == b'\x00' - BCharP = new_pointer_type(BChar) - BArray = new_array_type(BCharP, 10) - a = newp(BArray, b"hello") - assert len(a) == 10 - assert string(a) == b"hello" - p = a + 2 - assert string(p) == b"llo" - assert string(newp(new_array_type(BCharP, 4), b"abcd")) == b"abcd" - py.test.raises(RuntimeError, string, cast(BCharP, 0)) - assert string(a, 4) == b"hell" - assert string(a, 5) == b"hello" - assert string(a, 6) == b"hello" - -def test_string_byte(): - BByte = new_primitive_type("signed char") - assert string(cast(BByte, 42)) == b'*' - assert string(cast(BByte, 0)) == b'\x00' - BArray = new_array_type(new_pointer_type(BByte), None) - a = newp(BArray, [65, 66, 67]) - assert type(string(a)) is bytes and string(a) == b'ABC' - # - BByte = new_primitive_type("unsigned char") - assert string(cast(BByte, 42)) == b'*' - assert string(cast(BByte, 0)) == b'\x00' - BArray = new_array_type(new_pointer_type(BByte), None) - a = newp(BArray, [65, 66, 67]) - assert type(string(a)) is bytes and string(a) == b'ABC' - if 'PY_DOT_PY' not in globals() and sys.version_info < (3,): - assert string(a, 8).startswith(b'ABC') # may contain additional garbage - -def test_string_wchar(): - for typename in ["wchar_t", "char16_t", "char32_t"]: - _test_string_wchar_variant(typename) - -def _test_string_wchar_variant(typename): - BWChar = new_primitive_type(typename) - assert string(cast(BWChar, 42)) == u+'*' - assert string(cast(BWChar, 0x4253)) == u+'\u4253' - assert string(cast(BWChar, 0)) == u+'\x00' - BArray = new_array_type(new_pointer_type(BWChar), None) - a = newp(BArray, [u+'A', u+'B', u+'C']) - assert type(string(a)) is unicode and string(a) == u+'ABC' - if 'PY_DOT_PY' not in globals() and sys.version_info < (3,): - try: - # may contain additional garbage - assert string(a, 8).startswith(u+'ABC') - except ValueError: # garbage contains values > 0x10FFFF - assert sizeof(BWChar) == 4 - -def test_string_typeerror(): - BShort = new_primitive_type("short") - BArray = new_array_type(new_pointer_type(BShort), None) - a = newp(BArray, [65, 66, 67]) - py.test.raises(TypeError, string, a) - -def test_bug_convert_to_ptr(): - BChar = new_primitive_type("char") - BCharP = new_pointer_type(BChar) - BDouble = new_primitive_type("double") - x = cast(BDouble, 42) - py.test.raises(TypeError, newp, new_pointer_type(BCharP), x) - -def test_set_struct_fields(): - BChar = new_primitive_type("char") - BCharP = new_pointer_type(BChar) - BCharArray10 = new_array_type(BCharP, 10) - BStruct = new_struct_type("struct foo") - BStructPtr = new_pointer_type(BStruct) - complete_struct_or_union(BStruct, [('a1', BCharArray10, -1)]) - p = newp(BStructPtr, None) - assert string(p.a1) == b'' - p.a1 = b'foo' - assert string(p.a1) == b'foo' - assert list(p.a1) == [b'f', b'o', b'o'] + [b'\x00'] * 7 - p.a1 = [b'x', b'y'] - assert string(p.a1) == b'xyo' - -def test_invalid_function_result_types(): - BFunc = new_function_type((), new_void_type()) - BArray = new_array_type(new_pointer_type(BFunc), 5) # works - new_function_type((), BFunc) # works - new_function_type((), new_primitive_type("int")) - new_function_type((), new_pointer_type(BFunc)) - BUnion = new_union_type("union foo_u") - complete_struct_or_union(BUnion, []) - BFunc = new_function_type((), BUnion) - py.test.raises(NotImplementedError, cast(BFunc, 123)) - py.test.raises(TypeError, new_function_type, (), BArray) - -def test_struct_return_in_func(): - BChar = new_primitive_type("char") - BShort = new_primitive_type("short") - BFloat = new_primitive_type("float") - BDouble = new_primitive_type("double") - BInt = new_primitive_type("int") - BStruct = new_struct_type("struct foo_s") - complete_struct_or_union(BStruct, [('a1', BChar, -1), - ('a2', BShort, -1)]) - BFunc10 = new_function_type((BInt,), BStruct) - f = cast(BFunc10, _testfunc(10)) - s = f(40) - assert repr(s) == "<cdata 'struct foo_s' owning 4 bytes>" - assert s.a1 == bytechr(40) - assert s.a2 == 40 * 40 - # - BStruct11 = new_struct_type("struct test11") - complete_struct_or_union(BStruct11, [('a1', BInt, -1), - ('a2', BInt, -1)]) - BFunc11 = new_function_type((BInt,), BStruct11) - f = cast(BFunc11, _testfunc(11)) - s = f(40) - assert repr(s) == "<cdata 'struct test11' owning 8 bytes>" - assert s.a1 == 40 - assert s.a2 == 40 * 40 - # - BStruct12 = new_struct_type("struct test12") - complete_struct_or_union(BStruct12, [('a1', BDouble, -1), - ]) - BFunc12 = new_function_type((BInt,), BStruct12) - f = cast(BFunc12, _testfunc(12)) - s = f(40) - assert repr(s) == "<cdata 'struct test12' owning 8 bytes>" - assert s.a1 == 40.0 - # - BStruct13 = new_struct_type("struct test13") - complete_struct_or_union(BStruct13, [('a1', BInt, -1), - ('a2', BInt, -1), - ('a3', BInt, -1)]) - BFunc13 = new_function_type((BInt,), BStruct13) - f = cast(BFunc13, _testfunc(13)) - s = f(40) - assert repr(s) == "<cdata 'struct test13' owning 12 bytes>" - assert s.a1 == 40 - assert s.a2 == 40 * 40 - assert s.a3 == 40 * 40 * 40 - # - BStruct14 = new_struct_type("struct test14") - complete_struct_or_union(BStruct14, [('a1', BFloat, -1), - ]) - BFunc14 = new_function_type((BInt,), BStruct14) - f = cast(BFunc14, _testfunc(14)) - s = f(40) - assert repr(s) == "<cdata 'struct test14' owning 4 bytes>" - assert s.a1 == 40.0 - # - BStruct15 = new_struct_type("struct test15") - complete_struct_or_union(BStruct15, [('a1', BFloat, -1), - ('a2', BInt, -1)]) - BFunc15 = new_function_type((BInt,), BStruct15) - f = cast(BFunc15, _testfunc(15)) - s = f(40) - assert repr(s) == "<cdata 'struct test15' owning 8 bytes>" - assert s.a1 == 40.0 - assert s.a2 == 40 * 40 - # - BStruct16 = new_struct_type("struct test16") - complete_struct_or_union(BStruct16, [('a1', BFloat, -1), - ('a2', BFloat, -1)]) - BFunc16 = new_function_type((BInt,), BStruct16) - f = cast(BFunc16, _testfunc(16)) - s = f(40) - assert repr(s) == "<cdata 'struct test16' owning 8 bytes>" - assert s.a1 == 40.0 - assert s.a2 == -40.0 - # - BStruct17 = new_struct_type("struct test17") - complete_struct_or_union(BStruct17, [('a1', BInt, -1), - ('a2', BFloat, -1)]) - BFunc17 = new_function_type((BInt,), BStruct17) - f = cast(BFunc17, _testfunc(17)) - s = f(40) - assert repr(s) == "<cdata 'struct test17' owning 8 bytes>" - assert s.a1 == 40 - assert s.a2 == 40.0 * 40.0 - # - BStruct17Ptr = new_pointer_type(BStruct17) - BFunc18 = new_function_type((BStruct17Ptr,), BInt) - f = cast(BFunc18, _testfunc(18)) - x = f([[40, 2.5]]) - assert x == 42 - x = f([{'a2': 43.1}]) - assert x == 43 - -def test_cast_with_functionptr(): - BFunc = new_function_type((), new_void_type()) - BFunc2 = new_function_type((), new_primitive_type("short")) - BCharP = new_pointer_type(new_primitive_type("char")) - BIntP = new_pointer_type(new_primitive_type("int")) - BStruct = new_struct_type("struct foo") - BStructPtr = new_pointer_type(BStruct) - complete_struct_or_union(BStruct, [('a1', BFunc, -1)]) - newp(BStructPtr, [cast(BFunc, 0)]) - newp(BStructPtr, [cast(BCharP, 0)]) - py.test.raises(TypeError, newp, BStructPtr, [cast(BIntP, 0)]) - py.test.raises(TypeError, newp, BStructPtr, [cast(BFunc2, 0)]) - -def test_wchar(): - _test_wchar_variant("wchar_t") - if sys.platform.startswith("linux"): - BWChar = new_primitive_type("wchar_t") - assert sizeof(BWChar) == 4 - # wchar_t is often signed on Linux, but not always (e.g. on ARM) - assert int(cast(BWChar, -1)) in (-1, 4294967295) - -def test_char16(): - BChar16 = new_primitive_type("char16_t") - assert sizeof(BChar16) == 2 - _test_wchar_variant("char16_t") - assert int(cast(BChar16, -1)) == 0xffff # always unsigned - -def test_char32(): - BChar32 = new_primitive_type("char32_t") - assert sizeof(BChar32) == 4 - _test_wchar_variant("char32_t") - assert int(cast(BChar32, -1)) == 0xffffffff # always unsigned - -def _test_wchar_variant(typename): - BWChar = new_primitive_type(typename) - BInt = new_primitive_type("int") - pyuni4 = {1: True, 2: False}[len(u+'\U00012345')] - wchar4 = {2: False, 4: True}[sizeof(BWChar)] - assert str(cast(BWChar, 0x45)) == "<cdata '%s' %s'E'>" % ( - typename, mandatory_u_prefix) - assert str(cast(BWChar, 0x1234)) == "<cdata '%s' %s'\u1234'>" % ( - typename, mandatory_u_prefix) - if not _hacked_pypy_uni4(): - if wchar4: - x = cast(BWChar, 0x12345) - assert str(x) == "<cdata '%s' %s'\U00012345'>" % ( - typename, mandatory_u_prefix) - assert int(x) == 0x12345 - else: - x = cast(BWChar, 0x18345) - assert str(x) == "<cdata '%s' %s'\u8345'>" % ( - typename, mandatory_u_prefix) - assert int(x) == 0x8345 - # - BWCharP = new_pointer_type(BWChar) - BStruct = new_struct_type("struct foo_s") - BStructPtr = new_pointer_type(BStruct) - complete_struct_or_union(BStruct, [('a1', BWChar, -1), - ('a2', BWCharP, -1)]) - s = newp(BStructPtr) - s.a1 = u+'\x00' - assert s.a1 == u+'\x00' - 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: - if wchar4: - s.a1 = u+'\U00012345' - assert s.a1 == u+'\U00012345' - elif wchar4: - if not _hacked_pypy_uni4(): - s.a1 = cast(BWChar, 0x12345) - assert s.a1 == u+'\ud808\udf45' - s.a1 = u+'\ud807\udf44' - assert s.a1 == u+'\U00011f44' - else: - with pytest.raises(TypeError): - s.a1 = u+'\U00012345' - # - BWCharArray = new_array_type(BWCharP, None) - a = newp(BWCharArray, u+'hello \u1234 world') - assert len(a) == 14 # including the final null - assert string(a) == u+'hello \u1234 world' - a[13] = u+'!' - assert string(a) == u+'hello \u1234 world!' - assert str(a) == repr(a) - assert a[6] == u+'\u1234' - a[6] = u+'-' - assert string(a) == u+'hello - world!' - assert str(a) == repr(a) - # - if wchar4 and not _hacked_pypy_uni4(): - u1 = u+'\U00012345\U00012346\U00012347' - a = newp(BWCharArray, u1) - assert len(a) == 4 - assert string(a) == u1 - assert len(list(a)) == 4 - expected = [u+'\U00012345', u+'\U00012346', u+'\U00012347', unichr(0)] - assert list(a) == expected - got = [a[i] for i in range(4)] - assert got == expected - with pytest.raises(IndexError): - a[4] - # - w = cast(BWChar, 'a') - assert repr(w) == "<cdata '%s' %s'a'>" % (typename, mandatory_u_prefix) - assert str(w) == repr(w) - assert string(w) == u+'a' - assert int(w) == ord('a') - w = cast(BWChar, 0x1234) - assert repr(w) == "<cdata '%s' %s'\u1234'>" % (typename, mandatory_u_prefix) - assert str(w) == repr(w) - assert string(w) == u+'\u1234' - assert int(w) == 0x1234 - w = cast(BWChar, u+'\u8234') - assert repr(w) == "<cdata '%s' %s'\u8234'>" % (typename, mandatory_u_prefix) - assert str(w) == repr(w) - assert string(w) == u+'\u8234' - assert int(w) == 0x8234 - w = cast(BInt, u+'\u1234') - assert repr(w) == "<cdata 'int' 4660>" - if wchar4 and not _hacked_pypy_uni4(): - w = cast(BWChar, u+'\U00012345') - assert repr(w) == "<cdata '%s' %s'\U00012345'>" % ( - typename, mandatory_u_prefix) - assert str(w) == repr(w) - assert string(w) == u+'\U00012345' - assert int(w) == 0x12345 - w = cast(BInt, u+'\U00012345') - assert repr(w) == "<cdata 'int' 74565>" - py.test.raises(TypeError, cast, BInt, u+'') - py.test.raises(TypeError, cast, BInt, u+'XX') - assert int(cast(BInt, u+'a')) == ord('a') - # - a = newp(BWCharArray, u+'hello - world') - p = cast(BWCharP, a) - assert string(p) == u+'hello - world' - p[6] = u+'\u2345' - assert string(p) == u+'hello \u2345 world' - # - s = newp(BStructPtr, [u+'\u1234', p]) - assert s.a1 == u+'\u1234' - assert s.a2 == p - assert str(s.a2) == repr(s.a2) - assert string(s.a2) == u+'hello \u2345 world' - # - q = cast(BWCharP, 0) - assert str(q) == repr(q) - py.test.raises(RuntimeError, string, q) - # - def cb(p): - assert repr(p).startswith("<cdata '%s *' 0x" % typename) - return len(string(p)) - BFunc = new_function_type((BWCharP,), BInt, False) - f = callback(BFunc, cb, -42) - assert f(u+'a\u1234b') == 3 - # - if wchar4 and not pyuni4 and not _hacked_pypy_uni4(): - # try out-of-range wchar_t values - x = cast(BWChar, 1114112) - py.test.raises(ValueError, string, x) - x = cast(BWChar, -1) - py.test.raises(ValueError, string, x) - -def test_wchar_variants_mix(): - BWChar = new_primitive_type("wchar_t") - BChar16 = new_primitive_type("char16_t") - BChar32 = new_primitive_type("char32_t") - assert int(cast(BChar32, cast(BChar16, -2))) == 0xfffe - assert int(cast(BWChar, cast(BChar16, -2))) == 0xfffe - assert int(cast(BChar16, cast(BChar32, 0x0001f345))) == 0xf345 - assert int(cast(BChar16, cast(BWChar, 0x0001f345))) == 0xf345 - # - BChar16A = new_array_type(new_pointer_type(BChar16), None) - BChar32A = new_array_type(new_pointer_type(BChar32), None) - x = cast(BChar32, 'A') - py.test.raises(TypeError, newp, BChar16A, [x]) - x = cast(BChar16, 'A') - py.test.raises(TypeError, newp, BChar32A, [x]) - # - a = newp(BChar16A, u+'\U00012345') - assert len(a) == 3 - a = newp(BChar32A, u+'\U00012345') - assert len(a) == 2 # even if the Python unicode string above is 2 chars - -def test_keepalive_struct(): - # exception to the no-keepalive rule: p=newp(BStructPtr) returns a - # pointer owning the memory, and p[0] returns a pointer to the - # struct that *also* owns the memory - BStruct = new_struct_type("struct foo") - BStructPtr = new_pointer_type(BStruct) - complete_struct_or_union(BStruct, [('a1', new_primitive_type("int"), -1), - ('a2', new_primitive_type("int"), -1), - ('a3', new_primitive_type("int"), -1)]) - p = newp(BStructPtr) - assert repr(p) == "<cdata 'struct foo *' owning 12 bytes>" - q = p[0] - assert repr(q) == "<cdata 'struct foo' owning 12 bytes>" - q.a1 = 123456 - assert p.a1 == 123456 - r = cast(BStructPtr, p) - assert repr(r[0]).startswith("<cdata 'struct foo &' 0x") - del p - import gc; gc.collect() - assert q.a1 == 123456 - assert repr(q) == "<cdata 'struct foo' owning 12 bytes>" - assert q.a1 == 123456 - -def test_nokeepalive_struct(): - BStruct = new_struct_type("struct foo") - BStructPtr = new_pointer_type(BStruct) - BStructPtrPtr = new_pointer_type(BStructPtr) - complete_struct_or_union(BStruct, [('a1', new_primitive_type("int"), -1)]) - p = newp(BStructPtr) - pp = newp(BStructPtrPtr) - pp[0] = p - s = pp[0][0] - assert repr(s).startswith("<cdata 'struct foo &' 0x") - -def test_owning_repr(): - BInt = new_primitive_type("int") - BArray = new_array_type(new_pointer_type(BInt), None) # int[] - p = newp(BArray, 7) - assert repr(p) == "<cdata 'int[]' owning 28 bytes>" - assert sizeof(p) == 28 - # - BArray = new_array_type(new_pointer_type(BInt), 7) # int[7] - p = newp(BArray, None) - assert repr(p) == "<cdata 'int[7]' owning 28 bytes>" - assert sizeof(p) == 28 - -def test_cannot_dereference_void(): - BVoidP = new_pointer_type(new_void_type()) - p = cast(BVoidP, 123456) - with pytest.raises(TypeError): - p[0] - p = cast(BVoidP, 0) - with pytest.raises((TypeError, RuntimeError)): - p[0] - -def test_iter(): - BInt = new_primitive_type("int") - BIntP = new_pointer_type(BInt) - BArray = new_array_type(BIntP, None) # int[] - p = newp(BArray, 7) - assert list(p) == list(iter(p)) == [0] * 7 - # - py.test.raises(TypeError, iter, cast(BInt, 5)) - py.test.raises(TypeError, iter, cast(BIntP, 123456)) - -def test_cmp(): - BInt = new_primitive_type("int") - BIntP = new_pointer_type(BInt) - BVoidP = new_pointer_type(new_void_type()) - p = newp(BIntP, 123) - q = cast(BInt, 124) - assert (p == q) is False - assert (p != q) is True - assert (q == p) is False - assert (q != p) is True - if strict_compare: - 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 - assert (p == r) is True - assert (p != r) is False - assert (p > r) is False - assert (p >= r) is True - s = newp(BIntP, 125) - assert (p == s) is False - assert (p != s) is True - assert (p < s) is (p <= s) is (s > p) is (s >= p) - assert (p > s) is (p >= s) is (s < p) is (s <= p) - assert (p < s) ^ (p > s) - -def test_buffer(): - try: - import __builtin__ - except ImportError: - import builtins as __builtin__ - BShort = new_primitive_type("short") - s = newp(new_pointer_type(BShort), 100) - assert sizeof(s) == size_of_ptr() - assert sizeof(BShort) == 2 - assert len(buffer(s)) == 2 - # - BChar = new_primitive_type("char") - BCharArray = new_array_type(new_pointer_type(BChar), None) - c = newp(BCharArray, b"hi there") - # - buf = buffer(c) - assert repr(buf).startswith('<_cffi_backend.buffer object at 0x') - assert bytes(buf) == b"hi there\x00" - assert type(buf) is buffer - if sys.version_info < (3,): - assert str(buf) == "hi there\x00" - assert unicode(buf) == u+"hi there\x00" - else: - assert str(buf) == repr(buf) - # --mb_length-- - assert len(buf) == len(b"hi there\x00") - # --mb_item-- - for i in range(-12, 12): - try: - expected = b"hi there\x00"[i] - except IndexError: - with pytest.raises(IndexError): - buf[i] - else: - assert buf[i] == bitem2bchr(expected) - # --mb_slice-- - assert buf[:] == b"hi there\x00" - for i in range(-12, 12): - assert buf[i:] == b"hi there\x00"[i:] - assert buf[:i] == b"hi there\x00"[:i] - for j in range(-12, 12): - assert buf[i:j] == b"hi there\x00"[i:j] - # --misc-- - assert list(buf) == list(map(bitem2bchr, b"hi there\x00")) - # --mb_as_buffer-- - if hasattr(__builtin__, 'buffer'): # Python <= 2.7 - py.test.raises(TypeError, __builtin__.buffer, c) - bf1 = __builtin__.buffer(buf) - assert len(bf1) == len(buf) and bf1[3] == "t" - if hasattr(__builtin__, 'memoryview'): # Python >= 2.7 - py.test.raises(TypeError, memoryview, c) - mv1 = memoryview(buf) - assert len(mv1) == len(buf) and mv1[3] in (b"t", ord(b"t")) - # --mb_ass_item-- - expected = list(map(bitem2bchr, b"hi there\x00")) - for i in range(-12, 12): - try: - expected[i] = bytechr(i & 0xff) - except IndexError: - 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")) - 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" - assert buf[:] == b"HI there\x00" - buf[:2] = b"hi" - expected = list(map(bitem2bchr, b"hi there\x00")) - x = 0 - for i in range(-12, 12): - for j in range(-12, 12): - start = i if i >= 0 else i + len(buf) - stop = j if j >= 0 else j + len(buf) - start = max(0, min(len(buf), start)) - stop = max(0, min(len(buf), stop)) - sample = bytechr(x & 0xff) * (stop - start) - x += 1 - buf[i:j] = sample - expected[i:j] = map(bitem2bchr, sample) - assert list(buf) == expected - -def test_getcname(): - BUChar = new_primitive_type("unsigned char") - BArray = new_array_type(new_pointer_type(BUChar), 123) - assert getcname(BArray, "<-->") == "unsigned char<-->[123]" - -def test_errno(): - BVoid = new_void_type() - BFunc5 = new_function_type((), BVoid) - f = cast(BFunc5, _testfunc(5)) - set_errno(50) - f() - assert get_errno() == 65 - f(); f() - assert get_errno() == 95 - -def test_errno_callback(): - 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() - set_errno(e - 6) - BVoid = new_void_type() - BFunc5 = new_function_type((), BVoid) - f = callback(BFunc5, cb) - f() - assert get_errno() == 89 - f(); f() - assert get_errno() == 77 - -def test_cast_to_array(): - # not valid in C! extension to get a non-owning <cdata 'int[3]'> - BInt = new_primitive_type("int") - BIntP = new_pointer_type(BInt) - BArray = new_array_type(BIntP, 3) - x = cast(BArray, 0) - assert repr(x) == "<cdata 'int[3]' NULL>" - -def test_cast_invalid(): - BStruct = new_struct_type("struct foo") - complete_struct_or_union(BStruct, []) - p = cast(new_pointer_type(BStruct), 123456) - s = p[0] - py.test.raises(TypeError, cast, BStruct, s) - -def test_bug_float_convertion(): - BDouble = new_primitive_type("double") - BDoubleP = new_pointer_type(BDouble) - py.test.raises(TypeError, newp, BDoubleP, "foobar") - -def test_bug_delitem(): - BChar = new_primitive_type("char") - BCharP = new_pointer_type(BChar) - x = newp(BCharP) - 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)) - with pytest.raises(AttributeError): - del x.a1 - -def test_variable_length_struct(): - py.test.skip("later") - BLong = new_primitive_type("long") - BArray = new_array_type(new_pointer_type(BLong), None) - BStruct = new_struct_type("struct foo") - BStructP = new_pointer_type(BStruct) - complete_struct_or_union(BStruct, [('a1', BLong, -1), - ('a2', BArray, -1)]) - assert sizeof(BStruct) == size_of_long() - assert alignof(BStruct) == alignof(BLong) - # - py.test.raises(TypeError, newp, BStructP, None) - x = newp(BStructP, 5) - assert sizeof(x) == 6 * size_of_long() - x[4] = 123 - assert x[4] == 123 - with pytest.raises(IndexError): - x[5] - assert len(x.a2) == 5 - # - py.test.raises(TypeError, newp, BStructP, [123]) - x = newp(BStructP, [123, 5]) - assert x.a1 == 123 - assert len(x.a2) == 5 - assert list(x.a2) == [0] * 5 - # - x = newp(BStructP, {'a2': 5}) - assert x.a1 == 0 - assert len(x.a2) == 5 - assert list(x.a2) == [0] * 5 - # - x = newp(BStructP, [123, (4, 5)]) - assert x.a1 == 123 - assert len(x.a2) == 2 - assert list(x.a2) == [4, 5] - # - x = newp(BStructP, {'a2': (4, 5)}) - assert x.a1 == 0 - assert len(x.a2) == 2 - assert list(x.a2) == [4, 5] - -def test_autocast_int(): - BInt = new_primitive_type("int") - BIntPtr = new_pointer_type(BInt) - BLongLong = new_primitive_type("long long") - BULongLong = new_primitive_type("unsigned long long") - BULongLongPtr = new_pointer_type(BULongLong) - x = newp(BIntPtr, cast(BInt, 42)) - assert x[0] == 42 - x = newp(BIntPtr, cast(BLongLong, 42)) - assert x[0] == 42 - x = newp(BIntPtr, cast(BULongLong, 42)) - assert x[0] == 42 - x = newp(BULongLongPtr, cast(BInt, 42)) - assert x[0] == 42 - py.test.raises(OverflowError, newp, BULongLongPtr, cast(BInt, -42)) - x = cast(BInt, cast(BInt, 42)) - assert int(x) == 42 - x = cast(BInt, cast(BLongLong, 42)) - assert int(x) == 42 - x = cast(BInt, cast(BULongLong, 42)) - assert int(x) == 42 - x = cast(BULongLong, cast(BInt, 42)) - assert int(x) == 42 - x = cast(BULongLong, cast(BInt, -42)) - assert int(x) == 2 ** 64 - 42 - x = cast(BIntPtr, cast(BInt, 42)) - assert int(cast(BInt, x)) == 42 - -def test_autocast_float(): - BFloat = new_primitive_type("float") - BDouble = new_primitive_type("float") - BFloatPtr = new_pointer_type(BFloat) - x = newp(BFloatPtr, cast(BDouble, 12.5)) - assert x[0] == 12.5 - x = cast(BFloat, cast(BDouble, 12.5)) - assert float(x) == 12.5 - -def test_longdouble(): - py_py = 'PY_DOT_PY' in globals() - BInt = new_primitive_type("int") - BLongDouble = new_primitive_type("long double") - BLongDoublePtr = new_pointer_type(BLongDouble) - BLongDoubleArray = new_array_type(BLongDoublePtr, None) - a = newp(BLongDoubleArray, 1) - x = a[0] - if not py_py: - assert repr(x).startswith("<cdata 'long double' 0.0") - assert float(x) == 0.0 - assert int(x) == 0 - # - b = newp(BLongDoubleArray, [1.23]) - x = b[0] - if not py_py: - assert repr(x).startswith("<cdata 'long double' 1.23") - assert float(x) == 1.23 - assert int(x) == 1 - # - BFunc19 = new_function_type((BLongDouble, BInt), BLongDouble) - f = cast(BFunc19, _testfunc(19)) - start = lstart = 1.5 - for i in range(107): - start = 4 * start - start * start - lstart = f(lstart, 1) - lother = f(1.5, 107) - if not py_py: - assert float(lstart) == float(lother) - assert repr(lstart) == repr(lother) - if sizeof(BLongDouble) > sizeof(new_primitive_type("double")): - assert float(lstart) != start - assert repr(lstart).startswith("<cdata 'long double' ") - # - c = newp(BLongDoubleArray, [lstart]) - x = c[0] - assert float(f(lstart, 107)) == float(f(x, 107)) - -def test_get_array_of_length_zero(): - for length in [0, 5, 10]: - BLong = new_primitive_type("long") - BLongP = new_pointer_type(BLong) - BArray0 = new_array_type(BLongP, length) - BStruct = new_struct_type("struct foo") - BStructPtr = new_pointer_type(BStruct) - complete_struct_or_union(BStruct, [('a1', BArray0, -1)]) - p = newp(BStructPtr, None) - if length == 0: - assert repr(p.a1).startswith("<cdata 'long *' 0x") - else: - assert repr(p.a1).startswith("<cdata 'long[%d]' 0x" % length) - -def test_nested_anonymous_struct(): - BInt = new_primitive_type("int") - BChar = new_primitive_type("char") - BStruct = new_struct_type("struct foo") - BInnerStruct = new_struct_type("struct foo") - complete_struct_or_union(BInnerStruct, [('a1', BInt, -1), - ('a2', BChar, -1)]) - complete_struct_or_union(BStruct, [('', BInnerStruct, -1), - ('a3', BChar, -1)]) - assert sizeof(BInnerStruct) == sizeof(BInt) * 2 # with alignment - assert sizeof(BStruct) == sizeof(BInt) * 3 # 'a3' is placed after - d = BStruct.fields - assert len(d) == 3 - assert d[0][0] == 'a1' - assert d[0][1].type is BInt - assert d[0][1].offset == 0 - assert d[0][1].bitshift == -1 - assert d[0][1].bitsize == -1 - assert d[1][0] == 'a2' - assert d[1][1].type is BChar - assert d[1][1].offset == sizeof(BInt) - assert d[1][1].bitshift == -1 - assert d[1][1].bitsize == -1 - assert d[2][0] == 'a3' - assert d[2][1].type is BChar - assert d[2][1].offset == sizeof(BInt) * 2 - assert d[2][1].bitshift == -1 - assert d[2][1].bitsize == -1 - -def test_nested_anonymous_struct_2(): - BInt = new_primitive_type("int") - BStruct = new_struct_type("struct foo") - BInnerUnion = new_union_type("union bar") - complete_struct_or_union(BInnerUnion, [('a1', BInt, -1), - ('a2', BInt, -1)]) - complete_struct_or_union(BStruct, [('b1', BInt, -1), - ('', BInnerUnion, -1), - ('b2', BInt, -1)]) - assert sizeof(BInnerUnion) == sizeof(BInt) - assert sizeof(BStruct) == sizeof(BInt) * 3 - fields = [(name, fld.offset, fld.flags) for (name, fld) in BStruct.fields] - assert fields == [ - ('b1', 0 * sizeof(BInt), 0), - ('a1', 1 * sizeof(BInt), 0), - ('a2', 1 * sizeof(BInt), 1), - ('b2', 2 * sizeof(BInt), 0), - ] - -def test_sizeof_union(): - # a union has the largest alignment of its members, and a total size - # that is the largest of its items *possibly further aligned* if - # another smaller item has a larger alignment... - BChar = new_primitive_type("char") - BShort = new_primitive_type("short") - assert sizeof(BShort) == alignof(BShort) == 2 - BStruct = new_struct_type("struct foo") - complete_struct_or_union(BStruct, [('a1', BChar), - ('a2', BChar), - ('a3', BChar)]) - assert sizeof(BStruct) == 3 and alignof(BStruct) == 1 - BUnion = new_union_type("union u") - complete_struct_or_union(BUnion, [('s', BStruct), - ('i', BShort)]) - assert sizeof(BUnion) == 4 - assert alignof(BUnion) == 2 - -def test_unaligned_struct(): - BInt = new_primitive_type("int") - BStruct = new_struct_type("struct foo") - complete_struct_or_union(BStruct, [('b', BInt, -1, 1)], - None, 5, 1) - -def test_CData_CType(): - CData, CType = _get_types() - BChar = new_primitive_type("char") - BCharP = new_pointer_type(BChar) - nullchr = cast(BChar, 0) - chrref = newp(BCharP, None) - assert isinstance(nullchr, CData) - assert isinstance(chrref, CData) - assert not isinstance(BChar, CData) - assert not isinstance(nullchr, CType) - assert not isinstance(chrref, CType) - assert isinstance(BChar, CType) - -def test_no_cdata_float(): - BInt = new_primitive_type("int") - BIntP = new_pointer_type(BInt) - BUInt = new_primitive_type("unsigned int") - BUIntP = new_pointer_type(BUInt) - BFloat = new_primitive_type("float") - py.test.raises(TypeError, newp, BIntP, cast(BFloat, 0.0)) - py.test.raises(TypeError, newp, BUIntP, cast(BFloat, 0.0)) - -def test_bool(): - BBool = new_primitive_type("_Bool") - BBoolP = new_pointer_type(BBool) - assert int(cast(BBool, False)) == 0 - assert int(cast(BBool, True)) == 1 - assert bool(cast(BBool, False)) is False # since 1.7 - assert bool(cast(BBool, True)) is True - assert int(cast(BBool, 3)) == 1 - assert int(cast(BBool, long(3))) == 1 - assert int(cast(BBool, long(10)**4000)) == 1 - assert int(cast(BBool, -0.1)) == 1 - assert int(cast(BBool, -0.0)) == 0 - assert int(cast(BBool, '\x00')) == 0 - assert int(cast(BBool, '\xff')) == 1 - assert newp(BBoolP, False)[0] == 0 - assert newp(BBoolP, True)[0] == 1 - assert newp(BBoolP, 0)[0] == 0 - assert newp(BBoolP, 1)[0] == 1 - py.test.raises(TypeError, newp, BBoolP, 1.0) - py.test.raises(TypeError, newp, BBoolP, '\x00') - py.test.raises(OverflowError, newp, BBoolP, 2) - py.test.raises(OverflowError, newp, BBoolP, -1) - BCharP = new_pointer_type(new_primitive_type("char")) - p = newp(BCharP, b'\x01') - q = cast(BBoolP, p) - assert q[0] is True - p = newp(BCharP, b'\x00') - q = cast(BBoolP, p) - assert q[0] is False - py.test.raises(TypeError, string, cast(BBool, False)) - BDouble = new_primitive_type("double") - assert int(cast(BBool, cast(BDouble, 0.1))) == 1 - assert int(cast(BBool, cast(BDouble, 0.0))) == 0 - BBoolA = new_array_type(BBoolP, None) - p = newp(BBoolA, b'\x01\x00') - assert p[0] is True - assert p[1] is False - -def test_bool_forbidden_cases(): - BBool = new_primitive_type("_Bool") - BBoolP = new_pointer_type(BBool) - BBoolA = new_array_type(BBoolP, None) - BCharP = new_pointer_type(new_primitive_type("char")) - p = newp(BCharP, b'X') - q = cast(BBoolP, p) - 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 - py.test.raises(OverflowError, newp, BBoolP, 2) - py.test.raises(OverflowError, newp, BBoolP, -1) - py.test.raises(ValueError, newp, BBoolA, b'\x00\x01\x02') - py.test.raises(OverflowError, newp, BBoolA, [0, 1, 2]) - py.test.raises(TypeError, string, newp(BBoolP, 1)) - py.test.raises(TypeError, string, newp(BBoolA, [1])) - -def test_typeoffsetof(): - BChar = new_primitive_type("char") - BStruct = new_struct_type("struct foo") - BStructPtr = new_pointer_type(BStruct) - complete_struct_or_union(BStruct, [('a1', BChar, -1), - ('a2', BChar, -1), - ('a3', BChar, -1)]) - py.test.raises(TypeError, typeoffsetof, BStructPtr, None) - py.test.raises(TypeError, typeoffsetof, BStruct, None) - assert typeoffsetof(BStructPtr, 'a1') == (BChar, 0) - assert typeoffsetof(BStruct, 'a1') == (BChar, 0) - assert typeoffsetof(BStructPtr, 'a2') == (BChar, 1) - assert typeoffsetof(BStruct, 'a3') == (BChar, 2) - assert typeoffsetof(BStructPtr, 'a2', 0) == (BChar, 1) - assert typeoffsetof(BStruct, u+'a3') == (BChar, 2) - py.test.raises(TypeError, typeoffsetof, BStructPtr, 'a2', 1) - py.test.raises(KeyError, typeoffsetof, BStructPtr, 'a4') - py.test.raises(KeyError, typeoffsetof, BStruct, 'a5') - py.test.raises(TypeError, typeoffsetof, BStruct, 42) - py.test.raises(TypeError, typeoffsetof, BChar, 'a1') - -def test_typeoffsetof_array(): - BInt = new_primitive_type("int") - BIntP = new_pointer_type(BInt) - BArray = new_array_type(BIntP, None) - py.test.raises(TypeError, typeoffsetof, BArray, None) - py.test.raises(TypeError, typeoffsetof, BArray, 'a1') - assert typeoffsetof(BArray, 51) == (BInt, 51 * size_of_int()) - assert typeoffsetof(BIntP, 51) == (BInt, 51 * size_of_int()) - assert typeoffsetof(BArray, -51) == (BInt, -51 * size_of_int()) - MAX = sys.maxsize // size_of_int() - assert typeoffsetof(BArray, MAX) == (BInt, MAX * size_of_int()) - assert typeoffsetof(BIntP, MAX) == (BInt, MAX * size_of_int()) - py.test.raises(OverflowError, typeoffsetof, BArray, MAX + 1) - -def test_typeoffsetof_no_bitfield(): - BInt = new_primitive_type("int") - BStruct = new_struct_type("struct foo") - complete_struct_or_union(BStruct, [('a1', BInt, 4)]) - py.test.raises(TypeError, typeoffsetof, BStruct, 'a1') - -def test_rawaddressof(): - BChar = new_primitive_type("char") - BCharP = new_pointer_type(BChar) - BStruct = new_struct_type("struct foo") - BStructPtr = new_pointer_type(BStruct) - complete_struct_or_union(BStruct, [('a1', BChar, -1), - ('a2', BChar, -1), - ('a3', BChar, -1)]) - p = newp(BStructPtr) - assert repr(p) == "<cdata 'struct foo *' owning 3 bytes>" - s = p[0] - assert repr(s) == "<cdata 'struct foo' owning 3 bytes>" - a = rawaddressof(BStructPtr, s, 0) - assert repr(a).startswith("<cdata 'struct foo *' 0x") - py.test.raises(TypeError, rawaddressof, BStruct, s, 0) - b = rawaddressof(BCharP, s, 0) - assert b == cast(BCharP, p) - c = rawaddressof(BStructPtr, a, 0) - assert c == a - py.test.raises(TypeError, rawaddressof, BStructPtr, cast(BChar, '?'), 0) - # - d = rawaddressof(BCharP, s, 1) - assert d == cast(BCharP, p) + 1 - # - e = cast(BCharP, 109238) - f = rawaddressof(BCharP, e, 42) - assert f == e + 42 - # - BCharA = new_array_type(BCharP, None) - e = newp(BCharA, 50) - f = rawaddressof(BCharP, e, 42) - assert f == e + 42 - -def test_newp_signed_unsigned_char(): - BCharArray = new_array_type( - new_pointer_type(new_primitive_type("char")), None) - p = newp(BCharArray, b"foo") - assert len(p) == 4 - assert list(p) == [b"f", b"o", b"o", b"\x00"] - # - BUCharArray = new_array_type( - new_pointer_type(new_primitive_type("unsigned char")), None) - p = newp(BUCharArray, b"fo\xff") - assert len(p) == 4 - assert list(p) == [ord("f"), ord("o"), 0xff, 0] - # - BSCharArray = new_array_type( - new_pointer_type(new_primitive_type("signed char")), None) - p = newp(BSCharArray, b"fo\xff") - assert len(p) == 4 - assert list(p) == [ord("f"), ord("o"), -1, 0] - -def test_newp_from_bytearray_doesnt_work(): - BCharArray = new_array_type( - new_pointer_type(new_primitive_type("char")), None) - py.test.raises(TypeError, newp, BCharArray, bytearray(b"foo")) - p = newp(BCharArray, 5) - buffer(p)[:] = bytearray(b"foo.\x00") - assert len(p) == 5 - assert list(p) == [b"f", b"o", b"o", b".", b"\x00"] - p[1:3] = bytearray(b"XY") - assert list(p) == [b"f", b"X", b"Y", b".", b"\x00"] - -def test_string_assignment_to_byte_array(): - BByteArray = new_array_type( - new_pointer_type(new_primitive_type("unsigned char")), None) - p = newp(BByteArray, 5) - p[0:3] = bytearray(b"XYZ") - assert list(p) == [ord("X"), ord("Y"), ord("Z"), 0, 0] - -# XXX hack -if sys.version_info >= (3,): - try: - import posix, io - posix.fdopen = io.open - except ImportError: - pass # win32 - -def test_FILE(): - if sys.platform == "win32": - py.test.skip("testing FILE not implemented") - # - BFILE = new_struct_type("struct _IO_FILE") - BFILEP = new_pointer_type(BFILE) - BChar = new_primitive_type("char") - BCharP = new_pointer_type(BChar) - BInt = new_primitive_type("int") - BFunc = new_function_type((BCharP, BFILEP), BInt, False) - BFunc2 = new_function_type((BFILEP, BCharP), BInt, True) - ll = find_and_load_library('c') - fputs = ll.load_function(BFunc, "fputs") - fscanf = ll.load_function(BFunc2, "fscanf") - # - import posix - fdr, fdw = posix.pipe() - fr1 = posix.fdopen(fdr, 'rb', 256) - fw1 = posix.fdopen(fdw, 'wb', 256) - # - fw1.write(b"X") - res = fputs(b"hello world\n", fw1) - assert res >= 0 - fw1.flush() # should not be needed - # - p = newp(new_array_type(BCharP, 100), None) - res = fscanf(fr1, b"%s\n", p) - assert res == 1 - assert string(p) == b"Xhello" - fr1.close() - fw1.close() - -def test_FILE_only_for_FILE_arg(): - if sys.platform == "win32": - py.test.skip("testing FILE not implemented") - # - B_NOT_FILE = new_struct_type("struct NOT_FILE") - B_NOT_FILEP = new_pointer_type(B_NOT_FILE) - BChar = new_primitive_type("char") - BCharP = new_pointer_type(BChar) - BInt = new_primitive_type("int") - BFunc = new_function_type((BCharP, B_NOT_FILEP), BInt, False) - ll = find_and_load_library('c') - fputs = ll.load_function(BFunc, "fputs") - # - import posix - fdr, fdw = posix.pipe() - fr1 = posix.fdopen(fdr, 'r') - fw1 = posix.fdopen(fdw, 'w') - # - e = py.test.raises(TypeError, fputs, b"hello world\n", fw1) - assert str(e.value).startswith( - "initializer for ctype 'struct NOT_FILE *' must " - "be a cdata pointer, not ") - -def test_FILE_object(): - if sys.platform == "win32": - py.test.skip("testing FILE not implemented") - # - BFILE = new_struct_type("FILE") - BFILEP = new_pointer_type(BFILE) - BChar = new_primitive_type("char") - BCharP = new_pointer_type(BChar) - BInt = new_primitive_type("int") - BFunc = new_function_type((BCharP, BFILEP), BInt, False) - BFunc2 = new_function_type((BFILEP,), BInt, False) - ll = find_and_load_library('c') - fputs = ll.load_function(BFunc, "fputs") - fileno = ll.load_function(BFunc2, "fileno") - # - import posix - fdr, fdw = posix.pipe() - fw1 = posix.fdopen(fdw, 'wb', 256) - # - fw1p = cast(BFILEP, fw1) - fw1.write(b"X") - fw1.flush() - res = fputs(b"hello\n", fw1p) - assert res >= 0 - res = fileno(fw1p) - assert (res == fdw) == (sys.version_info < (3,)) - fw1.close() - # - data = posix.read(fdr, 256) - assert data == b"Xhello\n" - posix.close(fdr) - -def test_errno_saved(): - set_errno(42) - # a random function that will reset errno to 0 (at least on non-windows) - import os; os.stat('.') - # - res = get_errno() - assert res == 42 - -def test_GetLastError(): - if sys.platform != "win32": - py.test.skip("GetLastError(): only for Windows") - # - lib = find_and_load_library('KERNEL32.DLL') - BInt = new_primitive_type("int") - BVoid = new_void_type() - BFunc1 = new_function_type((BInt,), BVoid, False) - BFunc2 = new_function_type((), BInt, False) - SetLastError = lib.load_function(BFunc1, "SetLastError") - GetLastError = lib.load_function(BFunc2, "GetLastError") - # - SetLastError(42) - # a random function that will reset the real GetLastError() to 0 - import nt; nt.stat('.') - # - res = GetLastError() - assert res == 42 - # - SetLastError(2) - code, message = getwinerror() - assert code == 2 - assert message == "The system cannot find the file specified" - # - code, message = getwinerror(1155) - assert code == 1155 - assert message == ("No application is associated with the " - "specified file for this operation") - -def test_nonstandard_integer_types(): - for typename in ['int8_t', 'uint8_t', 'int16_t', 'uint16_t', 'int32_t', - 'uint32_t', 'int64_t', 'uint64_t', 'intptr_t', - 'uintptr_t', 'ptrdiff_t', 'size_t', 'ssize_t', - 'int_least8_t', 'uint_least8_t', - 'int_least16_t', 'uint_least16_t', - 'int_least32_t', 'uint_least32_t', - 'int_least64_t', 'uint_least64_t', - 'int_fast8_t', 'uint_fast8_t', - 'int_fast16_t', 'uint_fast16_t', - 'int_fast32_t', 'uint_fast32_t', - 'int_fast64_t', 'uint_fast64_t', - 'intmax_t', 'uintmax_t']: - new_primitive_type(typename) # works - -def test_cannot_convert_unicode_to_charp(): - BCharP = new_pointer_type(new_primitive_type("char")) - BCharArray = new_array_type(BCharP, None) - py.test.raises(TypeError, newp, BCharArray, u+'foobar') - -def test_buffer_keepalive(): - BCharP = new_pointer_type(new_primitive_type("char")) - BCharArray = new_array_type(BCharP, None) - buflist = [] - for i in range(20): - c = newp(BCharArray, str2bytes("hi there %d" % i)) - buflist.append(buffer(c)) - import gc; gc.collect() - for i in range(20): - buf = buflist[i] - assert buf[:] == str2bytes("hi there %d\x00" % i) - -def test_slice(): - BIntP = new_pointer_type(new_primitive_type("int")) - BIntArray = new_array_type(BIntP, None) - c = newp(BIntArray, 5) - assert len(c) == 5 - assert repr(c) == "<cdata 'int[]' owning 20 bytes>" - d = c[1:4] - assert len(d) == 3 - assert repr(d) == "<cdata 'int[]' sliced length 3>" - d[0] = 123 - d[2] = 456 - assert c[1] == 123 - assert c[3] == 456 - assert d[2] == 456 - with pytest.raises(IndexError): - d[3] - with pytest.raises(IndexError): - d[-1] - -def test_slice_ptr(): - BIntP = new_pointer_type(new_primitive_type("int")) - BIntArray = new_array_type(BIntP, None) - c = newp(BIntArray, 5) - d = (c+1)[0:2] - assert len(d) == 2 - assert repr(d) == "<cdata 'int[]' sliced length 2>" - d[1] += 50 - assert c[2] == 50 - -def test_slice_array_checkbounds(): - BIntP = new_pointer_type(new_primitive_type("int")) - BIntArray = new_array_type(BIntP, None) - c = newp(BIntArray, 5) - c[0:5] - assert len(c[5:5]) == 0 - with pytest.raises(IndexError): - c[-1:1] - cp = c + 0 - cp[-1:1] - -def test_nonstandard_slice(): - BIntP = new_pointer_type(new_primitive_type("int")) - BIntArray = new_array_type(BIntP, None) - c = newp(BIntArray, 5) - with pytest.raises(IndexError) as e: - c[:5] - assert str(e.value) == "slice start must be specified" - with pytest.raises(IndexError) as e: - c[4:] - assert str(e.value) == "slice stop must be specified" - with pytest.raises(IndexError) as e: - c[1:2:3] - assert str(e.value) == "slice with step not supported" - with pytest.raises(IndexError) as e: - c[1:2:1] - assert str(e.value) == "slice with step not supported" - with pytest.raises(IndexError) as e: - c[4:2] - assert str(e.value) == "slice start > stop" - with pytest.raises(IndexError) as e: - c[6:6] - assert str(e.value) == "index too large (expected 6 <= 5)" - -def test_setslice(): - BIntP = new_pointer_type(new_primitive_type("int")) - BIntArray = new_array_type(BIntP, None) - c = newp(BIntArray, 5) - c[1:3] = [100, 200] - assert list(c) == [0, 100, 200, 0, 0] - cp = c + 3 - cp[-1:1] = [300, 400] - assert list(c) == [0, 100, 300, 400, 0] - cp[-1:1] = iter([500, 600]) - assert list(c) == [0, 100, 500, 600, 0] - with pytest.raises(ValueError): - cp[-1:1] = [1000] - assert list(c) == [0, 100, 1000, 600, 0] - with pytest.raises(ValueError): - cp[-1:1] = (700, 800, 900) - assert list(c) == [0, 100, 700, 800, 0] - -def test_setslice_array(): - BIntP = new_pointer_type(new_primitive_type("int")) - BIntArray = new_array_type(BIntP, None) - c = newp(BIntArray, 5) - d = newp(BIntArray, [10, 20, 30]) - c[1:4] = d - assert list(c) == [0, 10, 20, 30, 0] - # - BShortP = new_pointer_type(new_primitive_type("short")) - BShortArray = new_array_type(BShortP, None) - d = newp(BShortArray, [40, 50]) - c[1:3] = d - assert list(c) == [0, 40, 50, 30, 0] - -def test_cdata_name_module_doc(): - p = new_primitive_type("signed char") - x = cast(p, 17) - assert x.__module__ == '_cffi_backend' - assert x.__name__ == '<cdata>' - assert hasattr(x, '__doc__') - -def test_different_types_of_ptr_equality(): - BVoidP = new_pointer_type(new_void_type()) - BIntP = new_pointer_type(new_primitive_type("int")) - x = cast(BVoidP, 12345) - assert x == cast(BIntP, 12345) - assert x != cast(BIntP, 12344) - assert hash(x) == hash(cast(BIntP, 12345)) - -def test_new_handle(): - import _weakref - BVoidP = new_pointer_type(new_void_type()) - BCharP = new_pointer_type(new_primitive_type("char")) - class mylist(list): - pass - o = mylist([2, 3, 4]) - x = newp_handle(BVoidP, o) - assert repr(x) == "<cdata 'void *' handle to [2, 3, 4]>" - assert x - assert from_handle(x) is o - assert from_handle(cast(BCharP, x)) is o - wr = _weakref.ref(o) - del o - import gc; gc.collect() - assert wr() is not None - assert from_handle(x) == list((2, 3, 4)) - assert from_handle(cast(BCharP, x)) == list((2, 3, 4)) - del x - for i in range(3): - if wr() is not None: - import gc; gc.collect() - assert wr() is None - py.test.raises(RuntimeError, from_handle, cast(BCharP, 0)) - -def test_new_handle_cycle(): - import _weakref - BVoidP = new_pointer_type(new_void_type()) - class A(object): - pass - o = A() - o.cycle = newp_handle(BVoidP, o) - wr = _weakref.ref(o) - del o - for i in range(3): - if wr() is not None: - import gc; gc.collect() - assert wr() is None - -def _test_bitfield_details(flag): - BChar = new_primitive_type("char") - BShort = new_primitive_type("short") - BInt = new_primitive_type("int") - BUInt = new_primitive_type("unsigned int") - BStruct = new_struct_type("struct foo1") - complete_struct_or_union(BStruct, [('a', BChar, -1), - ('b1', BInt, 9), - ('b2', BUInt, 7), - ('c', BChar, -1)], -1, -1, -1, flag) - if not (flag & SF_MSVC_BITFIELDS): # gcc, any variant - assert typeoffsetof(BStruct, 'c') == (BChar, 3) - assert sizeof(BStruct) == 4 - else: # msvc - assert typeoffsetof(BStruct, 'c') == (BChar, 8) - assert sizeof(BStruct) == 12 - assert alignof(BStruct) == 4 - # - p = newp(new_pointer_type(BStruct), None) - p.a = b'A' - p.b1 = -201 - p.b2 = 99 - p.c = b'\x9D' - raw = buffer(p)[:] - if sys.byteorder == 'little': - if flag & SF_MSVC_BITFIELDS: - assert raw == b'A\x00\x00\x007\xC7\x00\x00\x9D\x00\x00\x00' - elif flag & SF_GCC_LITTLE_ENDIAN: - assert raw == b'A7\xC7\x9D' - elif flag & SF_GCC_BIG_ENDIAN: - assert raw == b'A\xE3\x9B\x9D' - else: - raise AssertionError("bad flag") - else: - if flag & SF_MSVC_BITFIELDS: - assert raw == b'A\x00\x00\x00\x00\x00\xC77\x9D\x00\x00\x00' - elif flag & SF_GCC_LITTLE_ENDIAN: - assert raw == b'A\xC77\x9D' - elif flag & SF_GCC_BIG_ENDIAN: - assert raw == b'A\x9B\xE3\x9D' - else: - raise AssertionError("bad flag") - # - BStruct = new_struct_type("struct foo2") - complete_struct_or_union(BStruct, [('a', BChar, -1), - ('', BShort, 9), - ('c', BChar, -1)], -1, -1, -1, flag) - assert typeoffsetof(BStruct, 'c') == (BChar, 4) - if flag & SF_MSVC_BITFIELDS: - assert sizeof(BStruct) == 6 - assert alignof(BStruct) == 2 - elif flag & SF_GCC_X86_BITFIELDS: - assert sizeof(BStruct) == 5 - assert alignof(BStruct) == 1 - elif flag & SF_GCC_ARM_BITFIELDS: - assert sizeof(BStruct) == 6 - assert alignof(BStruct) == 2 - else: - raise AssertionError("bad flag") - # - BStruct = new_struct_type("struct foo2") - complete_struct_or_union(BStruct, [('a', BChar, -1), - ('', BInt, 0), - ('', BInt, 0), - ('c', BChar, -1)], -1, -1, -1, flag) - if flag & SF_MSVC_BITFIELDS: - assert typeoffsetof(BStruct, 'c') == (BChar, 1) - assert sizeof(BStruct) == 2 - assert alignof(BStruct) == 1 - elif flag & SF_GCC_X86_BITFIELDS: - assert typeoffsetof(BStruct, 'c') == (BChar, 4) - assert sizeof(BStruct) == 5 - assert alignof(BStruct) == 1 - elif flag & SF_GCC_ARM_BITFIELDS: - assert typeoffsetof(BStruct, 'c') == (BChar, 4) - assert sizeof(BStruct) == 8 - assert alignof(BStruct) == 4 - else: - raise AssertionError("bad flag") - - -SF_MSVC_BITFIELDS = 0x01 -SF_GCC_ARM_BITFIELDS = 0x02 -SF_GCC_X86_BITFIELDS = 0x10 - -SF_GCC_BIG_ENDIAN = 0x04 -SF_GCC_LITTLE_ENDIAN = 0x40 - -SF_PACKED = 0x08 - -def test_bitfield_as_x86_gcc(): - _test_bitfield_details(flag=SF_GCC_X86_BITFIELDS|SF_GCC_LITTLE_ENDIAN) - -def test_bitfield_as_msvc(): - _test_bitfield_details(flag=SF_MSVC_BITFIELDS|SF_GCC_LITTLE_ENDIAN) - -def test_bitfield_as_arm_gcc(): - _test_bitfield_details(flag=SF_GCC_ARM_BITFIELDS|SF_GCC_LITTLE_ENDIAN) - -def test_bitfield_as_ppc_gcc(): - # PowerPC uses the same format as X86, but is big-endian - _test_bitfield_details(flag=SF_GCC_X86_BITFIELDS|SF_GCC_BIG_ENDIAN) - - -def test_struct_array_no_length(): - BInt = new_primitive_type("int") - BIntP = new_pointer_type(BInt) - BArray = new_array_type(BIntP, None) - BStruct = new_struct_type("foo") - py.test.raises(TypeError, complete_struct_or_union, - BStruct, [('x', BArray), - ('y', BInt)]) - # - BStruct = new_struct_type("foo") - complete_struct_or_union(BStruct, [('x', BInt), - ('y', BArray)]) - assert sizeof(BStruct) == size_of_int() - d = BStruct.fields - assert len(d) == 2 - assert d[0][0] == 'x' - assert d[0][1].type is BInt - assert d[0][1].offset == 0 - assert d[0][1].bitshift == -1 - assert d[0][1].bitsize == -1 - assert d[1][0] == 'y' - assert d[1][1].type is BArray - assert d[1][1].offset == size_of_int() - assert d[1][1].bitshift == -2 - assert d[1][1].bitsize == -1 - # - p = newp(new_pointer_type(BStruct)) - p.x = 42 - assert p.x == 42 - assert typeof(p.y) is BArray - assert len(p.y) == 0 - assert p.y == cast(BIntP, p) + 1 - # - p = newp(new_pointer_type(BStruct), [100]) - assert p.x == 100 - assert len(p.y) == 0 - # - # Tests for - # ffi.new("struct_with_var_array *", [field.., [the_array_items..]]) - # ffi.new("struct_with_var_array *", [field.., array_size]) - plist = [] - for i in range(20): - if i % 2 == 0: - p = newp(new_pointer_type(BStruct), [100, [200, i, 400]]) - else: - p = newp(new_pointer_type(BStruct), [100, 3]) - p.y[1] = i - p.y[0] = 200 - assert p.y[2] == 0 - p.y[2] = 400 - assert len(p.y) == 3 - assert len(p[0].y) == 3 - assert len(buffer(p)) == sizeof(BInt) * 4 - assert sizeof(p[0]) == sizeof(BInt) * 4 - plist.append(p) - for i in range(20): - p = plist[i] - assert p.x == 100 - assert p.y[0] == 200 - assert p.y[1] == i - assert p.y[2] == 400 - assert list(p.y) == [200, i, 400] - # - # the following assignment works, as it normally would, for any array field - p.y = [501, 601] - assert list(p.y) == [501, 601, 400] - p[0].y = [500, 600] - assert list(p[0].y) == [500, 600, 400] - assert repr(p) == "<cdata 'foo *' owning %d bytes>" % ( - sizeof(BStruct) + 3 * sizeof(BInt),) - assert repr(p[0]) == "<cdata 'foo' owning %d bytes>" % ( - sizeof(BStruct) + 3 * sizeof(BInt),) - assert sizeof(p[0]) == sizeof(BStruct) + 3 * sizeof(BInt) - # - # from a non-owning pointer, we can't get the length - q = cast(new_pointer_type(BStruct), p) - assert q.y[0] == 500 - assert q[0].y[0] == 500 - py.test.raises(TypeError, len, q.y) - py.test.raises(TypeError, len, q[0].y) - assert typeof(q.y) is BIntP - assert typeof(q[0].y) is BIntP - assert sizeof(q[0]) == sizeof(BStruct) - # - # error cases - 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... - BStruct2 = new_struct_type("bar") - complete_struct_or_union(BStruct2, [('f', BStruct), - ('n', BInt)]) - p = newp(new_pointer_type(BStruct2), {'n': 42}) - assert p.n == 42 - # - # more error cases - py.test.raises(TypeError, newp, new_pointer_type(BStruct), [100, None]) - BArray4 = new_array_type(BIntP, 4) - BStruct4 = new_struct_type("test4") - complete_struct_or_union(BStruct4, [('a', BArray4)]) # not varsized - py.test.raises(TypeError, newp, new_pointer_type(BStruct4), [None]) - py.test.raises(TypeError, newp, new_pointer_type(BStruct4), [4]) - p = newp(new_pointer_type(BStruct4), [[10, 20, 30]]) - assert p.a[0] == 10 - 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") - BIntP = new_pointer_type(BInt) - BArray = new_array_type(BIntP, None) - BStruct = new_struct_type("foo") - complete_struct_or_union(BStruct, [('x', BArray, -1, 0), # actually 3 items - ('y', BInt, -1, 12)]) - p = newp(new_pointer_type(BStruct), [[10, 20], 30]) - assert p.x[0] == 10 - assert p.x[1] == 20 - assert p.x[2] == 0 - assert p.y == 30 - p = newp(new_pointer_type(BStruct), {'x': [40], 'y': 50}) - assert p.x[0] == 40 - assert p.x[1] == 0 - assert p.x[2] == 0 - assert p.y == 50 - p = newp(new_pointer_type(BStruct), {'y': 60}) - assert p.x[0] == 0 - assert p.x[1] == 0 - assert p.x[2] == 0 - assert p.y == 60 - # - # This "should" work too, allocating a larger structure - # (a bit strange in this case, but useful in general) - plist = [] - for i in range(20): - p = newp(new_pointer_type(BStruct), [[10, 20, 30, 40, 50, 60, 70]]) - plist.append(p) - for i in range(20): - p = plist[i] - assert p.x[0] == 10 - assert p.x[1] == 20 - assert p.x[2] == 30 - assert p.x[3] == 40 == p.y - assert p.x[4] == 50 - assert p.x[5] == 60 - assert p.x[6] == 70 - -def test_struct_array_not_aligned(): - # struct a { int x; char y; char z[]; }; - # ends up of size 8, but 'z' is at offset 5 - BChar = new_primitive_type("char") - BInt = new_primitive_type("int") - BCharP = new_pointer_type(BChar) - BArray = new_array_type(BCharP, None) - BStruct = new_struct_type("foo") - complete_struct_or_union(BStruct, [('x', BInt), - ('y', BChar), - ('z', BArray)]) - assert sizeof(BStruct) == 2 * size_of_int() - def offsetof(BType, fieldname): - return typeoffsetof(BType, fieldname)[1] - base = offsetof(BStruct, 'z') - assert base == size_of_int() + 1 - # - p = newp(new_pointer_type(BStruct), {'z': 3}) - assert sizeof(p[0]) == base + 3 - q = newp(new_pointer_type(BStruct), {'z': size_of_int()}) - assert sizeof(q) == size_of_ptr() - assert sizeof(q[0]) == base + size_of_int() - assert len(p.z) == 3 - assert len(p[0].z) == 3 - assert len(q.z) == size_of_int() - assert len(q[0].z) == size_of_int() - -def test_ass_slice(): - BChar = new_primitive_type("char") - BArray = new_array_type(new_pointer_type(BChar), None) - p = newp(BArray, b"foobar") - 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"] - 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) - BArray = new_array_type(new_pointer_type(BUniChar), None) - p = newp(BArray, u+"foobar") - 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"] - 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() - BInt = new_primitive_type("intptr_t") - p = cast(new_pointer_type(BVoid), 100000) - assert int(cast(BInt, p)) == 100000 - assert int(cast(BInt, p + 42)) == 100042 - assert int(cast(BInt, p - (-42))) == 100042 - assert (p + 42) - p == 42 - q = cast(new_pointer_type(new_primitive_type("char")), 100000) - 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") - BArray = new_array_type(new_pointer_type(BInt), 10) - p = newp(BArray, None) - assert sizeof(p[2:9]) == 7 * sizeof(BInt) - -def test_packed(): - BLong = new_primitive_type("long") - BChar = new_primitive_type("char") - BShort = new_primitive_type("short") - for extra_args in [(SF_PACKED,), (0, 1)]: - BStruct = new_struct_type("struct foo") - complete_struct_or_union(BStruct, [('a1', BLong, -1), - ('a2', BChar, -1), - ('a3', BShort, -1)], - None, -1, -1, *extra_args) - d = BStruct.fields - assert len(d) == 3 - assert d[0][0] == 'a1' - assert d[0][1].type is BLong - assert d[0][1].offset == 0 - assert d[0][1].bitshift == -1 - assert d[0][1].bitsize == -1 - assert d[1][0] == 'a2' - assert d[1][1].type is BChar - assert d[1][1].offset == sizeof(BLong) - assert d[1][1].bitshift == -1 - assert d[1][1].bitsize == -1 - assert d[2][0] == 'a3' - assert d[2][1].type is BShort - assert d[2][1].offset == sizeof(BLong) + sizeof(BChar) - assert d[2][1].bitshift == -1 - assert d[2][1].bitsize == -1 - assert sizeof(BStruct) == sizeof(BLong) + sizeof(BChar) + sizeof(BShort) - assert alignof(BStruct) == 1 - # - BStruct2 = new_struct_type("struct foo") - complete_struct_or_union(BStruct2, [('b1', BChar, -1), - ('b2', BLong, -1)], - None, -1, -1, 0, 2) - d = BStruct2.fields - assert len(d) == 2 - assert d[0][0] == 'b1' - assert d[0][1].type is BChar - assert d[0][1].offset == 0 - assert d[0][1].bitshift == -1 - assert d[0][1].bitsize == -1 - assert d[1][0] == 'b2' - assert d[1][1].type is BLong - assert d[1][1].offset == 2 - assert d[1][1].bitshift == -1 - assert d[1][1].bitsize == -1 - assert sizeof(BStruct2) == 2 + sizeof(BLong) - assert alignof(BStruct2) == 2 - -def test_packed_with_bitfields(): - if sys.platform == "win32": - py.test.skip("testing gcc behavior") - BLong = new_primitive_type("long") - BChar = new_primitive_type("char") - BStruct = new_struct_type("struct foo") - py.test.raises(NotImplementedError, - complete_struct_or_union, - BStruct, [('a1', BLong, 30), - ('a2', BChar, 5)], - None, -1, -1, SF_PACKED) - -def test_from_buffer(): - import array - a = array.array('H', [10000, 20000, 30000]) - BChar = new_primitive_type("char") - BCharP = new_pointer_type(BChar) - BCharA = new_array_type(BCharP, None) - c = from_buffer(BCharA, a) - assert typeof(c) is BCharA - assert len(c) == 6 - assert repr(c) == "<cdata 'char[]' buffer len 6 from 'array.array' object>" - p = new_pointer_type(new_primitive_type("unsigned short")) - cast(p, c)[1] += 500 - assert list(a) == [10000, 20500, 30000] - -def test_from_buffer_not_str_unicode(): - BChar = new_primitive_type("char") - BCharP = new_pointer_type(BChar) - BCharA = new_array_type(BCharP, None) - p1 = from_buffer(BCharA, b"foo") - assert p1 == from_buffer(BCharA, b"foo") - import gc; gc.collect() - assert p1 == from_buffer(BCharA, b"foo") - py.test.raises(TypeError, from_buffer, BCharA, u+"foo") - try: - from __builtin__ import buffer - except ImportError: - pass - else: - # Python 2 only - contents = from_buffer(BCharA, buffer(b"foo")) - assert len(contents) == len(p1) - for i in range(len(contents)): - assert contents[i] == p1[i] - p4 = buffer(u+"foo") - contents = from_buffer(BCharA, buffer(u+"foo")) - assert len(contents) == len(p4) - for i in range(len(contents)): - assert contents[i] == p4[i] - try: - from __builtin__ import memoryview - except ImportError: - pass - else: - contents = from_buffer(BCharA, memoryview(b"foo")) - assert len(contents) == len(p1) - for i in range(len(contents)): - assert contents[i] == p1[i] - - -def test_from_buffer_bytearray(): - a = bytearray(b"xyz") - BChar = new_primitive_type("char") - BCharP = new_pointer_type(BChar) - BCharA = new_array_type(BCharP, None) - p = from_buffer(BCharA, a) - assert typeof(p) is BCharA - assert len(p) == 3 - assert repr(p) == "<cdata 'char[]' buffer len 3 from 'bytearray' object>" - assert p[2] == b"z" - p[2] = b"." - assert a[2] == ord(".") - a[2] = ord("?") - assert p[2] == b"?" - -def test_from_buffer_more_cases(): - try: - from _cffi_backend import _testbuff - except ImportError: - py.test.skip("not for pypy") - BChar = new_primitive_type("char") - BCharP = new_pointer_type(BChar) - BCharA = new_array_type(BCharP, None) - # - def check1(bufobj, expected): - c = from_buffer(BCharA, bufobj) - assert typeof(c) is BCharA - if sys.version_info >= (3,): - expected = [bytes(c, "ascii") for c in expected] - assert list(c) == list(expected) - # - def check(methods, expected, expected_for_memoryview=None): - if sys.version_info >= (3,): - if methods <= 7: - return - if expected_for_memoryview is not None: - expected = expected_for_memoryview - class X(object): - pass - _testbuff(X, methods) - bufobj = X() - check1(bufobj, expected) - try: - from __builtin__ import buffer - bufobjb = buffer(bufobj) - except (TypeError, ImportError): - pass - else: - check1(bufobjb, expected) - try: - bufobjm = memoryview(bufobj) - except (TypeError, NameError): - pass - else: - check1(bufobjm, expected_for_memoryview or expected) - # - check(1, "RDB") - check(2, "WRB") - check(4, "CHB") - check(8, "GTB") - check(16, "ROB") - # - check(1 | 2, "RDB") - check(1 | 4, "RDB") - check(2 | 4, "CHB") - check(1 | 8, "RDB", "GTB") - check(1 | 16, "RDB", "ROB") - check(2 | 8, "WRB", "GTB") - check(2 | 16, "WRB", "ROB") - check(4 | 8, "CHB", "GTB") - check(4 | 16, "CHB", "ROB") - -def test_from_buffer_require_writable(): - BChar = new_primitive_type("char") - BCharP = new_pointer_type(BChar) - BCharA = new_array_type(BCharP, None) - p1 = from_buffer(BCharA, b"foo", False) - assert p1 == from_buffer(BCharA, b"foo", False) - py.test.raises((TypeError, BufferError), from_buffer, BCharA, b"foo", True) - ba = bytearray(b"foo") - p1 = from_buffer(BCharA, ba, True) - p1[0] = b"g" - assert ba == b"goo" - -def test_from_buffer_types(): - BInt = new_primitive_type("int") - BIntP = new_pointer_type(BInt) - BIntA = new_array_type(BIntP, None) - lst = [-12345678, 87654321, 489148] - 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 - assert len(p1) == 3 - assert p1[0] == lst[0] - assert p1[1] == lst[1] - assert p1[2] == lst[2] - with pytest.raises(IndexError): - p1[3] - with pytest.raises(IndexError): - p1[-1] - # - py.test.raises(TypeError, from_buffer, BInt, 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] - assert typeof(p2) is BIntA2 - assert len(p2) == 2 - assert p2[0] == lst[0] - assert p2[1] == lst[1] - 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) - # - BStruct = new_struct_type("foo") - complete_struct_or_union(BStruct, [('a1', BInt, -1), - ('a2', BInt, -1)]) - BStructP = new_pointer_type(BStruct) - BStructA = new_array_type(BStructP, None) - p1 = from_buffer(BStructA, bytestring2) # struct[] - assert len(p1) == 2 - assert typeof(p1) is BStructA - 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) - assert sizeof(BEmptyStruct) == 0 - BEmptyStructP = new_pointer_type(BEmptyStruct) - BEmptyStructA = new_array_type(BEmptyStructP, None) - py.test.raises(ZeroDivisionError, from_buffer, # empty[] - BEmptyStructA, bytestring) - # - BEmptyStructA5 = new_array_type(BEmptyStructP, 5) - 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) - 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") - ShortA = new_array_type(new_pointer_type(Short), None) - Char = new_primitive_type("char") - CharA = new_array_type(new_pointer_type(Char), None) - p = newp(ShortA, [-1234, -2345, -3456, -4567, -5678]) - memmove(p, p + 1, 4) - assert list(p) == [-2345, -3456, -3456, -4567, -5678] - p[2] = 999 - memmove(p + 2, p, 6) - assert list(p) == [-2345, -3456, -2345, -3456, 999] - memmove(p + 4, newp(CharA, b"\x71\x72"), 2) - if sys.byteorder == 'little': - assert list(p) == [-2345, -3456, -2345, -3456, 0x7271] - else: - assert list(p) == [-2345, -3456, -2345, -3456, 0x7172] - -def test_memmove_buffer(): - import array - Short = new_primitive_type("short") - ShortA = new_array_type(new_pointer_type(Short), None) - a = array.array('H', [10000, 20000, 30000]) - p = newp(ShortA, 5) - memmove(p, a, 6) - assert list(p) == [10000, 20000, 30000, 0, 0] - memmove(p + 1, a, 6) - assert list(p) == [10000, 10000, 20000, 30000, 0] - b = array.array('h', [-1000, -2000, -3000]) - memmove(b, a, 4) - assert b.tolist() == [10000, 20000, -3000] - assert a.tolist() == [10000, 20000, 30000] - p[0] = 999 - p[1] = 998 - p[2] = 997 - p[3] = 996 - p[4] = 995 - memmove(b, p, 2) - assert b.tolist() == [999, 20000, -3000] - memmove(b, p + 2, 4) - assert b.tolist() == [997, 996, -3000] - p[2] = -p[2] - p[3] = -p[3] - memmove(b, p + 2, 6) - assert b.tolist() == [-997, -996, 995] - -def test_memmove_readonly_readwrite(): - SignedChar = new_primitive_type("signed char") - SignedCharA = new_array_type(new_pointer_type(SignedChar), None) - p = newp(SignedCharA, 5) - memmove(p, b"abcde", 3) - assert list(p) == [ord("a"), ord("b"), ord("c"), 0, 0] - memmove(p, bytearray(b"ABCDE"), 2) - assert list(p) == [ord("A"), ord("B"), ord("c"), 0, 0] - py.test.raises((TypeError, BufferError), memmove, b"abcde", p, 3) - ba = bytearray(b"xxxxx") - memmove(dest=ba, src=p, n=3) - assert ba == bytearray(b"ABcxx") - memmove(ba, b"EFGH", 4) - assert ba == bytearray(b"EFGHx") - -def test_memmove_sign_check(): - SignedChar = new_primitive_type("signed char") - SignedCharA = new_array_type(new_pointer_type(SignedChar), None) - p = newp(SignedCharA, 5) - py.test.raises(ValueError, memmove, p, p + 1, -1) # not segfault - -def test_memmove_bad_cdata(): - BInt = new_primitive_type("int") - p = cast(BInt, 42) - py.test.raises(TypeError, memmove, p, bytearray(b'a'), 1) - py.test.raises(TypeError, memmove, bytearray(b'a'), p, 1) - -def test_dereference_null_ptr(): - BInt = new_primitive_type("int") - BIntPtr = new_pointer_type(BInt) - p = cast(BIntPtr, 0) - 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") - BStruct2 = new_struct_type("foo") # <= same name as BStruct1 - BStruct3 = new_struct_type("bar") - BStruct1Ptr = new_pointer_type(BStruct1) - BStruct2Ptr = new_pointer_type(BStruct2) - BStruct3Ptr = new_pointer_type(BStruct3) - BStruct1PtrPtr = new_pointer_type(BStruct1Ptr) - BStruct2PtrPtr = new_pointer_type(BStruct2Ptr) - BStruct3PtrPtr = new_pointer_type(BStruct3Ptr) - pp1 = newp(BStruct1PtrPtr) - pp2 = newp(BStruct2PtrPtr) - pp3 = newp(BStruct3PtrPtr) - pp1[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 *'") - 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 " - "instances)") - -def test_stdcall_function_type(): - assert FFI_CDECL == FFI_DEFAULT_ABI - try: - stdcall = FFI_STDCALL - except NameError: - stdcall = FFI_DEFAULT_ABI - BInt = new_primitive_type("int") - BFunc = new_function_type((BInt, BInt), BInt, False, stdcall) - if stdcall != FFI_DEFAULT_ABI: - assert repr(BFunc) == "<ctype 'int(__stdcall *)(int, int)'>" - else: - assert repr(BFunc) == "<ctype 'int(*)(int, int)'>" - -def test_get_common_types(): - d = {} - _get_common_types(d) - assert d['bool'] == '_Bool' - -def test_unpack(): - BChar = new_primitive_type("char") - BArray = new_array_type(new_pointer_type(BChar), 10) # char[10] - p = newp(BArray, b"abc\x00def") - p0 = p - assert unpack(p, 10) == b"abc\x00def\x00\x00\x00" - assert unpack(p+1, 5) == b"bc\x00de" - - for typename in ["wchar_t", "char16_t", "char32_t"]: - BWChar = new_primitive_type(typename) - BArray = new_array_type(new_pointer_type(BWChar), 10) # wchar_t[10] - p = newp(BArray, u"abc\x00def") - assert unpack(p, 10) == u"abc\x00def\x00\x00\x00" - - for typename, samples in [ - ("uint8_t", [0, 2**8-1]), - ("uint16_t", [0, 2**16-1]), - ("uint32_t", [0, 2**32-1]), - ("uint64_t", [0, 2**64-1]), - ("int8_t", [-2**7, 2**7-1]), - ("int16_t", [-2**15, 2**15-1]), - ("int32_t", [-2**31, 2**31-1]), - ("int64_t", [-2**63, 2**63-1]), - ("_Bool", [False, True]), - ("float", [0.0, 10.5]), - ("double", [12.34, 56.78]), - ]: - BItem = new_primitive_type(typename) - BArray = new_array_type(new_pointer_type(BItem), 10) - p = newp(BArray, samples) - result = unpack(p, len(samples)) - assert result == samples - for i in range(len(samples)): - assert result[i] == p[i] and type(result[i]) is type(p[i]) - assert (type(result[i]) is bool) == (type(samples[i]) is bool) - # - BInt = new_primitive_type("int") - py.test.raises(TypeError, unpack, p) - py.test.raises(TypeError, unpack, b"foobar", 6) - py.test.raises(TypeError, unpack, cast(BInt, 42), 1) - # - BPtr = new_pointer_type(BInt) - random_ptr = cast(BPtr, -424344) - other_ptr = cast(BPtr, 54321) - BArray = new_array_type(new_pointer_type(BPtr), None) - lst = unpack(newp(BArray, [random_ptr, other_ptr]), 2) - assert lst == [random_ptr, other_ptr] - # - BFunc = new_function_type((BInt, BInt), BInt, False) - BFuncPtr = new_pointer_type(BFunc) - lst = unpack(newp(new_array_type(BFuncPtr, None), 2), 2) - assert len(lst) == 2 - assert not lst[0] and not lst[1] - assert typeof(lst[0]) is BFunc - # - BStruct = new_struct_type("foo") - BStructPtr = new_pointer_type(BStruct) - e = py.test.raises(ValueError, unpack, cast(BStructPtr, 42), 5) - assert str(e.value) == "'foo *' points to items of unknown size" - complete_struct_or_union(BStruct, [('a1', BInt, -1), - ('a2', BInt, -1)]) - array_of_structs = newp(new_array_type(BStructPtr, None), [[4,5], [6,7]]) - lst = unpack(array_of_structs, 2) - assert typeof(lst[0]) is BStruct - assert lst[0].a1 == 4 and lst[1].a2 == 7 - # - py.test.raises(RuntimeError, unpack, cast(new_pointer_type(BChar), 0), 0) - py.test.raises(RuntimeError, unpack, cast(new_pointer_type(BChar), 0), 10) - # - py.test.raises(ValueError, unpack, p0, -1) - py.test.raises(ValueError, unpack, p, -1) - -def test_cdata_dir(): - BInt = new_primitive_type("int") - p = cast(BInt, 42) - check_dir(p, []) - p = newp(new_array_type(new_pointer_type(BInt), None), 5) - check_dir(p, []) - BStruct = new_struct_type("foo") - p = cast(new_pointer_type(BStruct), 0) - check_dir(p, []) # opaque - complete_struct_or_union(BStruct, [('a2', BInt, -1), - ('a1', BInt, -1)]) - check_dir(p, ['a1', 'a2']) # always sorted - p = newp(new_pointer_type(BStruct), None) - check_dir(p, ['a1', 'a2']) - check_dir(p[0], ['a1', 'a2']) - pp = newp(new_pointer_type(new_pointer_type(BStruct)), p) - check_dir(pp, []) - check_dir(pp[0], ['a1', 'a2']) - check_dir(pp[0][0], ['a1', 'a2']) - -def test_char_pointer_conversion(): - import warnings - assert __version__.startswith("1."), ( - "the warning will be an error if we ever release cffi 2.x") - BCharP = new_pointer_type(new_primitive_type("char")) - BIntP = new_pointer_type(new_primitive_type("int")) - BVoidP = new_pointer_type(new_void_type()) - BUCharP = new_pointer_type(new_primitive_type("unsigned char")) - z1 = cast(BCharP, 0) - z2 = cast(BIntP, 0) - z3 = cast(BVoidP, 0) - z4 = cast(BUCharP, 0) - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - newp(new_pointer_type(BIntP), z1) # warn - assert len(w) == 1 - newp(new_pointer_type(BVoidP), z1) # fine - assert len(w) == 1 - newp(new_pointer_type(BCharP), z2) # warn - assert len(w) == 2 - newp(new_pointer_type(BVoidP), z2) # fine - assert len(w) == 2 - newp(new_pointer_type(BCharP), z3) # fine - assert len(w) == 2 - newp(new_pointer_type(BIntP), z3) # fine - assert len(w) == 2 - newp(new_pointer_type(BCharP), z4) # fine (ignore signedness here) - assert len(w) == 2 - newp(new_pointer_type(BUCharP), z1) # fine (ignore signedness here) - assert len(w) == 2 - newp(new_pointer_type(BUCharP), z3) # fine - assert len(w) == 2 - # check that the warnings are associated with lines in this file - assert w[1].lineno == w[0].lineno + 4 - -def test_primitive_comparison(): - def assert_eq(a, b): - assert (a == b) is True - assert (b == a) is True - assert (a != b) is False - assert (b != a) is False - assert (a < b) is False - assert (a <= b) is True - assert (a > b) is False - assert (a >= b) is True - assert (b < a) is False - assert (b <= a) is True - assert (b > a) is False - assert (b >= a) is True - assert hash(a) == hash(b) - def assert_lt(a, b, check_hash=True): - assert (a == b) is False - assert (b == a) is False - assert (a != b) is True - assert (b != a) is True - assert (a < b) is True - assert (a <= b) is True - assert (a > b) is False - assert (a >= b) is False - assert (b < a) is False - assert (b <= a) is False - assert (b > a) is True - assert (b >= a) is True - if check_hash: - assert hash(a) != hash(b) # (or at least, it is unlikely) - def assert_gt(a, b, check_hash=True): - assert_lt(b, a, check_hash) - def assert_ne(a, b): - assert (a == b) is False - assert (b == a) is False - assert (a != b) is True - assert (b != a) is True - if strict_compare: - 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: - assert_lt(b, a) - assert_eq(5, 5) - assert_lt(3, 5) - assert_ne('5', 5) - # - t1 = new_primitive_type("char") - t2 = new_primitive_type("int") - t3 = new_primitive_type("unsigned char") - t4 = new_primitive_type("unsigned int") - t5 = new_primitive_type("float") - t6 = new_primitive_type("double") - assert_eq(cast(t1, 65), b'A') - assert_lt(cast(t1, 64), b'\x99') - assert_gt(cast(t1, 200), b'A') - assert_ne(cast(t1, 65), 65) - assert_eq(cast(t2, -25), -25) - assert_lt(cast(t2, -25), -24) - assert_gt(cast(t2, -25), -26) - assert_eq(cast(t3, 65), 65) - assert_ne(cast(t3, 65), b'A') - assert_ne(cast(t3, 65), cast(t1, 65)) - assert_gt(cast(t4, -1), -1, check_hash=False) - assert_gt(cast(t4, -1), cast(t2, -1), check_hash=False) - assert_gt(cast(t4, -1), 99999) - assert_eq(cast(t4, -1), 256 ** size_of_int() - 1) - assert_eq(cast(t5, 3.0), 3) - assert_eq(cast(t5, 3.5), 3.5) - assert_lt(cast(t5, 3.3), 3.3) # imperfect rounding - assert_eq(cast(t6, 3.3), 3.3) - assert_eq(cast(t5, 3.5), cast(t6, 3.5)) - assert_lt(cast(t5, 3.1), cast(t6, 3.1)) # imperfect rounding - assert_eq(cast(t5, 7.0), cast(t3, 7)) - assert_lt(cast(t5, 3.1), 3.101) - assert_gt(cast(t5, 3.1), 3) - -def test_explicit_release_new(): - # release() on a ffi.new() object has no effect on CPython, but - # really releases memory on PyPy. We can't test that effect - # though, because a released cdata is not marked. - BIntP = new_pointer_type(new_primitive_type("int")) - p = newp(BIntP) - p[0] = 42 - with pytest.raises(IndexError): - p[1] - release(p) - # here, reading p[0] might give garbage or segfault... - release(p) # no effect - # - BStruct = new_struct_type("struct foo") - BStructP = new_pointer_type(BStruct) - complete_struct_or_union(BStruct, [('p', BIntP, -1)]) - pstruct = newp(BStructP) - assert pstruct.p == cast(BIntP, 0) - release(pstruct) - # here, reading pstruct.p might give garbage or segfault... - release(pstruct) # no effect - -def test_explicit_release_new_contextmgr(): - BIntP = new_pointer_type(new_primitive_type("int")) - with newp(BIntP) as p: - p[0] = 42 - assert p[0] == 42 - # here, reading p[0] might give garbage or segfault... - release(p) # no effect - -def test_explicit_release_badtype(): - BIntP = new_pointer_type(new_primitive_type("int")) - p = cast(BIntP, 12345) - py.test.raises(ValueError, release, p) - py.test.raises(ValueError, release, p) - BStruct = new_struct_type("struct foo") - BStructP = new_pointer_type(BStruct) - complete_struct_or_union(BStruct, [('p', BIntP, -1)]) - pstruct = newp(BStructP) - py.test.raises(ValueError, release, pstruct[0]) - -def test_explicit_release_badtype_contextmgr(): - BIntP = new_pointer_type(new_primitive_type("int")) - p = cast(BIntP, 12345) - 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")) - seen = [] - intp1 = newp(BIntP, 12345) - p1 = cast(BIntP, intp1) - p = gcp(p1, seen.append) - assert seen == [] - release(p) - assert seen == [p1] - assert p1[0] == 12345 - assert p[0] == 12345 # true so far, but might change to raise RuntimeError - release(p) # no effect - -def test_explicit_release_gc_contextmgr(): - BIntP = new_pointer_type(new_primitive_type("int")) - seen = [] - intp1 = newp(BIntP, 12345) - p1 = cast(BIntP, intp1) - p = gcp(p1, seen.append) - with p: - assert p[0] == 12345 - assert seen == [] - assert seen == [p1] - assert p1[0] == 12345 - assert p[0] == 12345 # true so far, but might change to raise RuntimeError - release(p) # no effect - -def test_explicit_release_from_buffer(): - a = bytearray(b"xyz") - BChar = new_primitive_type("char") - BCharP = new_pointer_type(BChar) - 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(): - a = bytearray(b"xyz") - BChar = new_primitive_type("char") - BCharP = new_pointer_type(BChar) - BCharA = new_array_type(BCharP, None) - p = from_buffer(BCharA, a) - 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(): - if '__pypy__' in sys.builtin_module_names: - py.test.skip("pypy's bytearray are never locked") - a = bytearray(b"xyz") - BChar = new_primitive_type("char") - BCharP = new_pointer_type(BChar) - BCharA = new_array_type(BCharP, None) - a += b't' * 10 - p = from_buffer(BCharA, a) - 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' diff --git a/c/wchar_helper.h b/c/wchar_helper.h deleted file mode 100644 index 8e6ea58..0000000 --- a/c/wchar_helper.h +++ /dev/null @@ -1,246 +0,0 @@ -/* - * wchar_t helpers - */ - -typedef uint16_t cffi_char16_t; -typedef uint32_t cffi_char32_t; - - -#if Py_UNICODE_SIZE == 2 - -/* Before Python 2.7, PyUnicode_FromWideChar is not able to convert - wchar_t values greater than 65535 into two-unicode-characters surrogates. - But even the Python 2.7 version doesn't detect wchar_t values that are - out of range(1114112), and just returns nonsense. - - From cffi 1.11 we can't use it anyway, because we need a version - with char32_t input types. -*/ -static PyObject * -_my_PyUnicode_FromChar32(const cffi_char32_t *w, Py_ssize_t size) -{ - PyObject *unicode; - register Py_ssize_t i; - Py_ssize_t alloc; - const cffi_char32_t *orig_w; - - alloc = size; - orig_w = w; - for (i = size; i > 0; i--) { - if (*w > 0xFFFF) - alloc++; - w++; - } - w = orig_w; - unicode = PyUnicode_FromUnicode(NULL, alloc); - if (!unicode) - return NULL; - - /* Copy the wchar_t data into the new object */ - { - register Py_UNICODE *u; - u = PyUnicode_AS_UNICODE(unicode); - for (i = size; i > 0; i--) { - if (*w > 0xFFFF) { - cffi_char32_t ordinal; - if (*w > 0x10FFFF) { - PyErr_Format(PyExc_ValueError, - "char32_t out of range for " - "conversion to unicode: 0x%x", (int)*w); - Py_DECREF(unicode); - return NULL; - } - ordinal = *w++; - ordinal -= 0x10000; - *u++ = 0xD800 | (ordinal >> 10); - *u++ = 0xDC00 | (ordinal & 0x3FF); - } - else - *u++ = *w++; - } - } - return unicode; -} - -static PyObject * -_my_PyUnicode_FromChar16(const cffi_char16_t *w, Py_ssize_t size) -{ - return PyUnicode_FromUnicode((const Py_UNICODE *)w, size); -} - -#else /* Py_UNICODE_SIZE == 4 */ - -static PyObject * -_my_PyUnicode_FromChar32(const cffi_char32_t *w, Py_ssize_t size) -{ - return PyUnicode_FromUnicode((const Py_UNICODE *)w, size); -} - -static PyObject * -_my_PyUnicode_FromChar16(const cffi_char16_t *w, Py_ssize_t size) -{ - /* 'size' is the length of the 'w' array */ - PyObject *result = PyUnicode_FromUnicode(NULL, size); - - if (result != NULL) { - Py_UNICODE *u_base = PyUnicode_AS_UNICODE(result); - Py_UNICODE *u = u_base; - - if (size == 1) { /* performance only */ - *u = (cffi_char32_t)*w; - } - else { - while (size > 0) { - cffi_char32_t ch = *w++; - size--; - if (0xD800 <= ch && ch <= 0xDBFF && size > 0) { - cffi_char32_t ch2 = *w; - if (0xDC00 <= ch2 && ch2 <= 0xDFFF) { - ch = (((ch & 0x3FF)<<10) | (ch2 & 0x3FF)) + 0x10000; - w++; - size--; - } - } - *u++ = ch; - } - if (PyUnicode_Resize(&result, u - u_base) < 0) { - Py_DECREF(result); - return NULL; - } - } - } - return result; -} - -#endif - - -#define IS_SURROGATE(u) (0xD800 <= (u)[0] && (u)[0] <= 0xDBFF && \ - 0xDC00 <= (u)[1] && (u)[1] <= 0xDFFF) -#define AS_SURROGATE(u) (0x10000 + (((u)[0] - 0xD800) << 10) + \ - ((u)[1] - 0xDC00)) - -static int -_my_PyUnicode_AsSingleChar16(PyObject *unicode, cffi_char16_t *result, - char *err_got) -{ - Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode); - if (PyUnicode_GET_SIZE(unicode) != 1) { - sprintf(err_got, "unicode string of length %zd", - PyUnicode_GET_SIZE(unicode)); - return -1; - } -#if Py_UNICODE_SIZE == 4 - if (((unsigned int)u[0]) > 0xFFFF) - { - sprintf(err_got, "larger-than-0xFFFF character"); - return -1; - } -#endif - *result = (cffi_char16_t)u[0]; - return 0; -} - -static int -_my_PyUnicode_AsSingleChar32(PyObject *unicode, cffi_char32_t *result, - char *err_got) -{ - Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode); - if (PyUnicode_GET_SIZE(unicode) == 1) { - *result = (cffi_char32_t)u[0]; - return 0; - } -#if Py_UNICODE_SIZE == 2 - if (PyUnicode_GET_SIZE(unicode) == 2 && IS_SURROGATE(u)) { - *result = AS_SURROGATE(u); - return 0; - } -#endif - sprintf(err_got, "unicode string of length %zd", - PyUnicode_GET_SIZE(unicode)); - return -1; -} - -static Py_ssize_t _my_PyUnicode_SizeAsChar16(PyObject *unicode) -{ - Py_ssize_t length = PyUnicode_GET_SIZE(unicode); - Py_ssize_t result = length; - -#if Py_UNICODE_SIZE == 4 - Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode); - Py_ssize_t i; - - for (i=0; i<length; i++) { - if (u[i] > 0xFFFF) - result++; - } -#endif - return result; -} - -static Py_ssize_t _my_PyUnicode_SizeAsChar32(PyObject *unicode) -{ - Py_ssize_t length = PyUnicode_GET_SIZE(unicode); - Py_ssize_t result = length; - -#if Py_UNICODE_SIZE == 2 - Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode); - Py_ssize_t i; - - for (i=0; i<length-1; i++) { - if (IS_SURROGATE(u+i)) - result--; - } -#endif - return result; -} - -static int _my_PyUnicode_AsChar16(PyObject *unicode, - cffi_char16_t *result, - Py_ssize_t resultlen) -{ - Py_ssize_t len = PyUnicode_GET_SIZE(unicode); - Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode); - Py_ssize_t i; - for (i=0; i<len; i++) { -#if Py_UNICODE_SIZE == 2 - cffi_char16_t ordinal = u[i]; -#else - cffi_char32_t ordinal = u[i]; - if (ordinal > 0xFFFF) { - if (ordinal > 0x10FFFF) { - PyErr_Format(PyExc_ValueError, - "unicode character out of range for " - "conversion to char16_t: 0x%x", (int)ordinal); - return -1; - } - ordinal -= 0x10000; - *result++ = 0xD800 | (ordinal >> 10); - *result++ = 0xDC00 | (ordinal & 0x3FF); - continue; - } -#endif - *result++ = ordinal; - } - return 0; -} - -static int _my_PyUnicode_AsChar32(PyObject *unicode, - cffi_char32_t *result, - Py_ssize_t resultlen) -{ - Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode); - Py_ssize_t i; - for (i=0; i<resultlen; i++) { - cffi_char32_t ordinal = *u; -#if Py_UNICODE_SIZE == 2 - if (IS_SURROGATE(u)) { - ordinal = AS_SURROGATE(u); - u++; - } -#endif - result[i] = ordinal; - u++; - } - return 0; -} diff --git a/c/wchar_helper_3.h b/c/wchar_helper_3.h deleted file mode 100644 index f15464e..0000000 --- a/c/wchar_helper_3.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * wchar_t helpers, version CPython >= 3.3. - * - * CPython 3.3 added support for sys.maxunicode == 0x10FFFF on all - * platforms, even ones with wchar_t limited to 2 bytes. As such, - * this code here works from the outside like wchar_helper.h in the - * case Py_UNICODE_SIZE == 4, but the implementation is very different. - */ - -typedef uint16_t cffi_char16_t; -typedef uint32_t cffi_char32_t; - - -static PyObject * -_my_PyUnicode_FromChar32(const cffi_char32_t *w, Py_ssize_t size) -{ - return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, w, size); -} - -static PyObject * -_my_PyUnicode_FromChar16(const cffi_char16_t *w, Py_ssize_t size) -{ - /* are there any surrogate pairs, and if so, how many? */ - Py_ssize_t i, count_surrogates = 0; - for (i = 0; i < size - 1; i++) { - if (0xD800 <= w[i] && w[i] <= 0xDBFF && - 0xDC00 <= w[i+1] && w[i+1] <= 0xDFFF) - count_surrogates++; - } - if (count_surrogates == 0) { - /* no, fast path */ - return PyUnicode_FromKindAndData(PyUnicode_2BYTE_KIND, w, size); - } - else - { - PyObject *result = PyUnicode_New(size - count_surrogates, 0x10FFFF); - Py_UCS4 *data; - assert(PyUnicode_KIND(result) == PyUnicode_4BYTE_KIND); - data = PyUnicode_4BYTE_DATA(result); - - for (i = 0; i < size; i++) - { - cffi_char32_t ch = w[i]; - if (0xD800 <= ch && ch <= 0xDBFF && i < size - 1) { - cffi_char32_t ch2 = w[i + 1]; - if (0xDC00 <= ch2 && ch2 <= 0xDFFF) { - ch = (((ch & 0x3FF)<<10) | (ch2 & 0x3FF)) + 0x10000; - i++; - } - } - *data++ = ch; - } - return result; - } -} - -static int -_my_PyUnicode_AsSingleChar16(PyObject *unicode, cffi_char16_t *result, - char *err_got) -{ - cffi_char32_t ch; - if (PyUnicode_GET_LENGTH(unicode) != 1) { - sprintf(err_got, "unicode string of length %zd", - PyUnicode_GET_LENGTH(unicode)); - return -1; - } - ch = PyUnicode_READ_CHAR(unicode, 0); - - if (ch > 0xFFFF) - { - sprintf(err_got, "larger-than-0xFFFF character"); - return -1; - } - *result = (cffi_char16_t)ch; - return 0; -} - -static int -_my_PyUnicode_AsSingleChar32(PyObject *unicode, cffi_char32_t *result, - char *err_got) -{ - if (PyUnicode_GET_LENGTH(unicode) != 1) { - sprintf(err_got, "unicode string of length %zd", - PyUnicode_GET_LENGTH(unicode)); - return -1; - } - *result = PyUnicode_READ_CHAR(unicode, 0); - return 0; -} - -static Py_ssize_t _my_PyUnicode_SizeAsChar16(PyObject *unicode) -{ - Py_ssize_t length = PyUnicode_GET_LENGTH(unicode); - Py_ssize_t result = length; - unsigned int kind = PyUnicode_KIND(unicode); - - if (kind == PyUnicode_4BYTE_KIND) - { - Py_UCS4 *data = PyUnicode_4BYTE_DATA(unicode); - Py_ssize_t i; - for (i = 0; i < length; i++) { - if (data[i] > 0xFFFF) - result++; - } - } - return result; -} - -static Py_ssize_t _my_PyUnicode_SizeAsChar32(PyObject *unicode) -{ - return PyUnicode_GET_LENGTH(unicode); -} - -static int _my_PyUnicode_AsChar16(PyObject *unicode, - cffi_char16_t *result, - Py_ssize_t resultlen) -{ - Py_ssize_t len = PyUnicode_GET_LENGTH(unicode); - unsigned int kind = PyUnicode_KIND(unicode); - void *data = PyUnicode_DATA(unicode); - Py_ssize_t i; - - for (i = 0; i < len; i++) { - cffi_char32_t ordinal = PyUnicode_READ(kind, data, i); - if (ordinal > 0xFFFF) { - if (ordinal > 0x10FFFF) { - PyErr_Format(PyExc_ValueError, - "unicode character out of range for " - "conversion to char16_t: 0x%x", (int)ordinal); - return -1; - } - ordinal -= 0x10000; - *result++ = 0xD800 | (ordinal >> 10); - *result++ = 0xDC00 | (ordinal & 0x3FF); - } - else - *result++ = ordinal; - } - return 0; -} - -static int _my_PyUnicode_AsChar32(PyObject *unicode, - cffi_char32_t *result, - Py_ssize_t resultlen) -{ - if (PyUnicode_AsUCS4(unicode, (Py_UCS4 *)result, resultlen, 0) == NULL) - return -1; - return 0; -} diff --git a/cffi/Android.bp b/cffi/Android.bp deleted file mode 100644 index 2664d94..0000000 --- a/cffi/Android.bp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2019 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "external_python_cffi_license" - // to get the below license kinds: - // SPDX-license-identifier-MIT - default_applicable_licenses: ["external_python_cffi_license"], -} - -python_library { - name: "py-cffi", - host_supported: true, - srcs: [ - "*.py", - ], - data: [ - ":py-cffi-headers", - ], - libs: [ - "py-cffi-backend", - "py-cffi-backend-libffi", - "py-pycparser", - ], - pkg_path: "cffi", -} - -filegroup { - name: "py-cffi-headers", - srcs: [ - "*.h", - ], -} diff --git a/cffi/__init__.py b/cffi/__init__.py deleted file mode 100644 index 82a9618..0000000 --- a/cffi/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -__all__ = ['FFI', 'VerificationError', 'VerificationMissing', 'CDefError', - 'FFIError'] - -from .api import FFI -from .error import CDefError, FFIError, VerificationError, VerificationMissing -from .error import PkgConfigError - -__version__ = "1.15.0" -__version_info__ = (1, 15, 0) - -# The verifier module file names are based on the CRC32 of a string that -# contains the following version number. It may be older than __version__ -# if nothing is clearly incompatible. -__version_verifier_modules__ = "0.8.6" diff --git a/cffi/_cffi_errors.h b/cffi/_cffi_errors.h deleted file mode 100644 index 158e059..0000000 --- a/cffi/_cffi_errors.h +++ /dev/null @@ -1,149 +0,0 @@ -#ifndef CFFI_MESSAGEBOX -# ifdef _MSC_VER -# define CFFI_MESSAGEBOX 1 -# else -# define CFFI_MESSAGEBOX 0 -# endif -#endif - - -#if CFFI_MESSAGEBOX -/* Windows only: logic to take the Python-CFFI embedding logic - initialization errors and display them in a background thread - with MessageBox. The idea is that if the whole program closes - as a result of this problem, then likely it is already a console - program and you can read the stderr output in the console too. - If it is not a console program, then it will likely show its own - dialog to complain, or generally not abruptly close, and for this - case the background thread should stay alive. -*/ -static void *volatile _cffi_bootstrap_text; - -static PyObject *_cffi_start_error_capture(void) -{ - PyObject *result = NULL; - PyObject *x, *m, *bi; - - if (InterlockedCompareExchangePointer(&_cffi_bootstrap_text, - (void *)1, NULL) != NULL) - return (PyObject *)1; - - m = PyImport_AddModule("_cffi_error_capture"); - if (m == NULL) - goto error; - - result = PyModule_GetDict(m); - if (result == NULL) - goto error; - -#if PY_MAJOR_VERSION >= 3 - bi = PyImport_ImportModule("builtins"); -#else - bi = PyImport_ImportModule("__builtin__"); -#endif - if (bi == NULL) - goto error; - PyDict_SetItemString(result, "__builtins__", bi); - Py_DECREF(bi); - - x = PyRun_String( - "import sys\n" - "class FileLike:\n" - " def write(self, x):\n" - " try:\n" - " of.write(x)\n" - " except: pass\n" - " self.buf += x\n" - " def flush(self):\n" - " pass\n" - "fl = FileLike()\n" - "fl.buf = ''\n" - "of = sys.stderr\n" - "sys.stderr = fl\n" - "def done():\n" - " sys.stderr = of\n" - " return fl.buf\n", /* make sure the returned value stays alive */ - Py_file_input, - result, result); - Py_XDECREF(x); - - error: - if (PyErr_Occurred()) - { - PyErr_WriteUnraisable(Py_None); - PyErr_Clear(); - } - return result; -} - -#pragma comment(lib, "user32.lib") - -static DWORD WINAPI _cffi_bootstrap_dialog(LPVOID ignored) -{ - Sleep(666); /* may be interrupted if the whole process is closing */ -#if PY_MAJOR_VERSION >= 3 - MessageBoxW(NULL, (wchar_t *)_cffi_bootstrap_text, - L"Python-CFFI error", - MB_OK | MB_ICONERROR); -#else - MessageBoxA(NULL, (char *)_cffi_bootstrap_text, - "Python-CFFI error", - MB_OK | MB_ICONERROR); -#endif - _cffi_bootstrap_text = NULL; - return 0; -} - -static void _cffi_stop_error_capture(PyObject *ecap) -{ - PyObject *s; - void *text; - - if (ecap == (PyObject *)1) - return; - - if (ecap == NULL) - goto error; - - s = PyRun_String("done()", Py_eval_input, ecap, ecap); - if (s == NULL) - goto error; - - /* Show a dialog box, but in a background thread, and - never show multiple dialog boxes at once. */ -#if PY_MAJOR_VERSION >= 3 - text = PyUnicode_AsWideCharString(s, NULL); -#else - text = PyString_AsString(s); -#endif - - _cffi_bootstrap_text = text; - - if (text != NULL) - { - HANDLE h; - h = CreateThread(NULL, 0, _cffi_bootstrap_dialog, - NULL, 0, NULL); - if (h != NULL) - CloseHandle(h); - } - /* decref the string, but it should stay alive as 'fl.buf' - in the small module above. It will really be freed only if - we later get another similar error. So it's a leak of at - most one copy of the small module. That's fine for this - situation which is usually a "fatal error" anyway. */ - Py_DECREF(s); - PyErr_Clear(); - return; - - error: - _cffi_bootstrap_text = NULL; - PyErr_Clear(); -} - -#else - -static PyObject *_cffi_start_error_capture(void) { return NULL; } -static void _cffi_stop_error_capture(PyObject *ecap) { } - -#endif diff --git a/cffi/_cffi_include.h b/cffi/_cffi_include.h deleted file mode 100644 index e4c0a67..0000000 --- a/cffi/_cffi_include.h +++ /dev/null @@ -1,385 +0,0 @@ -#define _CFFI_ - -/* We try to define Py_LIMITED_API before including Python.h. - - Mess: we can only define it if Py_DEBUG, Py_TRACE_REFS and - Py_REF_DEBUG are not defined. This is a best-effort approximation: - we can learn about Py_DEBUG from pyconfig.h, but it is unclear if - the same works for the other two macros. Py_DEBUG implies them, - but not the other way around. - - The implementation is messy (issue #350): on Windows, with _MSC_VER, - we have to define Py_LIMITED_API even before including pyconfig.h. - In that case, we guess what pyconfig.h will do to the macros above, - and check our guess after the #include. - - Note that on Windows, with CPython 3.x, you need >= 3.5 and virtualenv - version >= 16.0.0. With older versions of either, you don't get a - copy of PYTHON3.DLL in the virtualenv. We can't check the version of - CPython *before* we even include pyconfig.h. ffi.set_source() puts - a ``#define _CFFI_NO_LIMITED_API'' at the start of this file if it is - running on Windows < 3.5, as an attempt at fixing it, but that's - arguably wrong because it may not be the target version of Python. - Still better than nothing I guess. As another workaround, you can - remove the definition of Py_LIMITED_API here. - - See also 'py_limited_api' in cffi/setuptools_ext.py. -*/ -#if !defined(_CFFI_USE_EMBEDDING) && !defined(Py_LIMITED_API) -# ifdef _MSC_VER -# if !defined(_DEBUG) && !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) && !defined(_CFFI_NO_LIMITED_API) -# define Py_LIMITED_API -# endif -# include <pyconfig.h> - /* sanity-check: Py_LIMITED_API will cause crashes if any of these - are also defined. Normally, the Python file PC/pyconfig.h does not - cause any of these to be defined, with the exception that _DEBUG - causes Py_DEBUG. Double-check that. */ -# ifdef Py_LIMITED_API -# if defined(Py_DEBUG) -# error "pyconfig.h unexpectedly defines Py_DEBUG, but Py_LIMITED_API is set" -# endif -# if defined(Py_TRACE_REFS) -# error "pyconfig.h unexpectedly defines Py_TRACE_REFS, but Py_LIMITED_API is set" -# endif -# if defined(Py_REF_DEBUG) -# error "pyconfig.h unexpectedly defines Py_REF_DEBUG, but Py_LIMITED_API is set" -# endif -# endif -# else -# include <pyconfig.h> -# if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) && !defined(_CFFI_NO_LIMITED_API) -# define Py_LIMITED_API -# endif -# endif -#endif - -#include <Python.h> -#ifdef __cplusplus -extern "C" { -#endif -#include <stddef.h> -#include "parse_c_type.h" - -/* this block of #ifs should be kept exactly identical between - c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py - and cffi/_cffi_include.h */ -#if defined(_MSC_VER) -# include <malloc.h> /* for alloca() */ -# if _MSC_VER < 1600 /* MSVC < 2010 */ - typedef __int8 int8_t; - typedef __int16 int16_t; - typedef __int32 int32_t; - typedef __int64 int64_t; - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; - typedef unsigned __int64 uint64_t; - typedef __int8 int_least8_t; - typedef __int16 int_least16_t; - typedef __int32 int_least32_t; - typedef __int64 int_least64_t; - typedef unsigned __int8 uint_least8_t; - typedef unsigned __int16 uint_least16_t; - typedef unsigned __int32 uint_least32_t; - typedef unsigned __int64 uint_least64_t; - typedef __int8 int_fast8_t; - typedef __int16 int_fast16_t; - typedef __int32 int_fast32_t; - typedef __int64 int_fast64_t; - typedef unsigned __int8 uint_fast8_t; - typedef unsigned __int16 uint_fast16_t; - typedef unsigned __int32 uint_fast32_t; - typedef unsigned __int64 uint_fast64_t; - typedef __int64 intmax_t; - typedef unsigned __int64 uintmax_t; -# else -# include <stdint.h> -# endif -# if _MSC_VER < 1800 /* MSVC < 2013 */ -# ifndef __cplusplus - typedef unsigned char _Bool; -# endif -# endif -#else -# include <stdint.h> -# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) -# include <alloca.h> -# endif -#endif - -#ifdef __GNUC__ -# define _CFFI_UNUSED_FN __attribute__((unused)) -#else -# define _CFFI_UNUSED_FN /* nothing */ -#endif - -#ifdef __cplusplus -# ifndef _Bool - typedef bool _Bool; /* semi-hackish: C++ has no _Bool; bool is builtin */ -# endif -#endif - -/********** CPython-specific section **********/ -#ifndef PYPY_VERSION - - -#if PY_MAJOR_VERSION >= 3 -# define PyInt_FromLong PyLong_FromLong -#endif - -#define _cffi_from_c_double PyFloat_FromDouble -#define _cffi_from_c_float PyFloat_FromDouble -#define _cffi_from_c_long PyInt_FromLong -#define _cffi_from_c_ulong PyLong_FromUnsignedLong -#define _cffi_from_c_longlong PyLong_FromLongLong -#define _cffi_from_c_ulonglong PyLong_FromUnsignedLongLong -#define _cffi_from_c__Bool PyBool_FromLong - -#define _cffi_to_c_double PyFloat_AsDouble -#define _cffi_to_c_float PyFloat_AsDouble - -#define _cffi_from_c_int(x, type) \ - (((type)-1) > 0 ? /* unsigned */ \ - (sizeof(type) < sizeof(long) ? \ - PyInt_FromLong((long)x) : \ - sizeof(type) == sizeof(long) ? \ - PyLong_FromUnsignedLong((unsigned long)x) : \ - PyLong_FromUnsignedLongLong((unsigned long long)x)) : \ - (sizeof(type) <= sizeof(long) ? \ - PyInt_FromLong((long)x) : \ - PyLong_FromLongLong((long long)x))) - -#define _cffi_to_c_int(o, type) \ - ((type)( \ - sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o) \ - : (type)_cffi_to_c_i8(o)) : \ - sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o) \ - : (type)_cffi_to_c_i16(o)) : \ - sizeof(type) == 4 ? (((type)-1) > 0 ? (type)_cffi_to_c_u32(o) \ - : (type)_cffi_to_c_i32(o)) : \ - sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o) \ - : (type)_cffi_to_c_i64(o)) : \ - (Py_FatalError("unsupported size for type " #type), (type)0))) - -#define _cffi_to_c_i8 \ - ((int(*)(PyObject *))_cffi_exports[1]) -#define _cffi_to_c_u8 \ - ((int(*)(PyObject *))_cffi_exports[2]) -#define _cffi_to_c_i16 \ - ((int(*)(PyObject *))_cffi_exports[3]) -#define _cffi_to_c_u16 \ - ((int(*)(PyObject *))_cffi_exports[4]) -#define _cffi_to_c_i32 \ - ((int(*)(PyObject *))_cffi_exports[5]) -#define _cffi_to_c_u32 \ - ((unsigned int(*)(PyObject *))_cffi_exports[6]) -#define _cffi_to_c_i64 \ - ((long long(*)(PyObject *))_cffi_exports[7]) -#define _cffi_to_c_u64 \ - ((unsigned long long(*)(PyObject *))_cffi_exports[8]) -#define _cffi_to_c_char \ - ((int(*)(PyObject *))_cffi_exports[9]) -#define _cffi_from_c_pointer \ - ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[10]) -#define _cffi_to_c_pointer \ - ((char *(*)(PyObject *, struct _cffi_ctypedescr *))_cffi_exports[11]) -#define _cffi_get_struct_layout \ - not used any more -#define _cffi_restore_errno \ - ((void(*)(void))_cffi_exports[13]) -#define _cffi_save_errno \ - ((void(*)(void))_cffi_exports[14]) -#define _cffi_from_c_char \ - ((PyObject *(*)(char))_cffi_exports[15]) -#define _cffi_from_c_deref \ - ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[16]) -#define _cffi_to_c \ - ((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[17]) -#define _cffi_from_c_struct \ - ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[18]) -#define _cffi_to_c_wchar_t \ - ((_cffi_wchar_t(*)(PyObject *))_cffi_exports[19]) -#define _cffi_from_c_wchar_t \ - ((PyObject *(*)(_cffi_wchar_t))_cffi_exports[20]) -#define _cffi_to_c_long_double \ - ((long double(*)(PyObject *))_cffi_exports[21]) -#define _cffi_to_c__Bool \ - ((_Bool(*)(PyObject *))_cffi_exports[22]) -#define _cffi_prepare_pointer_call_argument \ - ((Py_ssize_t(*)(struct _cffi_ctypedescr *, \ - PyObject *, char **))_cffi_exports[23]) -#define _cffi_convert_array_from_object \ - ((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[24]) -#define _CFFI_CPIDX 25 -#define _cffi_call_python \ - ((void(*)(struct _cffi_externpy_s *, char *))_cffi_exports[_CFFI_CPIDX]) -#define _cffi_to_c_wchar3216_t \ - ((int(*)(PyObject *))_cffi_exports[26]) -#define _cffi_from_c_wchar3216_t \ - ((PyObject *(*)(int))_cffi_exports[27]) -#define _CFFI_NUM_EXPORTS 28 - -struct _cffi_ctypedescr; - -static void *_cffi_exports[_CFFI_NUM_EXPORTS]; - -#define _cffi_type(index) ( \ - assert((((uintptr_t)_cffi_types[index]) & 1) == 0), \ - (struct _cffi_ctypedescr *)_cffi_types[index]) - -static PyObject *_cffi_init(const char *module_name, Py_ssize_t version, - const struct _cffi_type_context_s *ctx) -{ - PyObject *module, *o_arg, *new_module; - void *raw[] = { - (void *)module_name, - (void *)version, - (void *)_cffi_exports, - (void *)ctx, - }; - - module = PyImport_ImportModule("_cffi_backend"); - if (module == NULL) - goto failure; - - o_arg = PyLong_FromVoidPtr((void *)raw); - if (o_arg == NULL) - goto failure; - - new_module = PyObject_CallMethod( - module, (char *)"_init_cffi_1_0_external_module", (char *)"O", o_arg); - - Py_DECREF(o_arg); - Py_DECREF(module); - return new_module; - - failure: - Py_XDECREF(module); - return NULL; -} - - -#ifdef HAVE_WCHAR_H -typedef wchar_t _cffi_wchar_t; -#else -typedef uint16_t _cffi_wchar_t; /* same random pick as _cffi_backend.c */ -#endif - -_CFFI_UNUSED_FN static uint16_t _cffi_to_c_char16_t(PyObject *o) -{ - if (sizeof(_cffi_wchar_t) == 2) - return (uint16_t)_cffi_to_c_wchar_t(o); - else - return (uint16_t)_cffi_to_c_wchar3216_t(o); -} - -_CFFI_UNUSED_FN static PyObject *_cffi_from_c_char16_t(uint16_t x) -{ - if (sizeof(_cffi_wchar_t) == 2) - return _cffi_from_c_wchar_t((_cffi_wchar_t)x); - else - return _cffi_from_c_wchar3216_t((int)x); -} - -_CFFI_UNUSED_FN static int _cffi_to_c_char32_t(PyObject *o) -{ - if (sizeof(_cffi_wchar_t) == 4) - return (int)_cffi_to_c_wchar_t(o); - else - return (int)_cffi_to_c_wchar3216_t(o); -} - -_CFFI_UNUSED_FN static PyObject *_cffi_from_c_char32_t(unsigned int x) -{ - if (sizeof(_cffi_wchar_t) == 4) - return _cffi_from_c_wchar_t((_cffi_wchar_t)x); - else - return _cffi_from_c_wchar3216_t((int)x); -} - -union _cffi_union_alignment_u { - unsigned char m_char; - unsigned short m_short; - unsigned int m_int; - unsigned long m_long; - unsigned long long m_longlong; - float m_float; - double m_double; - long double m_longdouble; -}; - -struct _cffi_freeme_s { - struct _cffi_freeme_s *next; - union _cffi_union_alignment_u alignment; -}; - -_CFFI_UNUSED_FN static int -_cffi_convert_array_argument(struct _cffi_ctypedescr *ctptr, PyObject *arg, - char **output_data, Py_ssize_t datasize, - struct _cffi_freeme_s **freeme) -{ - char *p; - if (datasize < 0) - return -1; - - p = *output_data; - if (p == NULL) { - struct _cffi_freeme_s *fp = (struct _cffi_freeme_s *)PyObject_Malloc( - offsetof(struct _cffi_freeme_s, alignment) + (size_t)datasize); - if (fp == NULL) - return -1; - fp->next = *freeme; - *freeme = fp; - p = *output_data = (char *)&fp->alignment; - } - memset((void *)p, 0, (size_t)datasize); - return _cffi_convert_array_from_object(p, ctptr, arg); -} - -_CFFI_UNUSED_FN static void -_cffi_free_array_arguments(struct _cffi_freeme_s *freeme) -{ - do { - void *p = (void *)freeme; - freeme = freeme->next; - PyObject_Free(p); - } while (freeme != NULL); -} - -/********** end CPython-specific section **********/ -#else -_CFFI_UNUSED_FN -static void (*_cffi_call_python_org)(struct _cffi_externpy_s *, char *); -# define _cffi_call_python _cffi_call_python_org -#endif - - -#define _cffi_array_len(array) (sizeof(array) / sizeof((array)[0])) - -#define _cffi_prim_int(size, sign) \ - ((size) == 1 ? ((sign) ? _CFFI_PRIM_INT8 : _CFFI_PRIM_UINT8) : \ - (size) == 2 ? ((sign) ? _CFFI_PRIM_INT16 : _CFFI_PRIM_UINT16) : \ - (size) == 4 ? ((sign) ? _CFFI_PRIM_INT32 : _CFFI_PRIM_UINT32) : \ - (size) == 8 ? ((sign) ? _CFFI_PRIM_INT64 : _CFFI_PRIM_UINT64) : \ - _CFFI__UNKNOWN_PRIM) - -#define _cffi_prim_float(size) \ - ((size) == sizeof(float) ? _CFFI_PRIM_FLOAT : \ - (size) == sizeof(double) ? _CFFI_PRIM_DOUBLE : \ - (size) == sizeof(long double) ? _CFFI__UNKNOWN_LONG_DOUBLE : \ - _CFFI__UNKNOWN_FLOAT_PRIM) - -#define _cffi_check_int(got, got_nonpos, expected) \ - ((got_nonpos) == (expected <= 0) && \ - (got) == (unsigned long long)expected) - -#ifdef MS_WIN32 -# define _cffi_stdcall __stdcall -#else -# define _cffi_stdcall /* nothing */ -#endif - -#ifdef __cplusplus -} -#endif diff --git a/cffi/_embedding.h b/cffi/_embedding.h deleted file mode 100644 index e863d85..0000000 --- a/cffi/_embedding.h +++ /dev/null @@ -1,527 +0,0 @@ - -/***** Support code for embedding *****/ - -#ifdef __cplusplus -extern "C" { -#endif - - -#if defined(_WIN32) -# define CFFI_DLLEXPORT __declspec(dllexport) -#elif defined(__GNUC__) -# define CFFI_DLLEXPORT __attribute__((visibility("default"))) -#else -# define CFFI_DLLEXPORT /* nothing */ -#endif - - -/* There are two global variables of type _cffi_call_python_fnptr: - - * _cffi_call_python, which we declare just below, is the one called - by ``extern "Python"`` implementations. - - * _cffi_call_python_org, which on CPython is actually part of the - _cffi_exports[] array, is the function pointer copied from - _cffi_backend. - - After initialization is complete, both are equal. However, the - first one remains equal to &_cffi_start_and_call_python until the - very end of initialization, when we are (or should be) sure that - concurrent threads also see a completely initialized world, and - only then is it changed. -*/ -#undef _cffi_call_python -typedef void (*_cffi_call_python_fnptr)(struct _cffi_externpy_s *, char *); -static void _cffi_start_and_call_python(struct _cffi_externpy_s *, char *); -static _cffi_call_python_fnptr _cffi_call_python = &_cffi_start_and_call_python; - - -#ifndef _MSC_VER - /* --- Assuming a GCC not infinitely old --- */ -# define cffi_compare_and_swap(l,o,n) __sync_bool_compare_and_swap(l,o,n) -# define cffi_write_barrier() __sync_synchronize() -# if !defined(__amd64__) && !defined(__x86_64__) && \ - !defined(__i386__) && !defined(__i386) -# define cffi_read_barrier() __sync_synchronize() -# else -# define cffi_read_barrier() (void)0 -# endif -#else - /* --- Windows threads version --- */ -# include <Windows.h> -# define cffi_compare_and_swap(l,o,n) \ - (InterlockedCompareExchangePointer(l,n,o) == (o)) -# define cffi_write_barrier() InterlockedCompareExchange(&_cffi_dummy,0,0) -# define cffi_read_barrier() (void)0 -static volatile LONG _cffi_dummy; -#endif - -#ifdef WITH_THREAD -# ifndef _MSC_VER -# include <pthread.h> - static pthread_mutex_t _cffi_embed_startup_lock; -# else - static CRITICAL_SECTION _cffi_embed_startup_lock; -# endif - static char _cffi_embed_startup_lock_ready = 0; -#endif - -static void _cffi_acquire_reentrant_mutex(void) -{ - static void *volatile lock = NULL; - - while (!cffi_compare_and_swap(&lock, NULL, (void *)1)) { - /* should ideally do a spin loop instruction here, but - hard to do it portably and doesn't really matter I - think: pthread_mutex_init() should be very fast, and - this is only run at start-up anyway. */ - } - -#ifdef WITH_THREAD - if (!_cffi_embed_startup_lock_ready) { -# ifndef _MSC_VER - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&_cffi_embed_startup_lock, &attr); -# else - InitializeCriticalSection(&_cffi_embed_startup_lock); -# endif - _cffi_embed_startup_lock_ready = 1; - } -#endif - - while (!cffi_compare_and_swap(&lock, (void *)1, NULL)) - ; - -#ifndef _MSC_VER - pthread_mutex_lock(&_cffi_embed_startup_lock); -#else - EnterCriticalSection(&_cffi_embed_startup_lock); -#endif -} - -static void _cffi_release_reentrant_mutex(void) -{ -#ifndef _MSC_VER - pthread_mutex_unlock(&_cffi_embed_startup_lock); -#else - LeaveCriticalSection(&_cffi_embed_startup_lock); -#endif -} - - -/********** CPython-specific section **********/ -#ifndef PYPY_VERSION - -#include "_cffi_errors.h" - - -#define _cffi_call_python_org _cffi_exports[_CFFI_CPIDX] - -PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(void); /* forward */ - -static void _cffi_py_initialize(void) -{ - /* XXX use initsigs=0, which "skips initialization registration of - signal handlers, which might be useful when Python is - embedded" according to the Python docs. But review and think - if it should be a user-controllable setting. - - XXX we should also give a way to write errors to a buffer - instead of to stderr. - - XXX if importing 'site' fails, CPython (any version) calls - exit(). Should we try to work around this behavior here? - */ - Py_InitializeEx(0); -} - -static int _cffi_initialize_python(void) -{ - /* This initializes Python, imports _cffi_backend, and then the - present .dll/.so is set up as a CPython C extension module. - */ - int result; - PyGILState_STATE state; - PyObject *pycode=NULL, *global_dict=NULL, *x; - PyObject *builtins; - - state = PyGILState_Ensure(); - - /* Call the initxxx() function from the present module. It will - create and initialize us as a CPython extension module, instead - of letting the startup Python code do it---it might reimport - the same .dll/.so and get maybe confused on some platforms. - It might also have troubles locating the .dll/.so again for all - I know. - */ - (void)_CFFI_PYTHON_STARTUP_FUNC(); - if (PyErr_Occurred()) - goto error; - - /* Now run the Python code provided to ffi.embedding_init_code(). - */ - pycode = Py_CompileString(_CFFI_PYTHON_STARTUP_CODE, - "<init code for '" _CFFI_MODULE_NAME "'>", - Py_file_input); - if (pycode == NULL) - goto error; - global_dict = PyDict_New(); - if (global_dict == NULL) - goto error; - builtins = PyEval_GetBuiltins(); - if (builtins == NULL) - goto error; - if (PyDict_SetItemString(global_dict, "__builtins__", builtins) < 0) - goto error; - x = PyEval_EvalCode( -#if PY_MAJOR_VERSION < 3 - (PyCodeObject *) -#endif - pycode, global_dict, global_dict); - if (x == NULL) - goto error; - Py_DECREF(x); - - /* Done! Now if we've been called from - _cffi_start_and_call_python() in an ``extern "Python"``, we can - only hope that the Python code did correctly set up the - corresponding @ffi.def_extern() function. Otherwise, the - general logic of ``extern "Python"`` functions (inside the - _cffi_backend module) will find that the reference is still - missing and print an error. - */ - result = 0; - done: - Py_XDECREF(pycode); - Py_XDECREF(global_dict); - PyGILState_Release(state); - return result; - - error:; - { - /* Print as much information as potentially useful. - Debugging load-time failures with embedding is not fun - */ - PyObject *ecap; - PyObject *exception, *v, *tb, *f, *modules, *mod; - PyErr_Fetch(&exception, &v, &tb); - ecap = _cffi_start_error_capture(); - f = PySys_GetObject((char *)"stderr"); - if (f != NULL && f != Py_None) { - PyFile_WriteString( - "Failed to initialize the Python-CFFI embedding logic:\n\n", f); - } - - if (exception != NULL) { - PyErr_NormalizeException(&exception, &v, &tb); - PyErr_Display(exception, v, tb); - } - Py_XDECREF(exception); - Py_XDECREF(v); - Py_XDECREF(tb); - - if (f != NULL && f != Py_None) { - PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME - "\ncompiled with cffi version: 1.15.0" - "\n_cffi_backend module: ", f); - modules = PyImport_GetModuleDict(); - mod = PyDict_GetItemString(modules, "_cffi_backend"); - if (mod == NULL) { - PyFile_WriteString("not loaded", f); - } - else { - v = PyObject_GetAttrString(mod, "__file__"); - PyFile_WriteObject(v, f, 0); - Py_XDECREF(v); - } - PyFile_WriteString("\nsys.path: ", f); - PyFile_WriteObject(PySys_GetObject((char *)"path"), f, 0); - PyFile_WriteString("\n\n", f); - } - _cffi_stop_error_capture(ecap); - } - result = -1; - goto done; -} - -#if PY_VERSION_HEX < 0x03080000 -PyAPI_DATA(char *) _PyParser_TokenNames[]; /* from CPython */ -#endif - -static int _cffi_carefully_make_gil(void) -{ - /* This does the basic initialization of Python. It can be called - completely concurrently from unrelated threads. It assumes - that we don't hold the GIL before (if it exists), and we don't - hold it afterwards. - - (What it really does used to be completely different in Python 2 - and Python 3, with the Python 2 solution avoiding the spin-lock - around the Py_InitializeEx() call. However, after recent changes - to CPython 2.7 (issue #358) it no longer works. So we use the - Python 3 solution everywhere.) - - This initializes Python by calling Py_InitializeEx(). - Important: this must not be called concurrently at all. - So we use a global variable as a simple spin lock. This global - variable must be from 'libpythonX.Y.so', not from this - cffi-based extension module, because it must be shared from - different cffi-based extension modules. - - In Python < 3.8, we choose - _PyParser_TokenNames[0] as a completely arbitrary pointer value - that is never written to. The default is to point to the - string "ENDMARKER". We change it temporarily to point to the - next character in that string. (Yes, I know it's REALLY - obscure.) - - In Python >= 3.8, this string array is no longer writable, so - instead we pick PyCapsuleType.tp_version_tag. We can't change - Python < 3.8 because someone might use a mixture of cffi - embedded modules, some of which were compiled before this file - changed. - */ - -#ifdef WITH_THREAD -# if PY_VERSION_HEX < 0x03080000 - char *volatile *lock = (char *volatile *)_PyParser_TokenNames; - char *old_value, *locked_value; - - while (1) { /* spin loop */ - old_value = *lock; - locked_value = old_value + 1; - if (old_value[0] == 'E') { - assert(old_value[1] == 'N'); - if (cffi_compare_and_swap(lock, old_value, locked_value)) - break; - } - else { - assert(old_value[0] == 'N'); - /* should ideally do a spin loop instruction here, but - hard to do it portably and doesn't really matter I - think: PyEval_InitThreads() should be very fast, and - this is only run at start-up anyway. */ - } - } -# else - int volatile *lock = (int volatile *)&PyCapsule_Type.tp_version_tag; - int old_value, locked_value; - assert(!(PyCapsule_Type.tp_flags & Py_TPFLAGS_HAVE_VERSION_TAG)); - - while (1) { /* spin loop */ - old_value = *lock; - locked_value = -42; - if (old_value == 0) { - if (cffi_compare_and_swap(lock, old_value, locked_value)) - break; - } - else { - assert(old_value == locked_value); - /* should ideally do a spin loop instruction here, but - hard to do it portably and doesn't really matter I - think: PyEval_InitThreads() should be very fast, and - this is only run at start-up anyway. */ - } - } -# endif -#endif - - /* call Py_InitializeEx() */ - if (!Py_IsInitialized()) { - _cffi_py_initialize(); -#if PY_VERSION_HEX < 0x03070000 - PyEval_InitThreads(); -#endif - PyEval_SaveThread(); /* release the GIL */ - /* the returned tstate must be the one that has been stored into the - autoTLSkey by _PyGILState_Init() called from Py_Initialize(). */ - } - else { -#if PY_VERSION_HEX < 0x03070000 - /* PyEval_InitThreads() is always a no-op from CPython 3.7 */ - PyGILState_STATE state = PyGILState_Ensure(); - PyEval_InitThreads(); - PyGILState_Release(state); -#endif - } - -#ifdef WITH_THREAD - /* release the lock */ - while (!cffi_compare_and_swap(lock, locked_value, old_value)) - ; -#endif - - return 0; -} - -/********** end CPython-specific section **********/ - - -#else - - -/********** PyPy-specific section **********/ - -PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(const void *[]); /* forward */ - -static struct _cffi_pypy_init_s { - const char *name; - void *func; /* function pointer */ - const char *code; -} _cffi_pypy_init = { - _CFFI_MODULE_NAME, - _CFFI_PYTHON_STARTUP_FUNC, - _CFFI_PYTHON_STARTUP_CODE, -}; - -extern int pypy_carefully_make_gil(const char *); -extern int pypy_init_embedded_cffi_module(int, struct _cffi_pypy_init_s *); - -static int _cffi_carefully_make_gil(void) -{ - return pypy_carefully_make_gil(_CFFI_MODULE_NAME); -} - -static int _cffi_initialize_python(void) -{ - return pypy_init_embedded_cffi_module(0xB011, &_cffi_pypy_init); -} - -/********** end PyPy-specific section **********/ - - -#endif - - -#ifdef __GNUC__ -__attribute__((noinline)) -#endif -static _cffi_call_python_fnptr _cffi_start_python(void) -{ - /* Delicate logic to initialize Python. This function can be - called multiple times concurrently, e.g. when the process calls - its first ``extern "Python"`` functions in multiple threads at - once. It can also be called recursively, in which case we must - ignore it. We also have to consider what occurs if several - different cffi-based extensions reach this code in parallel - threads---it is a different copy of the code, then, and we - can't have any shared global variable unless it comes from - 'libpythonX.Y.so'. - - Idea: - - * _cffi_carefully_make_gil(): "carefully" call - PyEval_InitThreads() (possibly with Py_InitializeEx() first). - - * then we use a (local) custom lock to make sure that a call to this - cffi-based extension will wait if another call to the *same* - extension is running the initialization in another thread. - It is reentrant, so that a recursive call will not block, but - only one from a different thread. - - * then we grab the GIL and (Python 2) we call Py_InitializeEx(). - At this point, concurrent calls to Py_InitializeEx() are not - possible: we have the GIL. - - * do the rest of the specific initialization, which may - temporarily release the GIL but not the custom lock. - Only release the custom lock when we are done. - */ - static char called = 0; - - if (_cffi_carefully_make_gil() != 0) - return NULL; - - _cffi_acquire_reentrant_mutex(); - - /* Here the GIL exists, but we don't have it. We're only protected - from concurrency by the reentrant mutex. */ - - /* This file only initializes the embedded module once, the first - time this is called, even if there are subinterpreters. */ - if (!called) { - called = 1; /* invoke _cffi_initialize_python() only once, - but don't set '_cffi_call_python' right now, - otherwise concurrent threads won't call - this function at all (we need them to wait) */ - if (_cffi_initialize_python() == 0) { - /* now initialization is finished. Switch to the fast-path. */ - - /* We would like nobody to see the new value of - '_cffi_call_python' without also seeing the rest of the - data initialized. However, this is not possible. But - the new value of '_cffi_call_python' is the function - 'cffi_call_python()' from _cffi_backend. So: */ - cffi_write_barrier(); - /* ^^^ we put a write barrier here, and a corresponding - read barrier at the start of cffi_call_python(). This - ensures that after that read barrier, we see everything - done here before the write barrier. - */ - - assert(_cffi_call_python_org != NULL); - _cffi_call_python = (_cffi_call_python_fnptr)_cffi_call_python_org; - } - else { - /* initialization failed. Reset this to NULL, even if it was - already set to some other value. Future calls to - _cffi_start_python() are still forced to occur, and will - always return NULL from now on. */ - _cffi_call_python_org = NULL; - } - } - - _cffi_release_reentrant_mutex(); - - return (_cffi_call_python_fnptr)_cffi_call_python_org; -} - -static -void _cffi_start_and_call_python(struct _cffi_externpy_s *externpy, char *args) -{ - _cffi_call_python_fnptr fnptr; - int current_err = errno; -#ifdef _MSC_VER - int current_lasterr = GetLastError(); -#endif - fnptr = _cffi_start_python(); - if (fnptr == NULL) { - fprintf(stderr, "function %s() called, but initialization code " - "failed. Returning 0.\n", externpy->name); - memset(args, 0, externpy->size_of_result); - } -#ifdef _MSC_VER - SetLastError(current_lasterr); -#endif - errno = current_err; - - if (fnptr != NULL) - fnptr(externpy, args); -} - - -/* The cffi_start_python() function makes sure Python is initialized - and our cffi module is set up. It can be called manually from the - user C code. The same effect is obtained automatically from any - dll-exported ``extern "Python"`` function. This function returns - -1 if initialization failed, 0 if all is OK. */ -_CFFI_UNUSED_FN -static int cffi_start_python(void) -{ - if (_cffi_call_python == &_cffi_start_and_call_python) { - if (_cffi_start_python() == NULL) - return -1; - } - cffi_read_barrier(); - return 0; -} - -#undef cffi_compare_and_swap -#undef cffi_write_barrier -#undef cffi_read_barrier - -#ifdef __cplusplus -} -#endif diff --git a/cffi/api.py b/cffi/api.py deleted file mode 100644 index 999a8ae..0000000 --- a/cffi/api.py +++ /dev/null @@ -1,965 +0,0 @@ -import sys, types -from .lock import allocate_lock -from .error import CDefError -from . import model - -try: - callable -except NameError: - # Python 3.1 - from collections import Callable - callable = lambda x: isinstance(x, Callable) - -try: - basestring -except NameError: - # Python 3.x - basestring = str - -_unspecified = object() - - - -class FFI(object): - r''' - The main top-level class that you instantiate once, or once per module. - - Example usage: - - ffi = FFI() - ffi.cdef(""" - int printf(const char *, ...); - """) - - C = ffi.dlopen(None) # standard library - -or- - C = ffi.verify() # use a C compiler: verify the decl above is right - - C.printf("hello, %s!\n", ffi.new("char[]", "world")) - ''' - - def __init__(self, backend=None): - """Create an FFI instance. The 'backend' argument is used to - select a non-default backend, mostly for tests. - """ - if backend is None: - # You need PyPy (>= 2.0 beta), or a CPython (>= 2.6) with - # _cffi_backend.so compiled. - import _cffi_backend as backend - from . import __version__ - if backend.__version__ != __version__: - # bad version! Try to be as explicit as possible. - if hasattr(backend, '__file__'): - # CPython - raise Exception("Version mismatch: this is the 'cffi' package version %s, located in %r. When we import the top-level '_cffi_backend' extension module, we get version %s, located in %r. The two versions should be equal; check your installation." % ( - __version__, __file__, - backend.__version__, backend.__file__)) - else: - # PyPy - raise Exception("Version mismatch: this is the 'cffi' package version %s, located in %r. This interpreter comes with a built-in '_cffi_backend' module, which is version %s. The two versions should be equal; check your installation." % ( - __version__, __file__, backend.__version__)) - # (If you insist you can also try to pass the option - # 'backend=backend_ctypes.CTypesBackend()', but don't - # rely on it! It's probably not going to work well.) - - from . import cparser - self._backend = backend - self._lock = allocate_lock() - self._parser = cparser.Parser() - self._cached_btypes = {} - self._parsed_types = types.ModuleType('parsed_types').__dict__ - self._new_types = types.ModuleType('new_types').__dict__ - self._function_caches = [] - self._libraries = [] - self._cdefsources = [] - self._included_ffis = [] - self._windows_unicode = None - self._init_once_cache = {} - self._cdef_version = None - self._embedding = None - self._typecache = model.get_typecache(backend) - if hasattr(backend, 'set_ffi'): - backend.set_ffi(self) - for name in list(backend.__dict__): - if name.startswith('RTLD_'): - setattr(self, name, getattr(backend, name)) - # - with self._lock: - self.BVoidP = self._get_cached_btype(model.voidp_type) - self.BCharA = self._get_cached_btype(model.char_array_type) - if isinstance(backend, types.ModuleType): - # _cffi_backend: attach these constants to the class - if not hasattr(FFI, 'NULL'): - FFI.NULL = self.cast(self.BVoidP, 0) - FFI.CData, FFI.CType = backend._get_types() - else: - # ctypes backend: attach these constants to the instance - self.NULL = self.cast(self.BVoidP, 0) - self.CData, self.CType = backend._get_types() - self.buffer = backend.buffer - - def cdef(self, csource, override=False, packed=False, pack=None): - """Parse the given C source. This registers all declared functions, - types, and global variables. The functions and global variables can - then be accessed via either 'ffi.dlopen()' or 'ffi.verify()'. - The types can be used in 'ffi.new()' and other functions. - If 'packed' is specified as True, all structs declared inside this - cdef are packed, i.e. laid out without any field alignment at all. - Alternatively, 'pack' can be a small integer, and requests for - alignment greater than that are ignored (pack=1 is equivalent to - packed=True). - """ - self._cdef(csource, override=override, packed=packed, pack=pack) - - def embedding_api(self, csource, packed=False, pack=None): - self._cdef(csource, packed=packed, pack=pack, dllexport=True) - if self._embedding is None: - self._embedding = '' - - def _cdef(self, csource, override=False, **options): - if not isinstance(csource, str): # unicode, on Python 2 - if not isinstance(csource, basestring): - raise TypeError("cdef() argument must be a string") - csource = csource.encode('ascii') - with self._lock: - self._cdef_version = object() - self._parser.parse(csource, override=override, **options) - self._cdefsources.append(csource) - if override: - for cache in self._function_caches: - cache.clear() - finishlist = self._parser._recomplete - if finishlist: - self._parser._recomplete = [] - for tp in finishlist: - tp.finish_backend_type(self, finishlist) - - def dlopen(self, name, flags=0): - """Load and return a dynamic library identified by 'name'. - The standard C library can be loaded by passing None. - Note that functions and types declared by 'ffi.cdef()' are not - linked to a particular library, just like C headers; in the - library we only look for the actual (untyped) symbols. - """ - if not (isinstance(name, basestring) or - name is None or - isinstance(name, self.CData)): - raise TypeError("dlopen(name): name must be a file name, None, " - "or an already-opened 'void *' handle") - with self._lock: - lib, function_cache = _make_ffi_library(self, name, flags) - self._function_caches.append(function_cache) - self._libraries.append(lib) - return lib - - def dlclose(self, lib): - """Close a library obtained with ffi.dlopen(). After this call, - access to functions or variables from the library will fail - (possibly with a segmentation fault). - """ - type(lib).__cffi_close__(lib) - - def _typeof_locked(self, cdecl): - # call me with the lock! - key = cdecl - if key in self._parsed_types: - return self._parsed_types[key] - # - if not isinstance(cdecl, str): # unicode, on Python 2 - cdecl = cdecl.encode('ascii') - # - type = self._parser.parse_type(cdecl) - really_a_function_type = type.is_raw_function - if really_a_function_type: - type = type.as_function_pointer() - btype = self._get_cached_btype(type) - result = btype, really_a_function_type - self._parsed_types[key] = result - return result - - def _typeof(self, cdecl, consider_function_as_funcptr=False): - # string -> ctype object - try: - result = self._parsed_types[cdecl] - except KeyError: - with self._lock: - result = self._typeof_locked(cdecl) - # - btype, really_a_function_type = result - if really_a_function_type and not consider_function_as_funcptr: - raise CDefError("the type %r is a function type, not a " - "pointer-to-function type" % (cdecl,)) - return btype - - def typeof(self, cdecl): - """Parse the C type given as a string and return the - corresponding <ctype> object. - It can also be used on 'cdata' instance to get its C type. - """ - if isinstance(cdecl, basestring): - return self._typeof(cdecl) - if isinstance(cdecl, self.CData): - return self._backend.typeof(cdecl) - if isinstance(cdecl, types.BuiltinFunctionType): - res = _builtin_function_type(cdecl) - if res is not None: - return res - if (isinstance(cdecl, types.FunctionType) - and hasattr(cdecl, '_cffi_base_type')): - with self._lock: - return self._get_cached_btype(cdecl._cffi_base_type) - raise TypeError(type(cdecl)) - - def sizeof(self, cdecl): - """Return the size in bytes of the argument. It can be a - string naming a C type, or a 'cdata' instance. - """ - if isinstance(cdecl, basestring): - BType = self._typeof(cdecl) - return self._backend.sizeof(BType) - else: - return self._backend.sizeof(cdecl) - - def alignof(self, cdecl): - """Return the natural alignment size in bytes of the C type - given as a string. - """ - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - return self._backend.alignof(cdecl) - - def offsetof(self, cdecl, *fields_or_indexes): - """Return the offset of the named field inside the given - structure or array, which must be given as a C type name. - You can give several field names in case of nested structures. - You can also give numeric values which correspond to array - items, in case of an array type. - """ - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - return self._typeoffsetof(cdecl, *fields_or_indexes)[1] - - def new(self, cdecl, init=None): - """Allocate an instance according to the specified C type and - return a pointer to it. The specified C type must be either a - pointer or an array: ``new('X *')`` allocates an X and returns - a pointer to it, whereas ``new('X[n]')`` allocates an array of - n X'es and returns an array referencing it (which works - mostly like a pointer, like in C). You can also use - ``new('X[]', n)`` to allocate an array of a non-constant - length n. - - The memory is initialized following the rules of declaring a - global variable in C: by default it is zero-initialized, but - an explicit initializer can be given which can be used to - fill all or part of the memory. - - When the returned <cdata> object goes out of scope, the memory - is freed. In other words the returned <cdata> object has - ownership of the value of type 'cdecl' that it points to. This - means that the raw data can be used as long as this object is - kept alive, but must not be used for a longer time. Be careful - about that when copying the pointer to the memory somewhere - else, e.g. into another structure. - """ - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - return self._backend.newp(cdecl, init) - - def new_allocator(self, alloc=None, free=None, - should_clear_after_alloc=True): - """Return a new allocator, i.e. a function that behaves like ffi.new() - but uses the provided low-level 'alloc' and 'free' functions. - - 'alloc' is called with the size as argument. If it returns NULL, a - MemoryError is raised. 'free' is called with the result of 'alloc' - as argument. Both can be either Python function or directly C - functions. If 'free' is None, then no free function is called. - If both 'alloc' and 'free' are None, the default is used. - - If 'should_clear_after_alloc' is set to False, then the memory - returned by 'alloc' is assumed to be already cleared (or you are - fine with garbage); otherwise CFFI will clear it. - """ - compiled_ffi = self._backend.FFI() - allocator = compiled_ffi.new_allocator(alloc, free, - should_clear_after_alloc) - def allocate(cdecl, init=None): - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - return allocator(cdecl, init) - return allocate - - def cast(self, cdecl, source): - """Similar to a C cast: returns an instance of the named C - type initialized with the given 'source'. The source is - casted between integers or pointers of any type. - """ - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - return self._backend.cast(cdecl, source) - - def string(self, cdata, maxlen=-1): - """Return a Python string (or unicode string) from the 'cdata'. - If 'cdata' is a pointer or array of characters or bytes, returns - the null-terminated string. The returned string extends until - the first null character, or at most 'maxlen' characters. If - 'cdata' is an array then 'maxlen' defaults to its length. - - If 'cdata' is a pointer or array of wchar_t, returns a unicode - string following the same rules. - - If 'cdata' is a single character or byte or a wchar_t, returns - it as a string or unicode string. - - If 'cdata' is an enum, returns the value of the enumerator as a - string, or 'NUMBER' if the value is out of range. - """ - return self._backend.string(cdata, maxlen) - - def unpack(self, cdata, length): - """Unpack an array of C data of the given length, - returning a Python string/unicode/list. - - If 'cdata' is a pointer to 'char', returns a byte string. - It does not stop at the first null. This is equivalent to: - ffi.buffer(cdata, length)[:] - - If 'cdata' is a pointer to 'wchar_t', returns a unicode string. - 'length' is measured in wchar_t's; it is not the size in bytes. - - If 'cdata' is a pointer to anything else, returns a list of - 'length' items. This is a faster equivalent to: - [cdata[i] for i in range(length)] - """ - return self._backend.unpack(cdata, length) - - #def buffer(self, cdata, size=-1): - # """Return a read-write buffer object that references the raw C data - # pointed to by the given 'cdata'. The 'cdata' must be a pointer or - # an array. Can be passed to functions expecting a buffer, or directly - # manipulated with: - # - # buf[:] get a copy of it in a regular string, or - # buf[idx] as a single character - # buf[:] = ... - # buf[idx] = ... change the content - # """ - # note that 'buffer' is a type, set on this instance by __init__ - - def from_buffer(self, cdecl, python_buffer=_unspecified, - require_writable=False): - """Return a cdata of the given type pointing to the data of the - given Python object, which must support the buffer interface. - Note that this is not meant to be used on the built-in types - str or unicode (you can build 'char[]' arrays explicitly) - but only on objects containing large quantities of raw data - in some other format, like 'array.array' or numpy arrays. - - The first argument is optional and default to 'char[]'. - """ - if python_buffer is _unspecified: - cdecl, python_buffer = self.BCharA, cdecl - elif isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - return self._backend.from_buffer(cdecl, python_buffer, - require_writable) - - def memmove(self, dest, src, n): - """ffi.memmove(dest, src, n) copies n bytes of memory from src to dest. - - Like the C function memmove(), the memory areas may overlap; - apart from that it behaves like the C function memcpy(). - - 'src' can be any cdata ptr or array, or any Python buffer object. - 'dest' can be any cdata ptr or array, or a writable Python buffer - object. The size to copy, 'n', is always measured in bytes. - - Unlike other methods, this one supports all Python buffer including - byte strings and bytearrays---but it still does not support - non-contiguous buffers. - """ - return self._backend.memmove(dest, src, n) - - def callback(self, cdecl, python_callable=None, error=None, onerror=None): - """Return a callback object or a decorator making such a - callback object. 'cdecl' must name a C function pointer type. - The callback invokes the specified 'python_callable' (which may - be provided either directly or via a decorator). Important: the - callback object must be manually kept alive for as long as the - callback may be invoked from the C level. - """ - def callback_decorator_wrap(python_callable): - if not callable(python_callable): - raise TypeError("the 'python_callable' argument " - "is not callable") - return self._backend.callback(cdecl, python_callable, - error, onerror) - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl, consider_function_as_funcptr=True) - if python_callable is None: - return callback_decorator_wrap # decorator mode - else: - return callback_decorator_wrap(python_callable) # direct mode - - def getctype(self, cdecl, replace_with=''): - """Return a string giving the C type 'cdecl', which may be itself - a string or a <ctype> object. If 'replace_with' is given, it gives - extra text to append (or insert for more complicated C types), like - a variable name, or '*' to get actually the C type 'pointer-to-cdecl'. - """ - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - replace_with = replace_with.strip() - if (replace_with.startswith('*') - and '&[' in self._backend.getcname(cdecl, '&')): - replace_with = '(%s)' % replace_with - elif replace_with and not replace_with[0] in '[(': - replace_with = ' ' + replace_with - return self._backend.getcname(cdecl, replace_with) - - def gc(self, cdata, destructor, size=0): - """Return a new cdata object that points to the same - data. Later, when this new cdata object is garbage-collected, - 'destructor(old_cdata_object)' will be called. - - The optional 'size' gives an estimate of the size, used to - trigger the garbage collection more eagerly. So far only used - on PyPy. It tells the GC that the returned object keeps alive - roughly 'size' bytes of external memory. - """ - return self._backend.gcp(cdata, destructor, size) - - def _get_cached_btype(self, type): - assert self._lock.acquire(False) is False - # call me with the lock! - try: - BType = self._cached_btypes[type] - except KeyError: - finishlist = [] - BType = type.get_cached_btype(self, finishlist) - for type in finishlist: - type.finish_backend_type(self, finishlist) - return BType - - def verify(self, source='', tmpdir=None, **kwargs): - """Verify that the current ffi signatures compile on this - machine, and return a dynamic library object. The dynamic - library can be used to call functions and access global - variables declared in this 'ffi'. The library is compiled - by the C compiler: it gives you C-level API compatibility - (including calling macros). This is unlike 'ffi.dlopen()', - which requires binary compatibility in the signatures. - """ - from .verifier import Verifier, _caller_dir_pycache - # - # If set_unicode(True) was called, insert the UNICODE and - # _UNICODE macro declarations - if self._windows_unicode: - self._apply_windows_unicode(kwargs) - # - # Set the tmpdir here, and not in Verifier.__init__: it picks - # up the caller's directory, which we want to be the caller of - # ffi.verify(), as opposed to the caller of Veritier(). - tmpdir = tmpdir or _caller_dir_pycache() - # - # Make a Verifier() and use it to load the library. - self.verifier = Verifier(self, source, tmpdir, **kwargs) - lib = self.verifier.load_library() - # - # Save the loaded library for keep-alive purposes, even - # if the caller doesn't keep it alive itself (it should). - self._libraries.append(lib) - return lib - - def _get_errno(self): - return self._backend.get_errno() - def _set_errno(self, errno): - self._backend.set_errno(errno) - errno = property(_get_errno, _set_errno, None, - "the value of 'errno' from/to the C calls") - - def getwinerror(self, code=-1): - return self._backend.getwinerror(code) - - def _pointer_to(self, ctype): - with self._lock: - return model.pointer_cache(self, ctype) - - def addressof(self, cdata, *fields_or_indexes): - """Return the address of a <cdata 'struct-or-union'>. - If 'fields_or_indexes' are given, returns the address of that - field or array item in the structure or array, recursively in - case of nested structures. - """ - try: - ctype = self._backend.typeof(cdata) - except TypeError: - if '__addressof__' in type(cdata).__dict__: - return type(cdata).__addressof__(cdata, *fields_or_indexes) - raise - if fields_or_indexes: - ctype, offset = self._typeoffsetof(ctype, *fields_or_indexes) - else: - if ctype.kind == "pointer": - raise TypeError("addressof(pointer)") - offset = 0 - ctypeptr = self._pointer_to(ctype) - return self._backend.rawaddressof(ctypeptr, cdata, offset) - - def _typeoffsetof(self, ctype, field_or_index, *fields_or_indexes): - ctype, offset = self._backend.typeoffsetof(ctype, field_or_index) - for field1 in fields_or_indexes: - ctype, offset1 = self._backend.typeoffsetof(ctype, field1, 1) - offset += offset1 - return ctype, offset - - def include(self, ffi_to_include): - """Includes the typedefs, structs, unions and enums defined - in another FFI instance. Usage is similar to a #include in C, - where a part of the program might include types defined in - another part for its own usage. Note that the include() - method has no effect on functions, constants and global - variables, which must anyway be accessed directly from the - lib object returned by the original FFI instance. - """ - if not isinstance(ffi_to_include, FFI): - raise TypeError("ffi.include() expects an argument that is also of" - " type cffi.FFI, not %r" % ( - type(ffi_to_include).__name__,)) - if ffi_to_include is self: - raise ValueError("self.include(self)") - with ffi_to_include._lock: - with self._lock: - self._parser.include(ffi_to_include._parser) - self._cdefsources.append('[') - self._cdefsources.extend(ffi_to_include._cdefsources) - self._cdefsources.append(']') - self._included_ffis.append(ffi_to_include) - - def new_handle(self, x): - return self._backend.newp_handle(self.BVoidP, x) - - def from_handle(self, x): - return self._backend.from_handle(x) - - def release(self, x): - self._backend.release(x) - - def set_unicode(self, enabled_flag): - """Windows: if 'enabled_flag' is True, enable the UNICODE and - _UNICODE defines in C, and declare the types like TCHAR and LPTCSTR - to be (pointers to) wchar_t. If 'enabled_flag' is False, - declare these types to be (pointers to) plain 8-bit characters. - This is mostly for backward compatibility; you usually want True. - """ - if self._windows_unicode is not None: - raise ValueError("set_unicode() can only be called once") - enabled_flag = bool(enabled_flag) - if enabled_flag: - self.cdef("typedef wchar_t TBYTE;" - "typedef wchar_t TCHAR;" - "typedef const wchar_t *LPCTSTR;" - "typedef const wchar_t *PCTSTR;" - "typedef wchar_t *LPTSTR;" - "typedef wchar_t *PTSTR;" - "typedef TBYTE *PTBYTE;" - "typedef TCHAR *PTCHAR;") - else: - self.cdef("typedef char TBYTE;" - "typedef char TCHAR;" - "typedef const char *LPCTSTR;" - "typedef const char *PCTSTR;" - "typedef char *LPTSTR;" - "typedef char *PTSTR;" - "typedef TBYTE *PTBYTE;" - "typedef TCHAR *PTCHAR;") - self._windows_unicode = enabled_flag - - def _apply_windows_unicode(self, kwds): - defmacros = kwds.get('define_macros', ()) - if not isinstance(defmacros, (list, tuple)): - raise TypeError("'define_macros' must be a list or tuple") - defmacros = list(defmacros) + [('UNICODE', '1'), - ('_UNICODE', '1')] - kwds['define_macros'] = defmacros - - def _apply_embedding_fix(self, kwds): - # must include an argument like "-lpython2.7" for the compiler - def ensure(key, value): - lst = kwds.setdefault(key, []) - if value not in lst: - lst.append(value) - # - if '__pypy__' in sys.builtin_module_names: - import os - if sys.platform == "win32": - # we need 'libpypy-c.lib'. Current distributions of - # pypy (>= 4.1) contain it as 'libs/python27.lib'. - pythonlib = "python{0[0]}{0[1]}".format(sys.version_info) - if hasattr(sys, 'prefix'): - ensure('library_dirs', os.path.join(sys.prefix, 'libs')) - else: - # we need 'libpypy-c.{so,dylib}', which should be by - # default located in 'sys.prefix/bin' for installed - # systems. - if sys.version_info < (3,): - pythonlib = "pypy-c" - else: - pythonlib = "pypy3-c" - if hasattr(sys, 'prefix'): - ensure('library_dirs', os.path.join(sys.prefix, 'bin')) - # On uninstalled pypy's, the libpypy-c is typically found in - # .../pypy/goal/. - if hasattr(sys, 'prefix'): - ensure('library_dirs', os.path.join(sys.prefix, 'pypy', 'goal')) - else: - if sys.platform == "win32": - template = "python%d%d" - if hasattr(sys, 'gettotalrefcount'): - template += '_d' - else: - try: - import sysconfig - except ImportError: # 2.6 - from distutils import sysconfig - template = "python%d.%d" - if sysconfig.get_config_var('DEBUG_EXT'): - template += sysconfig.get_config_var('DEBUG_EXT') - pythonlib = (template % - (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) - if hasattr(sys, 'abiflags'): - pythonlib += sys.abiflags - ensure('libraries', pythonlib) - if sys.platform == "win32": - ensure('extra_link_args', '/MANIFEST') - - def set_source(self, module_name, source, source_extension='.c', **kwds): - import os - if hasattr(self, '_assigned_source'): - raise ValueError("set_source() cannot be called several times " - "per ffi object") - if not isinstance(module_name, basestring): - raise TypeError("'module_name' must be a string") - if os.sep in module_name or (os.altsep and os.altsep in module_name): - raise ValueError("'module_name' must not contain '/': use a dotted " - "name to make a 'package.module' location") - self._assigned_source = (str(module_name), source, - source_extension, kwds) - - def set_source_pkgconfig(self, module_name, pkgconfig_libs, source, - source_extension='.c', **kwds): - from . import pkgconfig - if not isinstance(pkgconfig_libs, list): - raise TypeError("the pkgconfig_libs argument must be a list " - "of package names") - kwds2 = pkgconfig.flags_from_pkgconfig(pkgconfig_libs) - pkgconfig.merge_flags(kwds, kwds2) - self.set_source(module_name, source, source_extension, **kwds) - - def distutils_extension(self, tmpdir='build', verbose=True): - from distutils.dir_util import mkpath - from .recompiler import recompile - # - if not hasattr(self, '_assigned_source'): - if hasattr(self, 'verifier'): # fallback, 'tmpdir' ignored - return self.verifier.get_extension() - raise ValueError("set_source() must be called before" - " distutils_extension()") - module_name, source, source_extension, kwds = self._assigned_source - if source is None: - raise TypeError("distutils_extension() is only for C extension " - "modules, not for dlopen()-style pure Python " - "modules") - mkpath(tmpdir) - ext, updated = recompile(self, module_name, - source, tmpdir=tmpdir, extradir=tmpdir, - source_extension=source_extension, - call_c_compiler=False, **kwds) - if verbose: - if updated: - sys.stderr.write("regenerated: %r\n" % (ext.sources[0],)) - else: - sys.stderr.write("not modified: %r\n" % (ext.sources[0],)) - return ext - - def emit_c_code(self, filename): - from .recompiler import recompile - # - if not hasattr(self, '_assigned_source'): - raise ValueError("set_source() must be called before emit_c_code()") - module_name, source, source_extension, kwds = self._assigned_source - if source is None: - raise TypeError("emit_c_code() is only for C extension modules, " - "not for dlopen()-style pure Python modules") - recompile(self, module_name, source, - c_file=filename, call_c_compiler=False, **kwds) - - def emit_python_code(self, filename): - from .recompiler import recompile - # - if not hasattr(self, '_assigned_source'): - raise ValueError("set_source() must be called before emit_c_code()") - module_name, source, source_extension, kwds = self._assigned_source - if source is not None: - raise TypeError("emit_python_code() is only for dlopen()-style " - "pure Python modules, not for C extension modules") - recompile(self, module_name, source, - c_file=filename, call_c_compiler=False, **kwds) - - def compile(self, tmpdir='.', verbose=0, target=None, debug=None): - """The 'target' argument gives the final file name of the - compiled DLL. Use '*' to force distutils' choice, suitable for - regular CPython C API modules. Use a file name ending in '.*' - to ask for the system's default extension for dynamic libraries - (.so/.dll/.dylib). - - The default is '*' when building a non-embedded C API extension, - and (module_name + '.*') when building an embedded library. - """ - from .recompiler import recompile - # - if not hasattr(self, '_assigned_source'): - raise ValueError("set_source() must be called before compile()") - module_name, source, source_extension, kwds = self._assigned_source - return recompile(self, module_name, source, tmpdir=tmpdir, - target=target, source_extension=source_extension, - compiler_verbose=verbose, debug=debug, **kwds) - - def init_once(self, func, tag): - # Read _init_once_cache[tag], which is either (False, lock) if - # we're calling the function now in some thread, or (True, result). - # Don't call setdefault() in most cases, to avoid allocating and - # immediately freeing a lock; but still use setdefaut() to avoid - # races. - try: - x = self._init_once_cache[tag] - except KeyError: - x = self._init_once_cache.setdefault(tag, (False, allocate_lock())) - # Common case: we got (True, result), so we return the result. - if x[0]: - return x[1] - # Else, it's a lock. Acquire it to serialize the following tests. - with x[1]: - # Read again from _init_once_cache the current status. - x = self._init_once_cache[tag] - if x[0]: - return x[1] - # Call the function and store the result back. - result = func() - self._init_once_cache[tag] = (True, result) - return result - - def embedding_init_code(self, pysource): - if self._embedding: - raise ValueError("embedding_init_code() can only be called once") - # fix 'pysource' before it gets dumped into the C file: - # - remove empty lines at the beginning, so it starts at "line 1" - # - dedent, if all non-empty lines are indented - # - check for SyntaxErrors - import re - match = re.match(r'\s*\n', pysource) - if match: - pysource = pysource[match.end():] - lines = pysource.splitlines() or [''] - prefix = re.match(r'\s*', lines[0]).group() - for i in range(1, len(lines)): - line = lines[i] - if line.rstrip(): - while not line.startswith(prefix): - prefix = prefix[:-1] - i = len(prefix) - lines = [line[i:]+'\n' for line in lines] - pysource = ''.join(lines) - # - compile(pysource, "cffi_init", "exec") - # - self._embedding = pysource - - def def_extern(self, *args, **kwds): - raise ValueError("ffi.def_extern() is only available on API-mode FFI " - "objects") - - def list_types(self): - """Returns the user type names known to this FFI instance. - This returns a tuple containing three lists of names: - (typedef_names, names_of_structs, names_of_unions) - """ - typedefs = [] - structs = [] - unions = [] - for key in self._parser._declarations: - if key.startswith('typedef '): - typedefs.append(key[8:]) - elif key.startswith('struct '): - structs.append(key[7:]) - elif key.startswith('union '): - unions.append(key[6:]) - typedefs.sort() - structs.sort() - unions.sort() - return (typedefs, structs, unions) - - -def _load_backend_lib(backend, name, flags): - import os - if not isinstance(name, basestring): - if sys.platform != "win32" or name is not None: - return backend.load_library(name, flags) - name = "c" # Windows: load_library(None) fails, but this works - # on Python 2 (backward compatibility hack only) - first_error = None - if '.' in name or '/' in name or os.sep in name: - try: - return backend.load_library(name, flags) - except OSError as e: - first_error = e - import ctypes.util - path = ctypes.util.find_library(name) - if path is None: - if name == "c" and sys.platform == "win32" and sys.version_info >= (3,): - raise OSError("dlopen(None) cannot work on Windows for Python 3 " - "(see http://bugs.python.org/issue23606)") - msg = ("ctypes.util.find_library() did not manage " - "to locate a library called %r" % (name,)) - if first_error is not None: - msg = "%s. Additionally, %s" % (first_error, msg) - raise OSError(msg) - return backend.load_library(path, flags) - -def _make_ffi_library(ffi, libname, flags): - backend = ffi._backend - backendlib = _load_backend_lib(backend, libname, flags) - # - def accessor_function(name): - key = 'function ' + name - tp, _ = ffi._parser._declarations[key] - BType = ffi._get_cached_btype(tp) - value = backendlib.load_function(BType, name) - library.__dict__[name] = value - # - def accessor_variable(name): - key = 'variable ' + name - tp, _ = ffi._parser._declarations[key] - BType = ffi._get_cached_btype(tp) - read_variable = backendlib.read_variable - write_variable = backendlib.write_variable - setattr(FFILibrary, name, property( - lambda self: read_variable(BType, name), - lambda self, value: write_variable(BType, name, value))) - # - def addressof_var(name): - try: - return addr_variables[name] - except KeyError: - with ffi._lock: - if name not in addr_variables: - key = 'variable ' + name - tp, _ = ffi._parser._declarations[key] - BType = ffi._get_cached_btype(tp) - if BType.kind != 'array': - BType = model.pointer_cache(ffi, BType) - p = backendlib.load_function(BType, name) - addr_variables[name] = p - return addr_variables[name] - # - def accessor_constant(name): - raise NotImplementedError("non-integer constant '%s' cannot be " - "accessed from a dlopen() library" % (name,)) - # - def accessor_int_constant(name): - library.__dict__[name] = ffi._parser._int_constants[name] - # - accessors = {} - accessors_version = [False] - addr_variables = {} - # - def update_accessors(): - if accessors_version[0] is ffi._cdef_version: - return - # - for key, (tp, _) in ffi._parser._declarations.items(): - if not isinstance(tp, model.EnumType): - tag, name = key.split(' ', 1) - if tag == 'function': - accessors[name] = accessor_function - elif tag == 'variable': - accessors[name] = accessor_variable - elif tag == 'constant': - accessors[name] = accessor_constant - else: - for i, enumname in enumerate(tp.enumerators): - def accessor_enum(name, tp=tp, i=i): - tp.check_not_partial() - library.__dict__[name] = tp.enumvalues[i] - accessors[enumname] = accessor_enum - for name in ffi._parser._int_constants: - accessors.setdefault(name, accessor_int_constant) - accessors_version[0] = ffi._cdef_version - # - def make_accessor(name): - with ffi._lock: - if name in library.__dict__ or name in FFILibrary.__dict__: - return # added by another thread while waiting for the lock - if name not in accessors: - update_accessors() - if name not in accessors: - raise AttributeError(name) - accessors[name](name) - # - class FFILibrary(object): - def __getattr__(self, name): - make_accessor(name) - return getattr(self, name) - def __setattr__(self, name, value): - try: - property = getattr(self.__class__, name) - except AttributeError: - make_accessor(name) - setattr(self, name, value) - else: - property.__set__(self, value) - def __dir__(self): - with ffi._lock: - update_accessors() - return accessors.keys() - def __addressof__(self, name): - if name in library.__dict__: - return library.__dict__[name] - if name in FFILibrary.__dict__: - return addressof_var(name) - make_accessor(name) - if name in library.__dict__: - return library.__dict__[name] - if name in FFILibrary.__dict__: - return addressof_var(name) - raise AttributeError("cffi library has no function or " - "global variable named '%s'" % (name,)) - def __cffi_close__(self): - backendlib.close_lib() - self.__dict__.clear() - # - if isinstance(libname, basestring): - try: - if not isinstance(libname, str): # unicode, on Python 2 - libname = libname.encode('utf-8') - FFILibrary.__name__ = 'FFILibrary_%s' % libname - except UnicodeError: - pass - library = FFILibrary() - return library, library.__dict__ - -def _builtin_function_type(func): - # a hack to make at least ffi.typeof(builtin_function) work, - # if the builtin function was obtained by 'vengine_cpy'. - import sys - try: - module = sys.modules[func.__module__] - ffi = module._cffi_original_ffi - types_of_builtin_funcs = module._cffi_types_of_builtin_funcs - tp = types_of_builtin_funcs[func] - except (KeyError, AttributeError, TypeError): - return None - else: - with ffi._lock: - return ffi._get_cached_btype(tp) diff --git a/cffi/backend_ctypes.py b/cffi/backend_ctypes.py deleted file mode 100644 index e7956a7..0000000 --- a/cffi/backend_ctypes.py +++ /dev/null @@ -1,1121 +0,0 @@ -import ctypes, ctypes.util, operator, sys -from . import model - -if sys.version_info < (3,): - bytechr = chr -else: - unicode = str - long = int - xrange = range - bytechr = lambda num: bytes([num]) - -class CTypesType(type): - pass - -class CTypesData(object): - __metaclass__ = CTypesType - __slots__ = ['__weakref__'] - __name__ = '<cdata>' - - def __init__(self, *args): - raise TypeError("cannot instantiate %r" % (self.__class__,)) - - @classmethod - def _newp(cls, init): - raise TypeError("expected a pointer or array ctype, got '%s'" - % (cls._get_c_name(),)) - - @staticmethod - def _to_ctypes(value): - raise TypeError - - @classmethod - def _arg_to_ctypes(cls, *value): - try: - ctype = cls._ctype - except AttributeError: - raise TypeError("cannot create an instance of %r" % (cls,)) - if value: - res = cls._to_ctypes(*value) - if not isinstance(res, ctype): - res = cls._ctype(res) - else: - res = cls._ctype() - return res - - @classmethod - def _create_ctype_obj(cls, init): - if init is None: - return cls._arg_to_ctypes() - else: - return cls._arg_to_ctypes(init) - - @staticmethod - def _from_ctypes(ctypes_value): - raise TypeError - - @classmethod - def _get_c_name(cls, replace_with=''): - return cls._reftypename.replace(' &', replace_with) - - @classmethod - def _fix_class(cls): - cls.__name__ = 'CData<%s>' % (cls._get_c_name(),) - cls.__qualname__ = 'CData<%s>' % (cls._get_c_name(),) - cls.__module__ = 'ffi' - - def _get_own_repr(self): - raise NotImplementedError - - def _addr_repr(self, address): - if address == 0: - return 'NULL' - else: - if address < 0: - address += 1 << (8*ctypes.sizeof(ctypes.c_void_p)) - return '0x%x' % address - - def __repr__(self, c_name=None): - own = self._get_own_repr() - return '<cdata %r %s>' % (c_name or self._get_c_name(), own) - - def _convert_to_address(self, BClass): - if BClass is None: - raise TypeError("cannot convert %r to an address" % ( - self._get_c_name(),)) - else: - raise TypeError("cannot convert %r to %r" % ( - self._get_c_name(), BClass._get_c_name())) - - @classmethod - def _get_size(cls): - return ctypes.sizeof(cls._ctype) - - def _get_size_of_instance(self): - return ctypes.sizeof(self._ctype) - - @classmethod - def _cast_from(cls, source): - raise TypeError("cannot cast to %r" % (cls._get_c_name(),)) - - def _cast_to_integer(self): - return self._convert_to_address(None) - - @classmethod - def _alignment(cls): - return ctypes.alignment(cls._ctype) - - def __iter__(self): - raise TypeError("cdata %r does not support iteration" % ( - self._get_c_name()),) - - def _make_cmp(name): - cmpfunc = getattr(operator, name) - def cmp(self, other): - v_is_ptr = not isinstance(self, CTypesGenericPrimitive) - w_is_ptr = (isinstance(other, CTypesData) and - not isinstance(other, CTypesGenericPrimitive)) - if v_is_ptr and w_is_ptr: - return cmpfunc(self._convert_to_address(None), - other._convert_to_address(None)) - elif v_is_ptr or w_is_ptr: - return NotImplemented - else: - if isinstance(self, CTypesGenericPrimitive): - self = self._value - if isinstance(other, CTypesGenericPrimitive): - other = other._value - return cmpfunc(self, other) - cmp.func_name = name - return cmp - - __eq__ = _make_cmp('__eq__') - __ne__ = _make_cmp('__ne__') - __lt__ = _make_cmp('__lt__') - __le__ = _make_cmp('__le__') - __gt__ = _make_cmp('__gt__') - __ge__ = _make_cmp('__ge__') - - def __hash__(self): - return hash(self._convert_to_address(None)) - - def _to_string(self, maxlen): - raise TypeError("string(): %r" % (self,)) - - -class CTypesGenericPrimitive(CTypesData): - __slots__ = [] - - def __hash__(self): - return hash(self._value) - - def _get_own_repr(self): - return repr(self._from_ctypes(self._value)) - - -class CTypesGenericArray(CTypesData): - __slots__ = [] - - @classmethod - def _newp(cls, init): - return cls(init) - - def __iter__(self): - for i in xrange(len(self)): - yield self[i] - - def _get_own_repr(self): - return self._addr_repr(ctypes.addressof(self._blob)) - - -class CTypesGenericPtr(CTypesData): - __slots__ = ['_address', '_as_ctype_ptr'] - _automatic_casts = False - kind = "pointer" - - @classmethod - def _newp(cls, init): - return cls(init) - - @classmethod - def _cast_from(cls, source): - if source is None: - address = 0 - elif isinstance(source, CTypesData): - address = source._cast_to_integer() - elif isinstance(source, (int, long)): - address = source - else: - raise TypeError("bad type for cast to %r: %r" % - (cls, type(source).__name__)) - return cls._new_pointer_at(address) - - @classmethod - def _new_pointer_at(cls, address): - self = cls.__new__(cls) - self._address = address - self._as_ctype_ptr = ctypes.cast(address, cls._ctype) - return self - - def _get_own_repr(self): - try: - return self._addr_repr(self._address) - except AttributeError: - return '???' - - def _cast_to_integer(self): - return self._address - - def __nonzero__(self): - return bool(self._address) - __bool__ = __nonzero__ - - @classmethod - def _to_ctypes(cls, value): - if not isinstance(value, CTypesData): - raise TypeError("unexpected %s object" % type(value).__name__) - address = value._convert_to_address(cls) - return ctypes.cast(address, cls._ctype) - - @classmethod - def _from_ctypes(cls, ctypes_ptr): - address = ctypes.cast(ctypes_ptr, ctypes.c_void_p).value or 0 - return cls._new_pointer_at(address) - - @classmethod - def _initialize(cls, ctypes_ptr, value): - if value: - ctypes_ptr.contents = cls._to_ctypes(value).contents - - def _convert_to_address(self, BClass): - if (BClass in (self.__class__, None) or BClass._automatic_casts - or self._automatic_casts): - return self._address - else: - return CTypesData._convert_to_address(self, BClass) - - -class CTypesBaseStructOrUnion(CTypesData): - __slots__ = ['_blob'] - - @classmethod - def _create_ctype_obj(cls, init): - # may be overridden - raise TypeError("cannot instantiate opaque type %s" % (cls,)) - - def _get_own_repr(self): - return self._addr_repr(ctypes.addressof(self._blob)) - - @classmethod - def _offsetof(cls, fieldname): - return getattr(cls._ctype, fieldname).offset - - def _convert_to_address(self, BClass): - if getattr(BClass, '_BItem', None) is self.__class__: - return ctypes.addressof(self._blob) - else: - return CTypesData._convert_to_address(self, BClass) - - @classmethod - def _from_ctypes(cls, ctypes_struct_or_union): - self = cls.__new__(cls) - self._blob = ctypes_struct_or_union - return self - - @classmethod - def _to_ctypes(cls, value): - return value._blob - - def __repr__(self, c_name=None): - return CTypesData.__repr__(self, c_name or self._get_c_name(' &')) - - -class CTypesBackend(object): - - PRIMITIVE_TYPES = { - 'char': ctypes.c_char, - 'short': ctypes.c_short, - 'int': ctypes.c_int, - 'long': ctypes.c_long, - 'long long': ctypes.c_longlong, - 'signed char': ctypes.c_byte, - 'unsigned char': ctypes.c_ubyte, - 'unsigned short': ctypes.c_ushort, - 'unsigned int': ctypes.c_uint, - 'unsigned long': ctypes.c_ulong, - 'unsigned long long': ctypes.c_ulonglong, - 'float': ctypes.c_float, - 'double': ctypes.c_double, - '_Bool': ctypes.c_bool, - } - - for _name in ['unsigned long long', 'unsigned long', - 'unsigned int', 'unsigned short', 'unsigned char']: - _size = ctypes.sizeof(PRIMITIVE_TYPES[_name]) - PRIMITIVE_TYPES['uint%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name] - if _size == ctypes.sizeof(ctypes.c_void_p): - PRIMITIVE_TYPES['uintptr_t'] = PRIMITIVE_TYPES[_name] - if _size == ctypes.sizeof(ctypes.c_size_t): - PRIMITIVE_TYPES['size_t'] = PRIMITIVE_TYPES[_name] - - for _name in ['long long', 'long', 'int', 'short', 'signed char']: - _size = ctypes.sizeof(PRIMITIVE_TYPES[_name]) - PRIMITIVE_TYPES['int%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name] - if _size == ctypes.sizeof(ctypes.c_void_p): - PRIMITIVE_TYPES['intptr_t'] = PRIMITIVE_TYPES[_name] - PRIMITIVE_TYPES['ptrdiff_t'] = PRIMITIVE_TYPES[_name] - if _size == ctypes.sizeof(ctypes.c_size_t): - PRIMITIVE_TYPES['ssize_t'] = PRIMITIVE_TYPES[_name] - - - def __init__(self): - self.RTLD_LAZY = 0 # not supported anyway by ctypes - self.RTLD_NOW = 0 - self.RTLD_GLOBAL = ctypes.RTLD_GLOBAL - self.RTLD_LOCAL = ctypes.RTLD_LOCAL - - def set_ffi(self, ffi): - self.ffi = ffi - - def _get_types(self): - return CTypesData, CTypesType - - def load_library(self, path, flags=0): - cdll = ctypes.CDLL(path, flags) - return CTypesLibrary(self, cdll) - - def new_void_type(self): - class CTypesVoid(CTypesData): - __slots__ = [] - _reftypename = 'void &' - @staticmethod - def _from_ctypes(novalue): - return None - @staticmethod - def _to_ctypes(novalue): - if novalue is not None: - raise TypeError("None expected, got %s object" % - (type(novalue).__name__,)) - return None - CTypesVoid._fix_class() - return CTypesVoid - - def new_primitive_type(self, name): - if name == 'wchar_t': - raise NotImplementedError(name) - ctype = self.PRIMITIVE_TYPES[name] - if name == 'char': - kind = 'char' - elif name in ('float', 'double'): - kind = 'float' - else: - if name in ('signed char', 'unsigned char'): - kind = 'byte' - elif name == '_Bool': - kind = 'bool' - else: - kind = 'int' - is_signed = (ctype(-1).value == -1) - # - def _cast_source_to_int(source): - if isinstance(source, (int, long, float)): - source = int(source) - elif isinstance(source, CTypesData): - source = source._cast_to_integer() - elif isinstance(source, bytes): - source = ord(source) - elif source is None: - source = 0 - else: - raise TypeError("bad type for cast to %r: %r" % - (CTypesPrimitive, type(source).__name__)) - return source - # - kind1 = kind - class CTypesPrimitive(CTypesGenericPrimitive): - __slots__ = ['_value'] - _ctype = ctype - _reftypename = '%s &' % name - kind = kind1 - - def __init__(self, value): - self._value = value - - @staticmethod - def _create_ctype_obj(init): - if init is None: - return ctype() - return ctype(CTypesPrimitive._to_ctypes(init)) - - if kind == 'int' or kind == 'byte': - @classmethod - def _cast_from(cls, source): - source = _cast_source_to_int(source) - source = ctype(source).value # cast within range - return cls(source) - def __int__(self): - return self._value - - if kind == 'bool': - @classmethod - def _cast_from(cls, source): - if not isinstance(source, (int, long, float)): - source = _cast_source_to_int(source) - return cls(bool(source)) - def __int__(self): - return int(self._value) - - if kind == 'char': - @classmethod - def _cast_from(cls, source): - source = _cast_source_to_int(source) - source = bytechr(source & 0xFF) - return cls(source) - def __int__(self): - return ord(self._value) - - if kind == 'float': - @classmethod - def _cast_from(cls, source): - if isinstance(source, float): - pass - elif isinstance(source, CTypesGenericPrimitive): - if hasattr(source, '__float__'): - source = float(source) - else: - source = int(source) - else: - source = _cast_source_to_int(source) - source = ctype(source).value # fix precision - return cls(source) - def __int__(self): - return int(self._value) - def __float__(self): - return self._value - - _cast_to_integer = __int__ - - if kind == 'int' or kind == 'byte' or kind == 'bool': - @staticmethod - def _to_ctypes(x): - if not isinstance(x, (int, long)): - if isinstance(x, CTypesData): - x = int(x) - else: - raise TypeError("integer expected, got %s" % - type(x).__name__) - if ctype(x).value != x: - if not is_signed and x < 0: - raise OverflowError("%s: negative integer" % name) - else: - raise OverflowError("%s: integer out of bounds" - % name) - return x - - if kind == 'char': - @staticmethod - def _to_ctypes(x): - if isinstance(x, bytes) and len(x) == 1: - return x - if isinstance(x, CTypesPrimitive): # <CData <char>> - return x._value - raise TypeError("character expected, got %s" % - type(x).__name__) - def __nonzero__(self): - return ord(self._value) != 0 - else: - def __nonzero__(self): - return self._value != 0 - __bool__ = __nonzero__ - - if kind == 'float': - @staticmethod - def _to_ctypes(x): - if not isinstance(x, (int, long, float, CTypesData)): - raise TypeError("float expected, got %s" % - type(x).__name__) - return ctype(x).value - - @staticmethod - def _from_ctypes(value): - return getattr(value, 'value', value) - - @staticmethod - def _initialize(blob, init): - blob.value = CTypesPrimitive._to_ctypes(init) - - if kind == 'char': - def _to_string(self, maxlen): - return self._value - if kind == 'byte': - def _to_string(self, maxlen): - return chr(self._value & 0xff) - # - CTypesPrimitive._fix_class() - return CTypesPrimitive - - def new_pointer_type(self, BItem): - getbtype = self.ffi._get_cached_btype - if BItem is getbtype(model.PrimitiveType('char')): - kind = 'charp' - elif BItem in (getbtype(model.PrimitiveType('signed char')), - getbtype(model.PrimitiveType('unsigned char'))): - kind = 'bytep' - elif BItem is getbtype(model.void_type): - kind = 'voidp' - else: - kind = 'generic' - # - class CTypesPtr(CTypesGenericPtr): - __slots__ = ['_own'] - if kind == 'charp': - __slots__ += ['__as_strbuf'] - _BItem = BItem - if hasattr(BItem, '_ctype'): - _ctype = ctypes.POINTER(BItem._ctype) - _bitem_size = ctypes.sizeof(BItem._ctype) - else: - _ctype = ctypes.c_void_p - if issubclass(BItem, CTypesGenericArray): - _reftypename = BItem._get_c_name('(* &)') - else: - _reftypename = BItem._get_c_name(' * &') - - def __init__(self, init): - ctypeobj = BItem._create_ctype_obj(init) - if kind == 'charp': - self.__as_strbuf = ctypes.create_string_buffer( - ctypeobj.value + b'\x00') - self._as_ctype_ptr = ctypes.cast( - self.__as_strbuf, self._ctype) - else: - self._as_ctype_ptr = ctypes.pointer(ctypeobj) - self._address = ctypes.cast(self._as_ctype_ptr, - ctypes.c_void_p).value - self._own = True - - def __add__(self, other): - if isinstance(other, (int, long)): - return self._new_pointer_at(self._address + - other * self._bitem_size) - else: - return NotImplemented - - def __sub__(self, other): - if isinstance(other, (int, long)): - return self._new_pointer_at(self._address - - other * self._bitem_size) - elif type(self) is type(other): - return (self._address - other._address) // self._bitem_size - else: - return NotImplemented - - def __getitem__(self, index): - if getattr(self, '_own', False) and index != 0: - raise IndexError - return BItem._from_ctypes(self._as_ctype_ptr[index]) - - def __setitem__(self, index, value): - self._as_ctype_ptr[index] = BItem._to_ctypes(value) - - if kind == 'charp' or kind == 'voidp': - @classmethod - def _arg_to_ctypes(cls, *value): - if value and isinstance(value[0], bytes): - return ctypes.c_char_p(value[0]) - else: - return super(CTypesPtr, cls)._arg_to_ctypes(*value) - - if kind == 'charp' or kind == 'bytep': - def _to_string(self, maxlen): - if maxlen < 0: - maxlen = sys.maxsize - p = ctypes.cast(self._as_ctype_ptr, - ctypes.POINTER(ctypes.c_char)) - n = 0 - while n < maxlen and p[n] != b'\x00': - n += 1 - return b''.join([p[i] for i in range(n)]) - - def _get_own_repr(self): - if getattr(self, '_own', False): - return 'owning %d bytes' % ( - ctypes.sizeof(self._as_ctype_ptr.contents),) - return super(CTypesPtr, self)._get_own_repr() - # - if (BItem is self.ffi._get_cached_btype(model.void_type) or - BItem is self.ffi._get_cached_btype(model.PrimitiveType('char'))): - CTypesPtr._automatic_casts = True - # - CTypesPtr._fix_class() - return CTypesPtr - - def new_array_type(self, CTypesPtr, length): - if length is None: - brackets = ' &[]' - else: - brackets = ' &[%d]' % length - BItem = CTypesPtr._BItem - getbtype = self.ffi._get_cached_btype - if BItem is getbtype(model.PrimitiveType('char')): - kind = 'char' - elif BItem in (getbtype(model.PrimitiveType('signed char')), - getbtype(model.PrimitiveType('unsigned char'))): - kind = 'byte' - else: - kind = 'generic' - # - class CTypesArray(CTypesGenericArray): - __slots__ = ['_blob', '_own'] - if length is not None: - _ctype = BItem._ctype * length - else: - __slots__.append('_ctype') - _reftypename = BItem._get_c_name(brackets) - _declared_length = length - _CTPtr = CTypesPtr - - def __init__(self, init): - if length is None: - if isinstance(init, (int, long)): - len1 = init - init = None - elif kind == 'char' and isinstance(init, bytes): - len1 = len(init) + 1 # extra null - else: - init = tuple(init) - len1 = len(init) - self._ctype = BItem._ctype * len1 - self._blob = self._ctype() - self._own = True - if init is not None: - self._initialize(self._blob, init) - - @staticmethod - def _initialize(blob, init): - if isinstance(init, bytes): - init = [init[i:i+1] for i in range(len(init))] - else: - if isinstance(init, CTypesGenericArray): - if (len(init) != len(blob) or - not isinstance(init, CTypesArray)): - raise TypeError("length/type mismatch: %s" % (init,)) - init = tuple(init) - if len(init) > len(blob): - raise IndexError("too many initializers") - addr = ctypes.cast(blob, ctypes.c_void_p).value - PTR = ctypes.POINTER(BItem._ctype) - itemsize = ctypes.sizeof(BItem._ctype) - for i, value in enumerate(init): - p = ctypes.cast(addr + i * itemsize, PTR) - BItem._initialize(p.contents, value) - - def __len__(self): - return len(self._blob) - - def __getitem__(self, index): - if not (0 <= index < len(self._blob)): - raise IndexError - return BItem._from_ctypes(self._blob[index]) - - def __setitem__(self, index, value): - if not (0 <= index < len(self._blob)): - raise IndexError - self._blob[index] = BItem._to_ctypes(value) - - if kind == 'char' or kind == 'byte': - def _to_string(self, maxlen): - if maxlen < 0: - maxlen = len(self._blob) - p = ctypes.cast(self._blob, - ctypes.POINTER(ctypes.c_char)) - n = 0 - while n < maxlen and p[n] != b'\x00': - n += 1 - return b''.join([p[i] for i in range(n)]) - - def _get_own_repr(self): - if getattr(self, '_own', False): - return 'owning %d bytes' % (ctypes.sizeof(self._blob),) - return super(CTypesArray, self)._get_own_repr() - - def _convert_to_address(self, BClass): - if BClass in (CTypesPtr, None) or BClass._automatic_casts: - return ctypes.addressof(self._blob) - else: - return CTypesData._convert_to_address(self, BClass) - - @staticmethod - def _from_ctypes(ctypes_array): - self = CTypesArray.__new__(CTypesArray) - self._blob = ctypes_array - return self - - @staticmethod - def _arg_to_ctypes(value): - return CTypesPtr._arg_to_ctypes(value) - - def __add__(self, other): - if isinstance(other, (int, long)): - return CTypesPtr._new_pointer_at( - ctypes.addressof(self._blob) + - other * ctypes.sizeof(BItem._ctype)) - else: - return NotImplemented - - @classmethod - def _cast_from(cls, source): - raise NotImplementedError("casting to %r" % ( - cls._get_c_name(),)) - # - CTypesArray._fix_class() - return CTypesArray - - def _new_struct_or_union(self, kind, name, base_ctypes_class): - # - class struct_or_union(base_ctypes_class): - pass - struct_or_union.__name__ = '%s_%s' % (kind, name) - kind1 = kind - # - class CTypesStructOrUnion(CTypesBaseStructOrUnion): - __slots__ = ['_blob'] - _ctype = struct_or_union - _reftypename = '%s &' % (name,) - _kind = kind = kind1 - # - CTypesStructOrUnion._fix_class() - return CTypesStructOrUnion - - def new_struct_type(self, name): - return self._new_struct_or_union('struct', name, ctypes.Structure) - - def new_union_type(self, name): - return self._new_struct_or_union('union', name, ctypes.Union) - - def complete_struct_or_union(self, CTypesStructOrUnion, fields, tp, - totalsize=-1, totalalignment=-1, sflags=0, - pack=0): - if totalsize >= 0 or totalalignment >= 0: - raise NotImplementedError("the ctypes backend of CFFI does not support " - "structures completed by verify(); please " - "compile and install the _cffi_backend module.") - struct_or_union = CTypesStructOrUnion._ctype - fnames = [fname for (fname, BField, bitsize) in fields] - btypes = [BField for (fname, BField, bitsize) in fields] - bitfields = [bitsize for (fname, BField, bitsize) in fields] - # - bfield_types = {} - cfields = [] - for (fname, BField, bitsize) in fields: - if bitsize < 0: - cfields.append((fname, BField._ctype)) - bfield_types[fname] = BField - else: - cfields.append((fname, BField._ctype, bitsize)) - bfield_types[fname] = Ellipsis - if sflags & 8: - struct_or_union._pack_ = 1 - elif pack: - struct_or_union._pack_ = pack - struct_or_union._fields_ = cfields - CTypesStructOrUnion._bfield_types = bfield_types - # - @staticmethod - def _create_ctype_obj(init): - result = struct_or_union() - if init is not None: - initialize(result, init) - return result - CTypesStructOrUnion._create_ctype_obj = _create_ctype_obj - # - def initialize(blob, init): - if is_union: - if len(init) > 1: - raise ValueError("union initializer: %d items given, but " - "only one supported (use a dict if needed)" - % (len(init),)) - if not isinstance(init, dict): - if isinstance(init, (bytes, unicode)): - raise TypeError("union initializer: got a str") - init = tuple(init) - if len(init) > len(fnames): - raise ValueError("too many values for %s initializer" % - CTypesStructOrUnion._get_c_name()) - init = dict(zip(fnames, init)) - addr = ctypes.addressof(blob) - for fname, value in init.items(): - BField, bitsize = name2fieldtype[fname] - assert bitsize < 0, \ - "not implemented: initializer with bit fields" - offset = CTypesStructOrUnion._offsetof(fname) - PTR = ctypes.POINTER(BField._ctype) - p = ctypes.cast(addr + offset, PTR) - BField._initialize(p.contents, value) - is_union = CTypesStructOrUnion._kind == 'union' - name2fieldtype = dict(zip(fnames, zip(btypes, bitfields))) - # - for fname, BField, bitsize in fields: - if fname == '': - raise NotImplementedError("nested anonymous structs/unions") - if hasattr(CTypesStructOrUnion, fname): - raise ValueError("the field name %r conflicts in " - "the ctypes backend" % fname) - if bitsize < 0: - def getter(self, fname=fname, BField=BField, - offset=CTypesStructOrUnion._offsetof(fname), - PTR=ctypes.POINTER(BField._ctype)): - addr = ctypes.addressof(self._blob) - p = ctypes.cast(addr + offset, PTR) - return BField._from_ctypes(p.contents) - def setter(self, value, fname=fname, BField=BField): - setattr(self._blob, fname, BField._to_ctypes(value)) - # - if issubclass(BField, CTypesGenericArray): - setter = None - if BField._declared_length == 0: - def getter(self, fname=fname, BFieldPtr=BField._CTPtr, - offset=CTypesStructOrUnion._offsetof(fname), - PTR=ctypes.POINTER(BField._ctype)): - addr = ctypes.addressof(self._blob) - p = ctypes.cast(addr + offset, PTR) - return BFieldPtr._from_ctypes(p) - # - else: - def getter(self, fname=fname, BField=BField): - return BField._from_ctypes(getattr(self._blob, fname)) - def setter(self, value, fname=fname, BField=BField): - # xxx obscure workaround - value = BField._to_ctypes(value) - oldvalue = getattr(self._blob, fname) - setattr(self._blob, fname, value) - if value != getattr(self._blob, fname): - setattr(self._blob, fname, oldvalue) - raise OverflowError("value too large for bitfield") - setattr(CTypesStructOrUnion, fname, property(getter, setter)) - # - CTypesPtr = self.ffi._get_cached_btype(model.PointerType(tp)) - for fname in fnames: - if hasattr(CTypesPtr, fname): - raise ValueError("the field name %r conflicts in " - "the ctypes backend" % fname) - def getter(self, fname=fname): - return getattr(self[0], fname) - def setter(self, value, fname=fname): - setattr(self[0], fname, value) - setattr(CTypesPtr, fname, property(getter, setter)) - - def new_function_type(self, BArgs, BResult, has_varargs): - nameargs = [BArg._get_c_name() for BArg in BArgs] - if has_varargs: - nameargs.append('...') - nameargs = ', '.join(nameargs) - # - class CTypesFunctionPtr(CTypesGenericPtr): - __slots__ = ['_own_callback', '_name'] - _ctype = ctypes.CFUNCTYPE(getattr(BResult, '_ctype', None), - *[BArg._ctype for BArg in BArgs], - use_errno=True) - _reftypename = BResult._get_c_name('(* &)(%s)' % (nameargs,)) - - def __init__(self, init, error=None): - # create a callback to the Python callable init() - import traceback - assert not has_varargs, "varargs not supported for callbacks" - if getattr(BResult, '_ctype', None) is not None: - error = BResult._from_ctypes( - BResult._create_ctype_obj(error)) - else: - error = None - def callback(*args): - args2 = [] - for arg, BArg in zip(args, BArgs): - args2.append(BArg._from_ctypes(arg)) - try: - res2 = init(*args2) - res2 = BResult._to_ctypes(res2) - except: - traceback.print_exc() - res2 = error - if issubclass(BResult, CTypesGenericPtr): - if res2: - res2 = ctypes.cast(res2, ctypes.c_void_p).value - # .value: http://bugs.python.org/issue1574593 - else: - res2 = None - #print repr(res2) - return res2 - if issubclass(BResult, CTypesGenericPtr): - # The only pointers callbacks can return are void*s: - # http://bugs.python.org/issue5710 - callback_ctype = ctypes.CFUNCTYPE( - ctypes.c_void_p, - *[BArg._ctype for BArg in BArgs], - use_errno=True) - else: - callback_ctype = CTypesFunctionPtr._ctype - self._as_ctype_ptr = callback_ctype(callback) - self._address = ctypes.cast(self._as_ctype_ptr, - ctypes.c_void_p).value - self._own_callback = init - - @staticmethod - def _initialize(ctypes_ptr, value): - if value: - raise NotImplementedError("ctypes backend: not supported: " - "initializers for function pointers") - - def __repr__(self): - c_name = getattr(self, '_name', None) - if c_name: - i = self._reftypename.index('(* &)') - if self._reftypename[i-1] not in ' )*': - c_name = ' ' + c_name - c_name = self._reftypename.replace('(* &)', c_name) - return CTypesData.__repr__(self, c_name) - - def _get_own_repr(self): - if getattr(self, '_own_callback', None) is not None: - return 'calling %r' % (self._own_callback,) - return super(CTypesFunctionPtr, self)._get_own_repr() - - def __call__(self, *args): - if has_varargs: - assert len(args) >= len(BArgs) - extraargs = args[len(BArgs):] - args = args[:len(BArgs)] - else: - assert len(args) == len(BArgs) - ctypes_args = [] - for arg, BArg in zip(args, BArgs): - ctypes_args.append(BArg._arg_to_ctypes(arg)) - if has_varargs: - for i, arg in enumerate(extraargs): - if arg is None: - ctypes_args.append(ctypes.c_void_p(0)) # NULL - continue - if not isinstance(arg, CTypesData): - raise TypeError( - "argument %d passed in the variadic part " - "needs to be a cdata object (got %s)" % - (1 + len(BArgs) + i, type(arg).__name__)) - ctypes_args.append(arg._arg_to_ctypes(arg)) - result = self._as_ctype_ptr(*ctypes_args) - return BResult._from_ctypes(result) - # - CTypesFunctionPtr._fix_class() - return CTypesFunctionPtr - - def new_enum_type(self, name, enumerators, enumvalues, CTypesInt): - assert isinstance(name, str) - reverse_mapping = dict(zip(reversed(enumvalues), - reversed(enumerators))) - # - class CTypesEnum(CTypesInt): - __slots__ = [] - _reftypename = '%s &' % name - - def _get_own_repr(self): - value = self._value - try: - return '%d: %s' % (value, reverse_mapping[value]) - except KeyError: - return str(value) - - def _to_string(self, maxlen): - value = self._value - try: - return reverse_mapping[value] - except KeyError: - return str(value) - # - CTypesEnum._fix_class() - return CTypesEnum - - def get_errno(self): - return ctypes.get_errno() - - def set_errno(self, value): - ctypes.set_errno(value) - - def string(self, b, maxlen=-1): - return b._to_string(maxlen) - - def buffer(self, bptr, size=-1): - raise NotImplementedError("buffer() with ctypes backend") - - def sizeof(self, cdata_or_BType): - if isinstance(cdata_or_BType, CTypesData): - return cdata_or_BType._get_size_of_instance() - else: - assert issubclass(cdata_or_BType, CTypesData) - return cdata_or_BType._get_size() - - def alignof(self, BType): - assert issubclass(BType, CTypesData) - return BType._alignment() - - def newp(self, BType, source): - if not issubclass(BType, CTypesData): - raise TypeError - return BType._newp(source) - - def cast(self, BType, source): - return BType._cast_from(source) - - def callback(self, BType, source, error, onerror): - assert onerror is None # XXX not implemented - return BType(source, error) - - _weakref_cache_ref = None - - def gcp(self, cdata, destructor, size=0): - if self._weakref_cache_ref is None: - import weakref - class MyRef(weakref.ref): - def __eq__(self, other): - myref = self() - return self is other or ( - myref is not None and myref is other()) - def __ne__(self, other): - return not (self == other) - def __hash__(self): - try: - return self._hash - except AttributeError: - self._hash = hash(self()) - return self._hash - self._weakref_cache_ref = {}, MyRef - weak_cache, MyRef = self._weakref_cache_ref - - if destructor is None: - try: - del weak_cache[MyRef(cdata)] - except KeyError: - raise TypeError("Can remove destructor only on a object " - "previously returned by ffi.gc()") - return None - - def remove(k): - cdata, destructor = weak_cache.pop(k, (None, None)) - if destructor is not None: - destructor(cdata) - - new_cdata = self.cast(self.typeof(cdata), cdata) - assert new_cdata is not cdata - weak_cache[MyRef(new_cdata, remove)] = (cdata, destructor) - return new_cdata - - typeof = type - - def getcname(self, BType, replace_with): - return BType._get_c_name(replace_with) - - def typeoffsetof(self, BType, fieldname, num=0): - if isinstance(fieldname, str): - if num == 0 and issubclass(BType, CTypesGenericPtr): - BType = BType._BItem - if not issubclass(BType, CTypesBaseStructOrUnion): - raise TypeError("expected a struct or union ctype") - BField = BType._bfield_types[fieldname] - if BField is Ellipsis: - raise TypeError("not supported for bitfields") - return (BField, BType._offsetof(fieldname)) - elif isinstance(fieldname, (int, long)): - if issubclass(BType, CTypesGenericArray): - BType = BType._CTPtr - if not issubclass(BType, CTypesGenericPtr): - raise TypeError("expected an array or ptr ctype") - BItem = BType._BItem - offset = BItem._get_size() * fieldname - if offset > sys.maxsize: - raise OverflowError - return (BItem, offset) - else: - raise TypeError(type(fieldname)) - - def rawaddressof(self, BTypePtr, cdata, offset=None): - if isinstance(cdata, CTypesBaseStructOrUnion): - ptr = ctypes.pointer(type(cdata)._to_ctypes(cdata)) - elif isinstance(cdata, CTypesGenericPtr): - if offset is None or not issubclass(type(cdata)._BItem, - CTypesBaseStructOrUnion): - raise TypeError("unexpected cdata type") - ptr = type(cdata)._to_ctypes(cdata) - elif isinstance(cdata, CTypesGenericArray): - ptr = type(cdata)._to_ctypes(cdata) - else: - raise TypeError("expected a <cdata 'struct-or-union'>") - if offset: - ptr = ctypes.cast( - ctypes.c_void_p( - ctypes.cast(ptr, ctypes.c_void_p).value + offset), - type(ptr)) - return BTypePtr._from_ctypes(ptr) - - -class CTypesLibrary(object): - - def __init__(self, backend, cdll): - self.backend = backend - self.cdll = cdll - - def load_function(self, BType, name): - c_func = getattr(self.cdll, name) - funcobj = BType._from_ctypes(c_func) - funcobj._name = name - return funcobj - - def read_variable(self, BType, name): - try: - ctypes_obj = BType._ctype.in_dll(self.cdll, name) - except AttributeError as e: - raise NotImplementedError(e) - return BType._from_ctypes(ctypes_obj) - - def write_variable(self, BType, name, value): - new_ctypes_obj = BType._to_ctypes(value) - ctypes_obj = BType._ctype.in_dll(self.cdll, name) - ctypes.memmove(ctypes.addressof(ctypes_obj), - ctypes.addressof(new_ctypes_obj), - ctypes.sizeof(BType._ctype)) diff --git a/cffi/cffi_opcode.py b/cffi/cffi_opcode.py deleted file mode 100644 index a0df98d..0000000 --- a/cffi/cffi_opcode.py +++ /dev/null @@ -1,187 +0,0 @@ -from .error import VerificationError - -class CffiOp(object): - def __init__(self, op, arg): - self.op = op - self.arg = arg - - def as_c_expr(self): - if self.op is None: - assert isinstance(self.arg, str) - return '(_cffi_opcode_t)(%s)' % (self.arg,) - classname = CLASS_NAME[self.op] - return '_CFFI_OP(_CFFI_OP_%s, %s)' % (classname, self.arg) - - def as_python_bytes(self): - if self.op is None and self.arg.isdigit(): - value = int(self.arg) # non-negative: '-' not in self.arg - if value >= 2**31: - raise OverflowError("cannot emit %r: limited to 2**31-1" - % (self.arg,)) - return format_four_bytes(value) - if isinstance(self.arg, str): - raise VerificationError("cannot emit to Python: %r" % (self.arg,)) - return format_four_bytes((self.arg << 8) | self.op) - - def __str__(self): - classname = CLASS_NAME.get(self.op, self.op) - return '(%s %s)' % (classname, self.arg) - -def format_four_bytes(num): - return '\\x%02X\\x%02X\\x%02X\\x%02X' % ( - (num >> 24) & 0xFF, - (num >> 16) & 0xFF, - (num >> 8) & 0xFF, - (num ) & 0xFF) - -OP_PRIMITIVE = 1 -OP_POINTER = 3 -OP_ARRAY = 5 -OP_OPEN_ARRAY = 7 -OP_STRUCT_UNION = 9 -OP_ENUM = 11 -OP_FUNCTION = 13 -OP_FUNCTION_END = 15 -OP_NOOP = 17 -OP_BITFIELD = 19 -OP_TYPENAME = 21 -OP_CPYTHON_BLTN_V = 23 # varargs -OP_CPYTHON_BLTN_N = 25 # noargs -OP_CPYTHON_BLTN_O = 27 # O (i.e. a single arg) -OP_CONSTANT = 29 -OP_CONSTANT_INT = 31 -OP_GLOBAL_VAR = 33 -OP_DLOPEN_FUNC = 35 -OP_DLOPEN_CONST = 37 -OP_GLOBAL_VAR_F = 39 -OP_EXTERN_PYTHON = 41 - -PRIM_VOID = 0 -PRIM_BOOL = 1 -PRIM_CHAR = 2 -PRIM_SCHAR = 3 -PRIM_UCHAR = 4 -PRIM_SHORT = 5 -PRIM_USHORT = 6 -PRIM_INT = 7 -PRIM_UINT = 8 -PRIM_LONG = 9 -PRIM_ULONG = 10 -PRIM_LONGLONG = 11 -PRIM_ULONGLONG = 12 -PRIM_FLOAT = 13 -PRIM_DOUBLE = 14 -PRIM_LONGDOUBLE = 15 - -PRIM_WCHAR = 16 -PRIM_INT8 = 17 -PRIM_UINT8 = 18 -PRIM_INT16 = 19 -PRIM_UINT16 = 20 -PRIM_INT32 = 21 -PRIM_UINT32 = 22 -PRIM_INT64 = 23 -PRIM_UINT64 = 24 -PRIM_INTPTR = 25 -PRIM_UINTPTR = 26 -PRIM_PTRDIFF = 27 -PRIM_SIZE = 28 -PRIM_SSIZE = 29 -PRIM_INT_LEAST8 = 30 -PRIM_UINT_LEAST8 = 31 -PRIM_INT_LEAST16 = 32 -PRIM_UINT_LEAST16 = 33 -PRIM_INT_LEAST32 = 34 -PRIM_UINT_LEAST32 = 35 -PRIM_INT_LEAST64 = 36 -PRIM_UINT_LEAST64 = 37 -PRIM_INT_FAST8 = 38 -PRIM_UINT_FAST8 = 39 -PRIM_INT_FAST16 = 40 -PRIM_UINT_FAST16 = 41 -PRIM_INT_FAST32 = 42 -PRIM_UINT_FAST32 = 43 -PRIM_INT_FAST64 = 44 -PRIM_UINT_FAST64 = 45 -PRIM_INTMAX = 46 -PRIM_UINTMAX = 47 -PRIM_FLOATCOMPLEX = 48 -PRIM_DOUBLECOMPLEX = 49 -PRIM_CHAR16 = 50 -PRIM_CHAR32 = 51 - -_NUM_PRIM = 52 -_UNKNOWN_PRIM = -1 -_UNKNOWN_FLOAT_PRIM = -2 -_UNKNOWN_LONG_DOUBLE = -3 - -_IO_FILE_STRUCT = -1 - -PRIMITIVE_TO_INDEX = { - 'char': PRIM_CHAR, - 'short': PRIM_SHORT, - 'int': PRIM_INT, - 'long': PRIM_LONG, - 'long long': PRIM_LONGLONG, - 'signed char': PRIM_SCHAR, - 'unsigned char': PRIM_UCHAR, - 'unsigned short': PRIM_USHORT, - 'unsigned int': PRIM_UINT, - 'unsigned long': PRIM_ULONG, - 'unsigned long long': PRIM_ULONGLONG, - 'float': PRIM_FLOAT, - 'double': PRIM_DOUBLE, - 'long double': PRIM_LONGDOUBLE, - 'float _Complex': PRIM_FLOATCOMPLEX, - 'double _Complex': PRIM_DOUBLECOMPLEX, - '_Bool': PRIM_BOOL, - 'wchar_t': PRIM_WCHAR, - 'char16_t': PRIM_CHAR16, - 'char32_t': PRIM_CHAR32, - 'int8_t': PRIM_INT8, - 'uint8_t': PRIM_UINT8, - 'int16_t': PRIM_INT16, - 'uint16_t': PRIM_UINT16, - 'int32_t': PRIM_INT32, - 'uint32_t': PRIM_UINT32, - 'int64_t': PRIM_INT64, - 'uint64_t': PRIM_UINT64, - 'intptr_t': PRIM_INTPTR, - 'uintptr_t': PRIM_UINTPTR, - 'ptrdiff_t': PRIM_PTRDIFF, - 'size_t': PRIM_SIZE, - 'ssize_t': PRIM_SSIZE, - 'int_least8_t': PRIM_INT_LEAST8, - 'uint_least8_t': PRIM_UINT_LEAST8, - 'int_least16_t': PRIM_INT_LEAST16, - 'uint_least16_t': PRIM_UINT_LEAST16, - 'int_least32_t': PRIM_INT_LEAST32, - 'uint_least32_t': PRIM_UINT_LEAST32, - 'int_least64_t': PRIM_INT_LEAST64, - 'uint_least64_t': PRIM_UINT_LEAST64, - 'int_fast8_t': PRIM_INT_FAST8, - 'uint_fast8_t': PRIM_UINT_FAST8, - 'int_fast16_t': PRIM_INT_FAST16, - 'uint_fast16_t': PRIM_UINT_FAST16, - 'int_fast32_t': PRIM_INT_FAST32, - 'uint_fast32_t': PRIM_UINT_FAST32, - 'int_fast64_t': PRIM_INT_FAST64, - 'uint_fast64_t': PRIM_UINT_FAST64, - 'intmax_t': PRIM_INTMAX, - 'uintmax_t': PRIM_UINTMAX, - } - -F_UNION = 0x01 -F_CHECK_FIELDS = 0x02 -F_PACKED = 0x04 -F_EXTERNAL = 0x08 -F_OPAQUE = 0x10 - -G_FLAGS = dict([('_CFFI_' + _key, globals()[_key]) - for _key in ['F_UNION', 'F_CHECK_FIELDS', 'F_PACKED', - 'F_EXTERNAL', 'F_OPAQUE']]) - -CLASS_NAME = {} -for _name, _value in list(globals().items()): - if _name.startswith('OP_') and isinstance(_value, int): - CLASS_NAME[_value] = _name[3:] diff --git a/cffi/commontypes.py b/cffi/commontypes.py deleted file mode 100644 index 8ec97c7..0000000 --- a/cffi/commontypes.py +++ /dev/null @@ -1,80 +0,0 @@ -import sys -from . import model -from .error import FFIError - - -COMMON_TYPES = {} - -try: - # fetch "bool" and all simple Windows types - from _cffi_backend import _get_common_types - _get_common_types(COMMON_TYPES) -except ImportError: - pass - -COMMON_TYPES['FILE'] = model.unknown_type('FILE', '_IO_FILE') -COMMON_TYPES['bool'] = '_Bool' # in case we got ImportError above - -for _type in model.PrimitiveType.ALL_PRIMITIVE_TYPES: - if _type.endswith('_t'): - COMMON_TYPES[_type] = _type -del _type - -_CACHE = {} - -def resolve_common_type(parser, commontype): - try: - return _CACHE[commontype] - except KeyError: - cdecl = COMMON_TYPES.get(commontype, commontype) - if not isinstance(cdecl, str): - result, quals = cdecl, 0 # cdecl is already a BaseType - elif cdecl in model.PrimitiveType.ALL_PRIMITIVE_TYPES: - result, quals = model.PrimitiveType(cdecl), 0 - elif cdecl == 'set-unicode-needed': - raise FFIError("The Windows type %r is only available after " - "you call ffi.set_unicode()" % (commontype,)) - else: - if commontype == cdecl: - raise FFIError( - "Unsupported type: %r. Please look at " - "http://cffi.readthedocs.io/en/latest/cdef.html#ffi-cdef-limitations " - "and file an issue if you think this type should really " - "be supported." % (commontype,)) - result, quals = parser.parse_type_and_quals(cdecl) # recursive - - assert isinstance(result, model.BaseTypeByIdentity) - _CACHE[commontype] = result, quals - return result, quals - - -# ____________________________________________________________ -# extra types for Windows (most of them are in commontypes.c) - - -def win_common_types(): - return { - "UNICODE_STRING": model.StructType( - "_UNICODE_STRING", - ["Length", - "MaximumLength", - "Buffer"], - [model.PrimitiveType("unsigned short"), - model.PrimitiveType("unsigned short"), - model.PointerType(model.PrimitiveType("wchar_t"))], - [-1, -1, -1]), - "PUNICODE_STRING": "UNICODE_STRING *", - "PCUNICODE_STRING": "const UNICODE_STRING *", - - "TBYTE": "set-unicode-needed", - "TCHAR": "set-unicode-needed", - "LPCTSTR": "set-unicode-needed", - "PCTSTR": "set-unicode-needed", - "LPTSTR": "set-unicode-needed", - "PTSTR": "set-unicode-needed", - "PTBYTE": "set-unicode-needed", - "PTCHAR": "set-unicode-needed", - } - -if sys.platform == 'win32': - COMMON_TYPES.update(win_common_types()) diff --git a/cffi/cparser.py b/cffi/cparser.py deleted file mode 100644 index 74830e9..0000000 --- a/cffi/cparser.py +++ /dev/null @@ -1,1006 +0,0 @@ -from . import model -from .commontypes import COMMON_TYPES, resolve_common_type -from .error import FFIError, CDefError -try: - from . import _pycparser as pycparser -except ImportError: - import pycparser -import weakref, re, sys - -try: - if sys.version_info < (3,): - import thread as _thread - else: - import _thread - lock = _thread.allocate_lock() -except ImportError: - lock = None - -def _workaround_for_static_import_finders(): - # Issue #392: packaging tools like cx_Freeze can not find these - # because pycparser uses exec dynamic import. This is an obscure - # workaround. This function is never called. - import pycparser.yacctab - import pycparser.lextab - -CDEF_SOURCE_STRING = "<cdef source string>" -_r_comment = re.compile(r"/\*.*?\*/|//([^\n\\]|\\.)*?$", - re.DOTALL | re.MULTILINE) -_r_define = re.compile(r"^\s*#\s*define\s+([A-Za-z_][A-Za-z_0-9]*)" - r"\b((?:[^\n\\]|\\.)*?)$", - re.DOTALL | re.MULTILINE) -_r_line_directive = re.compile(r"^[ \t]*#[ \t]*(?:line|\d+)\b.*$", re.MULTILINE) -_r_partial_enum = re.compile(r"=\s*\.\.\.\s*[,}]|\.\.\.\s*\}") -_r_enum_dotdotdot = re.compile(r"__dotdotdot\d+__$") -_r_partial_array = re.compile(r"\[\s*\.\.\.\s*\]") -_r_words = re.compile(r"\w+|\S") -_parser_cache = None -_r_int_literal = re.compile(r"-?0?x?[0-9a-f]+[lu]*$", re.IGNORECASE) -_r_stdcall1 = re.compile(r"\b(__stdcall|WINAPI)\b") -_r_stdcall2 = re.compile(r"[(]\s*(__stdcall|WINAPI)\b") -_r_cdecl = re.compile(r"\b__cdecl\b") -_r_extern_python = re.compile(r'\bextern\s*"' - r'(Python|Python\s*\+\s*C|C\s*\+\s*Python)"\s*.') -_r_star_const_space = re.compile( # matches "* const " - r"[*]\s*((const|volatile|restrict)\b\s*)+") -_r_int_dotdotdot = re.compile(r"(\b(int|long|short|signed|unsigned|char)\s*)+" - r"\.\.\.") -_r_float_dotdotdot = re.compile(r"\b(double|float)\s*\.\.\.") - -def _get_parser(): - global _parser_cache - if _parser_cache is None: - _parser_cache = pycparser.CParser() - return _parser_cache - -def _workaround_for_old_pycparser(csource): - # Workaround for a pycparser issue (fixed between pycparser 2.10 and - # 2.14): "char*const***" gives us a wrong syntax tree, the same as - # for "char***(*const)". This means we can't tell the difference - # afterwards. But "char(*const(***))" gives us the right syntax - # tree. The issue only occurs if there are several stars in - # sequence with no parenthesis inbetween, just possibly qualifiers. - # Attempt to fix it by adding some parentheses in the source: each - # time we see "* const" or "* const *", we add an opening - # parenthesis before each star---the hard part is figuring out where - # to close them. - parts = [] - while True: - match = _r_star_const_space.search(csource) - if not match: - break - #print repr(''.join(parts)+csource), '=>', - parts.append(csource[:match.start()]) - parts.append('('); closing = ')' - parts.append(match.group()) # e.g. "* const " - endpos = match.end() - if csource.startswith('*', endpos): - parts.append('('); closing += ')' - level = 0 - i = endpos - while i < len(csource): - c = csource[i] - if c == '(': - level += 1 - elif c == ')': - if level == 0: - break - level -= 1 - elif c in ',;=': - if level == 0: - break - i += 1 - csource = csource[endpos:i] + closing + csource[i:] - #print repr(''.join(parts)+csource) - parts.append(csource) - return ''.join(parts) - -def _preprocess_extern_python(csource): - # input: `extern "Python" int foo(int);` or - # `extern "Python" { int foo(int); }` - # output: - # void __cffi_extern_python_start; - # int foo(int); - # void __cffi_extern_python_stop; - # - # input: `extern "Python+C" int foo(int);` - # output: - # void __cffi_extern_python_plus_c_start; - # int foo(int); - # void __cffi_extern_python_stop; - parts = [] - while True: - match = _r_extern_python.search(csource) - if not match: - break - endpos = match.end() - 1 - #print - #print ''.join(parts)+csource - #print '=>' - parts.append(csource[:match.start()]) - if 'C' in match.group(1): - parts.append('void __cffi_extern_python_plus_c_start; ') - else: - parts.append('void __cffi_extern_python_start; ') - if csource[endpos] == '{': - # grouping variant - closing = csource.find('}', endpos) - if closing < 0: - raise CDefError("'extern \"Python\" {': no '}' found") - if csource.find('{', endpos + 1, closing) >= 0: - raise NotImplementedError("cannot use { } inside a block " - "'extern \"Python\" { ... }'") - parts.append(csource[endpos+1:closing]) - csource = csource[closing+1:] - else: - # non-grouping variant - semicolon = csource.find(';', endpos) - if semicolon < 0: - raise CDefError("'extern \"Python\": no ';' found") - parts.append(csource[endpos:semicolon+1]) - csource = csource[semicolon+1:] - parts.append(' void __cffi_extern_python_stop;') - #print ''.join(parts)+csource - #print - parts.append(csource) - return ''.join(parts) - -def _warn_for_string_literal(csource): - if '"' not in csource: - return - for line in csource.splitlines(): - if '"' in line and not line.lstrip().startswith('#'): - import warnings - warnings.warn("String literal found in cdef() or type source. " - "String literals are ignored here, but you should " - "remove them anyway because some character sequences " - "confuse pre-parsing.") - break - -def _warn_for_non_extern_non_static_global_variable(decl): - if not decl.storage: - import warnings - warnings.warn("Global variable '%s' in cdef(): for consistency " - "with C it should have a storage class specifier " - "(usually 'extern')" % (decl.name,)) - -def _remove_line_directives(csource): - # _r_line_directive matches whole lines, without the final \n, if they - # start with '#line' with some spacing allowed, or '#NUMBER'. This - # function stores them away and replaces them with exactly the string - # '#line@N', where N is the index in the list 'line_directives'. - line_directives = [] - def replace(m): - i = len(line_directives) - line_directives.append(m.group()) - return '#line@%d' % i - csource = _r_line_directive.sub(replace, csource) - return csource, line_directives - -def _put_back_line_directives(csource, line_directives): - def replace(m): - s = m.group() - if not s.startswith('#line@'): - raise AssertionError("unexpected #line directive " - "(should have been processed and removed") - return line_directives[int(s[6:])] - return _r_line_directive.sub(replace, csource) - -def _preprocess(csource): - # First, remove the lines of the form '#line N "filename"' because - # the "filename" part could confuse the rest - csource, line_directives = _remove_line_directives(csource) - # Remove comments. NOTE: this only work because the cdef() section - # should not contain any string literals (except in line directives)! - def replace_keeping_newlines(m): - return ' ' + m.group().count('\n') * '\n' - csource = _r_comment.sub(replace_keeping_newlines, csource) - # Remove the "#define FOO x" lines - macros = {} - for match in _r_define.finditer(csource): - macroname, macrovalue = match.groups() - macrovalue = macrovalue.replace('\\\n', '').strip() - macros[macroname] = macrovalue - csource = _r_define.sub('', csource) - # - if pycparser.__version__ < '2.14': - csource = _workaround_for_old_pycparser(csource) - # - # BIG HACK: replace WINAPI or __stdcall with "volatile const". - # It doesn't make sense for the return type of a function to be - # "volatile volatile const", so we abuse it to detect __stdcall... - # Hack number 2 is that "int(volatile *fptr)();" is not valid C - # syntax, so we place the "volatile" before the opening parenthesis. - csource = _r_stdcall2.sub(' volatile volatile const(', csource) - csource = _r_stdcall1.sub(' volatile volatile const ', csource) - csource = _r_cdecl.sub(' ', csource) - # - # Replace `extern "Python"` with start/end markers - csource = _preprocess_extern_python(csource) - # - # Now there should not be any string literal left; warn if we get one - _warn_for_string_literal(csource) - # - # Replace "[...]" with "[__dotdotdotarray__]" - csource = _r_partial_array.sub('[__dotdotdotarray__]', csource) - # - # Replace "...}" with "__dotdotdotNUM__}". This construction should - # occur only at the end of enums; at the end of structs we have "...;}" - # and at the end of vararg functions "...);". Also replace "=...[,}]" - # with ",__dotdotdotNUM__[,}]": this occurs in the enums too, when - # giving an unknown value. - matches = list(_r_partial_enum.finditer(csource)) - for number, match in enumerate(reversed(matches)): - p = match.start() - if csource[p] == '=': - p2 = csource.find('...', p, match.end()) - assert p2 > p - csource = '%s,__dotdotdot%d__ %s' % (csource[:p], number, - csource[p2+3:]) - else: - assert csource[p:p+3] == '...' - csource = '%s __dotdotdot%d__ %s' % (csource[:p], number, - csource[p+3:]) - # Replace "int ..." or "unsigned long int..." with "__dotdotdotint__" - csource = _r_int_dotdotdot.sub(' __dotdotdotint__ ', csource) - # Replace "float ..." or "double..." with "__dotdotdotfloat__" - csource = _r_float_dotdotdot.sub(' __dotdotdotfloat__ ', csource) - # Replace all remaining "..." with the same name, "__dotdotdot__", - # which is declared with a typedef for the purpose of C parsing. - csource = csource.replace('...', ' __dotdotdot__ ') - # Finally, put back the line directives - csource = _put_back_line_directives(csource, line_directives) - return csource, macros - -def _common_type_names(csource): - # Look in the source for what looks like usages of types from the - # list of common types. A "usage" is approximated here as the - # appearance of the word, minus a "definition" of the type, which - # is the last word in a "typedef" statement. Approximative only - # but should be fine for all the common types. - look_for_words = set(COMMON_TYPES) - look_for_words.add(';') - look_for_words.add(',') - look_for_words.add('(') - look_for_words.add(')') - look_for_words.add('typedef') - words_used = set() - is_typedef = False - paren = 0 - previous_word = '' - for word in _r_words.findall(csource): - if word in look_for_words: - if word == ';': - if is_typedef: - words_used.discard(previous_word) - look_for_words.discard(previous_word) - is_typedef = False - elif word == 'typedef': - is_typedef = True - paren = 0 - elif word == '(': - paren += 1 - elif word == ')': - paren -= 1 - elif word == ',': - if is_typedef and paren == 0: - words_used.discard(previous_word) - look_for_words.discard(previous_word) - else: # word in COMMON_TYPES - words_used.add(word) - previous_word = word - return words_used - - -class Parser(object): - - def __init__(self): - self._declarations = {} - self._included_declarations = set() - self._anonymous_counter = 0 - self._structnode2type = weakref.WeakKeyDictionary() - self._options = {} - self._int_constants = {} - self._recomplete = [] - self._uses_new_feature = None - - def _parse(self, csource): - csource, macros = _preprocess(csource) - # XXX: for more efficiency we would need to poke into the - # internals of CParser... the following registers the - # typedefs, because their presence or absence influences the - # parsing itself (but what they are typedef'ed to plays no role) - ctn = _common_type_names(csource) - typenames = [] - for name in sorted(self._declarations): - if name.startswith('typedef '): - name = name[8:] - typenames.append(name) - ctn.discard(name) - typenames += sorted(ctn) - # - csourcelines = [] - csourcelines.append('# 1 "<cdef automatic initialization code>"') - for typename in typenames: - csourcelines.append('typedef int %s;' % typename) - csourcelines.append('typedef int __dotdotdotint__, __dotdotdotfloat__,' - ' __dotdotdot__;') - # this forces pycparser to consider the following in the file - # called <cdef source string> from line 1 - csourcelines.append('# 1 "%s"' % (CDEF_SOURCE_STRING,)) - csourcelines.append(csource) - fullcsource = '\n'.join(csourcelines) - if lock is not None: - lock.acquire() # pycparser is not thread-safe... - try: - ast = _get_parser().parse(fullcsource) - except pycparser.c_parser.ParseError as e: - self.convert_pycparser_error(e, csource) - finally: - if lock is not None: - lock.release() - # csource will be used to find buggy source text - return ast, macros, csource - - def _convert_pycparser_error(self, e, csource): - # xxx look for "<cdef source string>:NUM:" at the start of str(e) - # and interpret that as a line number. This will not work if - # the user gives explicit ``# NUM "FILE"`` directives. - line = None - msg = str(e) - match = re.match(r"%s:(\d+):" % (CDEF_SOURCE_STRING,), msg) - if match: - linenum = int(match.group(1), 10) - csourcelines = csource.splitlines() - if 1 <= linenum <= len(csourcelines): - line = csourcelines[linenum-1] - return line - - def convert_pycparser_error(self, e, csource): - line = self._convert_pycparser_error(e, csource) - - msg = str(e) - if line: - msg = 'cannot parse "%s"\n%s' % (line.strip(), msg) - else: - msg = 'parse error\n%s' % (msg,) - raise CDefError(msg) - - def parse(self, csource, override=False, packed=False, pack=None, - dllexport=False): - if packed: - if packed != True: - raise ValueError("'packed' should be False or True; use " - "'pack' to give another value") - if pack: - raise ValueError("cannot give both 'pack' and 'packed'") - pack = 1 - elif pack: - if pack & (pack - 1): - raise ValueError("'pack' must be a power of two, not %r" % - (pack,)) - else: - pack = 0 - prev_options = self._options - try: - self._options = {'override': override, - 'packed': pack, - 'dllexport': dllexport} - self._internal_parse(csource) - finally: - self._options = prev_options - - def _internal_parse(self, csource): - ast, macros, csource = self._parse(csource) - # add the macros - self._process_macros(macros) - # find the first "__dotdotdot__" and use that as a separator - # between the repeated typedefs and the real csource - iterator = iter(ast.ext) - for decl in iterator: - if decl.name == '__dotdotdot__': - break - else: - assert 0 - current_decl = None - # - try: - self._inside_extern_python = '__cffi_extern_python_stop' - for decl in iterator: - current_decl = decl - if isinstance(decl, pycparser.c_ast.Decl): - self._parse_decl(decl) - elif isinstance(decl, pycparser.c_ast.Typedef): - if not decl.name: - raise CDefError("typedef does not declare any name", - decl) - quals = 0 - if (isinstance(decl.type.type, pycparser.c_ast.IdentifierType) and - decl.type.type.names[-1].startswith('__dotdotdot')): - realtype = self._get_unknown_type(decl) - elif (isinstance(decl.type, pycparser.c_ast.PtrDecl) and - isinstance(decl.type.type, pycparser.c_ast.TypeDecl) and - isinstance(decl.type.type.type, - pycparser.c_ast.IdentifierType) and - decl.type.type.type.names[-1].startswith('__dotdotdot')): - realtype = self._get_unknown_ptr_type(decl) - else: - realtype, quals = self._get_type_and_quals( - decl.type, name=decl.name, partial_length_ok=True, - typedef_example="*(%s *)0" % (decl.name,)) - self._declare('typedef ' + decl.name, realtype, quals=quals) - elif decl.__class__.__name__ == 'Pragma': - pass # skip pragma, only in pycparser 2.15 - else: - raise CDefError("unexpected <%s>: this construct is valid " - "C but not valid in cdef()" % - decl.__class__.__name__, decl) - except CDefError as e: - if len(e.args) == 1: - e.args = e.args + (current_decl,) - raise - except FFIError as e: - msg = self._convert_pycparser_error(e, csource) - if msg: - e.args = (e.args[0] + "\n *** Err: %s" % msg,) - raise - - def _add_constants(self, key, val): - if key in self._int_constants: - if self._int_constants[key] == val: - return # ignore identical double declarations - raise FFIError( - "multiple declarations of constant: %s" % (key,)) - self._int_constants[key] = val - - def _add_integer_constant(self, name, int_str): - int_str = int_str.lower().rstrip("ul") - neg = int_str.startswith('-') - if neg: - int_str = int_str[1:] - # "010" is not valid oct in py3 - if (int_str.startswith("0") and int_str != '0' - and not int_str.startswith("0x")): - int_str = "0o" + int_str[1:] - pyvalue = int(int_str, 0) - if neg: - pyvalue = -pyvalue - self._add_constants(name, pyvalue) - self._declare('macro ' + name, pyvalue) - - def _process_macros(self, macros): - for key, value in macros.items(): - value = value.strip() - if _r_int_literal.match(value): - self._add_integer_constant(key, value) - elif value == '...': - self._declare('macro ' + key, value) - else: - raise CDefError( - 'only supports one of the following syntax:\n' - ' #define %s ... (literally dot-dot-dot)\n' - ' #define %s NUMBER (with NUMBER an integer' - ' constant, decimal/hex/octal)\n' - 'got:\n' - ' #define %s %s' - % (key, key, key, value)) - - def _declare_function(self, tp, quals, decl): - tp = self._get_type_pointer(tp, quals) - if self._options.get('dllexport'): - tag = 'dllexport_python ' - elif self._inside_extern_python == '__cffi_extern_python_start': - tag = 'extern_python ' - elif self._inside_extern_python == '__cffi_extern_python_plus_c_start': - tag = 'extern_python_plus_c ' - else: - tag = 'function ' - self._declare(tag + decl.name, tp) - - def _parse_decl(self, decl): - node = decl.type - if isinstance(node, pycparser.c_ast.FuncDecl): - tp, quals = self._get_type_and_quals(node, name=decl.name) - assert isinstance(tp, model.RawFunctionType) - self._declare_function(tp, quals, decl) - else: - if isinstance(node, pycparser.c_ast.Struct): - self._get_struct_union_enum_type('struct', node) - elif isinstance(node, pycparser.c_ast.Union): - self._get_struct_union_enum_type('union', node) - elif isinstance(node, pycparser.c_ast.Enum): - self._get_struct_union_enum_type('enum', node) - elif not decl.name: - raise CDefError("construct does not declare any variable", - decl) - # - if decl.name: - tp, quals = self._get_type_and_quals(node, - partial_length_ok=True) - if tp.is_raw_function: - self._declare_function(tp, quals, decl) - elif (tp.is_integer_type() and - hasattr(decl, 'init') and - hasattr(decl.init, 'value') and - _r_int_literal.match(decl.init.value)): - self._add_integer_constant(decl.name, decl.init.value) - elif (tp.is_integer_type() and - isinstance(decl.init, pycparser.c_ast.UnaryOp) and - decl.init.op == '-' and - hasattr(decl.init.expr, 'value') and - _r_int_literal.match(decl.init.expr.value)): - self._add_integer_constant(decl.name, - '-' + decl.init.expr.value) - elif (tp is model.void_type and - decl.name.startswith('__cffi_extern_python_')): - # hack: `extern "Python"` in the C source is replaced - # with "void __cffi_extern_python_start;" and - # "void __cffi_extern_python_stop;" - self._inside_extern_python = decl.name - else: - if self._inside_extern_python !='__cffi_extern_python_stop': - raise CDefError( - "cannot declare constants or " - "variables with 'extern \"Python\"'") - if (quals & model.Q_CONST) and not tp.is_array_type: - self._declare('constant ' + decl.name, tp, quals=quals) - else: - _warn_for_non_extern_non_static_global_variable(decl) - self._declare('variable ' + decl.name, tp, quals=quals) - - def parse_type(self, cdecl): - return self.parse_type_and_quals(cdecl)[0] - - def parse_type_and_quals(self, cdecl): - ast, macros = self._parse('void __dummy(\n%s\n);' % cdecl)[:2] - assert not macros - exprnode = ast.ext[-1].type.args.params[0] - if isinstance(exprnode, pycparser.c_ast.ID): - raise CDefError("unknown identifier '%s'" % (exprnode.name,)) - return self._get_type_and_quals(exprnode.type) - - def _declare(self, name, obj, included=False, quals=0): - if name in self._declarations: - prevobj, prevquals = self._declarations[name] - if prevobj is obj and prevquals == quals: - return - if not self._options.get('override'): - raise FFIError( - "multiple declarations of %s (for interactive usage, " - "try cdef(xx, override=True))" % (name,)) - assert '__dotdotdot__' not in name.split() - self._declarations[name] = (obj, quals) - if included: - self._included_declarations.add(obj) - - def _extract_quals(self, type): - quals = 0 - if isinstance(type, (pycparser.c_ast.TypeDecl, - pycparser.c_ast.PtrDecl)): - if 'const' in type.quals: - quals |= model.Q_CONST - if 'volatile' in type.quals: - quals |= model.Q_VOLATILE - if 'restrict' in type.quals: - quals |= model.Q_RESTRICT - return quals - - def _get_type_pointer(self, type, quals, declname=None): - if isinstance(type, model.RawFunctionType): - return type.as_function_pointer() - if (isinstance(type, model.StructOrUnionOrEnum) and - type.name.startswith('$') and type.name[1:].isdigit() and - type.forcename is None and declname is not None): - return model.NamedPointerType(type, declname, quals) - return model.PointerType(type, quals) - - def _get_type_and_quals(self, typenode, name=None, partial_length_ok=False, - typedef_example=None): - # first, dereference typedefs, if we have it already parsed, we're good - if (isinstance(typenode, pycparser.c_ast.TypeDecl) and - isinstance(typenode.type, pycparser.c_ast.IdentifierType) and - len(typenode.type.names) == 1 and - ('typedef ' + typenode.type.names[0]) in self._declarations): - tp, quals = self._declarations['typedef ' + typenode.type.names[0]] - quals |= self._extract_quals(typenode) - return tp, quals - # - if isinstance(typenode, pycparser.c_ast.ArrayDecl): - # array type - if typenode.dim is None: - length = None - else: - length = self._parse_constant( - typenode.dim, partial_length_ok=partial_length_ok) - # a hack: in 'typedef int foo_t[...][...];', don't use '...' as - # the length but use directly the C expression that would be - # generated by recompiler.py. This lets the typedef be used in - # many more places within recompiler.py - if typedef_example is not None: - if length == '...': - length = '_cffi_array_len(%s)' % (typedef_example,) - typedef_example = "*" + typedef_example - # - tp, quals = self._get_type_and_quals(typenode.type, - partial_length_ok=partial_length_ok, - typedef_example=typedef_example) - return model.ArrayType(tp, length), quals - # - if isinstance(typenode, pycparser.c_ast.PtrDecl): - # pointer type - itemtype, itemquals = self._get_type_and_quals(typenode.type) - tp = self._get_type_pointer(itemtype, itemquals, declname=name) - quals = self._extract_quals(typenode) - return tp, quals - # - if isinstance(typenode, pycparser.c_ast.TypeDecl): - quals = self._extract_quals(typenode) - type = typenode.type - if isinstance(type, pycparser.c_ast.IdentifierType): - # assume a primitive type. get it from .names, but reduce - # synonyms to a single chosen combination - names = list(type.names) - if names != ['signed', 'char']: # keep this unmodified - prefixes = {} - while names: - name = names[0] - if name in ('short', 'long', 'signed', 'unsigned'): - prefixes[name] = prefixes.get(name, 0) + 1 - del names[0] - else: - break - # ignore the 'signed' prefix below, and reorder the others - newnames = [] - for prefix in ('unsigned', 'short', 'long'): - for i in range(prefixes.get(prefix, 0)): - newnames.append(prefix) - if not names: - names = ['int'] # implicitly - if names == ['int']: # but kill it if 'short' or 'long' - if 'short' in prefixes or 'long' in prefixes: - names = [] - names = newnames + names - ident = ' '.join(names) - if ident == 'void': - return model.void_type, quals - if ident == '__dotdotdot__': - raise FFIError(':%d: bad usage of "..."' % - typenode.coord.line) - tp0, quals0 = resolve_common_type(self, ident) - return tp0, (quals | quals0) - # - if isinstance(type, pycparser.c_ast.Struct): - # 'struct foobar' - tp = self._get_struct_union_enum_type('struct', type, name) - return tp, quals - # - if isinstance(type, pycparser.c_ast.Union): - # 'union foobar' - tp = self._get_struct_union_enum_type('union', type, name) - return tp, quals - # - if isinstance(type, pycparser.c_ast.Enum): - # 'enum foobar' - tp = self._get_struct_union_enum_type('enum', type, name) - return tp, quals - # - if isinstance(typenode, pycparser.c_ast.FuncDecl): - # a function type - return self._parse_function_type(typenode, name), 0 - # - # nested anonymous structs or unions end up here - if isinstance(typenode, pycparser.c_ast.Struct): - return self._get_struct_union_enum_type('struct', typenode, name, - nested=True), 0 - if isinstance(typenode, pycparser.c_ast.Union): - return self._get_struct_union_enum_type('union', typenode, name, - nested=True), 0 - # - raise FFIError(":%d: bad or unsupported type declaration" % - typenode.coord.line) - - def _parse_function_type(self, typenode, funcname=None): - params = list(getattr(typenode.args, 'params', [])) - for i, arg in enumerate(params): - if not hasattr(arg, 'type'): - raise CDefError("%s arg %d: unknown type '%s'" - " (if you meant to use the old C syntax of giving" - " untyped arguments, it is not supported)" - % (funcname or 'in expression', i + 1, - getattr(arg, 'name', '?'))) - ellipsis = ( - len(params) > 0 and - isinstance(params[-1].type, pycparser.c_ast.TypeDecl) and - isinstance(params[-1].type.type, - pycparser.c_ast.IdentifierType) and - params[-1].type.type.names == ['__dotdotdot__']) - if ellipsis: - params.pop() - if not params: - raise CDefError( - "%s: a function with only '(...)' as argument" - " is not correct C" % (funcname or 'in expression')) - args = [self._as_func_arg(*self._get_type_and_quals(argdeclnode.type)) - for argdeclnode in params] - if not ellipsis and args == [model.void_type]: - args = [] - result, quals = self._get_type_and_quals(typenode.type) - # the 'quals' on the result type are ignored. HACK: we absure them - # to detect __stdcall functions: we textually replace "__stdcall" - # with "volatile volatile const" above. - abi = None - if hasattr(typenode.type, 'quals'): # else, probable syntax error anyway - if typenode.type.quals[-3:] == ['volatile', 'volatile', 'const']: - abi = '__stdcall' - return model.RawFunctionType(tuple(args), result, ellipsis, abi) - - def _as_func_arg(self, type, quals): - if isinstance(type, model.ArrayType): - return model.PointerType(type.item, quals) - elif isinstance(type, model.RawFunctionType): - return type.as_function_pointer() - else: - return type - - def _get_struct_union_enum_type(self, kind, type, name=None, nested=False): - # First, a level of caching on the exact 'type' node of the AST. - # This is obscure, but needed because pycparser "unrolls" declarations - # such as "typedef struct { } foo_t, *foo_p" and we end up with - # an AST that is not a tree, but a DAG, with the "type" node of the - # two branches foo_t and foo_p of the trees being the same node. - # It's a bit silly but detecting "DAG-ness" in the AST tree seems - # to be the only way to distinguish this case from two independent - # structs. See test_struct_with_two_usages. - try: - return self._structnode2type[type] - except KeyError: - pass - # - # Note that this must handle parsing "struct foo" any number of - # times and always return the same StructType object. Additionally, - # one of these times (not necessarily the first), the fields of - # the struct can be specified with "struct foo { ...fields... }". - # If no name is given, then we have to create a new anonymous struct - # with no caching; in this case, the fields are either specified - # right now or never. - # - force_name = name - name = type.name - # - # get the type or create it if needed - if name is None: - # 'force_name' is used to guess a more readable name for - # anonymous structs, for the common case "typedef struct { } foo". - if force_name is not None: - explicit_name = '$%s' % force_name - else: - self._anonymous_counter += 1 - explicit_name = '$%d' % self._anonymous_counter - tp = None - else: - explicit_name = name - key = '%s %s' % (kind, name) - tp, _ = self._declarations.get(key, (None, None)) - # - if tp is None: - if kind == 'struct': - tp = model.StructType(explicit_name, None, None, None) - elif kind == 'union': - tp = model.UnionType(explicit_name, None, None, None) - elif kind == 'enum': - if explicit_name == '__dotdotdot__': - raise CDefError("Enums cannot be declared with ...") - tp = self._build_enum_type(explicit_name, type.values) - else: - raise AssertionError("kind = %r" % (kind,)) - if name is not None: - self._declare(key, tp) - else: - if kind == 'enum' and type.values is not None: - raise NotImplementedError( - "enum %s: the '{}' declaration should appear on the first " - "time the enum is mentioned, not later" % explicit_name) - if not tp.forcename: - tp.force_the_name(force_name) - if tp.forcename and '$' in tp.name: - self._declare('anonymous %s' % tp.forcename, tp) - # - self._structnode2type[type] = tp - # - # enums: done here - if kind == 'enum': - return tp - # - # is there a 'type.decls'? If yes, then this is the place in the - # C sources that declare the fields. If no, then just return the - # existing type, possibly still incomplete. - if type.decls is None: - return tp - # - if tp.fldnames is not None: - raise CDefError("duplicate declaration of struct %s" % name) - fldnames = [] - fldtypes = [] - fldbitsize = [] - fldquals = [] - for decl in type.decls: - if (isinstance(decl.type, pycparser.c_ast.IdentifierType) and - ''.join(decl.type.names) == '__dotdotdot__'): - # XXX pycparser is inconsistent: 'names' should be a list - # of strings, but is sometimes just one string. Use - # str.join() as a way to cope with both. - self._make_partial(tp, nested) - continue - if decl.bitsize is None: - bitsize = -1 - else: - bitsize = self._parse_constant(decl.bitsize) - self._partial_length = False - type, fqual = self._get_type_and_quals(decl.type, - partial_length_ok=True) - if self._partial_length: - self._make_partial(tp, nested) - if isinstance(type, model.StructType) and type.partial: - self._make_partial(tp, nested) - fldnames.append(decl.name or '') - fldtypes.append(type) - fldbitsize.append(bitsize) - fldquals.append(fqual) - tp.fldnames = tuple(fldnames) - tp.fldtypes = tuple(fldtypes) - tp.fldbitsize = tuple(fldbitsize) - tp.fldquals = tuple(fldquals) - if fldbitsize != [-1] * len(fldbitsize): - if isinstance(tp, model.StructType) and tp.partial: - raise NotImplementedError("%s: using both bitfields and '...;'" - % (tp,)) - tp.packed = self._options.get('packed') - if tp.completed: # must be re-completed: it is not opaque any more - tp.completed = 0 - self._recomplete.append(tp) - return tp - - def _make_partial(self, tp, nested): - if not isinstance(tp, model.StructOrUnion): - raise CDefError("%s cannot be partial" % (tp,)) - if not tp.has_c_name() and not nested: - raise NotImplementedError("%s is partial but has no C name" %(tp,)) - tp.partial = True - - def _parse_constant(self, exprnode, partial_length_ok=False): - # for now, limited to expressions that are an immediate number - # or positive/negative number - if isinstance(exprnode, pycparser.c_ast.Constant): - s = exprnode.value - if '0' <= s[0] <= '9': - s = s.rstrip('uUlL') - try: - if s.startswith('0'): - return int(s, 8) - else: - return int(s, 10) - except ValueError: - if len(s) > 1: - if s.lower()[0:2] == '0x': - return int(s, 16) - elif s.lower()[0:2] == '0b': - return int(s, 2) - raise CDefError("invalid constant %r" % (s,)) - elif s[0] == "'" and s[-1] == "'" and ( - len(s) == 3 or (len(s) == 4 and s[1] == "\\")): - return ord(s[-2]) - else: - raise CDefError("invalid constant %r" % (s,)) - # - if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and - exprnode.op == '+'): - return self._parse_constant(exprnode.expr) - # - if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and - exprnode.op == '-'): - return -self._parse_constant(exprnode.expr) - # load previously defined int constant - if (isinstance(exprnode, pycparser.c_ast.ID) and - exprnode.name in self._int_constants): - return self._int_constants[exprnode.name] - # - if (isinstance(exprnode, pycparser.c_ast.ID) and - exprnode.name == '__dotdotdotarray__'): - if partial_length_ok: - self._partial_length = True - return '...' - raise FFIError(":%d: unsupported '[...]' here, cannot derive " - "the actual array length in this context" - % exprnode.coord.line) - # - if isinstance(exprnode, pycparser.c_ast.BinaryOp): - left = self._parse_constant(exprnode.left) - right = self._parse_constant(exprnode.right) - if exprnode.op == '+': - return left + right - elif exprnode.op == '-': - return left - right - elif exprnode.op == '*': - return left * right - elif exprnode.op == '/': - return self._c_div(left, right) - elif exprnode.op == '%': - return left - self._c_div(left, right) * right - elif exprnode.op == '<<': - return left << right - elif exprnode.op == '>>': - return left >> right - elif exprnode.op == '&': - return left & right - elif exprnode.op == '|': - return left | right - elif exprnode.op == '^': - return left ^ right - # - raise FFIError(":%d: unsupported expression: expected a " - "simple numeric constant" % exprnode.coord.line) - - def _c_div(self, a, b): - result = a // b - if ((a < 0) ^ (b < 0)) and (a % b) != 0: - result += 1 - return result - - def _build_enum_type(self, explicit_name, decls): - if decls is not None: - partial = False - enumerators = [] - enumvalues = [] - nextenumvalue = 0 - for enum in decls.enumerators: - if _r_enum_dotdotdot.match(enum.name): - partial = True - continue - if enum.value is not None: - nextenumvalue = self._parse_constant(enum.value) - enumerators.append(enum.name) - enumvalues.append(nextenumvalue) - self._add_constants(enum.name, nextenumvalue) - nextenumvalue += 1 - enumerators = tuple(enumerators) - enumvalues = tuple(enumvalues) - tp = model.EnumType(explicit_name, enumerators, enumvalues) - tp.partial = partial - else: # opaque enum - tp = model.EnumType(explicit_name, (), ()) - return tp - - def include(self, other): - for name, (tp, quals) in other._declarations.items(): - if name.startswith('anonymous $enum_$'): - continue # fix for test_anonymous_enum_include - kind = name.split(' ', 1)[0] - if kind in ('struct', 'union', 'enum', 'anonymous', 'typedef'): - self._declare(name, tp, included=True, quals=quals) - for k, v in other._int_constants.items(): - self._add_constants(k, v) - - def _get_unknown_type(self, decl): - typenames = decl.type.type.names - if typenames == ['__dotdotdot__']: - return model.unknown_type(decl.name) - - if typenames == ['__dotdotdotint__']: - if self._uses_new_feature is None: - self._uses_new_feature = "'typedef int... %s'" % decl.name - return model.UnknownIntegerType(decl.name) - - if typenames == ['__dotdotdotfloat__']: - # note: not for 'long double' so far - if self._uses_new_feature is None: - self._uses_new_feature = "'typedef float... %s'" % decl.name - return model.UnknownFloatType(decl.name) - - raise FFIError(':%d: unsupported usage of "..." in typedef' - % decl.coord.line) - - def _get_unknown_ptr_type(self, decl): - if decl.type.type.type.names == ['__dotdotdot__']: - return model.unknown_ptr_type(decl.name) - raise FFIError(':%d: unsupported usage of "..." in typedef' - % decl.coord.line) diff --git a/cffi/error.py b/cffi/error.py deleted file mode 100644 index 0a27247..0000000 --- a/cffi/error.py +++ /dev/null @@ -1,31 +0,0 @@ - -class FFIError(Exception): - __module__ = 'cffi' - -class CDefError(Exception): - __module__ = 'cffi' - def __str__(self): - try: - current_decl = self.args[1] - filename = current_decl.coord.file - linenum = current_decl.coord.line - prefix = '%s:%d: ' % (filename, linenum) - except (AttributeError, TypeError, IndexError): - prefix = '' - return '%s%s' % (prefix, self.args[0]) - -class VerificationError(Exception): - """ An error raised when verification fails - """ - __module__ = 'cffi' - -class VerificationMissing(Exception): - """ An error raised when incomplete structures are passed into - cdef, but no verification has been done - """ - __module__ = 'cffi' - -class PkgConfigError(Exception): - """ An error raised for missing modules in pkg-config - """ - __module__ = 'cffi' diff --git a/cffi/ffiplatform.py b/cffi/ffiplatform.py deleted file mode 100644 index 8531346..0000000 --- a/cffi/ffiplatform.py +++ /dev/null @@ -1,127 +0,0 @@ -import sys, os -from .error import VerificationError - - -LIST_OF_FILE_NAMES = ['sources', 'include_dirs', 'library_dirs', - 'extra_objects', 'depends'] - -def get_extension(srcfilename, modname, sources=(), **kwds): - _hack_at_distutils() - from distutils.core import Extension - allsources = [srcfilename] - for src in sources: - allsources.append(os.path.normpath(src)) - return Extension(name=modname, sources=allsources, **kwds) - -def compile(tmpdir, ext, compiler_verbose=0, debug=None): - """Compile a C extension module using distutils.""" - - _hack_at_distutils() - saved_environ = os.environ.copy() - try: - outputfilename = _build(tmpdir, ext, compiler_verbose, debug) - outputfilename = os.path.abspath(outputfilename) - finally: - # workaround for a distutils bugs where some env vars can - # become longer and longer every time it is used - for key, value in saved_environ.items(): - if os.environ.get(key) != value: - os.environ[key] = value - return outputfilename - -def _build(tmpdir, ext, compiler_verbose=0, debug=None): - # XXX compact but horrible :-( - from distutils.core import Distribution - import distutils.errors, distutils.log - # - dist = Distribution({'ext_modules': [ext]}) - dist.parse_config_files() - options = dist.get_option_dict('build_ext') - if debug is None: - debug = sys.flags.debug - options['debug'] = ('ffiplatform', debug) - options['force'] = ('ffiplatform', True) - options['build_lib'] = ('ffiplatform', tmpdir) - options['build_temp'] = ('ffiplatform', tmpdir) - # - try: - old_level = distutils.log.set_threshold(0) or 0 - try: - distutils.log.set_verbosity(compiler_verbose) - dist.run_command('build_ext') - cmd_obj = dist.get_command_obj('build_ext') - [soname] = cmd_obj.get_outputs() - finally: - distutils.log.set_threshold(old_level) - except (distutils.errors.CompileError, - distutils.errors.LinkError) as e: - raise VerificationError('%s: %s' % (e.__class__.__name__, e)) - # - return soname - -try: - from os.path import samefile -except ImportError: - def samefile(f1, f2): - return os.path.abspath(f1) == os.path.abspath(f2) - -def maybe_relative_path(path): - if not os.path.isabs(path): - return path # already relative - dir = path - names = [] - while True: - prevdir = dir - dir, name = os.path.split(prevdir) - if dir == prevdir or not dir: - return path # failed to make it relative - names.append(name) - try: - if samefile(dir, os.curdir): - names.reverse() - return os.path.join(*names) - except OSError: - pass - -# ____________________________________________________________ - -try: - int_or_long = (int, long) - import cStringIO -except NameError: - int_or_long = int # Python 3 - import io as cStringIO - -def _flatten(x, f): - if isinstance(x, str): - f.write('%ds%s' % (len(x), x)) - elif isinstance(x, dict): - keys = sorted(x.keys()) - f.write('%dd' % len(keys)) - for key in keys: - _flatten(key, f) - _flatten(x[key], f) - elif isinstance(x, (list, tuple)): - f.write('%dl' % len(x)) - for value in x: - _flatten(value, f) - elif isinstance(x, int_or_long): - f.write('%di' % (x,)) - else: - raise TypeError( - "the keywords to verify() contains unsupported object %r" % (x,)) - -def flatten(x): - f = cStringIO.StringIO() - _flatten(x, f) - return f.getvalue() - -def _hack_at_distutils(): - # Windows-only workaround for some configurations: see - # https://bugs.python.org/issue23246 (Python 2.7 with - # a specific MS compiler suite download) - if sys.platform == "win32": - try: - import setuptools # for side-effects, patches distutils - except ImportError: - pass diff --git a/cffi/lock.py b/cffi/lock.py deleted file mode 100644 index db91b71..0000000 --- a/cffi/lock.py +++ /dev/null @@ -1,30 +0,0 @@ -import sys - -if sys.version_info < (3,): - try: - from thread import allocate_lock - except ImportError: - from dummy_thread import allocate_lock -else: - try: - from _thread import allocate_lock - except ImportError: - from _dummy_thread import allocate_lock - - -##import sys -##l1 = allocate_lock - -##class allocate_lock(object): -## def __init__(self): -## self._real = l1() -## def __enter__(self): -## for i in range(4, 0, -1): -## print sys._getframe(i).f_code -## print -## return self._real.__enter__() -## def __exit__(self, *args): -## return self._real.__exit__(*args) -## def acquire(self, f): -## assert f is False -## return self._real.acquire(f) diff --git a/cffi/model.py b/cffi/model.py deleted file mode 100644 index ad1c176..0000000 --- a/cffi/model.py +++ /dev/null @@ -1,617 +0,0 @@ -import types -import weakref - -from .lock import allocate_lock -from .error import CDefError, VerificationError, VerificationMissing - -# type qualifiers -Q_CONST = 0x01 -Q_RESTRICT = 0x02 -Q_VOLATILE = 0x04 - -def qualify(quals, replace_with): - if quals & Q_CONST: - replace_with = ' const ' + replace_with.lstrip() - if quals & Q_VOLATILE: - replace_with = ' volatile ' + replace_with.lstrip() - if quals & Q_RESTRICT: - # It seems that __restrict is supported by gcc and msvc. - # If you hit some different compiler, add a #define in - # _cffi_include.h for it (and in its copies, documented there) - replace_with = ' __restrict ' + replace_with.lstrip() - return replace_with - - -class BaseTypeByIdentity(object): - is_array_type = False - is_raw_function = False - - def get_c_name(self, replace_with='', context='a C file', quals=0): - result = self.c_name_with_marker - assert result.count('&') == 1 - # some logic duplication with ffi.getctype()... :-( - replace_with = replace_with.strip() - if replace_with: - if replace_with.startswith('*') and '&[' in result: - replace_with = '(%s)' % replace_with - elif not replace_with[0] in '[(': - replace_with = ' ' + replace_with - replace_with = qualify(quals, replace_with) - result = result.replace('&', replace_with) - if '$' in result: - raise VerificationError( - "cannot generate '%s' in %s: unknown type name" - % (self._get_c_name(), context)) - return result - - def _get_c_name(self): - return self.c_name_with_marker.replace('&', '') - - def has_c_name(self): - return '$' not in self._get_c_name() - - def is_integer_type(self): - return False - - def get_cached_btype(self, ffi, finishlist, can_delay=False): - try: - BType = ffi._cached_btypes[self] - except KeyError: - BType = self.build_backend_type(ffi, finishlist) - BType2 = ffi._cached_btypes.setdefault(self, BType) - assert BType2 is BType - return BType - - def __repr__(self): - return '<%s>' % (self._get_c_name(),) - - def _get_items(self): - return [(name, getattr(self, name)) for name in self._attrs_] - - -class BaseType(BaseTypeByIdentity): - - def __eq__(self, other): - return (self.__class__ == other.__class__ and - self._get_items() == other._get_items()) - - def __ne__(self, other): - return not self == other - - def __hash__(self): - return hash((self.__class__, tuple(self._get_items()))) - - -class VoidType(BaseType): - _attrs_ = () - - def __init__(self): - self.c_name_with_marker = 'void&' - - def build_backend_type(self, ffi, finishlist): - return global_cache(self, ffi, 'new_void_type') - -void_type = VoidType() - - -class BasePrimitiveType(BaseType): - def is_complex_type(self): - return False - - -class PrimitiveType(BasePrimitiveType): - _attrs_ = ('name',) - - ALL_PRIMITIVE_TYPES = { - 'char': 'c', - 'short': 'i', - 'int': 'i', - 'long': 'i', - 'long long': 'i', - 'signed char': 'i', - 'unsigned char': 'i', - 'unsigned short': 'i', - 'unsigned int': 'i', - 'unsigned long': 'i', - 'unsigned long long': 'i', - 'float': 'f', - 'double': 'f', - 'long double': 'f', - 'float _Complex': 'j', - 'double _Complex': 'j', - '_Bool': 'i', - # the following types are not primitive in the C sense - 'wchar_t': 'c', - 'char16_t': 'c', - 'char32_t': 'c', - 'int8_t': 'i', - 'uint8_t': 'i', - 'int16_t': 'i', - 'uint16_t': 'i', - 'int32_t': 'i', - 'uint32_t': 'i', - 'int64_t': 'i', - 'uint64_t': 'i', - 'int_least8_t': 'i', - 'uint_least8_t': 'i', - 'int_least16_t': 'i', - 'uint_least16_t': 'i', - 'int_least32_t': 'i', - 'uint_least32_t': 'i', - 'int_least64_t': 'i', - 'uint_least64_t': 'i', - 'int_fast8_t': 'i', - 'uint_fast8_t': 'i', - 'int_fast16_t': 'i', - 'uint_fast16_t': 'i', - 'int_fast32_t': 'i', - 'uint_fast32_t': 'i', - 'int_fast64_t': 'i', - 'uint_fast64_t': 'i', - 'intptr_t': 'i', - 'uintptr_t': 'i', - 'intmax_t': 'i', - 'uintmax_t': 'i', - 'ptrdiff_t': 'i', - 'size_t': 'i', - 'ssize_t': 'i', - } - - def __init__(self, name): - assert name in self.ALL_PRIMITIVE_TYPES - self.name = name - self.c_name_with_marker = name + '&' - - def is_char_type(self): - return self.ALL_PRIMITIVE_TYPES[self.name] == 'c' - def is_integer_type(self): - return self.ALL_PRIMITIVE_TYPES[self.name] == 'i' - def is_float_type(self): - return self.ALL_PRIMITIVE_TYPES[self.name] == 'f' - def is_complex_type(self): - return self.ALL_PRIMITIVE_TYPES[self.name] == 'j' - - def build_backend_type(self, ffi, finishlist): - return global_cache(self, ffi, 'new_primitive_type', self.name) - - -class UnknownIntegerType(BasePrimitiveType): - _attrs_ = ('name',) - - def __init__(self, name): - self.name = name - self.c_name_with_marker = name + '&' - - def is_integer_type(self): - return True - - def build_backend_type(self, ffi, finishlist): - raise NotImplementedError("integer type '%s' can only be used after " - "compilation" % self.name) - -class UnknownFloatType(BasePrimitiveType): - _attrs_ = ('name', ) - - def __init__(self, name): - self.name = name - self.c_name_with_marker = name + '&' - - def build_backend_type(self, ffi, finishlist): - raise NotImplementedError("float type '%s' can only be used after " - "compilation" % self.name) - - -class BaseFunctionType(BaseType): - _attrs_ = ('args', 'result', 'ellipsis', 'abi') - - def __init__(self, args, result, ellipsis, abi=None): - self.args = args - self.result = result - self.ellipsis = ellipsis - self.abi = abi - # - reprargs = [arg._get_c_name() for arg in self.args] - if self.ellipsis: - reprargs.append('...') - reprargs = reprargs or ['void'] - replace_with = self._base_pattern % (', '.join(reprargs),) - if abi is not None: - replace_with = replace_with[:1] + abi + ' ' + replace_with[1:] - self.c_name_with_marker = ( - self.result.c_name_with_marker.replace('&', replace_with)) - - -class RawFunctionType(BaseFunctionType): - # Corresponds to a C type like 'int(int)', which is the C type of - # a function, but not a pointer-to-function. The backend has no - # notion of such a type; it's used temporarily by parsing. - _base_pattern = '(&)(%s)' - is_raw_function = True - - def build_backend_type(self, ffi, finishlist): - raise CDefError("cannot render the type %r: it is a function " - "type, not a pointer-to-function type" % (self,)) - - def as_function_pointer(self): - return FunctionPtrType(self.args, self.result, self.ellipsis, self.abi) - - -class FunctionPtrType(BaseFunctionType): - _base_pattern = '(*&)(%s)' - - def build_backend_type(self, ffi, finishlist): - result = self.result.get_cached_btype(ffi, finishlist) - args = [] - for tp in self.args: - args.append(tp.get_cached_btype(ffi, finishlist)) - abi_args = () - if self.abi == "__stdcall": - if not self.ellipsis: # __stdcall ignored for variadic funcs - try: - abi_args = (ffi._backend.FFI_STDCALL,) - except AttributeError: - pass - return global_cache(self, ffi, 'new_function_type', - tuple(args), result, self.ellipsis, *abi_args) - - def as_raw_function(self): - return RawFunctionType(self.args, self.result, self.ellipsis, self.abi) - - -class PointerType(BaseType): - _attrs_ = ('totype', 'quals') - - def __init__(self, totype, quals=0): - self.totype = totype - self.quals = quals - extra = qualify(quals, " *&") - if totype.is_array_type: - extra = "(%s)" % (extra.lstrip(),) - self.c_name_with_marker = totype.c_name_with_marker.replace('&', extra) - - def build_backend_type(self, ffi, finishlist): - BItem = self.totype.get_cached_btype(ffi, finishlist, can_delay=True) - return global_cache(self, ffi, 'new_pointer_type', BItem) - -voidp_type = PointerType(void_type) - -def ConstPointerType(totype): - return PointerType(totype, Q_CONST) - -const_voidp_type = ConstPointerType(void_type) - - -class NamedPointerType(PointerType): - _attrs_ = ('totype', 'name') - - def __init__(self, totype, name, quals=0): - PointerType.__init__(self, totype, quals) - self.name = name - self.c_name_with_marker = name + '&' - - -class ArrayType(BaseType): - _attrs_ = ('item', 'length') - is_array_type = True - - def __init__(self, item, length): - self.item = item - self.length = length - # - if length is None: - brackets = '&[]' - elif length == '...': - brackets = '&[/*...*/]' - else: - brackets = '&[%s]' % length - self.c_name_with_marker = ( - self.item.c_name_with_marker.replace('&', brackets)) - - def length_is_unknown(self): - return isinstance(self.length, str) - - def resolve_length(self, newlength): - return ArrayType(self.item, newlength) - - def build_backend_type(self, ffi, finishlist): - if self.length_is_unknown(): - raise CDefError("cannot render the type %r: unknown length" % - (self,)) - self.item.get_cached_btype(ffi, finishlist) # force the item BType - BPtrItem = PointerType(self.item).get_cached_btype(ffi, finishlist) - return global_cache(self, ffi, 'new_array_type', BPtrItem, self.length) - -char_array_type = ArrayType(PrimitiveType('char'), None) - - -class StructOrUnionOrEnum(BaseTypeByIdentity): - _attrs_ = ('name',) - forcename = None - - def build_c_name_with_marker(self): - name = self.forcename or '%s %s' % (self.kind, self.name) - self.c_name_with_marker = name + '&' - - def force_the_name(self, forcename): - self.forcename = forcename - self.build_c_name_with_marker() - - def get_official_name(self): - assert self.c_name_with_marker.endswith('&') - return self.c_name_with_marker[:-1] - - -class StructOrUnion(StructOrUnionOrEnum): - fixedlayout = None - completed = 0 - partial = False - packed = 0 - - def __init__(self, name, fldnames, fldtypes, fldbitsize, fldquals=None): - self.name = name - self.fldnames = fldnames - self.fldtypes = fldtypes - self.fldbitsize = fldbitsize - self.fldquals = fldquals - self.build_c_name_with_marker() - - def anonymous_struct_fields(self): - if self.fldtypes is not None: - for name, type in zip(self.fldnames, self.fldtypes): - if name == '' and isinstance(type, StructOrUnion): - yield type - - def enumfields(self, expand_anonymous_struct_union=True): - fldquals = self.fldquals - if fldquals is None: - fldquals = (0,) * len(self.fldnames) - for name, type, bitsize, quals in zip(self.fldnames, self.fldtypes, - self.fldbitsize, fldquals): - if (name == '' and isinstance(type, StructOrUnion) - and expand_anonymous_struct_union): - # nested anonymous struct/union - for result in type.enumfields(): - yield result - else: - yield (name, type, bitsize, quals) - - def force_flatten(self): - # force the struct or union to have a declaration that lists - # directly all fields returned by enumfields(), flattening - # nested anonymous structs/unions. - names = [] - types = [] - bitsizes = [] - fldquals = [] - for name, type, bitsize, quals in self.enumfields(): - names.append(name) - types.append(type) - bitsizes.append(bitsize) - fldquals.append(quals) - self.fldnames = tuple(names) - self.fldtypes = tuple(types) - self.fldbitsize = tuple(bitsizes) - self.fldquals = tuple(fldquals) - - def get_cached_btype(self, ffi, finishlist, can_delay=False): - BType = StructOrUnionOrEnum.get_cached_btype(self, ffi, finishlist, - can_delay) - if not can_delay: - self.finish_backend_type(ffi, finishlist) - return BType - - def finish_backend_type(self, ffi, finishlist): - if self.completed: - if self.completed != 2: - raise NotImplementedError("recursive structure declaration " - "for '%s'" % (self.name,)) - return - BType = ffi._cached_btypes[self] - # - self.completed = 1 - # - if self.fldtypes is None: - pass # not completing it: it's an opaque struct - # - elif self.fixedlayout is None: - fldtypes = [tp.get_cached_btype(ffi, finishlist) - for tp in self.fldtypes] - lst = list(zip(self.fldnames, fldtypes, self.fldbitsize)) - extra_flags = () - if self.packed: - if self.packed == 1: - extra_flags = (8,) # SF_PACKED - else: - extra_flags = (0, self.packed) - ffi._backend.complete_struct_or_union(BType, lst, self, - -1, -1, *extra_flags) - # - else: - fldtypes = [] - fieldofs, fieldsize, totalsize, totalalignment = self.fixedlayout - for i in range(len(self.fldnames)): - fsize = fieldsize[i] - ftype = self.fldtypes[i] - # - if isinstance(ftype, ArrayType) and ftype.length_is_unknown(): - # fix the length to match the total size - BItemType = ftype.item.get_cached_btype(ffi, finishlist) - nlen, nrest = divmod(fsize, ffi.sizeof(BItemType)) - if nrest != 0: - self._verification_error( - "field '%s.%s' has a bogus size?" % ( - self.name, self.fldnames[i] or '{}')) - ftype = ftype.resolve_length(nlen) - self.fldtypes = (self.fldtypes[:i] + (ftype,) + - self.fldtypes[i+1:]) - # - BFieldType = ftype.get_cached_btype(ffi, finishlist) - if isinstance(ftype, ArrayType) and ftype.length is None: - assert fsize == 0 - else: - bitemsize = ffi.sizeof(BFieldType) - if bitemsize != fsize: - self._verification_error( - "field '%s.%s' is declared as %d bytes, but is " - "really %d bytes" % (self.name, - self.fldnames[i] or '{}', - bitemsize, fsize)) - fldtypes.append(BFieldType) - # - lst = list(zip(self.fldnames, fldtypes, self.fldbitsize, fieldofs)) - ffi._backend.complete_struct_or_union(BType, lst, self, - totalsize, totalalignment) - self.completed = 2 - - def _verification_error(self, msg): - raise VerificationError(msg) - - def check_not_partial(self): - if self.partial and self.fixedlayout is None: - raise VerificationMissing(self._get_c_name()) - - def build_backend_type(self, ffi, finishlist): - self.check_not_partial() - finishlist.append(self) - # - return global_cache(self, ffi, 'new_%s_type' % self.kind, - self.get_official_name(), key=self) - - -class StructType(StructOrUnion): - kind = 'struct' - - -class UnionType(StructOrUnion): - kind = 'union' - - -class EnumType(StructOrUnionOrEnum): - kind = 'enum' - partial = False - partial_resolved = False - - def __init__(self, name, enumerators, enumvalues, baseinttype=None): - self.name = name - self.enumerators = enumerators - self.enumvalues = enumvalues - self.baseinttype = baseinttype - self.build_c_name_with_marker() - - def force_the_name(self, forcename): - StructOrUnionOrEnum.force_the_name(self, forcename) - if self.forcename is None: - name = self.get_official_name() - self.forcename = '$' + name.replace(' ', '_') - - def check_not_partial(self): - if self.partial and not self.partial_resolved: - raise VerificationMissing(self._get_c_name()) - - def build_backend_type(self, ffi, finishlist): - self.check_not_partial() - base_btype = self.build_baseinttype(ffi, finishlist) - return global_cache(self, ffi, 'new_enum_type', - self.get_official_name(), - self.enumerators, self.enumvalues, - base_btype, key=self) - - def build_baseinttype(self, ffi, finishlist): - if self.baseinttype is not None: - return self.baseinttype.get_cached_btype(ffi, finishlist) - # - if self.enumvalues: - smallest_value = min(self.enumvalues) - largest_value = max(self.enumvalues) - else: - import warnings - try: - # XXX! The goal is to ensure that the warnings.warn() - # will not suppress the warning. We want to get it - # several times if we reach this point several times. - __warningregistry__.clear() - except NameError: - pass - warnings.warn("%r has no values explicitly defined; " - "guessing that it is equivalent to 'unsigned int'" - % self._get_c_name()) - smallest_value = largest_value = 0 - if smallest_value < 0: # needs a signed type - sign = 1 - candidate1 = PrimitiveType("int") - candidate2 = PrimitiveType("long") - else: - sign = 0 - candidate1 = PrimitiveType("unsigned int") - candidate2 = PrimitiveType("unsigned long") - btype1 = candidate1.get_cached_btype(ffi, finishlist) - btype2 = candidate2.get_cached_btype(ffi, finishlist) - size1 = ffi.sizeof(btype1) - size2 = ffi.sizeof(btype2) - if (smallest_value >= ((-1) << (8*size1-1)) and - largest_value < (1 << (8*size1-sign))): - return btype1 - if (smallest_value >= ((-1) << (8*size2-1)) and - largest_value < (1 << (8*size2-sign))): - return btype2 - raise CDefError("%s values don't all fit into either 'long' " - "or 'unsigned long'" % self._get_c_name()) - -def unknown_type(name, structname=None): - if structname is None: - structname = '$%s' % name - tp = StructType(structname, None, None, None) - tp.force_the_name(name) - tp.origin = "unknown_type" - return tp - -def unknown_ptr_type(name, structname=None): - if structname is None: - structname = '$$%s' % name - tp = StructType(structname, None, None, None) - return NamedPointerType(tp, name) - - -global_lock = allocate_lock() -_typecache_cffi_backend = weakref.WeakValueDictionary() - -def get_typecache(backend): - # returns _typecache_cffi_backend if backend is the _cffi_backend - # module, or type(backend).__typecache if backend is an instance of - # CTypesBackend (or some FakeBackend class during tests) - if isinstance(backend, types.ModuleType): - return _typecache_cffi_backend - with global_lock: - if not hasattr(type(backend), '__typecache'): - type(backend).__typecache = weakref.WeakValueDictionary() - return type(backend).__typecache - -def global_cache(srctype, ffi, funcname, *args, **kwds): - key = kwds.pop('key', (funcname, args)) - assert not kwds - try: - return ffi._typecache[key] - except KeyError: - pass - try: - res = getattr(ffi._backend, funcname)(*args) - except NotImplementedError as e: - raise NotImplementedError("%s: %r: %s" % (funcname, srctype, e)) - # note that setdefault() on WeakValueDictionary is not atomic - # and contains a rare bug (http://bugs.python.org/issue19542); - # we have to use a lock and do it ourselves - cache = ffi._typecache - with global_lock: - res1 = cache.get(key) - if res1 is None: - cache[key] = res - return res - else: - return res1 - -def pointer_cache(ffi, BType): - return global_cache('?', ffi, 'new_pointer_type', BType) - -def attach_exception_info(e, name): - if e.args and type(e.args[0]) is str: - e.args = ('%s: %s' % (name, e.args[0]),) + e.args[1:] diff --git a/cffi/parse_c_type.h b/cffi/parse_c_type.h deleted file mode 100644 index 84e4ef8..0000000 --- a/cffi/parse_c_type.h +++ /dev/null @@ -1,181 +0,0 @@ - -/* This part is from file 'cffi/parse_c_type.h'. It is copied at the - beginning of C sources generated by CFFI's ffi.set_source(). */ - -typedef void *_cffi_opcode_t; - -#define _CFFI_OP(opcode, arg) (_cffi_opcode_t)(opcode | (((uintptr_t)(arg)) << 8)) -#define _CFFI_GETOP(cffi_opcode) ((unsigned char)(uintptr_t)cffi_opcode) -#define _CFFI_GETARG(cffi_opcode) (((intptr_t)cffi_opcode) >> 8) - -#define _CFFI_OP_PRIMITIVE 1 -#define _CFFI_OP_POINTER 3 -#define _CFFI_OP_ARRAY 5 -#define _CFFI_OP_OPEN_ARRAY 7 -#define _CFFI_OP_STRUCT_UNION 9 -#define _CFFI_OP_ENUM 11 -#define _CFFI_OP_FUNCTION 13 -#define _CFFI_OP_FUNCTION_END 15 -#define _CFFI_OP_NOOP 17 -#define _CFFI_OP_BITFIELD 19 -#define _CFFI_OP_TYPENAME 21 -#define _CFFI_OP_CPYTHON_BLTN_V 23 // varargs -#define _CFFI_OP_CPYTHON_BLTN_N 25 // noargs -#define _CFFI_OP_CPYTHON_BLTN_O 27 // O (i.e. a single arg) -#define _CFFI_OP_CONSTANT 29 -#define _CFFI_OP_CONSTANT_INT 31 -#define _CFFI_OP_GLOBAL_VAR 33 -#define _CFFI_OP_DLOPEN_FUNC 35 -#define _CFFI_OP_DLOPEN_CONST 37 -#define _CFFI_OP_GLOBAL_VAR_F 39 -#define _CFFI_OP_EXTERN_PYTHON 41 - -#define _CFFI_PRIM_VOID 0 -#define _CFFI_PRIM_BOOL 1 -#define _CFFI_PRIM_CHAR 2 -#define _CFFI_PRIM_SCHAR 3 -#define _CFFI_PRIM_UCHAR 4 -#define _CFFI_PRIM_SHORT 5 -#define _CFFI_PRIM_USHORT 6 -#define _CFFI_PRIM_INT 7 -#define _CFFI_PRIM_UINT 8 -#define _CFFI_PRIM_LONG 9 -#define _CFFI_PRIM_ULONG 10 -#define _CFFI_PRIM_LONGLONG 11 -#define _CFFI_PRIM_ULONGLONG 12 -#define _CFFI_PRIM_FLOAT 13 -#define _CFFI_PRIM_DOUBLE 14 -#define _CFFI_PRIM_LONGDOUBLE 15 - -#define _CFFI_PRIM_WCHAR 16 -#define _CFFI_PRIM_INT8 17 -#define _CFFI_PRIM_UINT8 18 -#define _CFFI_PRIM_INT16 19 -#define _CFFI_PRIM_UINT16 20 -#define _CFFI_PRIM_INT32 21 -#define _CFFI_PRIM_UINT32 22 -#define _CFFI_PRIM_INT64 23 -#define _CFFI_PRIM_UINT64 24 -#define _CFFI_PRIM_INTPTR 25 -#define _CFFI_PRIM_UINTPTR 26 -#define _CFFI_PRIM_PTRDIFF 27 -#define _CFFI_PRIM_SIZE 28 -#define _CFFI_PRIM_SSIZE 29 -#define _CFFI_PRIM_INT_LEAST8 30 -#define _CFFI_PRIM_UINT_LEAST8 31 -#define _CFFI_PRIM_INT_LEAST16 32 -#define _CFFI_PRIM_UINT_LEAST16 33 -#define _CFFI_PRIM_INT_LEAST32 34 -#define _CFFI_PRIM_UINT_LEAST32 35 -#define _CFFI_PRIM_INT_LEAST64 36 -#define _CFFI_PRIM_UINT_LEAST64 37 -#define _CFFI_PRIM_INT_FAST8 38 -#define _CFFI_PRIM_UINT_FAST8 39 -#define _CFFI_PRIM_INT_FAST16 40 -#define _CFFI_PRIM_UINT_FAST16 41 -#define _CFFI_PRIM_INT_FAST32 42 -#define _CFFI_PRIM_UINT_FAST32 43 -#define _CFFI_PRIM_INT_FAST64 44 -#define _CFFI_PRIM_UINT_FAST64 45 -#define _CFFI_PRIM_INTMAX 46 -#define _CFFI_PRIM_UINTMAX 47 -#define _CFFI_PRIM_FLOATCOMPLEX 48 -#define _CFFI_PRIM_DOUBLECOMPLEX 49 -#define _CFFI_PRIM_CHAR16 50 -#define _CFFI_PRIM_CHAR32 51 - -#define _CFFI__NUM_PRIM 52 -#define _CFFI__UNKNOWN_PRIM (-1) -#define _CFFI__UNKNOWN_FLOAT_PRIM (-2) -#define _CFFI__UNKNOWN_LONG_DOUBLE (-3) - -#define _CFFI__IO_FILE_STRUCT (-1) - - -struct _cffi_global_s { - const char *name; - void *address; - _cffi_opcode_t type_op; - void *size_or_direct_fn; // OP_GLOBAL_VAR: size, or 0 if unknown - // OP_CPYTHON_BLTN_*: addr of direct function -}; - -struct _cffi_getconst_s { - unsigned long long value; - const struct _cffi_type_context_s *ctx; - int gindex; -}; - -struct _cffi_struct_union_s { - const char *name; - int type_index; // -> _cffi_types, on a OP_STRUCT_UNION - int flags; // _CFFI_F_* flags below - size_t size; - int alignment; - int first_field_index; // -> _cffi_fields array - int num_fields; -}; -#define _CFFI_F_UNION 0x01 // is a union, not a struct -#define _CFFI_F_CHECK_FIELDS 0x02 // complain if fields are not in the - // "standard layout" or if some are missing -#define _CFFI_F_PACKED 0x04 // for CHECK_FIELDS, assume a packed struct -#define _CFFI_F_EXTERNAL 0x08 // in some other ffi.include() -#define _CFFI_F_OPAQUE 0x10 // opaque - -struct _cffi_field_s { - const char *name; - size_t field_offset; - size_t field_size; - _cffi_opcode_t field_type_op; -}; - -struct _cffi_enum_s { - const char *name; - int type_index; // -> _cffi_types, on a OP_ENUM - int type_prim; // _CFFI_PRIM_xxx - const char *enumerators; // comma-delimited string -}; - -struct _cffi_typename_s { - const char *name; - int type_index; /* if opaque, points to a possibly artificial - OP_STRUCT which is itself opaque */ -}; - -struct _cffi_type_context_s { - _cffi_opcode_t *types; - const struct _cffi_global_s *globals; - const struct _cffi_field_s *fields; - const struct _cffi_struct_union_s *struct_unions; - const struct _cffi_enum_s *enums; - const struct _cffi_typename_s *typenames; - int num_globals; - int num_struct_unions; - int num_enums; - int num_typenames; - const char *const *includes; - int num_types; - int flags; /* future extension */ -}; - -struct _cffi_parse_info_s { - const struct _cffi_type_context_s *ctx; - _cffi_opcode_t *output; - unsigned int output_size; - size_t error_location; - const char *error_message; -}; - -struct _cffi_externpy_s { - const char *name; - size_t size_of_result; - void *reserved1, *reserved2; -}; - -#ifdef _CFFI_INTERNAL -static int parse_c_type(struct _cffi_parse_info_s *info, const char *input); -static int search_in_globals(const struct _cffi_type_context_s *ctx, - const char *search, size_t search_len); -static int search_in_struct_unions(const struct _cffi_type_context_s *ctx, - const char *search, size_t search_len); -#endif diff --git a/cffi/pkgconfig.py b/cffi/pkgconfig.py deleted file mode 100644 index 5c93f15..0000000 --- a/cffi/pkgconfig.py +++ /dev/null @@ -1,121 +0,0 @@ -# pkg-config, https://www.freedesktop.org/wiki/Software/pkg-config/ integration for cffi -import sys, os, subprocess - -from .error import PkgConfigError - - -def merge_flags(cfg1, cfg2): - """Merge values from cffi config flags cfg2 to cf1 - - Example: - merge_flags({"libraries": ["one"]}, {"libraries": ["two"]}) - {"libraries": ["one", "two"]} - """ - for key, value in cfg2.items(): - if key not in cfg1: - cfg1[key] = value - else: - if not isinstance(cfg1[key], list): - raise TypeError("cfg1[%r] should be a list of strings" % (key,)) - if not isinstance(value, list): - raise TypeError("cfg2[%r] should be a list of strings" % (key,)) - cfg1[key].extend(value) - return cfg1 - - -def call(libname, flag, encoding=sys.getfilesystemencoding()): - """Calls pkg-config and returns the output if found - """ - a = ["pkg-config", "--print-errors"] - a.append(flag) - a.append(libname) - try: - pc = subprocess.Popen(a, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - except EnvironmentError as e: - raise PkgConfigError("cannot run pkg-config: %s" % (str(e).strip(),)) - - bout, berr = pc.communicate() - if pc.returncode != 0: - try: - berr = berr.decode(encoding) - except Exception: - pass - raise PkgConfigError(berr.strip()) - - if sys.version_info >= (3,) and not isinstance(bout, str): # Python 3.x - try: - bout = bout.decode(encoding) - except UnicodeDecodeError: - raise PkgConfigError("pkg-config %s %s returned bytes that cannot " - "be decoded with encoding %r:\n%r" % - (flag, libname, encoding, bout)) - - if os.altsep != '\\' and '\\' in bout: - raise PkgConfigError("pkg-config %s %s returned an unsupported " - "backslash-escaped output:\n%r" % - (flag, libname, bout)) - return bout - - -def flags_from_pkgconfig(libs): - r"""Return compiler line flags for FFI.set_source based on pkg-config output - - Usage - ... - ffibuilder.set_source("_foo", pkgconfig = ["libfoo", "libbar >= 1.8.3"]) - - If pkg-config is installed on build machine, then arguments include_dirs, - library_dirs, libraries, define_macros, extra_compile_args and - extra_link_args are extended with an output of pkg-config for libfoo and - libbar. - - Raises PkgConfigError in case the pkg-config call fails. - """ - - def get_include_dirs(string): - return [x[2:] for x in string.split() if x.startswith("-I")] - - def get_library_dirs(string): - return [x[2:] for x in string.split() if x.startswith("-L")] - - def get_libraries(string): - return [x[2:] for x in string.split() if x.startswith("-l")] - - # convert -Dfoo=bar to list of tuples [("foo", "bar")] expected by distutils - def get_macros(string): - def _macro(x): - x = x[2:] # drop "-D" - if '=' in x: - return tuple(x.split("=", 1)) # "-Dfoo=bar" => ("foo", "bar") - else: - return (x, None) # "-Dfoo" => ("foo", None) - return [_macro(x) for x in string.split() if x.startswith("-D")] - - def get_other_cflags(string): - return [x for x in string.split() if not x.startswith("-I") and - not x.startswith("-D")] - - def get_other_libs(string): - return [x for x in string.split() if not x.startswith("-L") and - not x.startswith("-l")] - - # return kwargs for given libname - def kwargs(libname): - fse = sys.getfilesystemencoding() - all_cflags = call(libname, "--cflags") - all_libs = call(libname, "--libs") - return { - "include_dirs": get_include_dirs(all_cflags), - "library_dirs": get_library_dirs(all_libs), - "libraries": get_libraries(all_libs), - "define_macros": get_macros(all_cflags), - "extra_compile_args": get_other_cflags(all_cflags), - "extra_link_args": get_other_libs(all_libs), - } - - # merge all arguments together - ret = {} - for libname in libs: - lib_flags = kwargs(libname) - merge_flags(ret, lib_flags) - return ret diff --git a/cffi/recompiler.py b/cffi/recompiler.py deleted file mode 100644 index 86b37d7..0000000 --- a/cffi/recompiler.py +++ /dev/null @@ -1,1581 +0,0 @@ -import os, sys, io -from . import ffiplatform, model -from .error import VerificationError -from .cffi_opcode import * - -VERSION_BASE = 0x2601 -VERSION_EMBEDDED = 0x2701 -VERSION_CHAR16CHAR32 = 0x2801 - -USE_LIMITED_API = (sys.platform != 'win32' or sys.version_info < (3, 0) or - sys.version_info >= (3, 5)) - - -class GlobalExpr: - def __init__(self, name, address, type_op, size=0, check_value=0): - self.name = name - self.address = address - self.type_op = type_op - self.size = size - self.check_value = check_value - - def as_c_expr(self): - return ' { "%s", (void *)%s, %s, (void *)%s },' % ( - self.name, self.address, self.type_op.as_c_expr(), self.size) - - def as_python_expr(self): - return "b'%s%s',%d" % (self.type_op.as_python_bytes(), self.name, - self.check_value) - -class FieldExpr: - def __init__(self, name, field_offset, field_size, fbitsize, field_type_op): - self.name = name - self.field_offset = field_offset - self.field_size = field_size - self.fbitsize = fbitsize - self.field_type_op = field_type_op - - def as_c_expr(self): - spaces = " " * len(self.name) - return (' { "%s", %s,\n' % (self.name, self.field_offset) + - ' %s %s,\n' % (spaces, self.field_size) + - ' %s %s },' % (spaces, self.field_type_op.as_c_expr())) - - def as_python_expr(self): - raise NotImplementedError - - def as_field_python_expr(self): - if self.field_type_op.op == OP_NOOP: - size_expr = '' - elif self.field_type_op.op == OP_BITFIELD: - size_expr = format_four_bytes(self.fbitsize) - else: - raise NotImplementedError - return "b'%s%s%s'" % (self.field_type_op.as_python_bytes(), - size_expr, - self.name) - -class StructUnionExpr: - def __init__(self, name, type_index, flags, size, alignment, comment, - first_field_index, c_fields): - self.name = name - self.type_index = type_index - self.flags = flags - self.size = size - self.alignment = alignment - self.comment = comment - self.first_field_index = first_field_index - self.c_fields = c_fields - - def as_c_expr(self): - return (' { "%s", %d, %s,' % (self.name, self.type_index, self.flags) - + '\n %s, %s, ' % (self.size, self.alignment) - + '%d, %d ' % (self.first_field_index, len(self.c_fields)) - + ('/* %s */ ' % self.comment if self.comment else '') - + '},') - - def as_python_expr(self): - flags = eval(self.flags, G_FLAGS) - fields_expr = [c_field.as_field_python_expr() - for c_field in self.c_fields] - return "(b'%s%s%s',%s)" % ( - format_four_bytes(self.type_index), - format_four_bytes(flags), - self.name, - ','.join(fields_expr)) - -class EnumExpr: - def __init__(self, name, type_index, size, signed, allenums): - self.name = name - self.type_index = type_index - self.size = size - self.signed = signed - self.allenums = allenums - - def as_c_expr(self): - return (' { "%s", %d, _cffi_prim_int(%s, %s),\n' - ' "%s" },' % (self.name, self.type_index, - self.size, self.signed, self.allenums)) - - def as_python_expr(self): - prim_index = { - (1, 0): PRIM_UINT8, (1, 1): PRIM_INT8, - (2, 0): PRIM_UINT16, (2, 1): PRIM_INT16, - (4, 0): PRIM_UINT32, (4, 1): PRIM_INT32, - (8, 0): PRIM_UINT64, (8, 1): PRIM_INT64, - }[self.size, self.signed] - return "b'%s%s%s\\x00%s'" % (format_four_bytes(self.type_index), - format_four_bytes(prim_index), - self.name, self.allenums) - -class TypenameExpr: - def __init__(self, name, type_index): - self.name = name - self.type_index = type_index - - def as_c_expr(self): - return ' { "%s", %d },' % (self.name, self.type_index) - - def as_python_expr(self): - return "b'%s%s'" % (format_four_bytes(self.type_index), self.name) - - -# ____________________________________________________________ - - -class Recompiler: - _num_externpy = 0 - - def __init__(self, ffi, module_name, target_is_python=False): - self.ffi = ffi - self.module_name = module_name - self.target_is_python = target_is_python - self._version = VERSION_BASE - - def needs_version(self, ver): - self._version = max(self._version, ver) - - def collect_type_table(self): - self._typesdict = {} - self._generate("collecttype") - # - all_decls = sorted(self._typesdict, key=str) - # - # prepare all FUNCTION bytecode sequences first - self.cffi_types = [] - for tp in all_decls: - if tp.is_raw_function: - assert self._typesdict[tp] is None - self._typesdict[tp] = len(self.cffi_types) - self.cffi_types.append(tp) # placeholder - for tp1 in tp.args: - assert isinstance(tp1, (model.VoidType, - model.BasePrimitiveType, - model.PointerType, - model.StructOrUnionOrEnum, - model.FunctionPtrType)) - if self._typesdict[tp1] is None: - self._typesdict[tp1] = len(self.cffi_types) - self.cffi_types.append(tp1) # placeholder - self.cffi_types.append('END') # placeholder - # - # prepare all OTHER bytecode sequences - for tp in all_decls: - if not tp.is_raw_function and self._typesdict[tp] is None: - self._typesdict[tp] = len(self.cffi_types) - self.cffi_types.append(tp) # placeholder - if tp.is_array_type and tp.length is not None: - self.cffi_types.append('LEN') # placeholder - assert None not in self._typesdict.values() - # - # collect all structs and unions and enums - self._struct_unions = {} - self._enums = {} - for tp in all_decls: - if isinstance(tp, model.StructOrUnion): - self._struct_unions[tp] = None - elif isinstance(tp, model.EnumType): - self._enums[tp] = None - for i, tp in enumerate(sorted(self._struct_unions, - key=lambda tp: tp.name)): - self._struct_unions[tp] = i - for i, tp in enumerate(sorted(self._enums, - key=lambda tp: tp.name)): - self._enums[tp] = i - # - # emit all bytecode sequences now - for tp in all_decls: - method = getattr(self, '_emit_bytecode_' + tp.__class__.__name__) - method(tp, self._typesdict[tp]) - # - # consistency check - for op in self.cffi_types: - assert isinstance(op, CffiOp) - self.cffi_types = tuple(self.cffi_types) # don't change any more - - def _enum_fields(self, tp): - # When producing C, expand all anonymous struct/union fields. - # That's necessary to have C code checking the offsets of the - # individual fields contained in them. When producing Python, - # don't do it and instead write it like it is, with the - # corresponding fields having an empty name. Empty names are - # recognized at runtime when we import the generated Python - # file. - expand_anonymous_struct_union = not self.target_is_python - return tp.enumfields(expand_anonymous_struct_union) - - def _do_collect_type(self, tp): - if not isinstance(tp, model.BaseTypeByIdentity): - if isinstance(tp, tuple): - for x in tp: - self._do_collect_type(x) - return - if tp not in self._typesdict: - self._typesdict[tp] = None - if isinstance(tp, model.FunctionPtrType): - self._do_collect_type(tp.as_raw_function()) - elif isinstance(tp, model.StructOrUnion): - if tp.fldtypes is not None and ( - tp not in self.ffi._parser._included_declarations): - for name1, tp1, _, _ in self._enum_fields(tp): - self._do_collect_type(self._field_type(tp, name1, tp1)) - else: - for _, x in tp._get_items(): - self._do_collect_type(x) - - def _generate(self, step_name): - lst = self.ffi._parser._declarations.items() - for name, (tp, quals) in sorted(lst): - kind, realname = name.split(' ', 1) - try: - method = getattr(self, '_generate_cpy_%s_%s' % (kind, - step_name)) - except AttributeError: - raise VerificationError( - "not implemented in recompile(): %r" % name) - try: - self._current_quals = quals - method(tp, realname) - except Exception as e: - model.attach_exception_info(e, name) - raise - - # ---------- - - ALL_STEPS = ["global", "field", "struct_union", "enum", "typename"] - - def collect_step_tables(self): - # collect the declarations for '_cffi_globals', '_cffi_typenames', etc. - self._lsts = {} - for step_name in self.ALL_STEPS: - self._lsts[step_name] = [] - self._seen_struct_unions = set() - self._generate("ctx") - self._add_missing_struct_unions() - # - for step_name in self.ALL_STEPS: - lst = self._lsts[step_name] - if step_name != "field": - lst.sort(key=lambda entry: entry.name) - self._lsts[step_name] = tuple(lst) # don't change any more - # - # check for a possible internal inconsistency: _cffi_struct_unions - # should have been generated with exactly self._struct_unions - lst = self._lsts["struct_union"] - for tp, i in self._struct_unions.items(): - assert i < len(lst) - assert lst[i].name == tp.name - assert len(lst) == len(self._struct_unions) - # same with enums - lst = self._lsts["enum"] - for tp, i in self._enums.items(): - assert i < len(lst) - assert lst[i].name == tp.name - assert len(lst) == len(self._enums) - - # ---------- - - def _prnt(self, what=''): - self._f.write(what + '\n') - - def write_source_to_f(self, f, preamble): - if self.target_is_python: - assert preamble is None - self.write_py_source_to_f(f) - else: - assert preamble is not None - self.write_c_source_to_f(f, preamble) - - def _rel_readlines(self, filename): - g = open(os.path.join(os.path.dirname(__file__), filename), 'r') - lines = g.readlines() - g.close() - return lines - - def write_c_source_to_f(self, f, preamble): - self._f = f - prnt = self._prnt - if self.ffi._embedding is not None: - prnt('#define _CFFI_USE_EMBEDDING') - if not USE_LIMITED_API: - prnt('#define _CFFI_NO_LIMITED_API') - # - # first the '#include' (actually done by inlining the file's content) - lines = self._rel_readlines('_cffi_include.h') - i = lines.index('#include "parse_c_type.h"\n') - lines[i:i+1] = self._rel_readlines('parse_c_type.h') - prnt(''.join(lines)) - # - # if we have ffi._embedding != None, we give it here as a macro - # and include an extra file - base_module_name = self.module_name.split('.')[-1] - if self.ffi._embedding is not None: - prnt('#define _CFFI_MODULE_NAME "%s"' % (self.module_name,)) - prnt('static const char _CFFI_PYTHON_STARTUP_CODE[] = {') - self._print_string_literal_in_array(self.ffi._embedding) - prnt('0 };') - prnt('#ifdef PYPY_VERSION') - prnt('# define _CFFI_PYTHON_STARTUP_FUNC _cffi_pypyinit_%s' % ( - base_module_name,)) - prnt('#elif PY_MAJOR_VERSION >= 3') - prnt('# define _CFFI_PYTHON_STARTUP_FUNC PyInit_%s' % ( - base_module_name,)) - prnt('#else') - prnt('# define _CFFI_PYTHON_STARTUP_FUNC init%s' % ( - base_module_name,)) - prnt('#endif') - lines = self._rel_readlines('_embedding.h') - i = lines.index('#include "_cffi_errors.h"\n') - lines[i:i+1] = self._rel_readlines('_cffi_errors.h') - prnt(''.join(lines)) - self.needs_version(VERSION_EMBEDDED) - # - # then paste the C source given by the user, verbatim. - prnt('/************************************************************/') - prnt() - prnt(preamble) - prnt() - prnt('/************************************************************/') - prnt() - # - # the declaration of '_cffi_types' - prnt('static void *_cffi_types[] = {') - typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()]) - for i, op in enumerate(self.cffi_types): - comment = '' - if i in typeindex2type: - comment = ' // ' + typeindex2type[i]._get_c_name() - prnt('/* %2d */ %s,%s' % (i, op.as_c_expr(), comment)) - if not self.cffi_types: - prnt(' 0') - prnt('};') - prnt() - # - # call generate_cpy_xxx_decl(), for every xxx found from - # ffi._parser._declarations. This generates all the functions. - self._seen_constants = set() - self._generate("decl") - # - # the declaration of '_cffi_globals' and '_cffi_typenames' - nums = {} - for step_name in self.ALL_STEPS: - lst = self._lsts[step_name] - nums[step_name] = len(lst) - if nums[step_name] > 0: - prnt('static const struct _cffi_%s_s _cffi_%ss[] = {' % ( - step_name, step_name)) - for entry in lst: - prnt(entry.as_c_expr()) - prnt('};') - prnt() - # - # the declaration of '_cffi_includes' - if self.ffi._included_ffis: - prnt('static const char * const _cffi_includes[] = {') - for ffi_to_include in self.ffi._included_ffis: - try: - included_module_name, included_source = ( - ffi_to_include._assigned_source[:2]) - except AttributeError: - raise VerificationError( - "ffi object %r includes %r, but the latter has not " - "been prepared with set_source()" % ( - self.ffi, ffi_to_include,)) - if included_source is None: - raise VerificationError( - "not implemented yet: ffi.include() of a Python-based " - "ffi inside a C-based ffi") - prnt(' "%s",' % (included_module_name,)) - prnt(' NULL') - prnt('};') - prnt() - # - # the declaration of '_cffi_type_context' - prnt('static const struct _cffi_type_context_s _cffi_type_context = {') - prnt(' _cffi_types,') - for step_name in self.ALL_STEPS: - if nums[step_name] > 0: - prnt(' _cffi_%ss,' % step_name) - else: - prnt(' NULL, /* no %ss */' % step_name) - for step_name in self.ALL_STEPS: - if step_name != "field": - prnt(' %d, /* num_%ss */' % (nums[step_name], step_name)) - if self.ffi._included_ffis: - prnt(' _cffi_includes,') - else: - prnt(' NULL, /* no includes */') - prnt(' %d, /* num_types */' % (len(self.cffi_types),)) - flags = 0 - if self._num_externpy: - flags |= 1 # set to mean that we use extern "Python" - prnt(' %d, /* flags */' % flags) - prnt('};') - prnt() - # - # the init function - prnt('#ifdef __GNUC__') - prnt('# pragma GCC visibility push(default) /* for -fvisibility= */') - prnt('#endif') - prnt() - prnt('#ifdef PYPY_VERSION') - prnt('PyMODINIT_FUNC') - prnt('_cffi_pypyinit_%s(const void *p[])' % (base_module_name,)) - prnt('{') - if self._num_externpy: - prnt(' if (((intptr_t)p[0]) >= 0x0A03) {') - prnt(' _cffi_call_python_org = ' - '(void(*)(struct _cffi_externpy_s *, char *))p[1];') - prnt(' }') - prnt(' p[0] = (const void *)0x%x;' % self._version) - prnt(' p[1] = &_cffi_type_context;') - prnt('#if PY_MAJOR_VERSION >= 3') - prnt(' return NULL;') - prnt('#endif') - prnt('}') - # on Windows, distutils insists on putting init_cffi_xyz in - # 'export_symbols', so instead of fighting it, just give up and - # give it one - prnt('# ifdef _MSC_VER') - prnt(' PyMODINIT_FUNC') - prnt('# if PY_MAJOR_VERSION >= 3') - prnt(' PyInit_%s(void) { return NULL; }' % (base_module_name,)) - prnt('# else') - prnt(' init%s(void) { }' % (base_module_name,)) - prnt('# endif') - prnt('# endif') - prnt('#elif PY_MAJOR_VERSION >= 3') - prnt('PyMODINIT_FUNC') - prnt('PyInit_%s(void)' % (base_module_name,)) - prnt('{') - prnt(' return _cffi_init("%s", 0x%x, &_cffi_type_context);' % ( - self.module_name, self._version)) - prnt('}') - prnt('#else') - prnt('PyMODINIT_FUNC') - prnt('init%s(void)' % (base_module_name,)) - prnt('{') - prnt(' _cffi_init("%s", 0x%x, &_cffi_type_context);' % ( - self.module_name, self._version)) - prnt('}') - prnt('#endif') - prnt() - prnt('#ifdef __GNUC__') - prnt('# pragma GCC visibility pop') - prnt('#endif') - self._version = None - - def _to_py(self, x): - if isinstance(x, str): - return "b'%s'" % (x,) - if isinstance(x, (list, tuple)): - rep = [self._to_py(item) for item in x] - if len(rep) == 1: - rep.append('') - return "(%s)" % (','.join(rep),) - return x.as_python_expr() # Py2: unicode unexpected; Py3: bytes unexp. - - def write_py_source_to_f(self, f): - self._f = f - prnt = self._prnt - # - # header - prnt("# auto-generated file") - prnt("import _cffi_backend") - # - # the 'import' of the included ffis - num_includes = len(self.ffi._included_ffis or ()) - for i in range(num_includes): - ffi_to_include = self.ffi._included_ffis[i] - try: - included_module_name, included_source = ( - ffi_to_include._assigned_source[:2]) - except AttributeError: - raise VerificationError( - "ffi object %r includes %r, but the latter has not " - "been prepared with set_source()" % ( - self.ffi, ffi_to_include,)) - if included_source is not None: - raise VerificationError( - "not implemented yet: ffi.include() of a C-based " - "ffi inside a Python-based ffi") - prnt('from %s import ffi as _ffi%d' % (included_module_name, i)) - prnt() - prnt("ffi = _cffi_backend.FFI('%s'," % (self.module_name,)) - prnt(" _version = 0x%x," % (self._version,)) - self._version = None - # - # the '_types' keyword argument - self.cffi_types = tuple(self.cffi_types) # don't change any more - types_lst = [op.as_python_bytes() for op in self.cffi_types] - prnt(' _types = %s,' % (self._to_py(''.join(types_lst)),)) - typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()]) - # - # the keyword arguments from ALL_STEPS - for step_name in self.ALL_STEPS: - lst = self._lsts[step_name] - if len(lst) > 0 and step_name != "field": - prnt(' _%ss = %s,' % (step_name, self._to_py(lst))) - # - # the '_includes' keyword argument - if num_includes > 0: - prnt(' _includes = (%s,),' % ( - ', '.join(['_ffi%d' % i for i in range(num_includes)]),)) - # - # the footer - prnt(')') - - # ---------- - - def _gettypenum(self, type): - # a KeyError here is a bug. please report it! :-) - return self._typesdict[type] - - def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode): - extraarg = '' - if isinstance(tp, model.BasePrimitiveType) and not tp.is_complex_type(): - if tp.is_integer_type() and tp.name != '_Bool': - converter = '_cffi_to_c_int' - extraarg = ', %s' % tp.name - elif isinstance(tp, model.UnknownFloatType): - # don't check with is_float_type(): it may be a 'long - # double' here, and _cffi_to_c_double would loose precision - converter = '(%s)_cffi_to_c_double' % (tp.get_c_name(''),) - else: - cname = tp.get_c_name('') - converter = '(%s)_cffi_to_c_%s' % (cname, - tp.name.replace(' ', '_')) - if cname in ('char16_t', 'char32_t'): - self.needs_version(VERSION_CHAR16CHAR32) - errvalue = '-1' - # - elif isinstance(tp, model.PointerType): - self._convert_funcarg_to_c_ptr_or_array(tp, fromvar, - tovar, errcode) - return - # - elif (isinstance(tp, model.StructOrUnionOrEnum) or - isinstance(tp, model.BasePrimitiveType)): - # a struct (not a struct pointer) as a function argument; - # or, a complex (the same code works) - self._prnt(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)' - % (tovar, self._gettypenum(tp), fromvar)) - self._prnt(' %s;' % errcode) - return - # - elif isinstance(tp, model.FunctionPtrType): - converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('') - extraarg = ', _cffi_type(%d)' % self._gettypenum(tp) - errvalue = 'NULL' - # - else: - raise NotImplementedError(tp) - # - self._prnt(' %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg)) - self._prnt(' if (%s == (%s)%s && PyErr_Occurred())' % ( - tovar, tp.get_c_name(''), errvalue)) - self._prnt(' %s;' % errcode) - - def _extra_local_variables(self, tp, localvars, freelines): - if isinstance(tp, model.PointerType): - localvars.add('Py_ssize_t datasize') - localvars.add('struct _cffi_freeme_s *large_args_free = NULL') - freelines.add('if (large_args_free != NULL)' - ' _cffi_free_array_arguments(large_args_free);') - - def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode): - self._prnt(' datasize = _cffi_prepare_pointer_call_argument(') - self._prnt(' _cffi_type(%d), %s, (char **)&%s);' % ( - self._gettypenum(tp), fromvar, tovar)) - self._prnt(' if (datasize != 0) {') - self._prnt(' %s = ((size_t)datasize) <= 640 ? ' - '(%s)alloca((size_t)datasize) : NULL;' % ( - tovar, tp.get_c_name(''))) - self._prnt(' if (_cffi_convert_array_argument(_cffi_type(%d), %s, ' - '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar)) - self._prnt(' datasize, &large_args_free) < 0)') - self._prnt(' %s;' % errcode) - self._prnt(' }') - - def _convert_expr_from_c(self, tp, var, context): - if isinstance(tp, model.BasePrimitiveType): - if tp.is_integer_type() and tp.name != '_Bool': - return '_cffi_from_c_int(%s, %s)' % (var, tp.name) - elif isinstance(tp, model.UnknownFloatType): - return '_cffi_from_c_double(%s)' % (var,) - elif tp.name != 'long double' and not tp.is_complex_type(): - cname = tp.name.replace(' ', '_') - if cname in ('char16_t', 'char32_t'): - self.needs_version(VERSION_CHAR16CHAR32) - return '_cffi_from_c_%s(%s)' % (cname, var) - else: - return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - elif isinstance(tp, (model.PointerType, model.FunctionPtrType)): - return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - elif isinstance(tp, model.ArrayType): - return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( - var, self._gettypenum(model.PointerType(tp.item))) - elif isinstance(tp, model.StructOrUnion): - if tp.fldnames is None: - raise TypeError("'%s' is used as %s, but is opaque" % ( - tp._get_c_name(), context)) - return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - elif isinstance(tp, model.EnumType): - return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - else: - raise NotImplementedError(tp) - - # ---------- - # typedefs - - def _typedef_type(self, tp, name): - return self._global_type(tp, "(*(%s *)0)" % (name,)) - - def _generate_cpy_typedef_collecttype(self, tp, name): - self._do_collect_type(self._typedef_type(tp, name)) - - def _generate_cpy_typedef_decl(self, tp, name): - pass - - def _typedef_ctx(self, tp, name): - type_index = self._typesdict[tp] - self._lsts["typename"].append(TypenameExpr(name, type_index)) - - def _generate_cpy_typedef_ctx(self, tp, name): - tp = self._typedef_type(tp, name) - self._typedef_ctx(tp, name) - if getattr(tp, "origin", None) == "unknown_type": - self._struct_ctx(tp, tp.name, approxname=None) - elif isinstance(tp, model.NamedPointerType): - self._struct_ctx(tp.totype, tp.totype.name, approxname=tp.name, - named_ptr=tp) - - # ---------- - # function declarations - - def _generate_cpy_function_collecttype(self, tp, name): - self._do_collect_type(tp.as_raw_function()) - if tp.ellipsis and not self.target_is_python: - self._do_collect_type(tp) - - def _generate_cpy_function_decl(self, tp, name): - assert not self.target_is_python - assert isinstance(tp, model.FunctionPtrType) - if tp.ellipsis: - # cannot support vararg functions better than this: check for its - # exact type (including the fixed arguments), and build it as a - # constant function pointer (no CPython wrapper) - self._generate_cpy_constant_decl(tp, name) - return - prnt = self._prnt - numargs = len(tp.args) - if numargs == 0: - argname = 'noarg' - elif numargs == 1: - argname = 'arg0' - else: - argname = 'args' - # - # ------------------------------ - # the 'd' version of the function, only for addressof(lib, 'func') - arguments = [] - call_arguments = [] - context = 'argument of %s' % name - for i, type in enumerate(tp.args): - arguments.append(type.get_c_name(' x%d' % i, context)) - call_arguments.append('x%d' % i) - repr_arguments = ', '.join(arguments) - repr_arguments = repr_arguments or 'void' - if tp.abi: - abi = tp.abi + ' ' - else: - abi = '' - name_and_arguments = '%s_cffi_d_%s(%s)' % (abi, name, repr_arguments) - prnt('static %s' % (tp.result.get_c_name(name_and_arguments),)) - prnt('{') - call_arguments = ', '.join(call_arguments) - result_code = 'return ' - if isinstance(tp.result, model.VoidType): - result_code = '' - prnt(' %s%s(%s);' % (result_code, name, call_arguments)) - prnt('}') - # - prnt('#ifndef PYPY_VERSION') # ------------------------------ - # - prnt('static PyObject *') - prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname)) - prnt('{') - # - context = 'argument of %s' % name - for i, type in enumerate(tp.args): - arg = type.get_c_name(' x%d' % i, context) - prnt(' %s;' % arg) - # - localvars = set() - freelines = set() - for type in tp.args: - self._extra_local_variables(type, localvars, freelines) - for decl in sorted(localvars): - prnt(' %s;' % (decl,)) - # - if not isinstance(tp.result, model.VoidType): - result_code = 'result = ' - context = 'result of %s' % name - result_decl = ' %s;' % tp.result.get_c_name(' result', context) - prnt(result_decl) - prnt(' PyObject *pyresult;') - else: - result_decl = None - result_code = '' - # - if len(tp.args) > 1: - rng = range(len(tp.args)) - for i in rng: - prnt(' PyObject *arg%d;' % i) - prnt() - prnt(' if (!PyArg_UnpackTuple(args, "%s", %d, %d, %s))' % ( - name, len(rng), len(rng), - ', '.join(['&arg%d' % i for i in rng]))) - prnt(' return NULL;') - prnt() - # - for i, type in enumerate(tp.args): - self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i, - 'return NULL') - prnt() - # - prnt(' Py_BEGIN_ALLOW_THREADS') - prnt(' _cffi_restore_errno();') - call_arguments = ['x%d' % i for i in range(len(tp.args))] - call_arguments = ', '.join(call_arguments) - prnt(' { %s%s(%s); }' % (result_code, name, call_arguments)) - prnt(' _cffi_save_errno();') - prnt(' Py_END_ALLOW_THREADS') - prnt() - # - prnt(' (void)self; /* unused */') - if numargs == 0: - prnt(' (void)noarg; /* unused */') - if result_code: - prnt(' pyresult = %s;' % - self._convert_expr_from_c(tp.result, 'result', 'result type')) - for freeline in freelines: - prnt(' ' + freeline) - prnt(' return pyresult;') - else: - for freeline in freelines: - prnt(' ' + freeline) - prnt(' Py_INCREF(Py_None);') - prnt(' return Py_None;') - prnt('}') - # - prnt('#else') # ------------------------------ - # - # the PyPy version: need to replace struct/union arguments with - # pointers, and if the result is a struct/union, insert a first - # arg that is a pointer to the result. We also do that for - # complex args and return type. - def need_indirection(type): - return (isinstance(type, model.StructOrUnion) or - (isinstance(type, model.PrimitiveType) and - type.is_complex_type())) - difference = False - arguments = [] - call_arguments = [] - context = 'argument of %s' % name - for i, type in enumerate(tp.args): - indirection = '' - if need_indirection(type): - indirection = '*' - difference = True - arg = type.get_c_name(' %sx%d' % (indirection, i), context) - arguments.append(arg) - call_arguments.append('%sx%d' % (indirection, i)) - tp_result = tp.result - if need_indirection(tp_result): - context = 'result of %s' % name - arg = tp_result.get_c_name(' *result', context) - arguments.insert(0, arg) - tp_result = model.void_type - result_decl = None - result_code = '*result = ' - difference = True - if difference: - repr_arguments = ', '.join(arguments) - repr_arguments = repr_arguments or 'void' - name_and_arguments = '%s_cffi_f_%s(%s)' % (abi, name, - repr_arguments) - prnt('static %s' % (tp_result.get_c_name(name_and_arguments),)) - prnt('{') - if result_decl: - prnt(result_decl) - call_arguments = ', '.join(call_arguments) - prnt(' { %s%s(%s); }' % (result_code, name, call_arguments)) - if result_decl: - prnt(' return result;') - prnt('}') - else: - prnt('# define _cffi_f_%s _cffi_d_%s' % (name, name)) - # - prnt('#endif') # ------------------------------ - prnt() - - def _generate_cpy_function_ctx(self, tp, name): - if tp.ellipsis and not self.target_is_python: - self._generate_cpy_constant_ctx(tp, name) - return - type_index = self._typesdict[tp.as_raw_function()] - numargs = len(tp.args) - if self.target_is_python: - meth_kind = OP_DLOPEN_FUNC - elif numargs == 0: - meth_kind = OP_CPYTHON_BLTN_N # 'METH_NOARGS' - elif numargs == 1: - meth_kind = OP_CPYTHON_BLTN_O # 'METH_O' - else: - meth_kind = OP_CPYTHON_BLTN_V # 'METH_VARARGS' - self._lsts["global"].append( - GlobalExpr(name, '_cffi_f_%s' % name, - CffiOp(meth_kind, type_index), - size='_cffi_d_%s' % name)) - - # ---------- - # named structs or unions - - def _field_type(self, tp_struct, field_name, tp_field): - if isinstance(tp_field, model.ArrayType): - actual_length = tp_field.length - if actual_length == '...': - ptr_struct_name = tp_struct.get_c_name('*') - actual_length = '_cffi_array_len(((%s)0)->%s)' % ( - ptr_struct_name, field_name) - tp_item = self._field_type(tp_struct, '%s[0]' % field_name, - tp_field.item) - tp_field = model.ArrayType(tp_item, actual_length) - return tp_field - - def _struct_collecttype(self, tp): - self._do_collect_type(tp) - if self.target_is_python: - # also requires nested anon struct/unions in ABI mode, recursively - for fldtype in tp.anonymous_struct_fields(): - self._struct_collecttype(fldtype) - - def _struct_decl(self, tp, cname, approxname): - if tp.fldtypes is None: - return - prnt = self._prnt - checkfuncname = '_cffi_checkfld_%s' % (approxname,) - prnt('_CFFI_UNUSED_FN') - prnt('static void %s(%s *p)' % (checkfuncname, cname)) - prnt('{') - prnt(' /* only to generate compile-time warnings or errors */') - prnt(' (void)p;') - for fname, ftype, fbitsize, fqual in self._enum_fields(tp): - try: - if ftype.is_integer_type() or fbitsize >= 0: - # accept all integers, but complain on float or double - if fname != '': - prnt(" (void)((p->%s) | 0); /* check that '%s.%s' is " - "an integer */" % (fname, cname, fname)) - continue - # only accept exactly the type declared, except that '[]' - # is interpreted as a '*' and so will match any array length. - # (It would also match '*', but that's harder to detect...) - while (isinstance(ftype, model.ArrayType) - and (ftype.length is None or ftype.length == '...')): - ftype = ftype.item - fname = fname + '[0]' - prnt(' { %s = &p->%s; (void)tmp; }' % ( - ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual), - fname)) - except VerificationError as e: - prnt(' /* %s */' % str(e)) # cannot verify it, ignore - prnt('}') - prnt('struct _cffi_align_%s { char x; %s y; };' % (approxname, cname)) - prnt() - - def _struct_ctx(self, tp, cname, approxname, named_ptr=None): - type_index = self._typesdict[tp] - reason_for_not_expanding = None - flags = [] - if isinstance(tp, model.UnionType): - flags.append("_CFFI_F_UNION") - if tp.fldtypes is None: - flags.append("_CFFI_F_OPAQUE") - reason_for_not_expanding = "opaque" - if (tp not in self.ffi._parser._included_declarations and - (named_ptr is None or - named_ptr not in self.ffi._parser._included_declarations)): - if tp.fldtypes is None: - pass # opaque - elif tp.partial or any(tp.anonymous_struct_fields()): - pass # field layout obtained silently from the C compiler - else: - flags.append("_CFFI_F_CHECK_FIELDS") - if tp.packed: - if tp.packed > 1: - raise NotImplementedError( - "%r is declared with 'pack=%r'; only 0 or 1 are " - "supported in API mode (try to use \"...;\", which " - "does not require a 'pack' declaration)" % - (tp, tp.packed)) - flags.append("_CFFI_F_PACKED") - else: - flags.append("_CFFI_F_EXTERNAL") - reason_for_not_expanding = "external" - flags = '|'.join(flags) or '0' - c_fields = [] - if reason_for_not_expanding is None: - enumfields = list(self._enum_fields(tp)) - for fldname, fldtype, fbitsize, fqual in enumfields: - fldtype = self._field_type(tp, fldname, fldtype) - self._check_not_opaque(fldtype, - "field '%s.%s'" % (tp.name, fldname)) - # cname is None for _add_missing_struct_unions() only - op = OP_NOOP - if fbitsize >= 0: - op = OP_BITFIELD - size = '%d /* bits */' % fbitsize - elif cname is None or ( - isinstance(fldtype, model.ArrayType) and - fldtype.length is None): - size = '(size_t)-1' - else: - size = 'sizeof(((%s)0)->%s)' % ( - tp.get_c_name('*') if named_ptr is None - else named_ptr.name, - fldname) - if cname is None or fbitsize >= 0: - offset = '(size_t)-1' - elif named_ptr is not None: - offset = '((char *)&((%s)0)->%s) - (char *)0' % ( - named_ptr.name, fldname) - else: - offset = 'offsetof(%s, %s)' % (tp.get_c_name(''), fldname) - c_fields.append( - FieldExpr(fldname, offset, size, fbitsize, - CffiOp(op, self._typesdict[fldtype]))) - first_field_index = len(self._lsts["field"]) - self._lsts["field"].extend(c_fields) - # - if cname is None: # unknown name, for _add_missing_struct_unions - size = '(size_t)-2' - align = -2 - comment = "unnamed" - else: - if named_ptr is not None: - size = 'sizeof(*(%s)0)' % (named_ptr.name,) - align = '-1 /* unknown alignment */' - else: - size = 'sizeof(%s)' % (cname,) - align = 'offsetof(struct _cffi_align_%s, y)' % (approxname,) - comment = None - else: - size = '(size_t)-1' - align = -1 - first_field_index = -1 - comment = reason_for_not_expanding - self._lsts["struct_union"].append( - StructUnionExpr(tp.name, type_index, flags, size, align, comment, - first_field_index, c_fields)) - self._seen_struct_unions.add(tp) - - def _check_not_opaque(self, tp, location): - while isinstance(tp, model.ArrayType): - tp = tp.item - if isinstance(tp, model.StructOrUnion) and tp.fldtypes is None: - raise TypeError( - "%s is of an opaque type (not declared in cdef())" % location) - - def _add_missing_struct_unions(self): - # not very nice, but some struct declarations might be missing - # because they don't have any known C name. Check that they are - # not partial (we can't complete or verify them!) and emit them - # anonymously. - lst = list(self._struct_unions.items()) - lst.sort(key=lambda tp_order: tp_order[1]) - for tp, order in lst: - if tp not in self._seen_struct_unions: - if tp.partial: - raise NotImplementedError("internal inconsistency: %r is " - "partial but was not seen at " - "this point" % (tp,)) - if tp.name.startswith('$') and tp.name[1:].isdigit(): - approxname = tp.name[1:] - elif tp.name == '_IO_FILE' and tp.forcename == 'FILE': - approxname = 'FILE' - self._typedef_ctx(tp, 'FILE') - else: - raise NotImplementedError("internal inconsistency: %r" % - (tp,)) - self._struct_ctx(tp, None, approxname) - - def _generate_cpy_struct_collecttype(self, tp, name): - self._struct_collecttype(tp) - _generate_cpy_union_collecttype = _generate_cpy_struct_collecttype - - def _struct_names(self, tp): - cname = tp.get_c_name('') - if ' ' in cname: - return cname, cname.replace(' ', '_') - else: - return cname, '_' + cname - - def _generate_cpy_struct_decl(self, tp, name): - self._struct_decl(tp, *self._struct_names(tp)) - _generate_cpy_union_decl = _generate_cpy_struct_decl - - def _generate_cpy_struct_ctx(self, tp, name): - self._struct_ctx(tp, *self._struct_names(tp)) - _generate_cpy_union_ctx = _generate_cpy_struct_ctx - - # ---------- - # 'anonymous' declarations. These are produced for anonymous structs - # or unions; the 'name' is obtained by a typedef. - - def _generate_cpy_anonymous_collecttype(self, tp, name): - if isinstance(tp, model.EnumType): - self._generate_cpy_enum_collecttype(tp, name) - else: - self._struct_collecttype(tp) - - def _generate_cpy_anonymous_decl(self, tp, name): - if isinstance(tp, model.EnumType): - self._generate_cpy_enum_decl(tp) - else: - self._struct_decl(tp, name, 'typedef_' + name) - - def _generate_cpy_anonymous_ctx(self, tp, name): - if isinstance(tp, model.EnumType): - self._enum_ctx(tp, name) - else: - self._struct_ctx(tp, name, 'typedef_' + name) - - # ---------- - # constants, declared with "static const ..." - - def _generate_cpy_const(self, is_int, name, tp=None, category='const', - check_value=None): - if (category, name) in self._seen_constants: - raise VerificationError( - "duplicate declaration of %s '%s'" % (category, name)) - self._seen_constants.add((category, name)) - # - prnt = self._prnt - funcname = '_cffi_%s_%s' % (category, name) - if is_int: - prnt('static int %s(unsigned long long *o)' % funcname) - prnt('{') - prnt(' int n = (%s) <= 0;' % (name,)) - prnt(' *o = (unsigned long long)((%s) | 0);' - ' /* check that %s is an integer */' % (name, name)) - if check_value is not None: - if check_value > 0: - check_value = '%dU' % (check_value,) - prnt(' if (!_cffi_check_int(*o, n, %s))' % (check_value,)) - prnt(' n |= 2;') - prnt(' return n;') - prnt('}') - else: - assert check_value is None - prnt('static void %s(char *o)' % funcname) - prnt('{') - prnt(' *(%s)o = %s;' % (tp.get_c_name('*'), name)) - prnt('}') - prnt() - - def _generate_cpy_constant_collecttype(self, tp, name): - is_int = tp.is_integer_type() - if not is_int or self.target_is_python: - self._do_collect_type(tp) - - def _generate_cpy_constant_decl(self, tp, name): - is_int = tp.is_integer_type() - self._generate_cpy_const(is_int, name, tp) - - def _generate_cpy_constant_ctx(self, tp, name): - if not self.target_is_python and tp.is_integer_type(): - type_op = CffiOp(OP_CONSTANT_INT, -1) - else: - if self.target_is_python: - const_kind = OP_DLOPEN_CONST - else: - const_kind = OP_CONSTANT - type_index = self._typesdict[tp] - type_op = CffiOp(const_kind, type_index) - self._lsts["global"].append( - GlobalExpr(name, '_cffi_const_%s' % name, type_op)) - - # ---------- - # enums - - def _generate_cpy_enum_collecttype(self, tp, name): - self._do_collect_type(tp) - - def _generate_cpy_enum_decl(self, tp, name=None): - for enumerator in tp.enumerators: - self._generate_cpy_const(True, enumerator) - - def _enum_ctx(self, tp, cname): - type_index = self._typesdict[tp] - type_op = CffiOp(OP_ENUM, -1) - if self.target_is_python: - tp.check_not_partial() - for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): - self._lsts["global"].append( - GlobalExpr(enumerator, '_cffi_const_%s' % enumerator, type_op, - check_value=enumvalue)) - # - if cname is not None and '$' not in cname and not self.target_is_python: - size = "sizeof(%s)" % cname - signed = "((%s)-1) <= 0" % cname - else: - basetp = tp.build_baseinttype(self.ffi, []) - size = self.ffi.sizeof(basetp) - signed = int(int(self.ffi.cast(basetp, -1)) < 0) - allenums = ",".join(tp.enumerators) - self._lsts["enum"].append( - EnumExpr(tp.name, type_index, size, signed, allenums)) - - def _generate_cpy_enum_ctx(self, tp, name): - self._enum_ctx(tp, tp._get_c_name()) - - # ---------- - # macros: for now only for integers - - def _generate_cpy_macro_collecttype(self, tp, name): - pass - - def _generate_cpy_macro_decl(self, tp, name): - if tp == '...': - check_value = None - else: - check_value = tp # an integer - self._generate_cpy_const(True, name, check_value=check_value) - - def _generate_cpy_macro_ctx(self, tp, name): - if tp == '...': - if self.target_is_python: - raise VerificationError( - "cannot use the syntax '...' in '#define %s ...' when " - "using the ABI mode" % (name,)) - check_value = None - else: - check_value = tp # an integer - type_op = CffiOp(OP_CONSTANT_INT, -1) - self._lsts["global"].append( - GlobalExpr(name, '_cffi_const_%s' % name, type_op, - check_value=check_value)) - - # ---------- - # global variables - - def _global_type(self, tp, global_name): - if isinstance(tp, model.ArrayType): - actual_length = tp.length - if actual_length == '...': - actual_length = '_cffi_array_len(%s)' % (global_name,) - tp_item = self._global_type(tp.item, '%s[0]' % global_name) - tp = model.ArrayType(tp_item, actual_length) - return tp - - def _generate_cpy_variable_collecttype(self, tp, name): - self._do_collect_type(self._global_type(tp, name)) - - def _generate_cpy_variable_decl(self, tp, name): - prnt = self._prnt - tp = self._global_type(tp, name) - if isinstance(tp, model.ArrayType) and tp.length is None: - tp = tp.item - ampersand = '' - else: - ampersand = '&' - # This code assumes that casts from "tp *" to "void *" is a - # no-op, i.e. a function that returns a "tp *" can be called - # as if it returned a "void *". This should be generally true - # on any modern machine. The only exception to that rule (on - # uncommon architectures, and as far as I can tell) might be - # if 'tp' were a function type, but that is not possible here. - # (If 'tp' is a function _pointer_ type, then casts from "fn_t - # **" to "void *" are again no-ops, as far as I can tell.) - decl = '*_cffi_var_%s(void)' % (name,) - prnt('static ' + tp.get_c_name(decl, quals=self._current_quals)) - prnt('{') - prnt(' return %s(%s);' % (ampersand, name)) - prnt('}') - prnt() - - def _generate_cpy_variable_ctx(self, tp, name): - tp = self._global_type(tp, name) - type_index = self._typesdict[tp] - if self.target_is_python: - op = OP_GLOBAL_VAR - else: - op = OP_GLOBAL_VAR_F - self._lsts["global"].append( - GlobalExpr(name, '_cffi_var_%s' % name, CffiOp(op, type_index))) - - # ---------- - # extern "Python" - - def _generate_cpy_extern_python_collecttype(self, tp, name): - assert isinstance(tp, model.FunctionPtrType) - self._do_collect_type(tp) - _generate_cpy_dllexport_python_collecttype = \ - _generate_cpy_extern_python_plus_c_collecttype = \ - _generate_cpy_extern_python_collecttype - - def _extern_python_decl(self, tp, name, tag_and_space): - prnt = self._prnt - if isinstance(tp.result, model.VoidType): - size_of_result = '0' - else: - context = 'result of %s' % name - size_of_result = '(int)sizeof(%s)' % ( - tp.result.get_c_name('', context),) - prnt('static struct _cffi_externpy_s _cffi_externpy__%s =' % name) - prnt(' { "%s.%s", %s, 0, 0 };' % ( - self.module_name, name, size_of_result)) - prnt() - # - arguments = [] - context = 'argument of %s' % name - for i, type in enumerate(tp.args): - arg = type.get_c_name(' a%d' % i, context) - arguments.append(arg) - # - repr_arguments = ', '.join(arguments) - repr_arguments = repr_arguments or 'void' - name_and_arguments = '%s(%s)' % (name, repr_arguments) - if tp.abi == "__stdcall": - name_and_arguments = '_cffi_stdcall ' + name_and_arguments - # - def may_need_128_bits(tp): - return (isinstance(tp, model.PrimitiveType) and - tp.name == 'long double') - # - size_of_a = max(len(tp.args)*8, 8) - if may_need_128_bits(tp.result): - size_of_a = max(size_of_a, 16) - if isinstance(tp.result, model.StructOrUnion): - size_of_a = 'sizeof(%s) > %d ? sizeof(%s) : %d' % ( - tp.result.get_c_name(''), size_of_a, - tp.result.get_c_name(''), size_of_a) - prnt('%s%s' % (tag_and_space, tp.result.get_c_name(name_and_arguments))) - prnt('{') - prnt(' char a[%s];' % size_of_a) - prnt(' char *p = a;') - for i, type in enumerate(tp.args): - arg = 'a%d' % i - if (isinstance(type, model.StructOrUnion) or - may_need_128_bits(type)): - arg = '&' + arg - type = model.PointerType(type) - prnt(' *(%s)(p + %d) = %s;' % (type.get_c_name('*'), i*8, arg)) - prnt(' _cffi_call_python(&_cffi_externpy__%s, p);' % name) - if not isinstance(tp.result, model.VoidType): - prnt(' return *(%s)p;' % (tp.result.get_c_name('*'),)) - prnt('}') - prnt() - self._num_externpy += 1 - - def _generate_cpy_extern_python_decl(self, tp, name): - self._extern_python_decl(tp, name, 'static ') - - def _generate_cpy_dllexport_python_decl(self, tp, name): - self._extern_python_decl(tp, name, 'CFFI_DLLEXPORT ') - - def _generate_cpy_extern_python_plus_c_decl(self, tp, name): - self._extern_python_decl(tp, name, '') - - def _generate_cpy_extern_python_ctx(self, tp, name): - if self.target_is_python: - raise VerificationError( - "cannot use 'extern \"Python\"' in the ABI mode") - if tp.ellipsis: - raise NotImplementedError("a vararg function is extern \"Python\"") - type_index = self._typesdict[tp] - type_op = CffiOp(OP_EXTERN_PYTHON, type_index) - self._lsts["global"].append( - GlobalExpr(name, '&_cffi_externpy__%s' % name, type_op, name)) - - _generate_cpy_dllexport_python_ctx = \ - _generate_cpy_extern_python_plus_c_ctx = \ - _generate_cpy_extern_python_ctx - - def _print_string_literal_in_array(self, s): - prnt = self._prnt - prnt('// # NB. this is not a string because of a size limit in MSVC') - if not isinstance(s, bytes): # unicode - s = s.encode('utf-8') # -> bytes - else: - s.decode('utf-8') # got bytes, check for valid utf-8 - try: - s.decode('ascii') - except UnicodeDecodeError: - s = b'# -*- encoding: utf8 -*-\n' + s - for line in s.splitlines(True): - comment = line - if type('//') is bytes: # python2 - line = map(ord, line) # make a list of integers - else: # python3 - # type(line) is bytes, which enumerates like a list of integers - comment = ascii(comment)[1:-1] - prnt(('// ' + comment).rstrip()) - printed_line = '' - for c in line: - if len(printed_line) >= 76: - prnt(printed_line) - printed_line = '' - printed_line += '%d,' % (c,) - prnt(printed_line) - - # ---------- - # emitting the opcodes for individual types - - def _emit_bytecode_VoidType(self, tp, index): - self.cffi_types[index] = CffiOp(OP_PRIMITIVE, PRIM_VOID) - - def _emit_bytecode_PrimitiveType(self, tp, index): - prim_index = PRIMITIVE_TO_INDEX[tp.name] - self.cffi_types[index] = CffiOp(OP_PRIMITIVE, prim_index) - - def _emit_bytecode_UnknownIntegerType(self, tp, index): - s = ('_cffi_prim_int(sizeof(%s), (\n' - ' ((%s)-1) | 0 /* check that %s is an integer type */\n' - ' ) <= 0)' % (tp.name, tp.name, tp.name)) - self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s) - - def _emit_bytecode_UnknownFloatType(self, tp, index): - s = ('_cffi_prim_float(sizeof(%s) *\n' - ' (((%s)1) / 2) * 2 /* integer => 0, float => 1 */\n' - ' )' % (tp.name, tp.name)) - self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s) - - def _emit_bytecode_RawFunctionType(self, tp, index): - self.cffi_types[index] = CffiOp(OP_FUNCTION, self._typesdict[tp.result]) - index += 1 - for tp1 in tp.args: - realindex = self._typesdict[tp1] - if index != realindex: - if isinstance(tp1, model.PrimitiveType): - self._emit_bytecode_PrimitiveType(tp1, index) - else: - self.cffi_types[index] = CffiOp(OP_NOOP, realindex) - index += 1 - flags = int(tp.ellipsis) - if tp.abi is not None: - if tp.abi == '__stdcall': - flags |= 2 - else: - raise NotImplementedError("abi=%r" % (tp.abi,)) - self.cffi_types[index] = CffiOp(OP_FUNCTION_END, flags) - - def _emit_bytecode_PointerType(self, tp, index): - self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[tp.totype]) - - _emit_bytecode_ConstPointerType = _emit_bytecode_PointerType - _emit_bytecode_NamedPointerType = _emit_bytecode_PointerType - - def _emit_bytecode_FunctionPtrType(self, tp, index): - raw = tp.as_raw_function() - self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[raw]) - - def _emit_bytecode_ArrayType(self, tp, index): - item_index = self._typesdict[tp.item] - if tp.length is None: - self.cffi_types[index] = CffiOp(OP_OPEN_ARRAY, item_index) - elif tp.length == '...': - raise VerificationError( - "type %s badly placed: the '...' array length can only be " - "used on global arrays or on fields of structures" % ( - str(tp).replace('/*...*/', '...'),)) - else: - assert self.cffi_types[index + 1] == 'LEN' - self.cffi_types[index] = CffiOp(OP_ARRAY, item_index) - self.cffi_types[index + 1] = CffiOp(None, str(tp.length)) - - def _emit_bytecode_StructType(self, tp, index): - struct_index = self._struct_unions[tp] - self.cffi_types[index] = CffiOp(OP_STRUCT_UNION, struct_index) - _emit_bytecode_UnionType = _emit_bytecode_StructType - - def _emit_bytecode_EnumType(self, tp, index): - enum_index = self._enums[tp] - self.cffi_types[index] = CffiOp(OP_ENUM, enum_index) - - -if sys.version_info >= (3,): - NativeIO = io.StringIO -else: - class NativeIO(io.BytesIO): - def write(self, s): - if isinstance(s, unicode): - s = s.encode('ascii') - super(NativeIO, self).write(s) - -def _make_c_or_py_source(ffi, module_name, preamble, target_file, verbose): - if verbose: - print("generating %s" % (target_file,)) - recompiler = Recompiler(ffi, module_name, - target_is_python=(preamble is None)) - recompiler.collect_type_table() - recompiler.collect_step_tables() - f = NativeIO() - recompiler.write_source_to_f(f, preamble) - output = f.getvalue() - try: - with open(target_file, 'r') as f1: - if f1.read(len(output) + 1) != output: - raise IOError - if verbose: - print("(already up-to-date)") - return False # already up-to-date - except IOError: - tmp_file = '%s.~%d' % (target_file, os.getpid()) - with open(tmp_file, 'w') as f1: - f1.write(output) - try: - os.rename(tmp_file, target_file) - except OSError: - os.unlink(target_file) - os.rename(tmp_file, target_file) - return True - -def make_c_source(ffi, module_name, preamble, target_c_file, verbose=False): - assert preamble is not None - return _make_c_or_py_source(ffi, module_name, preamble, target_c_file, - verbose) - -def make_py_source(ffi, module_name, target_py_file, verbose=False): - return _make_c_or_py_source(ffi, module_name, None, target_py_file, - verbose) - -def _modname_to_file(outputdir, modname, extension): - parts = modname.split('.') - try: - os.makedirs(os.path.join(outputdir, *parts[:-1])) - except OSError: - pass - parts[-1] += extension - return os.path.join(outputdir, *parts), parts - - -# Aaargh. Distutils is not tested at all for the purpose of compiling -# DLLs that are not extension modules. Here are some hacks to work -# around that, in the _patch_for_*() functions... - -def _patch_meth(patchlist, cls, name, new_meth): - old = getattr(cls, name) - patchlist.append((cls, name, old)) - setattr(cls, name, new_meth) - return old - -def _unpatch_meths(patchlist): - for cls, name, old_meth in reversed(patchlist): - setattr(cls, name, old_meth) - -def _patch_for_embedding(patchlist): - if sys.platform == 'win32': - # we must not remove the manifest when building for embedding! - from distutils.msvc9compiler import MSVCCompiler - _patch_meth(patchlist, MSVCCompiler, '_remove_visual_c_ref', - lambda self, manifest_file: manifest_file) - - if sys.platform == 'darwin': - # we must not make a '-bundle', but a '-dynamiclib' instead - from distutils.ccompiler import CCompiler - def my_link_shared_object(self, *args, **kwds): - if '-bundle' in self.linker_so: - self.linker_so = list(self.linker_so) - i = self.linker_so.index('-bundle') - self.linker_so[i] = '-dynamiclib' - return old_link_shared_object(self, *args, **kwds) - old_link_shared_object = _patch_meth(patchlist, CCompiler, - 'link_shared_object', - my_link_shared_object) - -def _patch_for_target(patchlist, target): - from distutils.command.build_ext import build_ext - # if 'target' is different from '*', we need to patch some internal - # method to just return this 'target' value, instead of having it - # built from module_name - if target.endswith('.*'): - target = target[:-2] - if sys.platform == 'win32': - target += '.dll' - elif sys.platform == 'darwin': - target += '.dylib' - else: - target += '.so' - _patch_meth(patchlist, build_ext, 'get_ext_filename', - lambda self, ext_name: target) - - -def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True, - c_file=None, source_extension='.c', extradir=None, - compiler_verbose=1, target=None, debug=None, **kwds): - if not isinstance(module_name, str): - module_name = module_name.encode('ascii') - if ffi._windows_unicode: - ffi._apply_windows_unicode(kwds) - if preamble is not None: - embedding = (ffi._embedding is not None) - if embedding: - ffi._apply_embedding_fix(kwds) - if c_file is None: - c_file, parts = _modname_to_file(tmpdir, module_name, - source_extension) - if extradir: - parts = [extradir] + parts - ext_c_file = os.path.join(*parts) - else: - ext_c_file = c_file - # - if target is None: - if embedding: - target = '%s.*' % module_name - else: - target = '*' - # - ext = ffiplatform.get_extension(ext_c_file, module_name, **kwds) - updated = make_c_source(ffi, module_name, preamble, c_file, - verbose=compiler_verbose) - if call_c_compiler: - patchlist = [] - cwd = os.getcwd() - try: - if embedding: - _patch_for_embedding(patchlist) - if target != '*': - _patch_for_target(patchlist, target) - if compiler_verbose: - if tmpdir == '.': - msg = 'the current directory is' - else: - msg = 'setting the current directory to' - print('%s %r' % (msg, os.path.abspath(tmpdir))) - os.chdir(tmpdir) - outputfilename = ffiplatform.compile('.', ext, - compiler_verbose, debug) - finally: - os.chdir(cwd) - _unpatch_meths(patchlist) - return outputfilename - else: - return ext, updated - else: - if c_file is None: - c_file, _ = _modname_to_file(tmpdir, module_name, '.py') - updated = make_py_source(ffi, module_name, c_file, - verbose=compiler_verbose) - if call_c_compiler: - return c_file - else: - return None, updated - diff --git a/cffi/setuptools_ext.py b/cffi/setuptools_ext.py deleted file mode 100644 index 8fe3614..0000000 --- a/cffi/setuptools_ext.py +++ /dev/null @@ -1,219 +0,0 @@ -import os -import sys - -try: - basestring -except NameError: - # Python 3.x - basestring = str - -def error(msg): - from distutils.errors import DistutilsSetupError - raise DistutilsSetupError(msg) - - -def execfile(filename, glob): - # We use execfile() (here rewritten for Python 3) instead of - # __import__() to load the build script. The problem with - # a normal import is that in some packages, the intermediate - # __init__.py files may already try to import the file that - # we are generating. - with open(filename) as f: - src = f.read() - src += '\n' # Python 2.6 compatibility - code = compile(src, filename, 'exec') - exec(code, glob, glob) - - -def add_cffi_module(dist, mod_spec): - from cffi.api import FFI - - if not isinstance(mod_spec, basestring): - error("argument to 'cffi_modules=...' must be a str or a list of str," - " not %r" % (type(mod_spec).__name__,)) - mod_spec = str(mod_spec) - try: - build_file_name, ffi_var_name = mod_spec.split(':') - except ValueError: - error("%r must be of the form 'path/build.py:ffi_variable'" % - (mod_spec,)) - if not os.path.exists(build_file_name): - ext = '' - rewritten = build_file_name.replace('.', '/') + '.py' - if os.path.exists(rewritten): - ext = ' (rewrite cffi_modules to [%r])' % ( - rewritten + ':' + ffi_var_name,) - error("%r does not name an existing file%s" % (build_file_name, ext)) - - mod_vars = {'__name__': '__cffi__', '__file__': build_file_name} - execfile(build_file_name, mod_vars) - - try: - ffi = mod_vars[ffi_var_name] - except KeyError: - error("%r: object %r not found in module" % (mod_spec, - ffi_var_name)) - if not isinstance(ffi, FFI): - ffi = ffi() # maybe it's a function instead of directly an ffi - if not isinstance(ffi, FFI): - error("%r is not an FFI instance (got %r)" % (mod_spec, - type(ffi).__name__)) - if not hasattr(ffi, '_assigned_source'): - error("%r: the set_source() method was not called" % (mod_spec,)) - module_name, source, source_extension, kwds = ffi._assigned_source - if ffi._windows_unicode: - kwds = kwds.copy() - ffi._apply_windows_unicode(kwds) - - if source is None: - _add_py_module(dist, ffi, module_name) - else: - _add_c_module(dist, ffi, module_name, source, source_extension, kwds) - -def _set_py_limited_api(Extension, kwds): - """ - Add py_limited_api to kwds if setuptools >= 26 is in use. - Do not alter the setting if it already exists. - Setuptools takes care of ignoring the flag on Python 2 and PyPy. - - CPython itself should ignore the flag in a debugging version - (by not listing .abi3.so in the extensions it supports), but - it doesn't so far, creating troubles. That's why we check - for "not hasattr(sys, 'gettotalrefcount')" (the 2.7 compatible equivalent - of 'd' not in sys.abiflags). (http://bugs.python.org/issue28401) - - On Windows, with CPython <= 3.4, it's better not to use py_limited_api - because virtualenv *still* doesn't copy PYTHON3.DLL on these versions. - Recently (2020) we started shipping only >= 3.5 wheels, though. So - we'll give it another try and set py_limited_api on Windows >= 3.5. - """ - from cffi import recompiler - - if ('py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount') - and recompiler.USE_LIMITED_API): - import setuptools - try: - setuptools_major_version = int(setuptools.__version__.partition('.')[0]) - if setuptools_major_version >= 26: - kwds['py_limited_api'] = True - except ValueError: # certain development versions of setuptools - # If we don't know the version number of setuptools, we - # try to set 'py_limited_api' anyway. At worst, we get a - # warning. - kwds['py_limited_api'] = True - return kwds - -def _add_c_module(dist, ffi, module_name, source, source_extension, kwds): - from distutils.core import Extension - # We are a setuptools extension. Need this build_ext for py_limited_api. - from setuptools.command.build_ext import build_ext - from distutils.dir_util import mkpath - from distutils import log - from cffi import recompiler - - allsources = ['$PLACEHOLDER'] - allsources.extend(kwds.pop('sources', [])) - kwds = _set_py_limited_api(Extension, kwds) - ext = Extension(name=module_name, sources=allsources, **kwds) - - def make_mod(tmpdir, pre_run=None): - c_file = os.path.join(tmpdir, module_name + source_extension) - log.info("generating cffi module %r" % c_file) - mkpath(tmpdir) - # a setuptools-only, API-only hook: called with the "ext" and "ffi" - # arguments just before we turn the ffi into C code. To use it, - # subclass the 'distutils.command.build_ext.build_ext' class and - # add a method 'def pre_run(self, ext, ffi)'. - if pre_run is not None: - pre_run(ext, ffi) - updated = recompiler.make_c_source(ffi, module_name, source, c_file) - if not updated: - log.info("already up-to-date") - return c_file - - if dist.ext_modules is None: - dist.ext_modules = [] - dist.ext_modules.append(ext) - - base_class = dist.cmdclass.get('build_ext', build_ext) - class build_ext_make_mod(base_class): - def run(self): - if ext.sources[0] == '$PLACEHOLDER': - pre_run = getattr(self, 'pre_run', None) - ext.sources[0] = make_mod(self.build_temp, pre_run) - base_class.run(self) - dist.cmdclass['build_ext'] = build_ext_make_mod - # NB. multiple runs here will create multiple 'build_ext_make_mod' - # classes. Even in this case the 'build_ext' command should be - # run once; but just in case, the logic above does nothing if - # called again. - - -def _add_py_module(dist, ffi, module_name): - from distutils.dir_util import mkpath - from setuptools.command.build_py import build_py - from setuptools.command.build_ext import build_ext - from distutils import log - from cffi import recompiler - - def generate_mod(py_file): - log.info("generating cffi module %r" % py_file) - mkpath(os.path.dirname(py_file)) - updated = recompiler.make_py_source(ffi, module_name, py_file) - if not updated: - log.info("already up-to-date") - - base_class = dist.cmdclass.get('build_py', build_py) - class build_py_make_mod(base_class): - def run(self): - base_class.run(self) - module_path = module_name.split('.') - module_path[-1] += '.py' - generate_mod(os.path.join(self.build_lib, *module_path)) - def get_source_files(self): - # This is called from 'setup.py sdist' only. Exclude - # the generate .py module in this case. - saved_py_modules = self.py_modules - try: - if saved_py_modules: - self.py_modules = [m for m in saved_py_modules - if m != module_name] - return base_class.get_source_files(self) - finally: - self.py_modules = saved_py_modules - dist.cmdclass['build_py'] = build_py_make_mod - - # distutils and setuptools have no notion I could find of a - # generated python module. If we don't add module_name to - # dist.py_modules, then things mostly work but there are some - # combination of options (--root and --record) that will miss - # the module. So we add it here, which gives a few apparently - # harmless warnings about not finding the file outside the - # build directory. - # Then we need to hack more in get_source_files(); see above. - if dist.py_modules is None: - dist.py_modules = [] - dist.py_modules.append(module_name) - - # the following is only for "build_ext -i" - base_class_2 = dist.cmdclass.get('build_ext', build_ext) - class build_ext_make_mod(base_class_2): - def run(self): - base_class_2.run(self) - if self.inplace: - # from get_ext_fullpath() in distutils/command/build_ext.py - module_path = module_name.split('.') - package = '.'.join(module_path[:-1]) - build_py = self.get_finalized_command('build_py') - package_dir = build_py.get_package_dir(package) - file_name = module_path[-1] + '.py' - generate_mod(os.path.join(package_dir, file_name)) - dist.cmdclass['build_ext'] = build_ext_make_mod - -def cffi_modules(dist, attr, value): - assert attr == 'cffi_modules' - if isinstance(value, basestring): - value = [value] - - for cffi_module in value: - add_cffi_module(dist, cffi_module) diff --git a/cffi/vengine_cpy.py b/cffi/vengine_cpy.py deleted file mode 100644 index 6de0df0..0000000 --- a/cffi/vengine_cpy.py +++ /dev/null @@ -1,1076 +0,0 @@ -# -# DEPRECATED: implementation for ffi.verify() -# -import sys, imp -from . import model -from .error import VerificationError - - -class VCPythonEngine(object): - _class_key = 'x' - _gen_python_module = True - - def __init__(self, verifier): - self.verifier = verifier - self.ffi = verifier.ffi - self._struct_pending_verification = {} - self._types_of_builtin_functions = {} - - def patch_extension_kwds(self, kwds): - pass - - def find_module(self, module_name, path, so_suffixes): - try: - f, filename, descr = imp.find_module(module_name, path) - except ImportError: - return None - if f is not None: - f.close() - # Note that after a setuptools installation, there are both .py - # and .so files with the same basename. The code here relies on - # imp.find_module() locating the .so in priority. - if descr[0] not in so_suffixes: - return None - return filename - - def collect_types(self): - self._typesdict = {} - self._generate("collecttype") - - def _prnt(self, what=''): - self._f.write(what + '\n') - - def _gettypenum(self, type): - # a KeyError here is a bug. please report it! :-) - return self._typesdict[type] - - def _do_collect_type(self, tp): - if ((not isinstance(tp, model.PrimitiveType) - or tp.name == 'long double') - and tp not in self._typesdict): - num = len(self._typesdict) - self._typesdict[tp] = num - - def write_source_to_f(self): - self.collect_types() - # - # The new module will have a _cffi_setup() function that receives - # objects from the ffi world, and that calls some setup code in - # the module. This setup code is split in several independent - # functions, e.g. one per constant. The functions are "chained" - # by ending in a tail call to each other. - # - # This is further split in two chained lists, depending on if we - # can do it at import-time or if we must wait for _cffi_setup() to - # provide us with the <ctype> objects. This is needed because we - # need the values of the enum constants in order to build the - # <ctype 'enum'> that we may have to pass to _cffi_setup(). - # - # The following two 'chained_list_constants' items contains - # the head of these two chained lists, as a string that gives the - # call to do, if any. - self._chained_list_constants = ['((void)lib,0)', '((void)lib,0)'] - # - prnt = self._prnt - # first paste some standard set of lines that are mostly '#define' - prnt(cffimod_header) - prnt() - # then paste the C source given by the user, verbatim. - prnt(self.verifier.preamble) - prnt() - # - # call generate_cpy_xxx_decl(), for every xxx found from - # ffi._parser._declarations. This generates all the functions. - self._generate("decl") - # - # implement the function _cffi_setup_custom() as calling the - # head of the chained list. - self._generate_setup_custom() - prnt() - # - # produce the method table, including the entries for the - # generated Python->C function wrappers, which are done - # by generate_cpy_function_method(). - prnt('static PyMethodDef _cffi_methods[] = {') - self._generate("method") - prnt(' {"_cffi_setup", _cffi_setup, METH_VARARGS, NULL},') - prnt(' {NULL, NULL, 0, NULL} /* Sentinel */') - prnt('};') - prnt() - # - # standard init. - modname = self.verifier.get_module_name() - constants = self._chained_list_constants[False] - prnt('#if PY_MAJOR_VERSION >= 3') - prnt() - prnt('static struct PyModuleDef _cffi_module_def = {') - prnt(' PyModuleDef_HEAD_INIT,') - prnt(' "%s",' % modname) - prnt(' NULL,') - prnt(' -1,') - prnt(' _cffi_methods,') - prnt(' NULL, NULL, NULL, NULL') - prnt('};') - prnt() - prnt('PyMODINIT_FUNC') - prnt('PyInit_%s(void)' % modname) - prnt('{') - prnt(' PyObject *lib;') - prnt(' lib = PyModule_Create(&_cffi_module_def);') - prnt(' if (lib == NULL)') - prnt(' return NULL;') - prnt(' if (%s < 0 || _cffi_init() < 0) {' % (constants,)) - prnt(' Py_DECREF(lib);') - prnt(' return NULL;') - prnt(' }') - prnt(' return lib;') - prnt('}') - prnt() - prnt('#else') - prnt() - prnt('PyMODINIT_FUNC') - prnt('init%s(void)' % modname) - prnt('{') - prnt(' PyObject *lib;') - prnt(' lib = Py_InitModule("%s", _cffi_methods);' % modname) - prnt(' if (lib == NULL)') - prnt(' return;') - prnt(' if (%s < 0 || _cffi_init() < 0)' % (constants,)) - prnt(' return;') - prnt(' return;') - prnt('}') - prnt() - prnt('#endif') - - def load_library(self, flags=None): - # XXX review all usages of 'self' here! - # import it as a new extension module - imp.acquire_lock() - try: - if hasattr(sys, "getdlopenflags"): - previous_flags = sys.getdlopenflags() - try: - if hasattr(sys, "setdlopenflags") and flags is not None: - sys.setdlopenflags(flags) - module = imp.load_dynamic(self.verifier.get_module_name(), - self.verifier.modulefilename) - except ImportError as e: - error = "importing %r: %s" % (self.verifier.modulefilename, e) - raise VerificationError(error) - finally: - if hasattr(sys, "setdlopenflags"): - sys.setdlopenflags(previous_flags) - finally: - imp.release_lock() - # - # call loading_cpy_struct() to get the struct layout inferred by - # the C compiler - self._load(module, 'loading') - # - # the C code will need the <ctype> objects. Collect them in - # order in a list. - revmapping = dict([(value, key) - for (key, value) in self._typesdict.items()]) - lst = [revmapping[i] for i in range(len(revmapping))] - lst = list(map(self.ffi._get_cached_btype, lst)) - # - # build the FFILibrary class and instance and call _cffi_setup(). - # this will set up some fields like '_cffi_types', and only then - # it will invoke the chained list of functions that will really - # build (notably) the constant objects, as <cdata> if they are - # pointers, and store them as attributes on the 'library' object. - class FFILibrary(object): - _cffi_python_module = module - _cffi_ffi = self.ffi - _cffi_dir = [] - def __dir__(self): - return FFILibrary._cffi_dir + list(self.__dict__) - library = FFILibrary() - if module._cffi_setup(lst, VerificationError, library): - import warnings - warnings.warn("reimporting %r might overwrite older definitions" - % (self.verifier.get_module_name())) - # - # finally, call the loaded_cpy_xxx() functions. This will perform - # the final adjustments, like copying the Python->C wrapper - # functions from the module to the 'library' object, and setting - # up the FFILibrary class with properties for the global C variables. - self._load(module, 'loaded', library=library) - module._cffi_original_ffi = self.ffi - module._cffi_types_of_builtin_funcs = self._types_of_builtin_functions - return library - - def _get_declarations(self): - lst = [(key, tp) for (key, (tp, qual)) in - self.ffi._parser._declarations.items()] - lst.sort() - return lst - - def _generate(self, step_name): - for name, tp in self._get_declarations(): - kind, realname = name.split(' ', 1) - try: - method = getattr(self, '_generate_cpy_%s_%s' % (kind, - step_name)) - except AttributeError: - raise VerificationError( - "not implemented in verify(): %r" % name) - try: - method(tp, realname) - except Exception as e: - model.attach_exception_info(e, name) - raise - - def _load(self, module, step_name, **kwds): - for name, tp in self._get_declarations(): - kind, realname = name.split(' ', 1) - method = getattr(self, '_%s_cpy_%s' % (step_name, kind)) - try: - method(tp, realname, module, **kwds) - except Exception as e: - model.attach_exception_info(e, name) - raise - - def _generate_nothing(self, tp, name): - pass - - def _loaded_noop(self, tp, name, module, **kwds): - pass - - # ---------- - - def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode): - extraarg = '' - if isinstance(tp, model.PrimitiveType): - if tp.is_integer_type() and tp.name != '_Bool': - converter = '_cffi_to_c_int' - extraarg = ', %s' % tp.name - else: - converter = '(%s)_cffi_to_c_%s' % (tp.get_c_name(''), - tp.name.replace(' ', '_')) - errvalue = '-1' - # - elif isinstance(tp, model.PointerType): - self._convert_funcarg_to_c_ptr_or_array(tp, fromvar, - tovar, errcode) - return - # - elif isinstance(tp, (model.StructOrUnion, model.EnumType)): - # a struct (not a struct pointer) as a function argument - self._prnt(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)' - % (tovar, self._gettypenum(tp), fromvar)) - self._prnt(' %s;' % errcode) - return - # - elif isinstance(tp, model.FunctionPtrType): - converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('') - extraarg = ', _cffi_type(%d)' % self._gettypenum(tp) - errvalue = 'NULL' - # - else: - raise NotImplementedError(tp) - # - self._prnt(' %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg)) - self._prnt(' if (%s == (%s)%s && PyErr_Occurred())' % ( - tovar, tp.get_c_name(''), errvalue)) - self._prnt(' %s;' % errcode) - - def _extra_local_variables(self, tp, localvars, freelines): - if isinstance(tp, model.PointerType): - localvars.add('Py_ssize_t datasize') - localvars.add('struct _cffi_freeme_s *large_args_free = NULL') - freelines.add('if (large_args_free != NULL)' - ' _cffi_free_array_arguments(large_args_free);') - - def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode): - self._prnt(' datasize = _cffi_prepare_pointer_call_argument(') - self._prnt(' _cffi_type(%d), %s, (char **)&%s);' % ( - self._gettypenum(tp), fromvar, tovar)) - self._prnt(' if (datasize != 0) {') - self._prnt(' %s = ((size_t)datasize) <= 640 ? ' - 'alloca((size_t)datasize) : NULL;' % (tovar,)) - self._prnt(' if (_cffi_convert_array_argument(_cffi_type(%d), %s, ' - '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar)) - self._prnt(' datasize, &large_args_free) < 0)') - self._prnt(' %s;' % errcode) - self._prnt(' }') - - def _convert_expr_from_c(self, tp, var, context): - if isinstance(tp, model.PrimitiveType): - if tp.is_integer_type() and tp.name != '_Bool': - return '_cffi_from_c_int(%s, %s)' % (var, tp.name) - elif tp.name != 'long double': - return '_cffi_from_c_%s(%s)' % (tp.name.replace(' ', '_'), var) - else: - return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - elif isinstance(tp, (model.PointerType, model.FunctionPtrType)): - return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - elif isinstance(tp, model.ArrayType): - return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( - var, self._gettypenum(model.PointerType(tp.item))) - elif isinstance(tp, model.StructOrUnion): - if tp.fldnames is None: - raise TypeError("'%s' is used as %s, but is opaque" % ( - tp._get_c_name(), context)) - return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - elif isinstance(tp, model.EnumType): - return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - else: - raise NotImplementedError(tp) - - # ---------- - # typedefs: generates no code so far - - _generate_cpy_typedef_collecttype = _generate_nothing - _generate_cpy_typedef_decl = _generate_nothing - _generate_cpy_typedef_method = _generate_nothing - _loading_cpy_typedef = _loaded_noop - _loaded_cpy_typedef = _loaded_noop - - # ---------- - # function declarations - - def _generate_cpy_function_collecttype(self, tp, name): - assert isinstance(tp, model.FunctionPtrType) - if tp.ellipsis: - self._do_collect_type(tp) - else: - # don't call _do_collect_type(tp) in this common case, - # otherwise test_autofilled_struct_as_argument fails - for type in tp.args: - self._do_collect_type(type) - self._do_collect_type(tp.result) - - def _generate_cpy_function_decl(self, tp, name): - assert isinstance(tp, model.FunctionPtrType) - if tp.ellipsis: - # cannot support vararg functions better than this: check for its - # exact type (including the fixed arguments), and build it as a - # constant function pointer (no CPython wrapper) - self._generate_cpy_const(False, name, tp) - return - prnt = self._prnt - numargs = len(tp.args) - if numargs == 0: - argname = 'noarg' - elif numargs == 1: - argname = 'arg0' - else: - argname = 'args' - prnt('static PyObject *') - prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname)) - prnt('{') - # - context = 'argument of %s' % name - for i, type in enumerate(tp.args): - prnt(' %s;' % type.get_c_name(' x%d' % i, context)) - # - localvars = set() - freelines = set() - for type in tp.args: - self._extra_local_variables(type, localvars, freelines) - for decl in sorted(localvars): - prnt(' %s;' % (decl,)) - # - if not isinstance(tp.result, model.VoidType): - result_code = 'result = ' - context = 'result of %s' % name - prnt(' %s;' % tp.result.get_c_name(' result', context)) - prnt(' PyObject *pyresult;') - else: - result_code = '' - # - if len(tp.args) > 1: - rng = range(len(tp.args)) - for i in rng: - prnt(' PyObject *arg%d;' % i) - prnt() - prnt(' if (!PyArg_ParseTuple(args, "%s:%s", %s))' % ( - 'O' * numargs, name, ', '.join(['&arg%d' % i for i in rng]))) - prnt(' return NULL;') - prnt() - # - for i, type in enumerate(tp.args): - self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i, - 'return NULL') - prnt() - # - prnt(' Py_BEGIN_ALLOW_THREADS') - prnt(' _cffi_restore_errno();') - prnt(' { %s%s(%s); }' % ( - result_code, name, - ', '.join(['x%d' % i for i in range(len(tp.args))]))) - prnt(' _cffi_save_errno();') - prnt(' Py_END_ALLOW_THREADS') - prnt() - # - prnt(' (void)self; /* unused */') - if numargs == 0: - prnt(' (void)noarg; /* unused */') - if result_code: - prnt(' pyresult = %s;' % - self._convert_expr_from_c(tp.result, 'result', 'result type')) - for freeline in freelines: - prnt(' ' + freeline) - prnt(' return pyresult;') - else: - for freeline in freelines: - prnt(' ' + freeline) - prnt(' Py_INCREF(Py_None);') - prnt(' return Py_None;') - prnt('}') - prnt() - - def _generate_cpy_function_method(self, tp, name): - if tp.ellipsis: - return - numargs = len(tp.args) - if numargs == 0: - meth = 'METH_NOARGS' - elif numargs == 1: - meth = 'METH_O' - else: - meth = 'METH_VARARGS' - self._prnt(' {"%s", _cffi_f_%s, %s, NULL},' % (name, name, meth)) - - _loading_cpy_function = _loaded_noop - - def _loaded_cpy_function(self, tp, name, module, library): - if tp.ellipsis: - return - func = getattr(module, name) - setattr(library, name, func) - self._types_of_builtin_functions[func] = tp - - # ---------- - # named structs - - _generate_cpy_struct_collecttype = _generate_nothing - def _generate_cpy_struct_decl(self, tp, name): - assert name == tp.name - self._generate_struct_or_union_decl(tp, 'struct', name) - def _generate_cpy_struct_method(self, tp, name): - self._generate_struct_or_union_method(tp, 'struct', name) - def _loading_cpy_struct(self, tp, name, module): - self._loading_struct_or_union(tp, 'struct', name, module) - def _loaded_cpy_struct(self, tp, name, module, **kwds): - self._loaded_struct_or_union(tp) - - _generate_cpy_union_collecttype = _generate_nothing - def _generate_cpy_union_decl(self, tp, name): - assert name == tp.name - self._generate_struct_or_union_decl(tp, 'union', name) - def _generate_cpy_union_method(self, tp, name): - self._generate_struct_or_union_method(tp, 'union', name) - def _loading_cpy_union(self, tp, name, module): - self._loading_struct_or_union(tp, 'union', name, module) - def _loaded_cpy_union(self, tp, name, module, **kwds): - self._loaded_struct_or_union(tp) - - def _generate_struct_or_union_decl(self, tp, prefix, name): - if tp.fldnames is None: - return # nothing to do with opaque structs - checkfuncname = '_cffi_check_%s_%s' % (prefix, name) - layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) - cname = ('%s %s' % (prefix, name)).strip() - # - prnt = self._prnt - prnt('static void %s(%s *p)' % (checkfuncname, cname)) - prnt('{') - prnt(' /* only to generate compile-time warnings or errors */') - prnt(' (void)p;') - for fname, ftype, fbitsize, fqual in tp.enumfields(): - if (isinstance(ftype, model.PrimitiveType) - and ftype.is_integer_type()) or fbitsize >= 0: - # accept all integers, but complain on float or double - prnt(' (void)((p->%s) << 1);' % fname) - else: - # only accept exactly the type declared. - try: - prnt(' { %s = &p->%s; (void)tmp; }' % ( - ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual), - fname)) - except VerificationError as e: - prnt(' /* %s */' % str(e)) # cannot verify it, ignore - prnt('}') - prnt('static PyObject *') - prnt('%s(PyObject *self, PyObject *noarg)' % (layoutfuncname,)) - prnt('{') - prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname) - prnt(' static Py_ssize_t nums[] = {') - prnt(' sizeof(%s),' % cname) - prnt(' offsetof(struct _cffi_aligncheck, y),') - for fname, ftype, fbitsize, fqual in tp.enumfields(): - if fbitsize >= 0: - continue # xxx ignore fbitsize for now - prnt(' offsetof(%s, %s),' % (cname, fname)) - if isinstance(ftype, model.ArrayType) and ftype.length is None: - prnt(' 0, /* %s */' % ftype._get_c_name()) - else: - prnt(' sizeof(((%s *)0)->%s),' % (cname, fname)) - prnt(' -1') - prnt(' };') - prnt(' (void)self; /* unused */') - prnt(' (void)noarg; /* unused */') - prnt(' return _cffi_get_struct_layout(nums);') - prnt(' /* the next line is not executed, but compiled */') - prnt(' %s(0);' % (checkfuncname,)) - prnt('}') - prnt() - - def _generate_struct_or_union_method(self, tp, prefix, name): - if tp.fldnames is None: - return # nothing to do with opaque structs - layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) - self._prnt(' {"%s", %s, METH_NOARGS, NULL},' % (layoutfuncname, - layoutfuncname)) - - def _loading_struct_or_union(self, tp, prefix, name, module): - if tp.fldnames is None: - return # nothing to do with opaque structs - layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) - # - function = getattr(module, layoutfuncname) - layout = function() - if isinstance(tp, model.StructOrUnion) and tp.partial: - # use the function()'s sizes and offsets to guide the - # layout of the struct - totalsize = layout[0] - totalalignment = layout[1] - fieldofs = layout[2::2] - fieldsize = layout[3::2] - tp.force_flatten() - assert len(fieldofs) == len(fieldsize) == len(tp.fldnames) - tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment - else: - cname = ('%s %s' % (prefix, name)).strip() - self._struct_pending_verification[tp] = layout, cname - - def _loaded_struct_or_union(self, tp): - if tp.fldnames is None: - return # nothing to do with opaque structs - self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered - - if tp in self._struct_pending_verification: - # check that the layout sizes and offsets match the real ones - def check(realvalue, expectedvalue, msg): - if realvalue != expectedvalue: - raise VerificationError( - "%s (we have %d, but C compiler says %d)" - % (msg, expectedvalue, realvalue)) - ffi = self.ffi - BStruct = ffi._get_cached_btype(tp) - layout, cname = self._struct_pending_verification.pop(tp) - check(layout[0], ffi.sizeof(BStruct), "wrong total size") - check(layout[1], ffi.alignof(BStruct), "wrong total alignment") - i = 2 - for fname, ftype, fbitsize, fqual in tp.enumfields(): - if fbitsize >= 0: - continue # xxx ignore fbitsize for now - check(layout[i], ffi.offsetof(BStruct, fname), - "wrong offset for field %r" % (fname,)) - if layout[i+1] != 0: - BField = ffi._get_cached_btype(ftype) - check(layout[i+1], ffi.sizeof(BField), - "wrong size for field %r" % (fname,)) - i += 2 - assert i == len(layout) - - # ---------- - # 'anonymous' declarations. These are produced for anonymous structs - # or unions; the 'name' is obtained by a typedef. - - _generate_cpy_anonymous_collecttype = _generate_nothing - - def _generate_cpy_anonymous_decl(self, tp, name): - if isinstance(tp, model.EnumType): - self._generate_cpy_enum_decl(tp, name, '') - else: - self._generate_struct_or_union_decl(tp, '', name) - - def _generate_cpy_anonymous_method(self, tp, name): - if not isinstance(tp, model.EnumType): - self._generate_struct_or_union_method(tp, '', name) - - def _loading_cpy_anonymous(self, tp, name, module): - if isinstance(tp, model.EnumType): - self._loading_cpy_enum(tp, name, module) - else: - self._loading_struct_or_union(tp, '', name, module) - - def _loaded_cpy_anonymous(self, tp, name, module, **kwds): - if isinstance(tp, model.EnumType): - self._loaded_cpy_enum(tp, name, module, **kwds) - else: - self._loaded_struct_or_union(tp) - - # ---------- - # constants, likely declared with '#define' - - def _generate_cpy_const(self, is_int, name, tp=None, category='const', - vartp=None, delayed=True, size_too=False, - check_value=None): - prnt = self._prnt - funcname = '_cffi_%s_%s' % (category, name) - prnt('static int %s(PyObject *lib)' % funcname) - prnt('{') - prnt(' PyObject *o;') - prnt(' int res;') - if not is_int: - prnt(' %s;' % (vartp or tp).get_c_name(' i', name)) - else: - assert category == 'const' - # - if check_value is not None: - self._check_int_constant_value(name, check_value) - # - if not is_int: - if category == 'var': - realexpr = '&' + name - else: - realexpr = name - prnt(' i = (%s);' % (realexpr,)) - prnt(' o = %s;' % (self._convert_expr_from_c(tp, 'i', - 'variable type'),)) - assert delayed - else: - prnt(' o = _cffi_from_c_int_const(%s);' % name) - prnt(' if (o == NULL)') - prnt(' return -1;') - if size_too: - prnt(' {') - prnt(' PyObject *o1 = o;') - prnt(' o = Py_BuildValue("On", o1, (Py_ssize_t)sizeof(%s));' - % (name,)) - prnt(' Py_DECREF(o1);') - prnt(' if (o == NULL)') - prnt(' return -1;') - prnt(' }') - prnt(' res = PyObject_SetAttrString(lib, "%s", o);' % name) - prnt(' Py_DECREF(o);') - prnt(' if (res < 0)') - prnt(' return -1;') - prnt(' return %s;' % self._chained_list_constants[delayed]) - self._chained_list_constants[delayed] = funcname + '(lib)' - prnt('}') - prnt() - - def _generate_cpy_constant_collecttype(self, tp, name): - is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() - if not is_int: - self._do_collect_type(tp) - - def _generate_cpy_constant_decl(self, tp, name): - is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() - self._generate_cpy_const(is_int, name, tp) - - _generate_cpy_constant_method = _generate_nothing - _loading_cpy_constant = _loaded_noop - _loaded_cpy_constant = _loaded_noop - - # ---------- - # enums - - def _check_int_constant_value(self, name, value, err_prefix=''): - prnt = self._prnt - if value <= 0: - prnt(' if ((%s) > 0 || (long)(%s) != %dL) {' % ( - name, name, value)) - else: - prnt(' if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % ( - name, name, value)) - prnt(' char buf[64];') - prnt(' if ((%s) <= 0)' % name) - prnt(' snprintf(buf, 63, "%%ld", (long)(%s));' % name) - prnt(' else') - prnt(' snprintf(buf, 63, "%%lu", (unsigned long)(%s));' % - name) - prnt(' PyErr_Format(_cffi_VerificationError,') - prnt(' "%s%s has the real value %s, not %s",') - prnt(' "%s", "%s", buf, "%d");' % ( - err_prefix, name, value)) - prnt(' return -1;') - prnt(' }') - - def _enum_funcname(self, prefix, name): - # "$enum_$1" => "___D_enum____D_1" - name = name.replace('$', '___D_') - return '_cffi_e_%s_%s' % (prefix, name) - - def _generate_cpy_enum_decl(self, tp, name, prefix='enum'): - if tp.partial: - for enumerator in tp.enumerators: - self._generate_cpy_const(True, enumerator, delayed=False) - return - # - funcname = self._enum_funcname(prefix, name) - prnt = self._prnt - prnt('static int %s(PyObject *lib)' % funcname) - prnt('{') - for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): - self._check_int_constant_value(enumerator, enumvalue, - "enum %s: " % name) - prnt(' return %s;' % self._chained_list_constants[True]) - self._chained_list_constants[True] = funcname + '(lib)' - prnt('}') - prnt() - - _generate_cpy_enum_collecttype = _generate_nothing - _generate_cpy_enum_method = _generate_nothing - - def _loading_cpy_enum(self, tp, name, module): - if tp.partial: - enumvalues = [getattr(module, enumerator) - for enumerator in tp.enumerators] - tp.enumvalues = tuple(enumvalues) - tp.partial_resolved = True - - def _loaded_cpy_enum(self, tp, name, module, library): - for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): - setattr(library, enumerator, enumvalue) - - # ---------- - # macros: for now only for integers - - def _generate_cpy_macro_decl(self, tp, name): - if tp == '...': - check_value = None - else: - check_value = tp # an integer - self._generate_cpy_const(True, name, check_value=check_value) - - _generate_cpy_macro_collecttype = _generate_nothing - _generate_cpy_macro_method = _generate_nothing - _loading_cpy_macro = _loaded_noop - _loaded_cpy_macro = _loaded_noop - - # ---------- - # global variables - - def _generate_cpy_variable_collecttype(self, tp, name): - if isinstance(tp, model.ArrayType): - tp_ptr = model.PointerType(tp.item) - else: - tp_ptr = model.PointerType(tp) - self._do_collect_type(tp_ptr) - - def _generate_cpy_variable_decl(self, tp, name): - if isinstance(tp, model.ArrayType): - tp_ptr = model.PointerType(tp.item) - self._generate_cpy_const(False, name, tp, vartp=tp_ptr, - size_too = tp.length_is_unknown()) - else: - tp_ptr = model.PointerType(tp) - self._generate_cpy_const(False, name, tp_ptr, category='var') - - _generate_cpy_variable_method = _generate_nothing - _loading_cpy_variable = _loaded_noop - - def _loaded_cpy_variable(self, tp, name, module, library): - value = getattr(library, name) - if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the - # sense that "a=..." is forbidden - if tp.length_is_unknown(): - assert isinstance(value, tuple) - (value, size) = value - BItemType = self.ffi._get_cached_btype(tp.item) - length, rest = divmod(size, self.ffi.sizeof(BItemType)) - if rest != 0: - raise VerificationError( - "bad size: %r does not seem to be an array of %s" % - (name, tp.item)) - tp = tp.resolve_length(length) - # 'value' is a <cdata 'type *'> which we have to replace with - # a <cdata 'type[N]'> if the N is actually known - if tp.length is not None: - BArray = self.ffi._get_cached_btype(tp) - value = self.ffi.cast(BArray, value) - setattr(library, name, value) - return - # remove ptr=<cdata 'int *'> from the library instance, and replace - # it by a property on the class, which reads/writes into ptr[0]. - ptr = value - delattr(library, name) - def getter(library): - return ptr[0] - def setter(library, value): - ptr[0] = value - setattr(type(library), name, property(getter, setter)) - type(library)._cffi_dir.append(name) - - # ---------- - - def _generate_setup_custom(self): - prnt = self._prnt - prnt('static int _cffi_setup_custom(PyObject *lib)') - prnt('{') - prnt(' return %s;' % self._chained_list_constants[True]) - prnt('}') - -cffimod_header = r''' -#include <Python.h> -#include <stddef.h> - -/* this block of #ifs should be kept exactly identical between - c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py - and cffi/_cffi_include.h */ -#if defined(_MSC_VER) -# include <malloc.h> /* for alloca() */ -# if _MSC_VER < 1600 /* MSVC < 2010 */ - typedef __int8 int8_t; - typedef __int16 int16_t; - typedef __int32 int32_t; - typedef __int64 int64_t; - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; - typedef unsigned __int64 uint64_t; - typedef __int8 int_least8_t; - typedef __int16 int_least16_t; - typedef __int32 int_least32_t; - typedef __int64 int_least64_t; - typedef unsigned __int8 uint_least8_t; - typedef unsigned __int16 uint_least16_t; - typedef unsigned __int32 uint_least32_t; - typedef unsigned __int64 uint_least64_t; - typedef __int8 int_fast8_t; - typedef __int16 int_fast16_t; - typedef __int32 int_fast32_t; - typedef __int64 int_fast64_t; - typedef unsigned __int8 uint_fast8_t; - typedef unsigned __int16 uint_fast16_t; - typedef unsigned __int32 uint_fast32_t; - typedef unsigned __int64 uint_fast64_t; - typedef __int64 intmax_t; - typedef unsigned __int64 uintmax_t; -# else -# include <stdint.h> -# endif -# if _MSC_VER < 1800 /* MSVC < 2013 */ -# ifndef __cplusplus - typedef unsigned char _Bool; -# endif -# endif -#else -# include <stdint.h> -# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) -# include <alloca.h> -# endif -#endif - -#if PY_MAJOR_VERSION < 3 -# undef PyCapsule_CheckExact -# undef PyCapsule_GetPointer -# define PyCapsule_CheckExact(capsule) (PyCObject_Check(capsule)) -# define PyCapsule_GetPointer(capsule, name) \ - (PyCObject_AsVoidPtr(capsule)) -#endif - -#if PY_MAJOR_VERSION >= 3 -# define PyInt_FromLong PyLong_FromLong -#endif - -#define _cffi_from_c_double PyFloat_FromDouble -#define _cffi_from_c_float PyFloat_FromDouble -#define _cffi_from_c_long PyInt_FromLong -#define _cffi_from_c_ulong PyLong_FromUnsignedLong -#define _cffi_from_c_longlong PyLong_FromLongLong -#define _cffi_from_c_ulonglong PyLong_FromUnsignedLongLong -#define _cffi_from_c__Bool PyBool_FromLong - -#define _cffi_to_c_double PyFloat_AsDouble -#define _cffi_to_c_float PyFloat_AsDouble - -#define _cffi_from_c_int_const(x) \ - (((x) > 0) ? \ - ((unsigned long long)(x) <= (unsigned long long)LONG_MAX) ? \ - PyInt_FromLong((long)(x)) : \ - PyLong_FromUnsignedLongLong((unsigned long long)(x)) : \ - ((long long)(x) >= (long long)LONG_MIN) ? \ - PyInt_FromLong((long)(x)) : \ - PyLong_FromLongLong((long long)(x))) - -#define _cffi_from_c_int(x, type) \ - (((type)-1) > 0 ? /* unsigned */ \ - (sizeof(type) < sizeof(long) ? \ - PyInt_FromLong((long)x) : \ - sizeof(type) == sizeof(long) ? \ - PyLong_FromUnsignedLong((unsigned long)x) : \ - PyLong_FromUnsignedLongLong((unsigned long long)x)) : \ - (sizeof(type) <= sizeof(long) ? \ - PyInt_FromLong((long)x) : \ - PyLong_FromLongLong((long long)x))) - -#define _cffi_to_c_int(o, type) \ - ((type)( \ - sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o) \ - : (type)_cffi_to_c_i8(o)) : \ - sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o) \ - : (type)_cffi_to_c_i16(o)) : \ - sizeof(type) == 4 ? (((type)-1) > 0 ? (type)_cffi_to_c_u32(o) \ - : (type)_cffi_to_c_i32(o)) : \ - sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o) \ - : (type)_cffi_to_c_i64(o)) : \ - (Py_FatalError("unsupported size for type " #type), (type)0))) - -#define _cffi_to_c_i8 \ - ((int(*)(PyObject *))_cffi_exports[1]) -#define _cffi_to_c_u8 \ - ((int(*)(PyObject *))_cffi_exports[2]) -#define _cffi_to_c_i16 \ - ((int(*)(PyObject *))_cffi_exports[3]) -#define _cffi_to_c_u16 \ - ((int(*)(PyObject *))_cffi_exports[4]) -#define _cffi_to_c_i32 \ - ((int(*)(PyObject *))_cffi_exports[5]) -#define _cffi_to_c_u32 \ - ((unsigned int(*)(PyObject *))_cffi_exports[6]) -#define _cffi_to_c_i64 \ - ((long long(*)(PyObject *))_cffi_exports[7]) -#define _cffi_to_c_u64 \ - ((unsigned long long(*)(PyObject *))_cffi_exports[8]) -#define _cffi_to_c_char \ - ((int(*)(PyObject *))_cffi_exports[9]) -#define _cffi_from_c_pointer \ - ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[10]) -#define _cffi_to_c_pointer \ - ((char *(*)(PyObject *, CTypeDescrObject *))_cffi_exports[11]) -#define _cffi_get_struct_layout \ - ((PyObject *(*)(Py_ssize_t[]))_cffi_exports[12]) -#define _cffi_restore_errno \ - ((void(*)(void))_cffi_exports[13]) -#define _cffi_save_errno \ - ((void(*)(void))_cffi_exports[14]) -#define _cffi_from_c_char \ - ((PyObject *(*)(char))_cffi_exports[15]) -#define _cffi_from_c_deref \ - ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[16]) -#define _cffi_to_c \ - ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[17]) -#define _cffi_from_c_struct \ - ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[18]) -#define _cffi_to_c_wchar_t \ - ((wchar_t(*)(PyObject *))_cffi_exports[19]) -#define _cffi_from_c_wchar_t \ - ((PyObject *(*)(wchar_t))_cffi_exports[20]) -#define _cffi_to_c_long_double \ - ((long double(*)(PyObject *))_cffi_exports[21]) -#define _cffi_to_c__Bool \ - ((_Bool(*)(PyObject *))_cffi_exports[22]) -#define _cffi_prepare_pointer_call_argument \ - ((Py_ssize_t(*)(CTypeDescrObject *, PyObject *, char **))_cffi_exports[23]) -#define _cffi_convert_array_from_object \ - ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[24]) -#define _CFFI_NUM_EXPORTS 25 - -typedef struct _ctypedescr CTypeDescrObject; - -static void *_cffi_exports[_CFFI_NUM_EXPORTS]; -static PyObject *_cffi_types, *_cffi_VerificationError; - -static int _cffi_setup_custom(PyObject *lib); /* forward */ - -static PyObject *_cffi_setup(PyObject *self, PyObject *args) -{ - PyObject *library; - int was_alive = (_cffi_types != NULL); - (void)self; /* unused */ - if (!PyArg_ParseTuple(args, "OOO", &_cffi_types, &_cffi_VerificationError, - &library)) - return NULL; - Py_INCREF(_cffi_types); - Py_INCREF(_cffi_VerificationError); - if (_cffi_setup_custom(library) < 0) - return NULL; - return PyBool_FromLong(was_alive); -} - -union _cffi_union_alignment_u { - unsigned char m_char; - unsigned short m_short; - unsigned int m_int; - unsigned long m_long; - unsigned long long m_longlong; - float m_float; - double m_double; - long double m_longdouble; -}; - -struct _cffi_freeme_s { - struct _cffi_freeme_s *next; - union _cffi_union_alignment_u alignment; -}; - -#ifdef __GNUC__ - __attribute__((unused)) -#endif -static int _cffi_convert_array_argument(CTypeDescrObject *ctptr, PyObject *arg, - char **output_data, Py_ssize_t datasize, - struct _cffi_freeme_s **freeme) -{ - char *p; - if (datasize < 0) - return -1; - - p = *output_data; - if (p == NULL) { - struct _cffi_freeme_s *fp = (struct _cffi_freeme_s *)PyObject_Malloc( - offsetof(struct _cffi_freeme_s, alignment) + (size_t)datasize); - if (fp == NULL) - return -1; - fp->next = *freeme; - *freeme = fp; - p = *output_data = (char *)&fp->alignment; - } - memset((void *)p, 0, (size_t)datasize); - return _cffi_convert_array_from_object(p, ctptr, arg); -} - -#ifdef __GNUC__ - __attribute__((unused)) -#endif -static void _cffi_free_array_arguments(struct _cffi_freeme_s *freeme) -{ - do { - void *p = (void *)freeme; - freeme = freeme->next; - PyObject_Free(p); - } while (freeme != NULL); -} - -static int _cffi_init(void) -{ - PyObject *module, *c_api_object = NULL; - - module = PyImport_ImportModule("_cffi_backend"); - if (module == NULL) - goto failure; - - c_api_object = PyObject_GetAttrString(module, "_C_API"); - if (c_api_object == NULL) - goto failure; - if (!PyCapsule_CheckExact(c_api_object)) { - PyErr_SetNone(PyExc_ImportError); - goto failure; - } - memcpy(_cffi_exports, PyCapsule_GetPointer(c_api_object, "cffi"), - _CFFI_NUM_EXPORTS * sizeof(void *)); - - Py_DECREF(module); - Py_DECREF(c_api_object); - return 0; - - failure: - Py_XDECREF(module); - Py_XDECREF(c_api_object); - return -1; -} - -#define _cffi_type(num) ((CTypeDescrObject *)PyList_GET_ITEM(_cffi_types, num)) - -/**********/ -''' diff --git a/cffi/vengine_gen.py b/cffi/vengine_gen.py deleted file mode 100644 index 2642152..0000000 --- a/cffi/vengine_gen.py +++ /dev/null @@ -1,675 +0,0 @@ -# -# DEPRECATED: implementation for ffi.verify() -# -import sys, os -import types - -from . import model -from .error import VerificationError - - -class VGenericEngine(object): - _class_key = 'g' - _gen_python_module = False - - def __init__(self, verifier): - self.verifier = verifier - self.ffi = verifier.ffi - self.export_symbols = [] - self._struct_pending_verification = {} - - def patch_extension_kwds(self, kwds): - # add 'export_symbols' to the dictionary. Note that we add the - # list before filling it. When we fill it, it will thus also show - # up in kwds['export_symbols']. - kwds.setdefault('export_symbols', self.export_symbols) - - def find_module(self, module_name, path, so_suffixes): - for so_suffix in so_suffixes: - basename = module_name + so_suffix - if path is None: - path = sys.path - for dirname in path: - filename = os.path.join(dirname, basename) - if os.path.isfile(filename): - return filename - - def collect_types(self): - pass # not needed in the generic engine - - def _prnt(self, what=''): - self._f.write(what + '\n') - - def write_source_to_f(self): - prnt = self._prnt - # first paste some standard set of lines that are mostly '#include' - prnt(cffimod_header) - # then paste the C source given by the user, verbatim. - prnt(self.verifier.preamble) - # - # call generate_gen_xxx_decl(), for every xxx found from - # ffi._parser._declarations. This generates all the functions. - self._generate('decl') - # - # on Windows, distutils insists on putting init_cffi_xyz in - # 'export_symbols', so instead of fighting it, just give up and - # give it one - if sys.platform == 'win32': - if sys.version_info >= (3,): - prefix = 'PyInit_' - else: - prefix = 'init' - modname = self.verifier.get_module_name() - prnt("void %s%s(void) { }\n" % (prefix, modname)) - - def load_library(self, flags=0): - # import it with the CFFI backend - backend = self.ffi._backend - # needs to make a path that contains '/', on Posix - filename = os.path.join(os.curdir, self.verifier.modulefilename) - module = backend.load_library(filename, flags) - # - # call loading_gen_struct() to get the struct layout inferred by - # the C compiler - self._load(module, 'loading') - - # build the FFILibrary class and instance, this is a module subclass - # because modules are expected to have usually-constant-attributes and - # in PyPy this means the JIT is able to treat attributes as constant, - # which we want. - class FFILibrary(types.ModuleType): - _cffi_generic_module = module - _cffi_ffi = self.ffi - _cffi_dir = [] - def __dir__(self): - return FFILibrary._cffi_dir - library = FFILibrary("") - # - # finally, call the loaded_gen_xxx() functions. This will set - # up the 'library' object. - self._load(module, 'loaded', library=library) - return library - - def _get_declarations(self): - lst = [(key, tp) for (key, (tp, qual)) in - self.ffi._parser._declarations.items()] - lst.sort() - return lst - - def _generate(self, step_name): - for name, tp in self._get_declarations(): - kind, realname = name.split(' ', 1) - try: - method = getattr(self, '_generate_gen_%s_%s' % (kind, - step_name)) - except AttributeError: - raise VerificationError( - "not implemented in verify(): %r" % name) - try: - method(tp, realname) - except Exception as e: - model.attach_exception_info(e, name) - raise - - def _load(self, module, step_name, **kwds): - for name, tp in self._get_declarations(): - kind, realname = name.split(' ', 1) - method = getattr(self, '_%s_gen_%s' % (step_name, kind)) - try: - method(tp, realname, module, **kwds) - except Exception as e: - model.attach_exception_info(e, name) - raise - - def _generate_nothing(self, tp, name): - pass - - def _loaded_noop(self, tp, name, module, **kwds): - pass - - # ---------- - # typedefs: generates no code so far - - _generate_gen_typedef_decl = _generate_nothing - _loading_gen_typedef = _loaded_noop - _loaded_gen_typedef = _loaded_noop - - # ---------- - # function declarations - - def _generate_gen_function_decl(self, tp, name): - assert isinstance(tp, model.FunctionPtrType) - if tp.ellipsis: - # cannot support vararg functions better than this: check for its - # exact type (including the fixed arguments), and build it as a - # constant function pointer (no _cffi_f_%s wrapper) - self._generate_gen_const(False, name, tp) - return - prnt = self._prnt - numargs = len(tp.args) - argnames = [] - for i, type in enumerate(tp.args): - indirection = '' - if isinstance(type, model.StructOrUnion): - indirection = '*' - argnames.append('%sx%d' % (indirection, i)) - context = 'argument of %s' % name - arglist = [type.get_c_name(' %s' % arg, context) - for type, arg in zip(tp.args, argnames)] - tpresult = tp.result - if isinstance(tpresult, model.StructOrUnion): - arglist.insert(0, tpresult.get_c_name(' *r', context)) - tpresult = model.void_type - arglist = ', '.join(arglist) or 'void' - wrappername = '_cffi_f_%s' % name - self.export_symbols.append(wrappername) - if tp.abi: - abi = tp.abi + ' ' - else: - abi = '' - funcdecl = ' %s%s(%s)' % (abi, wrappername, arglist) - context = 'result of %s' % name - prnt(tpresult.get_c_name(funcdecl, context)) - prnt('{') - # - if isinstance(tp.result, model.StructOrUnion): - result_code = '*r = ' - elif not isinstance(tp.result, model.VoidType): - result_code = 'return ' - else: - result_code = '' - prnt(' %s%s(%s);' % (result_code, name, ', '.join(argnames))) - prnt('}') - prnt() - - _loading_gen_function = _loaded_noop - - def _loaded_gen_function(self, tp, name, module, library): - assert isinstance(tp, model.FunctionPtrType) - if tp.ellipsis: - newfunction = self._load_constant(False, tp, name, module) - else: - indirections = [] - base_tp = tp - if (any(isinstance(typ, model.StructOrUnion) for typ in tp.args) - or isinstance(tp.result, model.StructOrUnion)): - indirect_args = [] - for i, typ in enumerate(tp.args): - if isinstance(typ, model.StructOrUnion): - typ = model.PointerType(typ) - indirections.append((i, typ)) - indirect_args.append(typ) - indirect_result = tp.result - if isinstance(indirect_result, model.StructOrUnion): - if indirect_result.fldtypes is None: - raise TypeError("'%s' is used as result type, " - "but is opaque" % ( - indirect_result._get_c_name(),)) - indirect_result = model.PointerType(indirect_result) - indirect_args.insert(0, indirect_result) - indirections.insert(0, ("result", indirect_result)) - indirect_result = model.void_type - tp = model.FunctionPtrType(tuple(indirect_args), - indirect_result, tp.ellipsis) - BFunc = self.ffi._get_cached_btype(tp) - wrappername = '_cffi_f_%s' % name - newfunction = module.load_function(BFunc, wrappername) - for i, typ in indirections: - newfunction = self._make_struct_wrapper(newfunction, i, typ, - base_tp) - setattr(library, name, newfunction) - type(library)._cffi_dir.append(name) - - def _make_struct_wrapper(self, oldfunc, i, tp, base_tp): - backend = self.ffi._backend - BType = self.ffi._get_cached_btype(tp) - if i == "result": - ffi = self.ffi - def newfunc(*args): - res = ffi.new(BType) - oldfunc(res, *args) - return res[0] - else: - def newfunc(*args): - args = args[:i] + (backend.newp(BType, args[i]),) + args[i+1:] - return oldfunc(*args) - newfunc._cffi_base_type = base_tp - return newfunc - - # ---------- - # named structs - - def _generate_gen_struct_decl(self, tp, name): - assert name == tp.name - self._generate_struct_or_union_decl(tp, 'struct', name) - - def _loading_gen_struct(self, tp, name, module): - self._loading_struct_or_union(tp, 'struct', name, module) - - def _loaded_gen_struct(self, tp, name, module, **kwds): - self._loaded_struct_or_union(tp) - - def _generate_gen_union_decl(self, tp, name): - assert name == tp.name - self._generate_struct_or_union_decl(tp, 'union', name) - - def _loading_gen_union(self, tp, name, module): - self._loading_struct_or_union(tp, 'union', name, module) - - def _loaded_gen_union(self, tp, name, module, **kwds): - self._loaded_struct_or_union(tp) - - def _generate_struct_or_union_decl(self, tp, prefix, name): - if tp.fldnames is None: - return # nothing to do with opaque structs - checkfuncname = '_cffi_check_%s_%s' % (prefix, name) - layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) - cname = ('%s %s' % (prefix, name)).strip() - # - prnt = self._prnt - prnt('static void %s(%s *p)' % (checkfuncname, cname)) - prnt('{') - prnt(' /* only to generate compile-time warnings or errors */') - prnt(' (void)p;') - for fname, ftype, fbitsize, fqual in tp.enumfields(): - if (isinstance(ftype, model.PrimitiveType) - and ftype.is_integer_type()) or fbitsize >= 0: - # accept all integers, but complain on float or double - prnt(' (void)((p->%s) << 1);' % fname) - else: - # only accept exactly the type declared. - try: - prnt(' { %s = &p->%s; (void)tmp; }' % ( - ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual), - fname)) - except VerificationError as e: - prnt(' /* %s */' % str(e)) # cannot verify it, ignore - prnt('}') - self.export_symbols.append(layoutfuncname) - prnt('intptr_t %s(intptr_t i)' % (layoutfuncname,)) - prnt('{') - prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname) - prnt(' static intptr_t nums[] = {') - prnt(' sizeof(%s),' % cname) - prnt(' offsetof(struct _cffi_aligncheck, y),') - for fname, ftype, fbitsize, fqual in tp.enumfields(): - if fbitsize >= 0: - continue # xxx ignore fbitsize for now - prnt(' offsetof(%s, %s),' % (cname, fname)) - if isinstance(ftype, model.ArrayType) and ftype.length is None: - prnt(' 0, /* %s */' % ftype._get_c_name()) - else: - prnt(' sizeof(((%s *)0)->%s),' % (cname, fname)) - prnt(' -1') - prnt(' };') - prnt(' return nums[i];') - prnt(' /* the next line is not executed, but compiled */') - prnt(' %s(0);' % (checkfuncname,)) - prnt('}') - prnt() - - def _loading_struct_or_union(self, tp, prefix, name, module): - if tp.fldnames is None: - return # nothing to do with opaque structs - layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) - # - BFunc = self.ffi._typeof_locked("intptr_t(*)(intptr_t)")[0] - function = module.load_function(BFunc, layoutfuncname) - layout = [] - num = 0 - while True: - x = function(num) - if x < 0: break - layout.append(x) - num += 1 - if isinstance(tp, model.StructOrUnion) and tp.partial: - # use the function()'s sizes and offsets to guide the - # layout of the struct - totalsize = layout[0] - totalalignment = layout[1] - fieldofs = layout[2::2] - fieldsize = layout[3::2] - tp.force_flatten() - assert len(fieldofs) == len(fieldsize) == len(tp.fldnames) - tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment - else: - cname = ('%s %s' % (prefix, name)).strip() - self._struct_pending_verification[tp] = layout, cname - - def _loaded_struct_or_union(self, tp): - if tp.fldnames is None: - return # nothing to do with opaque structs - self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered - - if tp in self._struct_pending_verification: - # check that the layout sizes and offsets match the real ones - def check(realvalue, expectedvalue, msg): - if realvalue != expectedvalue: - raise VerificationError( - "%s (we have %d, but C compiler says %d)" - % (msg, expectedvalue, realvalue)) - ffi = self.ffi - BStruct = ffi._get_cached_btype(tp) - layout, cname = self._struct_pending_verification.pop(tp) - check(layout[0], ffi.sizeof(BStruct), "wrong total size") - check(layout[1], ffi.alignof(BStruct), "wrong total alignment") - i = 2 - for fname, ftype, fbitsize, fqual in tp.enumfields(): - if fbitsize >= 0: - continue # xxx ignore fbitsize for now - check(layout[i], ffi.offsetof(BStruct, fname), - "wrong offset for field %r" % (fname,)) - if layout[i+1] != 0: - BField = ffi._get_cached_btype(ftype) - check(layout[i+1], ffi.sizeof(BField), - "wrong size for field %r" % (fname,)) - i += 2 - assert i == len(layout) - - # ---------- - # 'anonymous' declarations. These are produced for anonymous structs - # or unions; the 'name' is obtained by a typedef. - - def _generate_gen_anonymous_decl(self, tp, name): - if isinstance(tp, model.EnumType): - self._generate_gen_enum_decl(tp, name, '') - else: - self._generate_struct_or_union_decl(tp, '', name) - - def _loading_gen_anonymous(self, tp, name, module): - if isinstance(tp, model.EnumType): - self._loading_gen_enum(tp, name, module, '') - else: - self._loading_struct_or_union(tp, '', name, module) - - def _loaded_gen_anonymous(self, tp, name, module, **kwds): - if isinstance(tp, model.EnumType): - self._loaded_gen_enum(tp, name, module, **kwds) - else: - self._loaded_struct_or_union(tp) - - # ---------- - # constants, likely declared with '#define' - - def _generate_gen_const(self, is_int, name, tp=None, category='const', - check_value=None): - prnt = self._prnt - funcname = '_cffi_%s_%s' % (category, name) - self.export_symbols.append(funcname) - if check_value is not None: - assert is_int - assert category == 'const' - prnt('int %s(char *out_error)' % funcname) - prnt('{') - self._check_int_constant_value(name, check_value) - prnt(' return 0;') - prnt('}') - elif is_int: - assert category == 'const' - prnt('int %s(long long *out_value)' % funcname) - prnt('{') - prnt(' *out_value = (long long)(%s);' % (name,)) - prnt(' return (%s) <= 0;' % (name,)) - prnt('}') - else: - assert tp is not None - assert check_value is None - if category == 'var': - ampersand = '&' - else: - ampersand = '' - extra = '' - if category == 'const' and isinstance(tp, model.StructOrUnion): - extra = 'const *' - ampersand = '&' - prnt(tp.get_c_name(' %s%s(void)' % (extra, funcname), name)) - prnt('{') - prnt(' return (%s%s);' % (ampersand, name)) - prnt('}') - prnt() - - def _generate_gen_constant_decl(self, tp, name): - is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() - self._generate_gen_const(is_int, name, tp) - - _loading_gen_constant = _loaded_noop - - def _load_constant(self, is_int, tp, name, module, check_value=None): - funcname = '_cffi_const_%s' % name - if check_value is not None: - assert is_int - self._load_known_int_constant(module, funcname) - value = check_value - elif is_int: - BType = self.ffi._typeof_locked("long long*")[0] - BFunc = self.ffi._typeof_locked("int(*)(long long*)")[0] - function = module.load_function(BFunc, funcname) - p = self.ffi.new(BType) - negative = function(p) - value = int(p[0]) - if value < 0 and not negative: - BLongLong = self.ffi._typeof_locked("long long")[0] - value += (1 << (8*self.ffi.sizeof(BLongLong))) - else: - assert check_value is None - fntypeextra = '(*)(void)' - if isinstance(tp, model.StructOrUnion): - fntypeextra = '*' + fntypeextra - BFunc = self.ffi._typeof_locked(tp.get_c_name(fntypeextra, name))[0] - function = module.load_function(BFunc, funcname) - value = function() - if isinstance(tp, model.StructOrUnion): - value = value[0] - return value - - def _loaded_gen_constant(self, tp, name, module, library): - is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() - value = self._load_constant(is_int, tp, name, module) - setattr(library, name, value) - type(library)._cffi_dir.append(name) - - # ---------- - # enums - - def _check_int_constant_value(self, name, value): - prnt = self._prnt - if value <= 0: - prnt(' if ((%s) > 0 || (long)(%s) != %dL) {' % ( - name, name, value)) - else: - prnt(' if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % ( - name, name, value)) - prnt(' char buf[64];') - prnt(' if ((%s) <= 0)' % name) - prnt(' sprintf(buf, "%%ld", (long)(%s));' % name) - prnt(' else') - prnt(' sprintf(buf, "%%lu", (unsigned long)(%s));' % - name) - prnt(' sprintf(out_error, "%s has the real value %s, not %s",') - prnt(' "%s", buf, "%d");' % (name[:100], value)) - prnt(' return -1;') - prnt(' }') - - def _load_known_int_constant(self, module, funcname): - BType = self.ffi._typeof_locked("char[]")[0] - BFunc = self.ffi._typeof_locked("int(*)(char*)")[0] - function = module.load_function(BFunc, funcname) - p = self.ffi.new(BType, 256) - if function(p) < 0: - error = self.ffi.string(p) - if sys.version_info >= (3,): - error = str(error, 'utf-8') - raise VerificationError(error) - - def _enum_funcname(self, prefix, name): - # "$enum_$1" => "___D_enum____D_1" - name = name.replace('$', '___D_') - return '_cffi_e_%s_%s' % (prefix, name) - - def _generate_gen_enum_decl(self, tp, name, prefix='enum'): - if tp.partial: - for enumerator in tp.enumerators: - self._generate_gen_const(True, enumerator) - return - # - funcname = self._enum_funcname(prefix, name) - self.export_symbols.append(funcname) - prnt = self._prnt - prnt('int %s(char *out_error)' % funcname) - prnt('{') - for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): - self._check_int_constant_value(enumerator, enumvalue) - prnt(' return 0;') - prnt('}') - prnt() - - def _loading_gen_enum(self, tp, name, module, prefix='enum'): - if tp.partial: - enumvalues = [self._load_constant(True, tp, enumerator, module) - for enumerator in tp.enumerators] - tp.enumvalues = tuple(enumvalues) - tp.partial_resolved = True - else: - funcname = self._enum_funcname(prefix, name) - self._load_known_int_constant(module, funcname) - - def _loaded_gen_enum(self, tp, name, module, library): - for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): - setattr(library, enumerator, enumvalue) - type(library)._cffi_dir.append(enumerator) - - # ---------- - # macros: for now only for integers - - def _generate_gen_macro_decl(self, tp, name): - if tp == '...': - check_value = None - else: - check_value = tp # an integer - self._generate_gen_const(True, name, check_value=check_value) - - _loading_gen_macro = _loaded_noop - - def _loaded_gen_macro(self, tp, name, module, library): - if tp == '...': - check_value = None - else: - check_value = tp # an integer - value = self._load_constant(True, tp, name, module, - check_value=check_value) - setattr(library, name, value) - type(library)._cffi_dir.append(name) - - # ---------- - # global variables - - def _generate_gen_variable_decl(self, tp, name): - if isinstance(tp, model.ArrayType): - if tp.length_is_unknown(): - prnt = self._prnt - funcname = '_cffi_sizeof_%s' % (name,) - self.export_symbols.append(funcname) - prnt("size_t %s(void)" % funcname) - prnt("{") - prnt(" return sizeof(%s);" % (name,)) - prnt("}") - tp_ptr = model.PointerType(tp.item) - self._generate_gen_const(False, name, tp_ptr) - else: - tp_ptr = model.PointerType(tp) - self._generate_gen_const(False, name, tp_ptr, category='var') - - _loading_gen_variable = _loaded_noop - - def _loaded_gen_variable(self, tp, name, module, library): - if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the - # sense that "a=..." is forbidden - if tp.length_is_unknown(): - funcname = '_cffi_sizeof_%s' % (name,) - BFunc = self.ffi._typeof_locked('size_t(*)(void)')[0] - function = module.load_function(BFunc, funcname) - size = function() - BItemType = self.ffi._get_cached_btype(tp.item) - length, rest = divmod(size, self.ffi.sizeof(BItemType)) - if rest != 0: - raise VerificationError( - "bad size: %r does not seem to be an array of %s" % - (name, tp.item)) - tp = tp.resolve_length(length) - tp_ptr = model.PointerType(tp.item) - value = self._load_constant(False, tp_ptr, name, module) - # 'value' is a <cdata 'type *'> which we have to replace with - # a <cdata 'type[N]'> if the N is actually known - if tp.length is not None: - BArray = self.ffi._get_cached_btype(tp) - value = self.ffi.cast(BArray, value) - setattr(library, name, value) - type(library)._cffi_dir.append(name) - return - # remove ptr=<cdata 'int *'> from the library instance, and replace - # it by a property on the class, which reads/writes into ptr[0]. - funcname = '_cffi_var_%s' % name - BFunc = self.ffi._typeof_locked(tp.get_c_name('*(*)(void)', name))[0] - function = module.load_function(BFunc, funcname) - ptr = function() - def getter(library): - return ptr[0] - def setter(library, value): - ptr[0] = value - setattr(type(library), name, property(getter, setter)) - type(library)._cffi_dir.append(name) - -cffimod_header = r''' -#include <stdio.h> -#include <stddef.h> -#include <stdarg.h> -#include <errno.h> -#include <sys/types.h> /* XXX for ssize_t on some platforms */ - -/* this block of #ifs should be kept exactly identical between - c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py - and cffi/_cffi_include.h */ -#if defined(_MSC_VER) -# include <malloc.h> /* for alloca() */ -# if _MSC_VER < 1600 /* MSVC < 2010 */ - typedef __int8 int8_t; - typedef __int16 int16_t; - typedef __int32 int32_t; - typedef __int64 int64_t; - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; - typedef unsigned __int64 uint64_t; - typedef __int8 int_least8_t; - typedef __int16 int_least16_t; - typedef __int32 int_least32_t; - typedef __int64 int_least64_t; - typedef unsigned __int8 uint_least8_t; - typedef unsigned __int16 uint_least16_t; - typedef unsigned __int32 uint_least32_t; - typedef unsigned __int64 uint_least64_t; - typedef __int8 int_fast8_t; - typedef __int16 int_fast16_t; - typedef __int32 int_fast32_t; - typedef __int64 int_fast64_t; - typedef unsigned __int8 uint_fast8_t; - typedef unsigned __int16 uint_fast16_t; - typedef unsigned __int32 uint_fast32_t; - typedef unsigned __int64 uint_fast64_t; - typedef __int64 intmax_t; - typedef unsigned __int64 uintmax_t; -# else -# include <stdint.h> -# endif -# if _MSC_VER < 1800 /* MSVC < 2013 */ -# ifndef __cplusplus - typedef unsigned char _Bool; -# endif -# endif -#else -# include <stdint.h> -# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) -# include <alloca.h> -# endif -#endif -''' diff --git a/cffi/verifier.py b/cffi/verifier.py deleted file mode 100644 index a500c78..0000000 --- a/cffi/verifier.py +++ /dev/null @@ -1,307 +0,0 @@ -# -# DEPRECATED: implementation for ffi.verify() -# -import sys, os, binascii, shutil, io -from . import __version_verifier_modules__ -from . import ffiplatform -from .error import VerificationError - -if sys.version_info >= (3, 3): - import importlib.machinery - def _extension_suffixes(): - return importlib.machinery.EXTENSION_SUFFIXES[:] -else: - import imp - def _extension_suffixes(): - return [suffix for suffix, _, type in imp.get_suffixes() - if type == imp.C_EXTENSION] - - -if sys.version_info >= (3,): - NativeIO = io.StringIO -else: - class NativeIO(io.BytesIO): - def write(self, s): - if isinstance(s, unicode): - s = s.encode('ascii') - super(NativeIO, self).write(s) - - -class Verifier(object): - - def __init__(self, ffi, preamble, tmpdir=None, modulename=None, - ext_package=None, tag='', force_generic_engine=False, - source_extension='.c', flags=None, relative_to=None, **kwds): - if ffi._parser._uses_new_feature: - raise VerificationError( - "feature not supported with ffi.verify(), but only " - "with ffi.set_source(): %s" % (ffi._parser._uses_new_feature,)) - self.ffi = ffi - self.preamble = preamble - if not modulename: - flattened_kwds = ffiplatform.flatten(kwds) - vengine_class = _locate_engine_class(ffi, force_generic_engine) - self._vengine = vengine_class(self) - self._vengine.patch_extension_kwds(kwds) - self.flags = flags - self.kwds = self.make_relative_to(kwds, relative_to) - # - if modulename: - if tag: - raise TypeError("can't specify both 'modulename' and 'tag'") - else: - key = '\x00'.join(['%d.%d' % sys.version_info[:2], - __version_verifier_modules__, - preamble, flattened_kwds] + - ffi._cdefsources) - if sys.version_info >= (3,): - key = key.encode('utf-8') - k1 = hex(binascii.crc32(key[0::2]) & 0xffffffff) - k1 = k1.lstrip('0x').rstrip('L') - k2 = hex(binascii.crc32(key[1::2]) & 0xffffffff) - k2 = k2.lstrip('0').rstrip('L') - modulename = '_cffi_%s_%s%s%s' % (tag, self._vengine._class_key, - k1, k2) - suffix = _get_so_suffixes()[0] - self.tmpdir = tmpdir or _caller_dir_pycache() - self.sourcefilename = os.path.join(self.tmpdir, modulename + source_extension) - self.modulefilename = os.path.join(self.tmpdir, modulename + suffix) - self.ext_package = ext_package - self._has_source = False - self._has_module = False - - def write_source(self, file=None): - """Write the C source code. It is produced in 'self.sourcefilename', - which can be tweaked beforehand.""" - with self.ffi._lock: - if self._has_source and file is None: - raise VerificationError( - "source code already written") - self._write_source(file) - - def compile_module(self): - """Write the C source code (if not done already) and compile it. - This produces a dynamic link library in 'self.modulefilename'.""" - with self.ffi._lock: - if self._has_module: - raise VerificationError("module already compiled") - if not self._has_source: - self._write_source() - self._compile_module() - - def load_library(self): - """Get a C module from this Verifier instance. - Returns an instance of a FFILibrary class that behaves like the - objects returned by ffi.dlopen(), but that delegates all - operations to the C module. If necessary, the C code is written - and compiled first. - """ - with self.ffi._lock: - if not self._has_module: - self._locate_module() - if not self._has_module: - if not self._has_source: - self._write_source() - self._compile_module() - return self._load_library() - - def get_module_name(self): - basename = os.path.basename(self.modulefilename) - # kill both the .so extension and the other .'s, as introduced - # by Python 3: 'basename.cpython-33m.so' - basename = basename.split('.', 1)[0] - # and the _d added in Python 2 debug builds --- but try to be - # conservative and not kill a legitimate _d - if basename.endswith('_d') and hasattr(sys, 'gettotalrefcount'): - basename = basename[:-2] - return basename - - def get_extension(self): - ffiplatform._hack_at_distutils() # backward compatibility hack - if not self._has_source: - with self.ffi._lock: - if not self._has_source: - self._write_source() - sourcename = ffiplatform.maybe_relative_path(self.sourcefilename) - modname = self.get_module_name() - return ffiplatform.get_extension(sourcename, modname, **self.kwds) - - def generates_python_module(self): - return self._vengine._gen_python_module - - def make_relative_to(self, kwds, relative_to): - if relative_to and os.path.dirname(relative_to): - dirname = os.path.dirname(relative_to) - kwds = kwds.copy() - for key in ffiplatform.LIST_OF_FILE_NAMES: - if key in kwds: - lst = kwds[key] - if not isinstance(lst, (list, tuple)): - raise TypeError("keyword '%s' should be a list or tuple" - % (key,)) - lst = [os.path.join(dirname, fn) for fn in lst] - kwds[key] = lst - return kwds - - # ---------- - - def _locate_module(self): - if not os.path.isfile(self.modulefilename): - if self.ext_package: - try: - pkg = __import__(self.ext_package, None, None, ['__doc__']) - except ImportError: - return # cannot import the package itself, give up - # (e.g. it might be called differently before installation) - path = pkg.__path__ - else: - path = None - filename = self._vengine.find_module(self.get_module_name(), path, - _get_so_suffixes()) - if filename is None: - return - self.modulefilename = filename - self._vengine.collect_types() - self._has_module = True - - def _write_source_to(self, file): - self._vengine._f = file - try: - self._vengine.write_source_to_f() - finally: - del self._vengine._f - - def _write_source(self, file=None): - if file is not None: - self._write_source_to(file) - else: - # Write our source file to an in memory file. - f = NativeIO() - self._write_source_to(f) - source_data = f.getvalue() - - # Determine if this matches the current file - if os.path.exists(self.sourcefilename): - with open(self.sourcefilename, "r") as fp: - needs_written = not (fp.read() == source_data) - else: - needs_written = True - - # Actually write the file out if it doesn't match - if needs_written: - _ensure_dir(self.sourcefilename) - with open(self.sourcefilename, "w") as fp: - fp.write(source_data) - - # Set this flag - self._has_source = True - - def _compile_module(self): - # compile this C source - tmpdir = os.path.dirname(self.sourcefilename) - outputfilename = ffiplatform.compile(tmpdir, self.get_extension()) - try: - same = ffiplatform.samefile(outputfilename, self.modulefilename) - except OSError: - same = False - if not same: - _ensure_dir(self.modulefilename) - shutil.move(outputfilename, self.modulefilename) - self._has_module = True - - def _load_library(self): - assert self._has_module - if self.flags is not None: - return self._vengine.load_library(self.flags) - else: - return self._vengine.load_library() - -# ____________________________________________________________ - -_FORCE_GENERIC_ENGINE = False # for tests - -def _locate_engine_class(ffi, force_generic_engine): - if _FORCE_GENERIC_ENGINE: - force_generic_engine = True - if not force_generic_engine: - if '__pypy__' in sys.builtin_module_names: - force_generic_engine = True - else: - try: - import _cffi_backend - except ImportError: - _cffi_backend = '?' - if ffi._backend is not _cffi_backend: - force_generic_engine = True - if force_generic_engine: - from . import vengine_gen - return vengine_gen.VGenericEngine - else: - from . import vengine_cpy - return vengine_cpy.VCPythonEngine - -# ____________________________________________________________ - -_TMPDIR = None - -def _caller_dir_pycache(): - if _TMPDIR: - return _TMPDIR - result = os.environ.get('CFFI_TMPDIR') - if result: - return result - filename = sys._getframe(2).f_code.co_filename - return os.path.abspath(os.path.join(os.path.dirname(filename), - '__pycache__')) - -def set_tmpdir(dirname): - """Set the temporary directory to use instead of __pycache__.""" - global _TMPDIR - _TMPDIR = dirname - -def cleanup_tmpdir(tmpdir=None, keep_so=False): - """Clean up the temporary directory by removing all files in it - called `_cffi_*.{c,so}` as well as the `build` subdirectory.""" - tmpdir = tmpdir or _caller_dir_pycache() - try: - filelist = os.listdir(tmpdir) - except OSError: - return - if keep_so: - suffix = '.c' # only remove .c files - else: - suffix = _get_so_suffixes()[0].lower() - for fn in filelist: - if fn.lower().startswith('_cffi_') and ( - fn.lower().endswith(suffix) or fn.lower().endswith('.c')): - try: - os.unlink(os.path.join(tmpdir, fn)) - except OSError: - pass - clean_dir = [os.path.join(tmpdir, 'build')] - for dir in clean_dir: - try: - for fn in os.listdir(dir): - fn = os.path.join(dir, fn) - if os.path.isdir(fn): - clean_dir.append(fn) - else: - os.unlink(fn) - except OSError: - pass - -def _get_so_suffixes(): - suffixes = _extension_suffixes() - if not suffixes: - # bah, no C_EXTENSION available. Occurs on pypy without cpyext - if sys.platform == 'win32': - suffixes = [".pyd"] - else: - suffixes = [".so"] - - return suffixes - -def _ensure_dir(filename): - dirname = os.path.dirname(filename) - if dirname and not os.path.isdir(dirname): - os.makedirs(dirname) diff --git a/demo/_curses.py b/demo/_curses.py deleted file mode 100644 index 46443c2..0000000 --- a/demo/_curses.py +++ /dev/null @@ -1,1075 +0,0 @@ -"""Reimplementation of the standard extension module '_curses' using cffi.""" - -import sys -from functools import wraps - -from _curses_cffi import ffi, lib - - -def _copy_to_globals(name): - globals()[name] = getattr(lib, name) - - -def _setup(): - for name in ['ERR', 'OK', 'KEY_MIN', 'KEY_MAX', - 'A_ATTRIBUTES', 'A_NORMAL', 'A_STANDOUT', 'A_UNDERLINE', - 'A_REVERSE', 'A_BLINK', 'A_DIM', 'A_BOLD', 'A_ALTCHARSET', - 'A_PROTECT', 'A_CHARTEXT', 'A_COLOR', - 'COLOR_BLACK', 'COLOR_RED', 'COLOR_GREEN', 'COLOR_YELLOW', - 'COLOR_BLUE', 'COLOR_MAGENTA', 'COLOR_CYAN', 'COLOR_WHITE', - ]: - _copy_to_globals(name) - - if not lib._m_NetBSD: - _copy_to_globals('A_INVIS') - - for name in ['A_HORIZONTAL', 'A_LEFT', 'A_LOW', 'A_RIGHT', 'A_TOP', - 'A_VERTICAL', - ]: - if hasattr(lib, name): - _copy_to_globals(name) - - if lib._m_NCURSES_MOUSE_VERSION: - for name in ["BUTTON1_PRESSED", "BUTTON1_RELEASED", "BUTTON1_CLICKED", - "BUTTON1_DOUBLE_CLICKED", "BUTTON1_TRIPLE_CLICKED", - "BUTTON2_PRESSED", "BUTTON2_RELEASED", "BUTTON2_CLICKED", - "BUTTON2_DOUBLE_CLICKED", "BUTTON2_TRIPLE_CLICKED", - "BUTTON3_PRESSED", "BUTTON3_RELEASED", "BUTTON3_CLICKED", - "BUTTON3_DOUBLE_CLICKED", "BUTTON3_TRIPLE_CLICKED", - "BUTTON4_PRESSED", "BUTTON4_RELEASED", "BUTTON4_CLICKED", - "BUTTON4_DOUBLE_CLICKED", "BUTTON4_TRIPLE_CLICKED", - "BUTTON_SHIFT", "BUTTON_CTRL", "BUTTON_ALT", - "ALL_MOUSE_EVENTS", "REPORT_MOUSE_POSITION", - ]: - _copy_to_globals(name) - - if not lib._m_NetBSD: - for key in range(lib.KEY_MIN, lib.KEY_MAX): - key_n = lib.keyname(key) - if key_n == ffi.NULL: - continue - key_n = ffi.string(key_n) - if key_n == b"UNKNOWN KEY": - continue - if not isinstance(key_n, str): # python 3 - key_n = key_n.decode() - key_n = key_n.replace('(', '').replace(')', '') - globals()[key_n] = key - -_setup() - -# Do we want this? -# version = "2.2" -# __version__ = "2.2" - - -# ____________________________________________________________ - - -_initialised_setupterm = False -_initialised = False -_initialised_color = False - - -def _ensure_initialised_setupterm(): - if not _initialised_setupterm: - raise error("must call (at least) setupterm() first") - - -def _ensure_initialised(): - if not _initialised: - raise error("must call initscr() first") - - -def _ensure_initialised_color(): - if not _initialised and _initialised_color: - raise error("must call start_color() first") - - -def _check_ERR(code, fname): - if code != lib.ERR: - return None - elif fname is None: - raise error("curses function returned ERR") - else: - raise error("%s() returned ERR" % (fname,)) - - -def _check_NULL(rval): - if rval == ffi.NULL: - raise error("curses function returned NULL") - return rval - - -def _call_lib(method_name, *args): - return getattr(lib, method_name)(*args) - - -def _call_lib_check_ERR(method_name, *args): - return _check_ERR(_call_lib(method_name, *args), method_name) - - -def _mk_no_return(method_name): - def _execute(): - _ensure_initialised() - return _call_lib_check_ERR(method_name) - _execute.__name__ = method_name - return _execute - - -def _mk_flag_func(method_name): - # This is in the CPython implementation, but not documented anywhere. - # We have to support it, though, even if it make me sad. - def _execute(flag=True): - _ensure_initialised() - if flag: - return _call_lib_check_ERR(method_name) - else: - return _call_lib_check_ERR('no' + method_name) - _execute.__name__ = method_name - return _execute - - -def _mk_return_val(method_name): - def _execute(): - return _call_lib(method_name) - _execute.__name__ = method_name - return _execute - - -def _mk_w_getyx(method_name): - def _execute(self): - y = _call_lib(method_name + 'y', self._win) - x = _call_lib(method_name + 'x', self._win) - return (y, x) - _execute.__name__ = method_name - return _execute - - -def _mk_w_no_return(method_name): - def _execute(self, *args): - return _call_lib_check_ERR(method_name, self._win, *args) - _execute.__name__ = method_name - return _execute - - -def _mk_w_return_val(method_name): - def _execute(self, *args): - return _call_lib(method_name, self._win, *args) - _execute.__name__ = method_name - return _execute - - -def _chtype(ch): - return int(ffi.cast("chtype", ch)) - -def _texttype(text): - if isinstance(text, str): - return text - elif isinstance(text, unicode): - return str(text) # default encoding - else: - raise TypeError("str or unicode expected, got a '%s' object" - % (type(text).__name__,)) - - -def _extract_yx(args): - if len(args) >= 2: - return (args[0], args[1], args[2:]) - return (None, None, args) - - -def _process_args(funcname, args, count, optcount, frontopt=0): - outargs = [] - if frontopt: - if len(args) > count + optcount: - # We have the front optional args here. - outargs.extend(args[:frontopt]) - args = args[frontopt:] - else: - # No front optional args, so make them None. - outargs.extend([None] * frontopt) - if (len(args) < count) or (len(args) > count + optcount): - raise error("%s requires %s to %s arguments" % ( - funcname, count, count + optcount + frontopt)) - outargs.extend(args) - return outargs - - -def _argspec(count, optcount=0, frontopt=0): - def _argspec_deco(func): - @wraps(func) - def _wrapped(self, *args): - outargs = _process_args( - func.__name__, args, count, optcount, frontopt) - return func(self, *outargs) - return _wrapped - return _argspec_deco - - -# ____________________________________________________________ - - -class error(Exception): - pass - - -class Window(object): - def __init__(self, window): - self._win = window - - def __del__(self): - if self._win != lib.stdscr: - lib.delwin(self._win) - - untouchwin = _mk_w_no_return("untouchwin") - touchwin = _mk_w_no_return("touchwin") - redrawwin = _mk_w_no_return("redrawwin") - insertln = _mk_w_no_return("winsertln") - erase = _mk_w_no_return("werase") - deleteln = _mk_w_no_return("wdeleteln") - - is_wintouched = _mk_w_return_val("is_wintouched") - - syncdown = _mk_w_return_val("wsyncdown") - syncup = _mk_w_return_val("wsyncup") - standend = _mk_w_return_val("wstandend") - standout = _mk_w_return_val("wstandout") - cursyncup = _mk_w_return_val("wcursyncup") - clrtoeol = _mk_w_return_val("wclrtoeol") - clrtobot = _mk_w_return_val("wclrtobot") - clear = _mk_w_return_val("wclear") - - idcok = _mk_w_no_return("idcok") - immedok = _mk_w_no_return("immedok") - timeout = _mk_w_no_return("wtimeout") - - getyx = _mk_w_getyx("getcur") - getbegyx = _mk_w_getyx("getbeg") - getmaxyx = _mk_w_getyx("getmax") - getparyx = _mk_w_getyx("getpar") - - clearok = _mk_w_no_return("clearok") - idlok = _mk_w_no_return("idlok") - leaveok = _mk_w_no_return("leaveok") - notimeout = _mk_w_no_return("notimeout") - scrollok = _mk_w_no_return("scrollok") - insdelln = _mk_w_no_return("winsdelln") - syncok = _mk_w_no_return("syncok") - - mvwin = _mk_w_no_return("mvwin") - mvderwin = _mk_w_no_return("mvderwin") - move = _mk_w_no_return("wmove") - - if not lib._m_STRICT_SYSV_CURSES: - resize = _mk_w_no_return("wresize") - - if lib._m_NetBSD: - keypad = _mk_w_return_val("keypad") - nodelay = _mk_w_return_val("nodelay") - else: - keypad = _mk_w_no_return("keypad") - nodelay = _mk_w_no_return("nodelay") - - @_argspec(1, 1, 2) - def addch(self, y, x, ch, attr=None): - if attr is None: - attr = lib.A_NORMAL - ch = _chtype(ch) - - if y is not None: - code = lib.mvwaddch(self._win, y, x, ch | attr) - else: - code = lib.waddch(self._win, ch | attr) - return _check_ERR(code, "addch") - - @_argspec(1, 1, 2) - def addstr(self, y, x, text, attr=None): - text = _texttype(text) - if attr is not None: - attr_old = lib.getattrs(self._win) - lib.wattrset(self._win, attr) - if y is not None: - code = lib.mvwaddstr(self._win, y, x, text) - else: - code = lib.waddstr(self._win, text) - if attr is not None: - lib.wattrset(self._win, attr_old) - return _check_ERR(code, "addstr") - - @_argspec(2, 1, 2) - def addnstr(self, y, x, text, n, attr=None): - text = _texttype(text) - if attr is not None: - attr_old = lib.getattrs(self._win) - lib.wattrset(self._win, attr) - if y is not None: - code = lib.mvwaddnstr(self._win, y, x, text, n) - else: - code = lib.waddnstr(self._win, text, n) - if attr is not None: - lib.wattrset(self._win, attr_old) - return _check_ERR(code, "addnstr") - - def bkgd(self, ch, attr=None): - if attr is None: - attr = lib.A_NORMAL - return _check_ERR(lib.wbkgd(self._win, _chtype(ch) | attr), "bkgd") - - attroff = _mk_w_no_return("wattroff") - attron = _mk_w_no_return("wattron") - attrset = _mk_w_no_return("wattrset") - - def bkgdset(self, ch, attr=None): - if attr is None: - attr = lib.A_NORMAL - lib.wbkgdset(self._win, _chtype(ch) | attr) - return None - - def border(self, ls=0, rs=0, ts=0, bs=0, tl=0, tr=0, bl=0, br=0): - lib.wborder(self._win, - _chtype(ls), _chtype(rs), _chtype(ts), _chtype(bs), - _chtype(tl), _chtype(tr), _chtype(bl), _chtype(br)) - return None - - def box(self, vertint=0, horint=0): - lib.box(self._win, vertint, horint) - return None - - @_argspec(1, 1, 2) - def chgat(self, y, x, num, attr=None): - # These optional args are in a weird order. - if attr is None: - attr = num - num = -1 - - color = ((attr >> 8) & 0xff) - attr = attr - (color << 8) - - if y is not None: - code = lib.mvwchgat(self._win, y, x, num, attr, color, ffi.NULL) - lib.touchline(self._win, y, 1) - else: - yy, _ = self.getyx() - code = lib.wchgat(self._win, num, attr, color, ffi.NULL) - lib.touchline(self._win, yy, 1) - return _check_ERR(code, "chgat") - - def delch(self, *args): - if len(args) == 0: - code = lib.wdelch(self._win) - elif len(args) == 2: - code = lib.mvwdelch(self._win, *args) - else: - raise error("delch requires 0 or 2 arguments") - return _check_ERR(code, "[mv]wdelch") - - def derwin(self, *args): - nlines = 0 - ncols = 0 - if len(args) == 2: - begin_y, begin_x = args - elif len(args) == 4: - nlines, ncols, begin_y, begin_x = args - else: - raise error("derwin requires 2 or 4 arguments") - - win = lib.derwin(self._win, nlines, ncols, begin_y, begin_x) - return Window(_check_NULL(win)) - - def echochar(self, ch, attr=None): - if attr is None: - attr = lib.A_NORMAL - ch = _chtype(ch) - - if lib._m_ispad(self._win): - code = lib.pechochar(self._win, ch | attr) - else: - code = lib.wechochar(self._win, ch | attr) - return _check_ERR(code, "echochar") - - if lib._m_NCURSES_MOUSE_VERSION: - enclose = _mk_w_return_val("wenclose") - - getbkgd = _mk_w_return_val("getbkgd") - - def getch(self, *args): - if len(args) == 0: - val = lib.wgetch(self._win) - elif len(args) == 2: - val = lib.mvwgetch(self._win, *args) - else: - raise error("getch requires 0 or 2 arguments") - return val - - def getkey(self, *args): - if len(args) == 0: - val = lib.wgetch(self._win) - elif len(args) == 2: - val = lib.mvwgetch(self._win, *args) - else: - raise error("getkey requires 0 or 2 arguments") - - if val == lib.ERR: - raise error("no input") - elif val <= 255: - return chr(val) - else: - # XXX: The following line is different if `__NetBSD__` is defined. - val = lib.keyname(val) - if val == ffi.NULL: - return "" - return ffi.string(val) - - @_argspec(0, 1, 2) - def getstr(self, y, x, n=1023): - n = min(n, 1023) - buf = ffi.new("char[1024]") # /* This should be big enough.. I hope */ - - if y is None: - val = lib.wgetnstr(self._win, buf, n) - else: - val = lib.mvwgetnstr(self._win, y, x, buf, n) - - if val == lib.ERR: - return "" - return ffi.string(buf) - - @_argspec(2, 1, 2) - def hline(self, y, x, ch, n, attr=None): - ch = _chtype(ch) - if attr is None: - attr = lib.A_NORMAL - if y is not None: - _check_ERR(lib.wmove(self._win, y, x), "wmove") - return _check_ERR(lib.whline(self._win, ch | attr, n), "hline") - - @_argspec(1, 1, 2) - def insch(self, y, x, ch, attr=None): - ch = _chtype(ch) - if attr is None: - attr = lib.A_NORMAL - if y is not None: - code = lib.mvwinsch(self._win, y, x, ch | attr) - else: - code = lib.winsch(self._win, ch | attr) - return _check_ERR(code, "insch") - - def inch(self, *args): - if len(args) == 0: - return lib.winch(self._win) - elif len(args) == 2: - return lib.mvwinch(self._win, *args) - else: - raise error("inch requires 0 or 2 arguments") - - @_argspec(0, 1, 2) - def instr(self, y, x, n=1023): - n = min(n, 1023) - buf = ffi.new("char[1024]") # /* This should be big enough.. I hope */ - if y is None: - code = lib.winnstr(self._win, buf, n) - else: - code = lib.mvwinnstr(self._win, y, x, buf, n) - - if code == lib.ERR: - return "" - return ffi.string(buf) - - @_argspec(1, 1, 2) - def insstr(self, y, x, text, attr=None): - text = _texttype(text) - if attr is not None: - attr_old = lib.getattrs(self._win) - lib.wattrset(self._win, attr) - if y is not None: - code = lib.mvwinsstr(self._win, y, x, text) - else: - code = lib.winsstr(self._win, text) - if attr is not None: - lib.wattrset(self._win, attr_old) - return _check_ERR(code, "insstr") - - @_argspec(2, 1, 2) - def insnstr(self, y, x, text, n, attr=None): - text = _texttype(text) - if attr is not None: - attr_old = lib.getattrs(self._win) - lib.wattrset(self._win, attr) - if y is not None: - code = lib.mvwinsnstr(self._win, y, x, text, n) - else: - code = lib.winsnstr(self._win, text, n) - if attr is not None: - lib.wattrset(self._win, attr_old) - return _check_ERR(code, "insnstr") - - def is_linetouched(self, line): - code = lib.is_linetouched(self._win, line) - if code == lib.ERR: - raise error("is_linetouched: line number outside of boundaries") - if code == lib.FALSE: - return False - return True - - def noutrefresh(self, *args): - if lib._m_ispad(self._win): - if len(args) != 6: - raise error( - "noutrefresh() called for a pad requires 6 arguments") - return _check_ERR(lib.pnoutrefresh(self._win, *args), - "pnoutrefresh") - else: - # XXX: Better args check here? We need zero args. - return _check_ERR(lib.wnoutrefresh(self._win, *args), - "wnoutrefresh") - - nooutrefresh = noutrefresh # "to be removed in 2.3", but in 2.7, 3.x. - - def _copywin(self, dstwin, overlay, - sminr, sminc, dminr, dminc, dmaxr, dmaxc): - return _check_ERR(lib.copywin(self._win, dstwin._win, - sminr, sminc, dminr, dminc, dmaxr, dmaxc, - overlay), "copywin") - - def overlay(self, dstwin, *args): - if len(args) == 6: - return self._copywin(dstwin, True, *args) - elif len(args) == 0: - return _check_ERR(lib.overlay(self._win, dstwin._win), "overlay") - else: - raise error("overlay requires one or seven arguments") - - def overwrite(self, dstwin, *args): - if len(args) == 6: - return self._copywin(dstwin, False, *args) - elif len(args) == 0: - return _check_ERR(lib.overwrite(self._win, dstwin._win), - "overwrite") - else: - raise error("overwrite requires one or seven arguments") - - def putwin(self, filep): - # filestar = ffi.new("FILE *", filep) - return _check_ERR(lib.putwin(self._win, filep), "putwin") - - def redrawln(self, beg, num): - return _check_ERR(lib.wredrawln(self._win, beg, num), "redrawln") - - def refresh(self, *args): - if lib._m_ispad(self._win): - if len(args) != 6: - raise error( - "noutrefresh() called for a pad requires 6 arguments") - return _check_ERR(lib.prefresh(self._win, *args), "prefresh") - else: - # XXX: Better args check here? We need zero args. - return _check_ERR(lib.wrefresh(self._win, *args), "wrefresh") - - def setscrreg(self, y, x): - return _check_ERR(lib.wsetscrreg(self._win, y, x), "wsetscrreg") - - def subwin(self, *args): - nlines = 0 - ncols = 0 - if len(args) == 2: - begin_y, begin_x = args - elif len(args) == 4: - nlines, ncols, begin_y, begin_x = args - else: - raise error("subwin requires 2 or 4 arguments") - - if lib._m_ispad(self._win): - win = lib.subpad(self._win, nlines, ncols, begin_y, begin_x) - else: - win = lib.subwin(self._win, nlines, ncols, begin_y, begin_x) - return Window(_check_NULL(win)) - - def scroll(self, nlines=None): - if nlines is None: - return _check_ERR(lib.scroll(self._win), "scroll") - else: - return _check_ERR(lib.wscrl(self._win, nlines), "scroll") - - def touchline(self, st, cnt, val=None): - if val is None: - return _check_ERR(lib.touchline(self._win, st, cnt), "touchline") - else: - return _check_ERR(lib.wtouchln(self._win, st, cnt, val), - "touchline") - - @_argspec(2, 1, 2) - def vline(self, y, x, ch, n, attr=None): - ch = _chtype(ch) - if attr is None: - attr = lib.A_NORMAL - if y is not None: - _check_ERR(lib.wmove(self._win, y, x), "wmove") - return _check_ERR(lib.wvline(self._win, ch | attr, n), "vline") - - -beep = _mk_no_return("beep") -def_prog_mode = _mk_no_return("def_prog_mode") -def_shell_mode = _mk_no_return("def_shell_mode") -doupdate = _mk_no_return("doupdate") -endwin = _mk_no_return("endwin") -flash = _mk_no_return("flash") -nocbreak = _mk_no_return("nocbreak") -noecho = _mk_no_return("noecho") -nonl = _mk_no_return("nonl") -noraw = _mk_no_return("noraw") -reset_prog_mode = _mk_no_return("reset_prog_mode") -reset_shell_mode = _mk_no_return("reset_shell_mode") -resetty = _mk_no_return("resetty") -savetty = _mk_no_return("savetty") - -cbreak = _mk_flag_func("cbreak") -echo = _mk_flag_func("echo") -nl = _mk_flag_func("nl") -raw = _mk_flag_func("raw") - -baudrate = _mk_return_val("baudrate") -termattrs = _mk_return_val("termattrs") - -termname = _mk_return_val("termname") -longname = _mk_return_val("longname") - -can_change_color = _mk_return_val("can_change_color") -has_colors = _mk_return_val("has_colors") -has_ic = _mk_return_val("has_ic") -has_il = _mk_return_val("has_il") -isendwin = _mk_return_val("isendwin") -flushinp = _mk_return_val("flushinp") -noqiflush = _mk_return_val("noqiflush") - - -def filter(): - lib.filter() - return None - - -def color_content(color): - _ensure_initialised_color() - r, g, b = ffi.new("short *"), ffi.new("short *"), ffi.new("short *") - if lib.color_content(color, r, g, b) == lib.ERR: - raise error("Argument 1 was out of range. Check value of COLORS.") - return (r[0], g[0], b[0]) - - -def color_pair(n): - _ensure_initialised_color() - return (n << 8) - - -def curs_set(vis): - _ensure_initialised() - val = lib.curs_set(vis) - _check_ERR(val, "curs_set") - return val - - -def delay_output(ms): - _ensure_initialised() - return _check_ERR(lib.delay_output(ms), "delay_output") - - -def erasechar(): - _ensure_initialised() - return lib.erasechar() - - -def getsyx(): - _ensure_initialised() - yx = ffi.new("int[2]") - lib._m_getsyx(yx) - return (yx[0], yx[1]) - - -if lib._m_NCURSES_MOUSE_VERSION: - - def getmouse(): - _ensure_initialised() - mevent = ffi.new("MEVENT *") - _check_ERR(lib.getmouse(mevent), "getmouse") - return (mevent.id, mevent.x, mevent.y, mevent.z, mevent.bstate) - - def ungetmouse(id, x, y, z, bstate): - _ensure_initialised() - mevent = ffi.new("MEVENT *") - mevent.id, mevent.x, mevent.y, mevent.z, mevent.bstate = ( - id, x, y, z, bstate) - return _check_ERR(lib.ungetmouse(mevent), "ungetmouse") - - -def getwin(filep): - return Window(_check_NULL(lib.getwin(filep))) - - -def halfdelay(tenths): - _ensure_initialised() - return _check_ERR(lib.halfdelay(tenths), "halfdelay") - - -if not lib._m_STRICT_SYSV_CURSES: - def has_key(ch): - _ensure_initialised() - return lib.has_key(ch) - - -def init_color(color, r, g, b): - _ensure_initialised_color() - return _check_ERR(lib.init_color(color, r, g, b), "init_color") - - -def init_pair(pair, f, b): - _ensure_initialised_color() - return _check_ERR(lib.init_pair(pair, f, b), "init_pair") - - -def _mk_acs(name, ichar): - if len(ichar) == 1: - globals()[name] = lib.acs_map[ord(ichar)] - else: - globals()[name] = globals()[ichar] - - -def _map_acs(): - _mk_acs("ACS_ULCORNER", 'l') - _mk_acs("ACS_LLCORNER", 'm') - _mk_acs("ACS_URCORNER", 'k') - _mk_acs("ACS_LRCORNER", 'j') - _mk_acs("ACS_LTEE", 't') - _mk_acs("ACS_RTEE", 'u') - _mk_acs("ACS_BTEE", 'v') - _mk_acs("ACS_TTEE", 'w') - _mk_acs("ACS_HLINE", 'q') - _mk_acs("ACS_VLINE", 'x') - _mk_acs("ACS_PLUS", 'n') - _mk_acs("ACS_S1", 'o') - _mk_acs("ACS_S9", 's') - _mk_acs("ACS_DIAMOND", '`') - _mk_acs("ACS_CKBOARD", 'a') - _mk_acs("ACS_DEGREE", 'f') - _mk_acs("ACS_PLMINUS", 'g') - _mk_acs("ACS_BULLET", '~') - _mk_acs("ACS_LARROW", ',') - _mk_acs("ACS_RARROW", '+') - _mk_acs("ACS_DARROW", '.') - _mk_acs("ACS_UARROW", '-') - _mk_acs("ACS_BOARD", 'h') - _mk_acs("ACS_LANTERN", 'i') - _mk_acs("ACS_BLOCK", '0') - _mk_acs("ACS_S3", 'p') - _mk_acs("ACS_S7", 'r') - _mk_acs("ACS_LEQUAL", 'y') - _mk_acs("ACS_GEQUAL", 'z') - _mk_acs("ACS_PI", '{') - _mk_acs("ACS_NEQUAL", '|') - _mk_acs("ACS_STERLING", '}') - _mk_acs("ACS_BSSB", "ACS_ULCORNER") - _mk_acs("ACS_SSBB", "ACS_LLCORNER") - _mk_acs("ACS_BBSS", "ACS_URCORNER") - _mk_acs("ACS_SBBS", "ACS_LRCORNER") - _mk_acs("ACS_SBSS", "ACS_RTEE") - _mk_acs("ACS_SSSB", "ACS_LTEE") - _mk_acs("ACS_SSBS", "ACS_BTEE") - _mk_acs("ACS_BSSS", "ACS_TTEE") - _mk_acs("ACS_BSBS", "ACS_HLINE") - _mk_acs("ACS_SBSB", "ACS_VLINE") - _mk_acs("ACS_SSSS", "ACS_PLUS") - - -def initscr(): - if _initialised: - lib.wrefresh(lib.stdscr) - return Window(lib.stdscr) - - win = _check_NULL(lib.initscr()) - globals()['_initialised_setupterm'] = True - globals()['_initialised'] = True - - _map_acs() - - globals()["LINES"] = lib.LINES - globals()["COLS"] = lib.COLS - - return Window(win) - - -def setupterm(term=None, fd=-1): - if fd == -1: - # XXX: Check for missing stdout here? - fd = sys.stdout.fileno() - - if _initialised_setupterm: - return None - - if term is None: - term = ffi.NULL - err = ffi.new("int *") - if lib.setupterm(term, fd, err) == lib.ERR: - err = err[0] - if err == 0: - raise error("setupterm: could not find terminal") - elif err == -1: - raise error("setupterm: could not find terminfo database") - else: - raise error("setupterm: unknown error") - - globals()["_initialised_setupterm"] = True - return None - - -def intrflush(ch): - _ensure_initialised() - return _check_ERR(lib.intrflush(ffi.NULL, ch), "intrflush") - - -# XXX: #ifdef HAVE_CURSES_IS_TERM_RESIZED -def is_term_resized(lines, columns): - _ensure_initialised() - return lib.is_term_resized(lines, columns) - - -if not lib._m_NetBSD: - def keyname(ch): - _ensure_initialised() - if ch < 0: - raise error("invalid key number") - knp = lib.keyname(ch) - if knp == ffi.NULL: - return "" - return ffi.string(knp) - - -def killchar(): - return lib.killchar() - - -def meta(ch): - return _check_ERR(lib.meta(lib.stdscr, ch), "meta") - - -if lib._m_NCURSES_MOUSE_VERSION: - - def mouseinterval(interval): - _ensure_initialised() - return _check_ERR(lib.mouseinterval(interval), "mouseinterval") - - def mousemask(newmask): - _ensure_initialised() - oldmask = ffi.new("mmask_t *") - availmask = lib.mousemask(newmask, oldmask) - return (availmask, oldmask) - - -def napms(ms): - _ensure_initialised() - return lib.napms(ms) - - -def newpad(nlines, ncols): - _ensure_initialised() - return Window(_check_NULL(lib.newpad(nlines, ncols))) - - -def newwin(nlines, ncols, begin_y=None, begin_x=None): - _ensure_initialised() - if begin_x is None: - if begin_y is not None: - raise error("newwin requires 2 or 4 arguments") - begin_y = begin_x = 0 - - return Window(_check_NULL(lib.newwin(nlines, ncols, begin_y, begin_x))) - - -def pair_content(pair): - _ensure_initialised_color() - f = ffi.new("short *") - b = ffi.new("short *") - if lib.pair_content(pair, f, b) == lib.ERR: - raise error("Argument 1 was out of range. (1..COLOR_PAIRS-1)") - return (f, b) - - -def pair_number(pairvalue): - _ensure_initialised_color() - return (pairvalue & lib.A_COLOR) >> 8 - - -def putp(text): - text = _texttype(text) - return _check_ERR(lib.putp(text), "putp") - - -def qiflush(flag=True): - _ensure_initialised() - if flag: - lib.qiflush() - else: - lib.noqiflush() - return None - - -# XXX: Do something about the following? -# /* Internal helper used for updating curses.LINES, curses.COLS, _curses.LINES -# * and _curses.COLS */ -# #if defined(HAVE_CURSES_RESIZETERM) || defined(HAVE_CURSES_RESIZE_TERM) -# static int -# update_lines_cols(void) -# { -# PyObject *o; -# PyObject *m = PyImport_ImportModuleNoBlock("curses"); - -# if (!m) -# return 0; - -# o = PyInt_FromLong(LINES); -# if (!o) { -# Py_DECREF(m); -# return 0; -# } -# if (PyObject_SetAttrString(m, "LINES", o)) { -# Py_DECREF(m); -# Py_DECREF(o); -# return 0; -# } -# if (PyDict_SetItemString(ModDict, "LINES", o)) { -# Py_DECREF(m); -# Py_DECREF(o); -# return 0; -# } -# Py_DECREF(o); -# o = PyInt_FromLong(COLS); -# if (!o) { -# Py_DECREF(m); -# return 0; -# } -# if (PyObject_SetAttrString(m, "COLS", o)) { -# Py_DECREF(m); -# Py_DECREF(o); -# return 0; -# } -# if (PyDict_SetItemString(ModDict, "COLS", o)) { -# Py_DECREF(m); -# Py_DECREF(o); -# return 0; -# } -# Py_DECREF(o); -# Py_DECREF(m); -# return 1; -# } -# #endif - -# #ifdef HAVE_CURSES_RESIZETERM -# static PyObject * -# PyCurses_ResizeTerm(PyObject *self, PyObject *args) -# { -# int lines; -# int columns; -# PyObject *result; - -# PyCursesInitialised; - -# if (!PyArg_ParseTuple(args,"ii:resizeterm", &lines, &columns)) -# return NULL; - -# result = PyCursesCheckERR(resizeterm(lines, columns), "resizeterm"); -# if (!result) -# return NULL; -# if (!update_lines_cols()) -# return NULL; -# return result; -# } - -# #endif - -# #ifdef HAVE_CURSES_RESIZE_TERM -# static PyObject * -# PyCurses_Resize_Term(PyObject *self, PyObject *args) -# { -# int lines; -# int columns; - -# PyObject *result; - -# PyCursesInitialised; - -# if (!PyArg_ParseTuple(args,"ii:resize_term", &lines, &columns)) -# return NULL; - -# result = PyCursesCheckERR(resize_term(lines, columns), "resize_term"); -# if (!result) -# return NULL; -# if (!update_lines_cols()) -# return NULL; -# return result; -# } -# #endif /* HAVE_CURSES_RESIZE_TERM */ - - -def setsyx(y, x): - _ensure_initialised() - lib.setsyx(y, x) - return None - - -def start_color(): - _check_ERR(lib.start_color(), "start_color") - globals()["COLORS"] = lib.COLORS - globals()["COLOR_PAIRS"] = lib.COLOR_PAIRS - globals()["_initialised_color"] = True - return None - - -def tigetflag(capname): - _ensure_initialised_setupterm() - return lib.tigetflag(capname) - - -def tigetnum(capname): - _ensure_initialised_setupterm() - return lib.tigetnum(capname) - - -def tigetstr(capname): - _ensure_initialised_setupterm() - val = lib.tigetstr(capname) - if int(ffi.cast("intptr_t", val)) in (0, -1): - return None - return ffi.string(val) - - -def tparm(fmt, i1=0, i2=0, i3=0, i4=0, i5=0, i6=0, i7=0, i8=0, i9=0): - args = [ffi.cast("int", i) for i in (i1, i2, i3, i4, i5, i6, i7, i8, i9)] - result = lib.tparm(fmt, *args) - if result == ffi.NULL: - raise error("tparm() returned NULL") - return ffi.string(result) - - -def typeahead(fd): - _ensure_initialised() - return _check_ERR(lib.typeahead(fd), "typeahead") - - -def unctrl(ch): - _ensure_initialised() - return lib.unctrl(_chtype(ch)) - - -def ungetch(ch): - _ensure_initialised() - return _check_ERR(lib.ungetch(_chtype(ch)), "ungetch") - - -def use_env(flag): - lib.use_env(flag) - return None - - -if not lib._m_STRICT_SYSV_CURSES: - - def use_default_colors(): - _ensure_initialised_color() - return _check_ERR(lib.use_default_colors(), "use_default_colors") diff --git a/demo/_curses_build.py b/demo/_curses_build.py deleted file mode 100644 index 1a1a3ec..0000000 --- a/demo/_curses_build.py +++ /dev/null @@ -1,327 +0,0 @@ -import sys -if sys.platform == 'win32': - #This module does not exist in windows - raise ImportError('No module named _curses') - -from cffi import FFI - -ffi = FFI() - -ffi.cdef(""" -typedef ... WINDOW; -typedef ... SCREEN; -typedef unsigned long... mmask_t; -typedef unsigned char bool; -typedef unsigned long... chtype; -typedef chtype attr_t; - -typedef struct -{ - short id; /* ID to distinguish multiple devices */ - int x, y, z; /* event coordinates (character-cell) */ - mmask_t bstate; /* button state bits */ -} -MEVENT; - -static const int ERR, OK; -static const int TRUE, FALSE; -static const int KEY_MIN, KEY_MAX; - -static const int COLOR_BLACK; -static const int COLOR_RED; -static const int COLOR_GREEN; -static const int COLOR_YELLOW; -static const int COLOR_BLUE; -static const int COLOR_MAGENTA; -static const int COLOR_CYAN; -static const int COLOR_WHITE; - -static const chtype A_ATTRIBUTES; -static const chtype A_NORMAL; -static const chtype A_STANDOUT; -static const chtype A_UNDERLINE; -static const chtype A_REVERSE; -static const chtype A_BLINK; -static const chtype A_DIM; -static const chtype A_BOLD; -static const chtype A_ALTCHARSET; -static const chtype A_INVIS; -static const chtype A_PROTECT; -static const chtype A_CHARTEXT; -static const chtype A_COLOR; - -static const int BUTTON1_RELEASED; -static const int BUTTON1_PRESSED; -static const int BUTTON1_CLICKED; -static const int BUTTON1_DOUBLE_CLICKED; -static const int BUTTON1_TRIPLE_CLICKED; -static const int BUTTON2_RELEASED; -static const int BUTTON2_PRESSED; -static const int BUTTON2_CLICKED; -static const int BUTTON2_DOUBLE_CLICKED; -static const int BUTTON2_TRIPLE_CLICKED; -static const int BUTTON3_RELEASED; -static const int BUTTON3_PRESSED; -static const int BUTTON3_CLICKED; -static const int BUTTON3_DOUBLE_CLICKED; -static const int BUTTON3_TRIPLE_CLICKED; -static const int BUTTON4_RELEASED; -static const int BUTTON4_PRESSED; -static const int BUTTON4_CLICKED; -static const int BUTTON4_DOUBLE_CLICKED; -static const int BUTTON4_TRIPLE_CLICKED; -static const int BUTTON_SHIFT; -static const int BUTTON_CTRL; -static const int BUTTON_ALT; -static const int ALL_MOUSE_EVENTS; -static const int REPORT_MOUSE_POSITION; - -int setupterm(char *, int, int *); - -WINDOW *stdscr; -int COLORS; -int COLOR_PAIRS; -int COLS; -int LINES; - -int baudrate(void); -int beep(void); -int box(WINDOW *, chtype, chtype); -bool can_change_color(void); -int cbreak(void); -int clearok(WINDOW *, bool); -int color_content(short, short*, short*, short*); -int copywin(const WINDOW*, WINDOW*, int, int, int, int, int, int, int); -int curs_set(int); -int def_prog_mode(void); -int def_shell_mode(void); -int delay_output(int); -int delwin(WINDOW *); -WINDOW * derwin(WINDOW *, int, int, int, int); -int doupdate(void); -int echo(void); -int endwin(void); -char erasechar(void); -void filter(void); -int flash(void); -int flushinp(void); -chtype getbkgd(WINDOW *); -WINDOW * getwin(FILE *); -int halfdelay(int); -bool has_colors(void); -bool has_ic(void); -bool has_il(void); -void idcok(WINDOW *, bool); -int idlok(WINDOW *, bool); -void immedok(WINDOW *, bool); -WINDOW * initscr(void); -int init_color(short, short, short, short); -int init_pair(short, short, short); -int intrflush(WINDOW *, bool); -bool isendwin(void); -bool is_linetouched(WINDOW *, int); -bool is_wintouched(WINDOW *); -const char * keyname(int); -int keypad(WINDOW *, bool); -char killchar(void); -int leaveok(WINDOW *, bool); -char * longname(void); -int meta(WINDOW *, bool); -int mvderwin(WINDOW *, int, int); -int mvwaddch(WINDOW *, int, int, const chtype); -int mvwaddnstr(WINDOW *, int, int, const char *, int); -int mvwaddstr(WINDOW *, int, int, const char *); -int mvwchgat(WINDOW *, int, int, int, attr_t, short, const void *); -int mvwdelch(WINDOW *, int, int); -int mvwgetch(WINDOW *, int, int); -int mvwgetnstr(WINDOW *, int, int, char *, int); -int mvwin(WINDOW *, int, int); -chtype mvwinch(WINDOW *, int, int); -int mvwinnstr(WINDOW *, int, int, char *, int); -int mvwinsch(WINDOW *, int, int, chtype); -int mvwinsnstr(WINDOW *, int, int, const char *, int); -int mvwinsstr(WINDOW *, int, int, const char *); -int napms(int); -WINDOW * newpad(int, int); -WINDOW * newwin(int, int, int, int); -int nl(void); -int nocbreak(void); -int nodelay(WINDOW *, bool); -int noecho(void); -int nonl(void); -void noqiflush(void); -int noraw(void); -int notimeout(WINDOW *, bool); -int overlay(const WINDOW*, WINDOW *); -int overwrite(const WINDOW*, WINDOW *); -int pair_content(short, short*, short*); -int pechochar(WINDOW *, const chtype); -int pnoutrefresh(WINDOW*, int, int, int, int, int, int); -int prefresh(WINDOW *, int, int, int, int, int, int); -int putwin(WINDOW *, FILE *); -void qiflush(void); -int raw(void); -int redrawwin(WINDOW *); -int resetty(void); -int reset_prog_mode(void); -int reset_shell_mode(void); -int savetty(void); -int scroll(WINDOW *); -int scrollok(WINDOW *, bool); -int start_color(void); -WINDOW * subpad(WINDOW *, int, int, int, int); -WINDOW * subwin(WINDOW *, int, int, int, int); -int syncok(WINDOW *, bool); -chtype termattrs(void); -char * termname(void); -int touchline(WINDOW *, int, int); -int touchwin(WINDOW *); -int typeahead(int); -int ungetch(int); -int untouchwin(WINDOW *); -void use_env(bool); -int waddch(WINDOW *, const chtype); -int waddnstr(WINDOW *, const char *, int); -int waddstr(WINDOW *, const char *); -int wattron(WINDOW *, int); -int wattroff(WINDOW *, int); -int wattrset(WINDOW *, int); -int wbkgd(WINDOW *, chtype); -void wbkgdset(WINDOW *, chtype); -int wborder(WINDOW *, chtype, chtype, chtype, chtype, - chtype, chtype, chtype, chtype); -int wchgat(WINDOW *, int, attr_t, short, const void *); -int wclear(WINDOW *); -int wclrtobot(WINDOW *); -int wclrtoeol(WINDOW *); -void wcursyncup(WINDOW *); -int wdelch(WINDOW *); -int wdeleteln(WINDOW *); -int wechochar(WINDOW *, const chtype); -int werase(WINDOW *); -int wgetch(WINDOW *); -int wgetnstr(WINDOW *, char *, int); -int whline(WINDOW *, chtype, int); -chtype winch(WINDOW *); -int winnstr(WINDOW *, char *, int); -int winsch(WINDOW *, chtype); -int winsdelln(WINDOW *, int); -int winsertln(WINDOW *); -int winsnstr(WINDOW *, const char *, int); -int winsstr(WINDOW *, const char *); -int wmove(WINDOW *, int, int); -int wresize(WINDOW *, int, int); -int wnoutrefresh(WINDOW *); -int wredrawln(WINDOW *, int, int); -int wrefresh(WINDOW *); -int wscrl(WINDOW *, int); -int wsetscrreg(WINDOW *, int, int); -int wstandout(WINDOW *); -int wstandend(WINDOW *); -void wsyncdown(WINDOW *); -void wsyncup(WINDOW *); -void wtimeout(WINDOW *, int); -int wtouchln(WINDOW *, int, int, int); -int wvline(WINDOW *, chtype, int); -int tigetflag(char *); -int tigetnum(char *); -char * tigetstr(char *); -int putp(const char *); -char * tparm(const char *, ...); -int getattrs(const WINDOW *); -int getcurx(const WINDOW *); -int getcury(const WINDOW *); -int getbegx(const WINDOW *); -int getbegy(const WINDOW *); -int getmaxx(const WINDOW *); -int getmaxy(const WINDOW *); -int getparx(const WINDOW *); -int getpary(const WINDOW *); - -int getmouse(MEVENT *); -int ungetmouse(MEVENT *); -mmask_t mousemask(mmask_t, mmask_t *); -bool wenclose(const WINDOW *, int, int); -int mouseinterval(int); - -void setsyx(int y, int x); -const char *unctrl(chtype); -int use_default_colors(void); - -int has_key(int); -bool is_term_resized(int, int); - -#define _m_STRICT_SYSV_CURSES ... -#define _m_NCURSES_MOUSE_VERSION ... -#define _m_NetBSD ... -int _m_ispad(WINDOW *); - -chtype acs_map[]; - -// For _curses_panel: - -typedef ... PANEL; - -WINDOW *panel_window(const PANEL *); -void update_panels(void); -int hide_panel(PANEL *); -int show_panel(PANEL *); -int del_panel(PANEL *); -int top_panel(PANEL *); -int bottom_panel(PANEL *); -PANEL *new_panel(WINDOW *); -PANEL *panel_above(const PANEL *); -PANEL *panel_below(const PANEL *); -int set_panel_userptr(PANEL *, void *); -const void *panel_userptr(const PANEL *); -int move_panel(PANEL *, int, int); -int replace_panel(PANEL *,WINDOW *); -int panel_hidden(const PANEL *); - -void _m_getsyx(int *yx); -""") - - -ffi.set_source("_curses_cffi", """ -#ifdef __APPLE__ -/* the following define is necessary for OS X 10.6+; without it, the - Apple-supplied ncurses.h sets NCURSES_OPAQUE to 1, and then Python - can't get at the WINDOW flags field. */ -#define NCURSES_OPAQUE 0 -#endif - -#include <ncurses.h> -#include <panel.h> -#include <term.h> - -#if defined STRICT_SYSV_CURSES -#define _m_STRICT_SYSV_CURSES TRUE -#else -#define _m_STRICT_SYSV_CURSES FALSE -#endif - -#if defined NCURSES_MOUSE_VERSION -#define _m_NCURSES_MOUSE_VERSION TRUE -#else -#define _m_NCURSES_MOUSE_VERSION FALSE -#endif - -#if defined __NetBSD__ -#define _m_NetBSD TRUE -#else -#define _m_NetBSD FALSE -#endif - -int _m_ispad(WINDOW *win) { - // <curses.h> may not have _flags (and possibly _ISPAD), - // but for now let's assume that <ncurses.h> always has it - return (win->_flags & _ISPAD); -} - -void _m_getsyx(int *yx) { - getsyx(yx[0], yx[1]); -} -""", libraries=['ncurses', 'panel']) - -if __name__ == '__main__': - ffi.compile() diff --git a/demo/_curses_setup.py b/demo/_curses_setup.py deleted file mode 100644 index ec3d20e..0000000 --- a/demo/_curses_setup.py +++ /dev/null @@ -1,13 +0,0 @@ -from setuptools import setup - -setup( - name="_curses", - version="0.1", - py_modules=["_curses"], - setup_requires=["cffi>=1.0.dev0"], - cffi_modules=[ - "_curses_build.py:ffi", - ], - install_requires=["cffi>=1.0.dev0"], # should maybe be "cffi-backend" only? - zip_safe=False, -) diff --git a/demo/api.py b/demo/api.py deleted file mode 100644 index 8cc6407..0000000 --- a/demo/api.py +++ /dev/null @@ -1,62 +0,0 @@ -import cffi -from cffi import FFI - -class PythonFFI(FFI): - - def __init__(self, backend=None): - FFI.__init__(self, backend=backend) - self._pyexports = {} - - def pyexport(self, signature): - tp = self._typeof(signature, consider_function_as_funcptr=True) - def decorator(func): - name = func.__name__ - if name in self._pyexports: - raise cffi.CDefError("duplicate pyexport'ed function %r" - % (name,)) - callback_var = self.getctype(tp, name) - self.cdef("%s;" % callback_var) - self._pyexports[name] = _PyExport(tp, func) - return decorator - - def verify(self, source='', **kwargs): - extras = [] - pyexports = sorted(self._pyexports.items()) - for name, export in pyexports: - callback_var = self.getctype(export.tp, name) - extras.append("%s;" % callback_var) - extras.append(source) - source = '\n'.join(extras) - lib = FFI.verify(self, source, **kwargs) - for name, export in pyexports: - cb = self.callback(export.tp, export.func) - export.cb = cb - setattr(lib, name, cb) - return lib - - -class _PyExport(object): - def __init__(self, tp, func): - self.tp = tp - self.func = func - - -if __name__ == '__main__': - ffi = PythonFFI() - - @ffi.pyexport("int(int)") - def add1(n): - print n - return n + 1 - - ffi.cdef(""" - int f(int); - """) - - lib = ffi.verify(""" - int f(int x) { - return add1(add1(x)); - } - """) - - assert lib.f(5) == 7 diff --git a/demo/bsdopendirtype.py b/demo/bsdopendirtype.py deleted file mode 100644 index 75a996a..0000000 --- a/demo/bsdopendirtype.py +++ /dev/null @@ -1,48 +0,0 @@ -from _bsdopendirtype import ffi, lib - - -def _posix_error(): - raise OSError(ffi.errno, os.strerror(ffi.errno)) - -_dtype_to_smode = { - lib.DT_BLK: 0o060000, - lib.DT_CHR: 0o020000, - lib.DT_DIR: 0o040000, - lib.DT_FIFO: 0o010000, - lib.DT_LNK: 0o120000, - lib.DT_REG: 0o100000, - lib.DT_SOCK: 0o140000, -} - -def opendir(dir): - if len(dir) == 0: - dir = b'.' - dirname = dir - if not dirname.endswith(b'/'): - dirname += b'/' - dirp = lib.opendir(dir) - if dirp == ffi.NULL: - raise _posix_error() - try: - while True: - ffi.errno = 0 - dirent = lib.readdir(dirp) - if dirent == ffi.NULL: - if ffi.errno != 0: - raise _posix_error() - return - name = ffi.string(dirent.d_name) - if name == b'.' or name == b'..': - continue - name = dirname + name - try: - smode = _dtype_to_smode[dirent.d_type] - except KeyError: - smode = os.lstat(name).st_mode - yield name, smode - finally: - lib.closedir(dirp) - -if __name__ == '__main__': - for name, smode in opendir(b'/tmp'): - print(hex(smode), name) diff --git a/demo/bsdopendirtype_build.py b/demo/bsdopendirtype_build.py deleted file mode 100644 index 3c5bb0b..0000000 --- a/demo/bsdopendirtype_build.py +++ /dev/null @@ -1,23 +0,0 @@ -from cffi import FFI - -ffibuilder = FFI() -ffibuilder.cdef(""" - typedef ... DIR; - struct dirent { - unsigned char d_type; /* type of file */ - char d_name[]; /* filename */ - ...; - }; - DIR *opendir(const char *name); - int closedir(DIR *dirp); - struct dirent *readdir(DIR *dirp); - static const int DT_BLK, DT_CHR, DT_DIR, DT_FIFO, DT_LNK, DT_REG, DT_SOCK; -""") - -ffibuilder.set_source("_bsdopendirtype", """ - #include <sys/types.h> - #include <dirent.h> -""") - -if __name__ == '__main__': - ffibuilder.compile(verbose=True) diff --git a/demo/bsdopendirtype_setup.py b/demo/bsdopendirtype_setup.py deleted file mode 100644 index 30a6cfb..0000000 --- a/demo/bsdopendirtype_setup.py +++ /dev/null @@ -1,13 +0,0 @@ -from setuptools import setup - -setup( - name="example", - version="0.1", - py_modules=["bsdopendirtype"], - setup_requires=["cffi>=1.0.dev0"], - cffi_modules=[ - "bsdopendirtype_build.py:ffibuilder", - ], - install_requires=["cffi>=1.0.dev0"], # should maybe be "cffi-backend" only? - zip_safe=False, -) diff --git a/demo/btrfs-snap.py b/demo/btrfs-snap.py deleted file mode 100644 index fceeaa1..0000000 --- a/demo/btrfs-snap.py +++ /dev/null @@ -1,52 +0,0 @@ -""" -btrfs-snap.py: source target newname - -creates a exactly named snapshots and bails out if they exist -""" - -import argparse -import fcntl -import os -import sys - -import cffi - -ffi = cffi.FFI() - -ffi.cdef(""" - #define BTRFS_IOC_SNAP_CREATE_V2 ... - struct btrfs_ioctl_vol_args_v2 { - int64_t fd; - char name[]; - ...; - }; -""") - -ffi.set_source("_btrfs_cffi", "#include <btrfs/ioctl.h>") -ffi.compile() - -# ____________________________________________________________ - - -from _btrfs_cffi import ffi, lib - -parser = argparse.ArgumentParser(usage=__doc__.strip()) -parser.add_argument('source', help='source subvolume') -parser.add_argument('target', help='target directory') -parser.add_argument('newname', help='name of the new snapshot') -opts = parser.parse_args() - -source = os.open(opts.source, os.O_DIRECTORY) -target = os.open(opts.target, os.O_DIRECTORY) - - -args = ffi.new('struct btrfs_ioctl_vol_args_v2 *') -args.name = opts.newname -args.fd = source -args_buffer = ffi.buffer(args) -try: - fcntl.ioctl(target, lib.BTRFS_IOC_SNAP_CREATE_V2, args_buffer) -except IOError as e: - print e - sys.exit(1) - diff --git a/demo/cffi-cocoa.py b/demo/cffi-cocoa.py deleted file mode 100644 index 9e86d99..0000000 --- a/demo/cffi-cocoa.py +++ /dev/null @@ -1,102 +0,0 @@ -# Based on http://cocoawithlove.com/2010/09/minimalist-cocoa-programming.html -# by Juraj Sukop. This demo was eventually expanded into a more complete -# Cocoa library available at https://bitbucket.org/sukop/nspython . - -from cffi import FFI - -ffi = FFI() -ffi.cdef(''' - - typedef signed char BOOL; - - typedef long NSInteger; - typedef unsigned long NSUInteger; - typedef NSInteger NSApplicationActivationPolicy; - typedef NSUInteger NSBackingStoreType; - typedef NSUInteger NSStringEncoding; - - typedef double CGFloat; - struct CGPoint { - CGFloat x; - CGFloat y; - }; - typedef struct CGPoint CGPoint; - struct CGSize { - CGFloat width; - CGFloat height; - }; - typedef struct CGSize CGSize; - struct CGRect { - CGPoint origin; - CGSize size; - }; - typedef struct CGRect CGRect; - - typedef CGPoint NSPoint; - typedef CGSize NSSize; - typedef CGRect NSRect; - - typedef struct objc_class *Class; - typedef struct objc_object { - Class isa; - } *id; - typedef struct objc_selector *SEL; - - SEL sel_registerName(const char *str); - id objc_getClass(const char *name); - id objc_msgSend(id theReceiver, SEL theSelector, ...); - -''') - -objc = ffi.dlopen('objc') -appkit = ffi.dlopen('AppKit') - -nil = ffi.NULL -YES = ffi.cast('BOOL', 1) -NO = ffi.cast('BOOL', 0) - -NSASCIIStringEncoding = ffi.cast('NSStringEncoding', 1) -NSApplicationActivationPolicyRegular = ffi.cast('NSApplicationActivationPolicy', 0) -NSTitledWindowMask = ffi.cast('NSUInteger', 1) -NSBackingStoreBuffered = ffi.cast('NSBackingStoreType', 2) - -NSMakePoint = lambda x, y: ffi.new('NSPoint *', (x, y))[0] -NSMakeRect = lambda x, y, w, h: ffi.new('NSRect *', ((x, y), (w, h)))[0] - -get, send, sel = objc.objc_getClass, objc.objc_msgSend, objc.sel_registerName -at = lambda s: send( - get('NSString'), - sel('stringWithCString:encoding:'), - ffi.new('char[]', s), NSASCIIStringEncoding) - -send(get('NSAutoreleasePool'), sel('new')) -app = send(get('NSApplication'), sel('sharedApplication')) -send(app, sel('setActivationPolicy:'), NSApplicationActivationPolicyRegular) - -menubar = send(send(get('NSMenu'), sel('new')), sel('autorelease')) -appMenuItem = send(send(get('NSMenuItem'), sel('new')), sel('autorelease')) -send(menubar, sel('addItem:'), appMenuItem) -send(app, sel('setMainMenu:'), menubar) - -appMenu = send(send(get('NSMenu'), sel('new')), sel('autorelease')) -appName = send(send(get('NSProcessInfo'), sel('processInfo')), sel('processName')) -quitTitle = send(at('Quit '), sel('stringByAppendingString:'), appName) -quitMenuItem = send(send(send( - get('NSMenuItem'), sel('alloc')), - sel('initWithTitle:action:keyEquivalent:'), - quitTitle, sel('terminate:'), at('q')), - sel('autorelease')) -send(appMenu, sel('addItem:'), quitMenuItem) -send(appMenuItem, sel('setSubmenu:'), appMenu) - -window = send(send(send( - get('NSWindow'), sel('alloc')), - sel('initWithContentRect:styleMask:backing:defer:'), - NSMakeRect(0, 0, 200, 200), NSTitledWindowMask, NSBackingStoreBuffered, NO), - sel('autorelease')) -send(window, sel('cascadeTopLeftFromPoint:'), NSMakePoint(20, 20)) -send(window, sel('setTitle:'), appName) -send(window, sel('makeKeyAndOrderFront:'), nil) - -send(app, sel('activateIgnoringOtherApps:'), YES) -send(app, sel('run')) diff --git a/demo/embedding.py b/demo/embedding.py deleted file mode 100644 index b15c050..0000000 --- a/demo/embedding.py +++ /dev/null @@ -1,21 +0,0 @@ -import cffi - -ffibuilder = cffi.FFI() - -ffibuilder.embedding_api(""" - int add(int, int); -""") - -ffibuilder.embedding_init_code(""" - from _embedding_cffi import ffi - print("preparing") # printed once - - @ffi.def_extern() - def add(x, y): - print("adding %d and %d" % (x, y)) - return x + y -""") - -ffibuilder.set_source("_embedding_cffi", "") - -ffibuilder.compile(verbose=True) diff --git a/demo/embedding_test.c b/demo/embedding_test.c deleted file mode 100644 index ede8cb9..0000000 --- a/demo/embedding_test.c +++ /dev/null @@ -1,43 +0,0 @@ -/* There are two options: - - =====1===== - - Link this program with _embedding_test.so. - E.g. with gcc: - - gcc -o embedding_test embedding_test.c _embedding_cffi*.so - - You must then run the executable with the right command - (LD_LIBRARY_PATH on Linux), otherwise it won't find the - _embedding_cffi*.so: - - LD_LIBRARY_PATH=. ./embedding_test - - There are platform-specific options to gcc to avoid needing - that, too. Linux: - - gcc -o embedding_test embedding_test.c _embedding_cffi*.so \ - -Wl,-rpath=\$ORIGIN/ - - =====2===== - - Compile and link the _embedding_test.c source code together with - this example (e.g. with PyPy): - - gcc -o embedding_test embedding_test.c _embedding_cffi.c \ - -I/opt/pypy/include -pthread -lpypy-c -*/ - -#include <stdio.h> - -extern int add(int x, int y); - - -int main(void) -{ - int res = add(40, 2); - printf("result: %d\n", res); - res = add(100, -5); - printf("result: %d\n", res); - return 0; -} diff --git a/demo/extern_python.py b/demo/extern_python.py deleted file mode 100644 index f315cc5..0000000 --- a/demo/extern_python.py +++ /dev/null @@ -1,26 +0,0 @@ -import cffi - -ffi = cffi.FFI() - -ffi.cdef("""int my_algo(int); extern "Python" int f(int);""") - -ffi.set_source("_extern_python_cffi", """ - static int f(int); - static int my_algo(int n) { - int i, sum = 0; - for (i = 0; i < n; i++) - sum += f(i); - return sum; - } -""") - -ffi.compile() - - -from _extern_python_cffi import ffi, lib - -@ffi.def_extern() -def f(n): - return n * n - -assert lib.my_algo(10) == 0+1+4+9+16+25+36+49+64+81 diff --git a/demo/extern_python_varargs.py b/demo/extern_python_varargs.py deleted file mode 100644 index ee78079..0000000 --- a/demo/extern_python_varargs.py +++ /dev/null @@ -1,61 +0,0 @@ -import cffi - -ffi = cffi.FFI() - -ffi.cdef(""" - int my_algo(int); - typedef ... va_list; - extern "Python" int f(int, va_list *); - - int fetch_int(va_list *); - double fetch_double(va_list *); - void *fetch_ptr(va_list *); -""") - -ffi.set_source("_extern_python_cffi", """ - #include <stdarg.h> - - static int f(int, va_list *); - - static int f1(int n, ...) - { - va_list ap; - va_start(ap, n); - int res = f(n, &ap); - va_end(ap); - return res; - } - - static int fetch_int(va_list *va) { return va_arg((*va), int); } - static double fetch_double(va_list *va) { return va_arg((*va), double); } - static void * fetch_ptr(va_list *va) { return va_arg((*va), void *); } - - static int my_algo(int n) { - return f1(3, n, n+1, n+2) + f1(1, &n) + f1(2, 12.3, 45.6); - } -""") - -ffi.compile() - - -from _extern_python_cffi import ffi, lib - -@ffi.def_extern() -def f(n, va): - if n == 3: - x = lib.fetch_int(va) - y = lib.fetch_int(va) - z = lib.fetch_int(va) - print (x, y, z) - elif n == 1: - ptr = lib.fetch_ptr(va) - print 'ptr to:', ffi.cast("int *", ptr)[0] - elif n == 2: - x = lib.fetch_double(va) - y = lib.fetch_double(va) - print (x, y) - else: - raise AssertionError(n) - return 14 - -print lib.my_algo(10) diff --git a/demo/fastcsv.py b/demo/fastcsv.py deleted file mode 100644 index 6b8d0b4..0000000 --- a/demo/fastcsv.py +++ /dev/null @@ -1,266 +0,0 @@ -import csv -import cffi - -# IN-PROGRESS. See the demo at the end of the file - - -def _make_ffi_from_dialect(dialect_name): - dialect = csv.get_dialect(dialect_name) - - ffi = cffi.FFI() - - ffi.cdef(""" - long parse_line(char *rawline, long inputlength); - """) - - d = {'quotechar': ord(dialect.quotechar), - 'quoting': int(dialect.quoting), - 'skipinitialspace': int(dialect.skipinitialspace), - 'delimiter': ord(dialect.delimiter), - 'doublequote': int(dialect.doublequote), - 'strict': int(dialect.strict), - } - if dialect.escapechar is not None: - d['is_escape_char'] = '== %d' % ord(dialect.escapechar) - else: - d['is_escape_char'] = '&& 0' - - ffi.set_source('_fastcsv_' + dialect_name, r''' - - typedef enum { - START_RECORD, START_FIELD, ESCAPED_CHAR, IN_FIELD, - IN_QUOTED_FIELD, ESCAPE_IN_QUOTED_FIELD, QUOTE_IN_QUOTED_FIELD, - EAT_CRNL - } ParserState; - - typedef enum { - QUOTE_MINIMAL, QUOTE_ALL, QUOTE_NONNUMERIC, QUOTE_NONE - } QuoteStyle; - - typedef struct { - ParserState state; /* current CSV parse state */ - char *field; /* build current field in here */ - int field_size; /* size of allocated buffer */ - int field_len; /* length of current field */ - int numeric_field; /* treat field as numeric */ - } ReaderObj; - - static void - parse_add_char(ReaderObj *self, char c) - { - *self->field++ = c; - } - - static void - parse_save_field(ReaderObj *self) - { - *self->field++ = 0; - } - - static int - parse_process_char(ReaderObj *self, char c) - { - switch (self->state) { - case START_RECORD: - /* start of record */ - if (c == '\0') - /* empty line - return [] */ - break; - else if (c == '\n' || c == '\r') { - self->state = EAT_CRNL; - break; - } - /* normal character - handle as START_FIELD */ - self->state = START_FIELD; - /* fallthru */ - case START_FIELD: - /* expecting field */ - if (c == '\n' || c == '\r' || c == '\0') { - /* save empty field - return [fields] */ - parse_save_field(self); - self->state = (c == '\0' ? START_RECORD : EAT_CRNL); - } - else if (c == %(quotechar)d && - %(quoting)d != QUOTE_NONE) { - /* start quoted field */ - self->state = IN_QUOTED_FIELD; - } - else if (c %(is_escape_char)s) { - /* possible escaped character */ - self->state = ESCAPED_CHAR; - } - else if (c == ' ' && %(skipinitialspace)d) - /* ignore space at start of field */ - ; - else if (c == %(delimiter)d) { - /* save empty field */ - parse_save_field(self); - } - else { - /* begin new unquoted field */ - if (%(quoting)d == QUOTE_NONNUMERIC) - self->numeric_field = 1; - parse_add_char(self, c); - self->state = IN_FIELD; - } - break; - - case ESCAPED_CHAR: - if (c == '\0') - c = '\n'; - parse_add_char(self, c); - self->state = IN_FIELD; - break; - - case IN_FIELD: - /* in unquoted field */ - if (c == '\n' || c == '\r' || c == '\0') { - /* end of line - return [fields] */ - parse_save_field(self); - self->state = (c == '\0' ? START_RECORD : EAT_CRNL); - } - else if (c %(is_escape_char)s) { - /* possible escaped character */ - self->state = ESCAPED_CHAR; - } - else if (c == %(delimiter)d) { - /* save field - wait for new field */ - parse_save_field(self); - self->state = START_FIELD; - } - else { - /* normal character - save in field */ - parse_add_char(self, c); - } - break; - - case IN_QUOTED_FIELD: - /* in quoted field */ - if (c == '\0') - ; - else if (c %(is_escape_char)s) { - /* Possible escape character */ - self->state = ESCAPE_IN_QUOTED_FIELD; - } - else if (c == %(quotechar)d && - %(quoting)d != QUOTE_NONE) { - if (%(doublequote)d) { - /* doublequote; " represented by "" */ - self->state = QUOTE_IN_QUOTED_FIELD; - } - else { - /* end of quote part of field */ - self->state = IN_FIELD; - } - } - else { - /* normal character - save in field */ - parse_add_char(self, c); - } - break; - - case ESCAPE_IN_QUOTED_FIELD: - if (c == '\0') - c = '\n'; - parse_add_char(self, c); - self->state = IN_QUOTED_FIELD; - break; - - case QUOTE_IN_QUOTED_FIELD: - /* doublequote - seen a quote in an quoted field */ - if (%(quoting)d != QUOTE_NONE && - c == %(quotechar)d) { - /* save "" as " */ - parse_add_char(self, c); - self->state = IN_QUOTED_FIELD; - } - else if (c == %(delimiter)d) { - /* save field - wait for new field */ - parse_save_field(self); - self->state = START_FIELD; - } - else if (c == '\n' || c == '\r' || c == '\0') { - /* end of line - return [fields] */ - parse_save_field(self); - self->state = (c == '\0' ? START_RECORD : EAT_CRNL); - } - else if (!%(strict)d) { - parse_add_char(self, c); - self->state = IN_FIELD; - } - else { - /* illegal */ - /*PyErr_Format(error_obj, "'%%c' expected after '%%c'", - dialect->delimiter, - dialect->quotechar);*/ - return -1; - } - break; - - case EAT_CRNL: - if (c == '\n' || c == '\r') - ; - else if (c == '\0') - self->state = START_RECORD; - else { - /*PyErr_Format(error_obj, "new-line character seen in unquoted field - do you need to open the file in universal-newline mode?");*/ - return -1; - } - break; - - } - return 0; - } - - static void - parse_reset(ReaderObj *self, char *rawline) - { - self->field = rawline; - self->state = START_RECORD; - self->numeric_field = 0; - } - - long parse_line(char *rawline, long inputlength) - { - char *p; - ReaderObj reader; - parse_reset(&reader, rawline); - - for (p=rawline; inputlength > 0; inputlength--, p++) { - if (parse_process_char(&reader, *p) < 0) - return -1; - } - if (parse_process_char(&reader, 0) < 0) - return -1; - return reader.field - rawline - 1; - } - ''' % d) - - ffi.compile() - - -def fastcsv_reader(f, dialect_name): - try: - module = __import__('_fastcsv_' + dialect_name) - except ImportError: - _make_ffi_from_dialect(dialect_name) - module = __import__('_fastcsv_' + dialect_name) - ffi, lib = module.ffi, module.lib - # - linelen = -1 - for line in f: - if linelen <= len(line): - linelen = 2 * len(line) - rawline = ffi.new("char[]", linelen) - ffi.buffer(rawline, len(line))[:] = line - n = lib.parse_line(rawline, len(line)) - assert n >= 0 - yield ffi.buffer(rawline, n)[:].split('\x00') - - -if __name__ == '__main__': - csv.register_dialect('unixpwd', delimiter=':', quoting=csv.QUOTE_NONE) - with open('/etc/passwd', 'rb') as f: - reader = fastcsv_reader(f, 'unixpwd') - for row in reader: - print row diff --git a/demo/gmp.py b/demo/gmp.py deleted file mode 100644 index 44f233c..0000000 --- a/demo/gmp.py +++ /dev/null @@ -1,33 +0,0 @@ -import sys -# -# This is only a demo based on the GMP library. -# There is a rather more complete (but perhaps outdated) version available at: -# http://bazaar.launchpad.net/~tolot-solar-empire/+junk/gmpy_cffi/files -# - -try: - from _gmp_cffi import ffi, lib -except ImportError: - print 'run gmp_build first, then make sure the shared object is on sys.path' - sys.exit(1) - -# ffi "knows" about the declared variables and functions from the -# cdef parts of the module created from gmp_build -# lib "knows" how to call the functions from the set_source parts -# of the module. - -# ____________________________________________________________ - -a = ffi.new("mpz_t") -b = ffi.new("mpz_t") - -if len(sys.argv) < 3: - print 'call as %s bigint1, bigint2' % sys.argv[0] - sys.exit(2) - -lib.mpz_init_set_str(a, sys.argv[1], 10) # Assume decimal integers -lib.mpz_init_set_str(b, sys.argv[2], 10) # Assume decimal integers -lib.mpz_add(a, a, b) # a=a+b - -s = lib.mpz_get_str(ffi.NULL, 10, a) -print ffi.string(s) diff --git a/demo/gmp_build.py b/demo/gmp_build.py deleted file mode 100644 index e1a6000..0000000 --- a/demo/gmp_build.py +++ /dev/null @@ -1,26 +0,0 @@ -import cffi - -# -# This is only a demo based on the GMP library. -# There is a rather more complete (but perhaps outdated) version available at: -# http://bazaar.launchpad.net/~tolot-solar-empire/+junk/gmpy_cffi/files -# - -ffibuilder = cffi.FFI() - -ffibuilder.cdef(""" - - typedef struct { ...; } MP_INT; - typedef MP_INT mpz_t[1]; - - int mpz_init_set_str (MP_INT *dest_integer, char *src_cstring, int base); - void mpz_add (MP_INT *sum, MP_INT *addend1, MP_INT *addend2); - char * mpz_get_str (char *string, int base, MP_INT *integer); - -""") - -ffibuilder.set_source('_gmp_cffi', "#include <gmp.h>", - libraries=['gmp', 'm']) - -if __name__ == '__main__': - ffibuilder.compile(verbose=True) diff --git a/demo/manual.c b/demo/manual.c deleted file mode 100644 index 5b360e8..0000000 --- a/demo/manual.c +++ /dev/null @@ -1,166 +0,0 @@ -#include "_cffi_include.h" - - -#define AA (42) -#define BB (&bb) -static int bb = 16261; - -int foo42(int a, int *b) -{ - return a - *b; -} - -int foo64(int a) -{ - return ~a; -} - -struct foo_s { - int a; -}; - -/************************************************************/ - -static void *_cffi_types[] = { - _CFFI_OP(_CFFI_OP_FUNCTION, 1), - _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_INT), - _CFFI_OP(_CFFI_OP_POINTER, 1), - _CFFI_OP(_CFFI_OP_FUNCTION_END, 0), - _CFFI_OP(_CFFI_OP_FUNCTION, 1), - _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_INT), - _CFFI_OP(_CFFI_OP_FUNCTION_END, 0), - _CFFI_OP(_CFFI_OP_STRUCT_UNION, 0), -}; - -#ifndef PYPY_VERSION -static PyObject * -_cffi_f_foo42(PyObject *self, PyObject *args) -{ - int x0; - int * x1; - Py_ssize_t datasize; - int result; - PyObject *arg0; - PyObject *arg1; - - if (!PyArg_ParseTuple(args, "OO:foo42", &arg0, &arg1)) - return NULL; - - x0 = _cffi_to_c_int(arg0, int); - if (x0 == (int)-1 && PyErr_Occurred()) - return NULL; - - datasize = _cffi_prepare_pointer_call_argument( - _cffi_types[1], arg1, (char **)&x1); - if (datasize != 0) { - if (datasize < 0) - return NULL; - x1 = alloca(datasize); - memset((void *)x1, 0, datasize); - if (_cffi_convert_array_from_object((char *)x1, _cffi_types[1], arg1) < 0) - return NULL; - } - - Py_BEGIN_ALLOW_THREADS - _cffi_restore_errno(); - { result = foo42(x0, x1); } - _cffi_save_errno(); - Py_END_ALLOW_THREADS - - return _cffi_from_c_int(result, int); -} -#else -static int _cffi_f_foo42(int x0, int *x1) -{ - return foo42(x0, x1); -} -#endif - -#ifndef PYPY_VERSION -static PyObject * -_cffi_f_foo64(PyObject *self, PyObject *arg0) -{ - int x0; - int result; - - x0 = _cffi_to_c_int(arg0, int); - if (x0 == (int)-1 && PyErr_Occurred()) - return NULL; - - Py_BEGIN_ALLOW_THREADS - _cffi_restore_errno(); - { result = foo64(x0); } - _cffi_save_errno(); - Py_END_ALLOW_THREADS - - return _cffi_from_c_int(result, int); -} -#else -static int _cffi_f_foo64(int x0) -{ - return foo64(x0); -} -#endif - -static int _cffi_const_AA(unsigned long long *output) -{ - *output = (unsigned long long)((AA) << 0); // integer - return (AA) <= 0; -} - -static void _cffi_const_BB(char *output) -{ - *(int **)output = BB; -} - -static const struct _cffi_global_s _cffi_globals[] = { - { "AA", &_cffi_const_AA, _CFFI_OP(_CFFI_OP_CONSTANT_INT, 0) }, - { "BB", &_cffi_const_BB, _CFFI_OP(_CFFI_OP_CONSTANT, 2) }, - { "bb", &bb, _CFFI_OP(_CFFI_OP_GLOBAL_VAR, 1) }, - { "foo42", &_cffi_f_foo42, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_V, 0) }, - { "foo64", &_cffi_f_foo64, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_O, 4) }, -}; - -struct _cffi_align_foo_s { char x; struct foo_s y; }; - -static const struct _cffi_struct_union_s _cffi_struct_unions[] = { - { "foo_s", 7, 0, - sizeof(struct foo_s), - offsetof(struct _cffi_align_foo_s, y), - 1, 0 }, -}; - -static const struct _cffi_field_s _cffi_fields[] = { - { "a", offsetof(struct foo_s, a), sizeof(((struct foo_s *)0)->a), - _CFFI_OP(_CFFI_OP_NOOP, 1) }, -}; - -static const struct _cffi_type_context_s _cffi_type_context = { - _cffi_types, - _cffi_globals, - _cffi_fields, - _cffi_struct_unions, - NULL, - NULL, - 5, /* num_globals */ - 1, /* num_struct_unions */ - 0, - 0, - NULL, - 8, /* num_types */ -}; - -#ifndef PYPY_VERSION -PyMODINIT_FUNC -initmanual(void) -{ - _cffi_init("manual", 0x2601, &_cffi_type_context); -} -#else -PyMODINIT_FUNC -_cffi_pypyinit_manual(const void *p[]) -{ - p[0] = (const void *)0x2601; - p[1] = &_cffi_type_context; -} -#endif diff --git a/demo/manual2.py b/demo/manual2.py deleted file mode 100644 index 2986244..0000000 --- a/demo/manual2.py +++ /dev/null @@ -1,34 +0,0 @@ -import _cffi_backend - -ffi = _cffi_backend.FFI(b"manual2", - _version = 0x2601, - _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x00\x09\x00\x00\x00\x0B\x00\x00\x01\x03', - _globals = (b'\xff\xff\xff\x0bAA',0,b'\xff\xff\xff\x0bBB',-1,b'\xff\xff\xff\x0bCC',2,b'\xff\xff\xff\x1fFOO',0x9999999999999999,b'\x00\x00\x00#close',0,b'\x00\x00\x05#stdout',0), - _struct_unions = ((b'\x00\x00\x00\x03\x00\x00\x00\x00point_s',b'\x00\x00\x01\x11\xff\xff\xff\xffx',b'\x00\x00\x01\x11\xff\xff\xff\xffy'),), - _enums = (b'\x00\x00\x00\x04\x00\x00\x00\x07myenum_e\x00AA,BB,CC',), - _typenames = (b'\x00\x00\x00\x01myint_t',), -) - - - -# trying it out -lib = ffi.dlopen(None) -assert lib.AA == 0 -assert lib.BB == -1 -assert lib.FOO == 0x9999999999999999 -x = lib.close(-42) -assert x == -1 - -print lib.stdout - -print ffi.new("struct point_s *") -print ffi.offsetof("struct point_s", "x") -print ffi.offsetof("struct point_s", "y") -print ffi.new("struct point_s[CC]") -assert ffi.sizeof("struct point_s[CC]") == 2 * ffi.sizeof("struct point_s") - -print ffi.cast("enum myenum_e", 2) -print ffi.cast("myint_t", -2) -assert ffi.typeof("myint_t") == ffi.typeof("int") - -del ffi, lib diff --git a/demo/pwuid.py b/demo/pwuid.py deleted file mode 100644 index dda9299..0000000 --- a/demo/pwuid.py +++ /dev/null @@ -1,7 +0,0 @@ -import sys, os - -# run pwuid_build first, then make sure the shared object is on sys.path -from _pwuid_cffi import ffi, lib - - -print ffi.string(lib.getpwuid(0).pw_name) diff --git a/demo/pwuid_build.py b/demo/pwuid_build.py deleted file mode 100644 index 7ef0d76..0000000 --- a/demo/pwuid_build.py +++ /dev/null @@ -1,18 +0,0 @@ -from cffi import FFI -ffi = FFI() -ffi.cdef(""" // some declarations from the man page - struct passwd { - char *pw_name; - ...; - }; - struct passwd *getpwuid(int uid); -""") - -ffi.set_source('_pwuid_cffi', """ // passed to the real C compiler -#include <sys/types.h> -#include <pwd.h> -""") - - -if __name__ == '__main__': - ffi.compile() diff --git a/demo/py.cleanup b/demo/py.cleanup deleted file mode 100755 index 512389f..0000000 --- a/demo/py.cleanup +++ /dev/null @@ -1,31 +0,0 @@ -#! /usr/bin/env python -import sys, os, stat -from bsdopendirtype import opendir - -def clean(path): - global count - try: - content = opendir(path) - except OSError: - print >> sys.stderr, "skipping", path - return - for filename, smode in content: - if stat.S_ISDIR(smode): - clean(filename) - if filename.endswith('/__pycache__'): - try: - os.rmdir(filename) - except OSError: - pass - elif (filename.endswith('.pyc') or filename.endswith('.pyo') or - filename.endswith('.pyc~') or filename.endswith('.pyo~')): - os.unlink(filename) - count += 1 - -count = 0 - -for arg in sys.argv[1:] or ['.']: - print "cleaning path", arg, "of .pyc/.pyo/__pycache__ files" - clean(arg) - -print "%d files removed" % (count,) diff --git a/demo/pyobj.py b/demo/pyobj.py deleted file mode 100644 index b40343a..0000000 --- a/demo/pyobj.py +++ /dev/null @@ -1,124 +0,0 @@ - -referents = [] # list "object descriptor -> python object" -freelist = None - -def store(x): - "Store the object 'x' and returns a new object descriptor for it." - global freelist - p = freelist - if p is None: - p = len(referents) - referents.append(x) - else: - freelist = referents[p] - referents[p] = x - return p - -def discard(p): - """Discard (i.e. close) the object descriptor 'p'. - Return the original object that was attached to 'p'.""" - global freelist - x = referents[p] - referents[p] = freelist - freelist = p - return x - -class Ref(object): - """For use in 'with Ref(x) as ob': open an object descriptor - and returns it in 'ob', and close it automatically when the - 'with' statement finishes.""" - def __init__(self, x): - self.x = x - def __enter__(self): - self.p = p = store(self.x) - return p - def __exit__(self, *args): - discard(self.p) - -def count_pyobj_alive(): - result = len(referents) - p = freelist - while p is not None: - assert result > 0 - result -= 1 - p = referents[p] - return result - -# ------------------------------------------------------------ - -if __name__ == '__main__': - import api - - ffi = api.PythonFFI() - - ffi.cdef(""" - typedef int pyobj_t; - int sum_integers(pyobj_t p_list); - pyobj_t sum_objects(pyobj_t p_list, pyobj_t p_initial); - """) - - @ffi.pyexport("int(pyobj_t)") - def length(p_list): - list = referents[p_list] - return len(list) - - @ffi.pyexport("int(pyobj_t, int)") - def getitem(p_list, index): - list = referents[p_list] - return list[index] - - @ffi.pyexport("pyobj_t(pyobj_t)") - def pyobj_dup(p): - return store(referents[p]) - - @ffi.pyexport("void(pyobj_t)") - def pyobj_close(p): - discard(p) - - @ffi.pyexport("pyobj_t(pyobj_t, int)") - def pyobj_getitem(p_list, index): - list = referents[p_list] - return store(list[index]) - - @ffi.pyexport("pyobj_t(pyobj_t, pyobj_t)") - def pyobj_add(p1, p2): - return store(referents[p1] + referents[p2]) - - lib = ffi.verify(""" - typedef int pyobj_t; /* an "object descriptor" number */ - - int sum_integers(pyobj_t p_list) { - /* this a demo function written in C, using the API - defined above: length() and getitem(). */ - int i, result = 0; - int count = length(p_list); - for (i=0; i<count; i++) { - int n = getitem(p_list, i); - result += n; - } - return result; - } - - pyobj_t sum_objects(pyobj_t p_list, pyobj_t p_initial) { - /* same as above, but keeps all additions as Python objects */ - int i; - int count = length(p_list); - pyobj_t p1 = pyobj_dup(p_initial); - for (i=0; i<count; i++) { - pyobj_t p2 = pyobj_getitem(p_list, i); - pyobj_t p3 = pyobj_add(p1, p2); - pyobj_close(p2); - pyobj_close(p1); - p1 = p3; - } - return p1; - } - """) - - with Ref([10, 20, 30, 40]) as p_list: - print lib.sum_integers(p_list) - with Ref(5) as p_initial: - result = discard(lib.sum_objects(p_list, p_initial)) - print result - - assert count_pyobj_alive() == 0 diff --git a/demo/readdir.py b/demo/readdir.py deleted file mode 100644 index b966246..0000000 --- a/demo/readdir.py +++ /dev/null @@ -1,35 +0,0 @@ -# A Linux-only demo -# -import sys - -if not sys.platform.startswith('linux'): - raise Exception("Linux-only demo") - -from _readdir import ffi -lib = ffi.dlopen(None) - - -def walk(basefd, path): - print '{', path - dirfd = lib.openat(basefd, path, 0) - if dirfd < 0: - # error in openat() - return - dir = lib.fdopendir(dirfd) - dirent = ffi.new("struct dirent *") - result = ffi.new("struct dirent **") - while True: - if lib.readdir_r(dir, dirent, result): - # error in readdir_r() - break - if result[0] == ffi.NULL: - break - name = ffi.string(dirent.d_name) - print '%3d %s' % (dirent.d_type, name) - if dirent.d_type == 4 and name != '.' and name != '..': - walk(dirfd, name) - lib.closedir(dir) - print '}' - - -walk(-1, "/tmp") diff --git a/demo/readdir2.py b/demo/readdir2.py deleted file mode 100644 index b564b51..0000000 --- a/demo/readdir2.py +++ /dev/null @@ -1,35 +0,0 @@ -# A Linux-only demo, using set_source() instead of hard-coding the exact layouts -# -import sys - -if not sys.platform.startswith('linux'): - raise Exception("Linux-only demo") - -# run readdir2_build first, then make sure the shared object is on sys.path -from _readdir2_cffi import ffi, lib - - -def walk(basefd, path): - print '{', path - dirfd = lib.openat(basefd, path, 0) - if dirfd < 0: - # error in openat() - return - dir = lib.fdopendir(dirfd) - dirent = ffi.new("struct dirent *") - result = ffi.new("struct dirent **") - while True: - if lib.readdir_r(dir, dirent, result): - # error in readdir_r() - break - if result[0] == ffi.NULL: - break - name = ffi.string(dirent.d_name) - print '%3d %s' % (dirent.d_type, name) - if dirent.d_type == lib.DT_DIR and name != '.' and name != '..': - walk(dirfd, name) - lib.closedir(dir) - print '}' - - -walk(-1, "/tmp") diff --git a/demo/readdir2_build.py b/demo/readdir2_build.py deleted file mode 100644 index 5cfd872..0000000 --- a/demo/readdir2_build.py +++ /dev/null @@ -1,36 +0,0 @@ -from cffi import FFI - -ffi = FFI() -ffi.cdef(""" - - typedef ... DIR; - - struct dirent { - unsigned char d_type; /* type of file; not supported - by all file system types */ - char d_name[...]; /* filename */ - ...; - }; - - int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); - int openat(int dirfd, const char *pathname, int flags); - DIR *fdopendir(int fd); - int closedir(DIR *dirp); - - static const int DT_DIR; - -""") -ffi.set_source("_readdir2_cffi", """ -#ifndef _ATFILE_SOURCE -# define _ATFILE_SOURCE -#endif -#ifndef _BSD_SOURCE -# define _BSD_SOURCE -#endif -#include <fcntl.h> -#include <sys/types.h> -#include <dirent.h> -""") - -if __name__ == '__main__': - ffi.compile() diff --git a/demo/readdir2_setup.py b/demo/readdir2_setup.py deleted file mode 100644 index bd8c19f..0000000 --- a/demo/readdir2_setup.py +++ /dev/null @@ -1,9 +0,0 @@ -from distutils.core import setup -import readdir2_build - -setup( - name="readdir2", - version="0.1", - py_modules=["readdir2"], - ext_modules=[readdir2_build.ffi.distutils_extension('build')], -) diff --git a/demo/readdir_build.py b/demo/readdir_build.py deleted file mode 100644 index f97f404..0000000 --- a/demo/readdir_build.py +++ /dev/null @@ -1,33 +0,0 @@ -import sys -from cffi import FFI - -if not sys.platform.startswith('linux'): - raise Exception("Linux-only demo") - - -ffi = FFI() -ffi.cdef(""" - - typedef void DIR; - typedef long ino_t; - typedef long off_t; - - struct dirent { - ino_t d_ino; /* inode number */ - off_t d_off; /* offset to the next dirent */ - unsigned short d_reclen; /* length of this record */ - unsigned char d_type; /* type of file; not supported - by all file system types */ - char d_name[256]; /* filename */ - }; - - int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); - int openat(int dirfd, const char *pathname, int flags); - DIR *fdopendir(int fd); - int closedir(DIR *dirp); - -""") -ffi.set_source("_readdir", None) - -if __name__ == '__main__': - ffi.compile() diff --git a/demo/readdir_ctypes.py b/demo/readdir_ctypes.py deleted file mode 100644 index 4fd1d17..0000000 --- a/demo/readdir_ctypes.py +++ /dev/null @@ -1,69 +0,0 @@ -# A Linux-only demo -# -# For comparison purposes, this is a ctypes version of readdir.py. -import sys -import ctypes - -if not sys.platform.startswith('linux'): - raise Exception("Linux-only demo") - - -DIR_p = ctypes.c_void_p -ino_t = ctypes.c_long -off_t = ctypes.c_long - -class DIRENT(ctypes.Structure): - _fields_ = [ - ('d_ino', ino_t), # inode number - ('d_off', off_t), # offset to the next dirent - ('d_reclen', ctypes.c_ushort), # length of this record - ('d_type', ctypes.c_ubyte), # type of file; not supported - # by all file system types - ('d_name', ctypes.c_char * 256), # filename - ] -DIRENT_p = ctypes.POINTER(DIRENT) -DIRENT_pp = ctypes.POINTER(DIRENT_p) - -C = ctypes.CDLL(None) - -readdir_r = C.readdir_r -readdir_r.argtypes = [DIR_p, DIRENT_p, DIRENT_pp] -readdir_r.restype = ctypes.c_int - -openat = C.openat -openat.argtypes = [ctypes.c_int, ctypes.c_char_p, ctypes.c_int] -openat.restype = ctypes.c_int - -fdopendir = C.fdopendir -fdopendir.argtypes = [ctypes.c_int] -fdopendir.restype = DIR_p - -closedir = C.closedir -closedir.argtypes = [DIR_p] -closedir.restype = ctypes.c_int - - -def walk(basefd, path): - print '{', path - dirfd = openat(basefd, path, 0) - if dirfd < 0: - # error in openat() - return - dir = fdopendir(dirfd) - dirent = DIRENT() - result = DIRENT_p() - while True: - if readdir_r(dir, dirent, result): - # error in readdir_r() - break - if not result: - break - name = dirent.d_name - print '%3d %s' % (dirent.d_type, name) - if dirent.d_type == 4 and name != '.' and name != '..': - walk(dirfd, name) - closedir(dir) - print '}' - - -walk(-1, "/tmp") diff --git a/demo/readdir_setup.py b/demo/readdir_setup.py deleted file mode 100644 index c8abdcb..0000000 --- a/demo/readdir_setup.py +++ /dev/null @@ -1,11 +0,0 @@ -from setuptools import setup - -setup( - name="example", - version="0.1", - py_modules=["readdir"], - setup_requires=["cffi>=1.0.dev0"], - cffi_modules=["readdir_build.py:ffi"], - install_requires=["cffi>=1.0.dev0"], - zip_safe=False, -) diff --git a/demo/recopendirtype.py b/demo/recopendirtype.py deleted file mode 100644 index 768318b..0000000 --- a/demo/recopendirtype.py +++ /dev/null @@ -1,50 +0,0 @@ -from _recopendirtype import ffi, lib - - -def _posix_error(): - raise OSError(ffi.errno, os.strerror(ffi.errno)) - -_dtype_to_smode = { - lib.DT_BLK: 0o060000, - lib.DT_CHR: 0o020000, - lib.DT_DIR: 0o040000, - lib.DT_FIFO: 0o010000, - lib.DT_LNK: 0o120000, - lib.DT_REG: 0o100000, - lib.DT_SOCK: 0o140000, -} - -def opendir(dir): - if len(dir) == 0: - dir = b'.' - dirname = dir - if not dirname.endswith(b'/'): - dirname += b'/' - dirp = lib.opendir(dir) - if dirp == ffi.NULL: - raise _posix_error() - dirent = ffi.new("struct dirent *") - result = ffi.new("struct dirent **") - try: - while True: - ffi.errno = 0 - err = lib.readdir_r(dirp, dirent, result) - if err: # really got an error - raise OSError(err, os.strerror(err)) - if result[0] == ffi.NULL: - return # - name = ffi.string(dirent.d_name) - if name == b'.' or name == b'..': - continue - name = dirname + name - try: - smode = _dtype_to_smode[dirent.d_type] - except KeyError: - smode = os.lstat(name).st_mode - yield name, smode - finally: - lib.closedir(dirp) - -if __name__ == '__main__': - for name, smode in opendir(b'/tmp'): - print(hex(smode), name) diff --git a/demo/recopendirtype_build.py b/demo/recopendirtype_build.py deleted file mode 100644 index fa62a05..0000000 --- a/demo/recopendirtype_build.py +++ /dev/null @@ -1,19 +0,0 @@ -from cffi import FFI -import bsdopendirtype_build - -ffi = FFI() - -# ========== This is a demo of ffi.include() ========== -ffi.include(bsdopendirtype_build.ffi) - -ffi.cdef(""" - int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); -""") - -ffi.set_source("_recopendirtype", """ - #include <sys/types.h> - #include <dirent.h> -""") - -if __name__ == '__main__': - ffi.compile() diff --git a/demo/setup_manual.py b/demo/setup_manual.py deleted file mode 100644 index 2569bb4..0000000 --- a/demo/setup_manual.py +++ /dev/null @@ -1,5 +0,0 @@ -from distutils.core import setup -from distutils.extension import Extension -setup(name='manual', - ext_modules=[Extension(name='manual', - sources=['manual.c'])]) diff --git a/demo/winclipboard.py b/demo/winclipboard.py deleted file mode 100644 index 5278cd0..0000000 --- a/demo/winclipboard.py +++ /dev/null @@ -1,40 +0,0 @@ -__author__ = "Israel Fruchter <israel.fruchter@gmail.com>" - -import sys, os - -if not sys.platform == 'win32': - raise Exception("Windows-only demo") - -try: - from _winclipboard_cffi import ffi, lib -except ImportError: - print 'run winclipboard_build first, then make sure the shared object is on sys.path' - sys.exit(1) - -# ffi "knows" about the declared variables and functions from the -# cdef parts of the module _winclipboard_cffi created, -# lib "knows" how to call the functions from the set_source parts -# of the module. - -def CopyToClipboard(string): - ''' - use win32 api to copy `string` to the clipboard - ''' - hWnd = lib.GetConsoleWindow() - - if lib.OpenClipboard(hWnd): - cstring = ffi.new("char[]", string) - size = ffi.sizeof(cstring) - - # make it a moveable memory for other processes - hGlobal = lib.GlobalAlloc(lib.GMEM_MOVEABLE, size) - buffer = lib.GlobalLock(hGlobal) - lib.memcpy(buffer, cstring, size) - lib.GlobalUnlock(hGlobal) - - res = lib.EmptyClipboard() - res = lib.SetClipboardData(lib.CF_TEXT, buffer) - - lib.CloseClipboard() - -CopyToClipboard("hello world from cffi") diff --git a/demo/winclipboard_build.py b/demo/winclipboard_build.py deleted file mode 100644 index 1a510eb..0000000 --- a/demo/winclipboard_build.py +++ /dev/null @@ -1,36 +0,0 @@ -from cffi import FFI - -ffi = FFI() -ffi.cdef(''' - typedef void * HANDLE; - typedef HANDLE HWND; - typedef int BOOL; - typedef unsigned int UINT; - typedef int SIZE_T; - typedef char * LPTSTR; - typedef HANDLE HGLOBAL; - typedef HANDLE LPVOID; - - HWND GetConsoleWindow(void); - - LPVOID GlobalLock( HGLOBAL hMem ); - BOOL GlobalUnlock( HGLOBAL hMem ); - HGLOBAL GlobalAlloc(UINT uFlags, SIZE_T dwBytes); - - BOOL OpenClipboard(HWND hWndNewOwner); - BOOL CloseClipboard(void); - BOOL EmptyClipboard(void); - HANDLE SetClipboardData(UINT uFormat, HANDLE hMem); - - #define CF_TEXT ... - #define GMEM_MOVEABLE ... - - void * memcpy(void * s1, void * s2, int n); - ''') - -ffi.set_source('_winclipboard_cffi', ''' - #include <windows.h> -''', libraries=["user32"]) - -if __name__ == '__main__': - ffi.compile() diff --git a/demo/xclient.py b/demo/xclient.py deleted file mode 100644 index e4b3dd2..0000000 --- a/demo/xclient.py +++ /dev/null @@ -1,27 +0,0 @@ -import sys, os - -# run xclient_build first, then make sure the shared object is on sys.path -from _xclient_cffi import ffi, lib - - -# ffi "knows" about the declared variables and functions from the -# cdef parts of the module xclient_build created, -# lib "knows" how to call the functions from the set_source parts -# of the module. - - -class XError(Exception): - pass - -def main(): - display = lib.XOpenDisplay(ffi.NULL) - if display == ffi.NULL: - raise XError("cannot open display") - w = lib.XCreateSimpleWindow(display, lib.DefaultRootWindow(display), - 10, 10, 500, 350, 0, 0, 0) - lib.XMapRaised(display, w) - event = ffi.new("XEvent *") - lib.XNextEvent(display, event) - -if __name__ == '__main__': - main() diff --git a/demo/xclient_build.py b/demo/xclient_build.py deleted file mode 100644 index d6ce9da..0000000 --- a/demo/xclient_build.py +++ /dev/null @@ -1,25 +0,0 @@ -from cffi import FFI -ffi = FFI() -ffi.cdef(""" - -typedef ... Display; -typedef struct { ...; } Window; - -typedef struct { int type; ...; } XEvent; - -Display *XOpenDisplay(char *display_name); -Window DefaultRootWindow(Display *display); -int XMapRaised(Display *display, Window w); -Window XCreateSimpleWindow(Display *display, Window parent, int x, int y, - unsigned int width, unsigned int height, - unsigned int border_width, unsigned long border, - unsigned long background); -int XNextEvent(Display *display, XEvent *event_return); -""") - -ffi.set_source('_xclient_cffi', """ - #include <X11/Xlib.h> -""", libraries=['X11']) - -if __name__ == '__main__': - ffi.compile(verbose=True) diff --git a/doc/Makefile b/doc/Makefile deleted file mode 100644 index 285361c..0000000 --- a/doc/Makefile +++ /dev/null @@ -1,89 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = build - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source - -.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest - -help: - @echo "Please use \`make <target>' where <target> is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - -rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/CFFI.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/CFFI.qhc" - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ - "run these through (pdf)latex." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." diff --git a/doc/make.bat b/doc/make.bat deleted file mode 100644 index 5daa6b5..0000000 --- a/doc/make.bat +++ /dev/null @@ -1,113 +0,0 @@ -@ECHO OFF - -REM Command file for Sphinx documentation - -set SPHINXBUILD=sphinx-build -set BUILDDIR=build -set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source -if NOT "%PAPER%" == "" ( - set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% -) - -if "%1" == "" goto help - -if "%1" == "help" ( - :help - echo.Please use `make ^<target^>` where ^<target^> is one of - echo. html to make standalone HTML files - echo. dirhtml to make HTML files named index.html in directories - echo. pickle to make pickle files - echo. json to make JSON files - echo. htmlhelp to make HTML files and a HTML help project - echo. qthelp to make HTML files and a qthelp project - echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter - echo. changes to make an overview over all changed/added/deprecated items - echo. linkcheck to check all external links for integrity - echo. doctest to run all doctests embedded in the documentation if enabled - goto end -) - -if "%1" == "clean" ( - for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i - del /q /s %BUILDDIR%\* - goto end -) - -if "%1" == "html" ( - %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/html. - goto end -) - -if "%1" == "dirhtml" ( - %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. - goto end -) - -if "%1" == "pickle" ( - %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle - echo. - echo.Build finished; now you can process the pickle files. - goto end -) - -if "%1" == "json" ( - %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json - echo. - echo.Build finished; now you can process the JSON files. - goto end -) - -if "%1" == "htmlhelp" ( - %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp - echo. - echo.Build finished; now you can run HTML Help Workshop with the ^ -.hhp project file in %BUILDDIR%/htmlhelp. - goto end -) - -if "%1" == "qthelp" ( - %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp - echo. - echo.Build finished; now you can run "qcollectiongenerator" with the ^ -.qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\CFFI.qhcp - echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\CFFI.ghc - goto end -) - -if "%1" == "latex" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - echo. - echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "changes" ( - %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes - echo. - echo.The overview file is in %BUILDDIR%/changes. - goto end -) - -if "%1" == "linkcheck" ( - %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck - echo. - echo.Link check complete; look for any errors in the above output ^ -or in %BUILDDIR%/linkcheck/output.txt. - goto end -) - -if "%1" == "doctest" ( - %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest - echo. - echo.Testing of doctests in the sources finished, look at the ^ -results in %BUILDDIR%/doctest/output.txt. - goto end -) - -:end diff --git a/doc/misc/design.rst b/doc/misc/design.rst deleted file mode 100644 index 390049c..0000000 --- a/doc/misc/design.rst +++ /dev/null @@ -1,51 +0,0 @@ -================ -Design decisions -================ - -* Generally follow LuaJIT's ffi: http://luajit.org/ext_ffi.html - -* Be explicit: almost no automatic conversions. Here is the set - of automatic conversions: the various C integer types are - automatically wrapped and unwrapped to regular applevel integers. The - type ``char`` might correspond to single-character strings instead; - for integer correspondance you would use ``signed char`` or ``unsigned - char``. We might also decide that ``const char *`` automatically maps - to strings; for cases where you don't want that, use ``char *``. - -* Integers are not automatically converted when passed as vararg - arguments. You have to use explicitly ``ffi.new("int", 42)`` or - ``ffi.new("long", 42)`` to resolve the ambiguity. Floats would be - fine (varargs in C can only accept ``double``, not ``float``), but - there is again ambiguity between characters and strings. Even with - floats the result is a bit strange because passing a float works - but passing an integer not. I would fix this once and for all by - saying that varargs must *always* be a cdata (from ``ffi.new()``). - The possibly acceptable exception would be None (for ``NULL``). - -* The internal class ``blob`` is used for raw-malloced data. You only - get a class that has internally a ``blob`` instance (or maybe is a - subclass of ``blob``) by calling ``ffi.new(struct-or-array-type)``. - The other cases, namely the cases where the type is a pointer or a - primitive, don't need a blob because it's not possible to take their - raw address. - -* It would be possible to add a debug mode: when we cast ``struct foo`` - to ``struct foo *`` or store it in some other struct, then we would - additionally record a weakref to the original ``struct foo`` blob. - If later we try to access the ``struct foo *`` but the weakref shows - that the blob was freed, we complain. This is a difference with - ctypes, which in these cases would store a strong reference and - keep the blob alive. "Explicit is better than implicit", so we ask - the user to keep a reference to the original blob alive as long as - it may be used (instead of doing the right things in 90% of the cases - but still crashing in the remaining 10%). - -* LuaJIT uses ``struct foo &`` for a number of things, like for ``p[0]`` - if ``p`` is a ``struct foo *``. I suppose it's not a bad idea at least - to have internally such types, even if you can't specify them through - pycparser. Basically ``struct foo &`` is a type that doesn't own a - blob, whereas ``struct foo`` is the type that does. - -* LuaJIT uses ``int[?]`` which pycparser doesn't accept. I propose - instead to use ``int[]`` for the same purpose (its use is anyway quite - close to the C standard's use of ``int[]``). diff --git a/doc/misc/grant-cffi-1.0.rst b/doc/misc/grant-cffi-1.0.rst deleted file mode 100644 index b026209..0000000 --- a/doc/misc/grant-cffi-1.0.rst +++ /dev/null @@ -1,124 +0,0 @@ - -=========================== -Grant Proposal for CFFI 1.0 -=========================== - -*Accepted by the PSF board on April 4, 2015* - -This Grant Proposal is to give a boost towards "CFFI 1.0". Two main -issues with the current CFFI need to be solved: the difficulties of -installation, and the potentially large time taken at import. - -1. The difficulties of installation can be seen from outside by looking -at various workarounds and 3rd-party documentation that have grown into -existence. For example, the `setup.py` of projects like cryptography, -PyNaCl and bcrypt deploys workarounds that are explicitly documented in -https://caremad.io/2014/11/distributing-a-cffi-project/. - -2. The time taken at import is excessive in some cases. For example, -importing `pygame-cffi` on a Raspberry Pi ARM board takes on the order -of 10 to 20 seconds (and this is the "fast" case where the compiler -doesn't need to be invoked any more). - - -Technical Overview ------------------- - -"CFFI" is an existing Python project which complements the ctypes, -SWIG and Cython approaches to ease writing C Extension Modules for -Python. It has several advantages over the previous approaches, which -are presented at the start of the documentation at -http://cffi.readthedocs.org/en/latest/ . It has been very successful -so far: http://pypi-ranking.info/alltime records almost 7 million -downloads (for comparison, the #1 of all packages has almost 36 -million downloads). CFFI works on any Python >= 2.6, including 3.x, -as well as on PyPy. - -One problem is that while getting started with CFFI is very easy, the -installation process of a package that uses CFFI has got its rough -edges. CFFI (at least in its "verify()" mode) is based on calling the -C compiler to get information about the exact C types, structures, -argument types to functions, and so on. The C compiler is invoked -transparently at run-time, and the results cached. A -correctly-installed package using CFFI should cache the results at -installation time, but it can be difficult to ensure that no more -run-time compiler invocation is needed; doing so requires following -some extra guidelines or understanding some internal details. (The -problem is particularly acute on Windows where a typical user might -not have a proper C compiler installed.) - -To fix this, we have in mind adding a different CFFI mode (replacing -"verify()"), while keeping the access to the underlying C library -unmodified. In this mode, the code containing the cdef() and verify() -invocations would be moved to a separate Python source file. Running -that Python file would produce a dynamically-linked library. There -would be no caching logic involved; you would need to run it -explicitly during development whenever you made changes to it, to -re-generate and re-compile the dynamically-linked library. - -When distributed, the same file would be run (once) during -installation. This can be fully automated in setuptools-based -setup.py files; alternatively, it can be done in distutils-based -setup.py files by requiring prior manual installation of CFFI itself. - -A major difference with the existing verify() approach would be that -the ``.so/.dll/.dylib`` file would not be immediately loaded into the -process; you would load it only from the installed program at -run-time, and get the ``ffi`` and ``lib`` objects in this way (these -are the two objects that you use so far to access a C library with -verify()). - -Additionally, this would solve another issue: every import of a large -CFFI-using package takes a while so far. This is caused by CFFI -needing to parse again the C source code given in the cdef() (adding a -run-time dependency to the ``pycparser`` and ``ply`` packages). CFFI -also computes a CRC to know if it can reuse its cache. In the -proposed change, all the cdef() code would be pre-parsed and stored in -the dynamically-linked library, and no CRC would be needed. This -would massively reduce the import times. - - -Grant objective ---------------- - -The objective is to give a boost towards "CFFI 1.0", which needs to have -the functionalities described above in order to solve the two main -issues with the current CFFI: the difficulties of installation, and the -time taken at import. - -Included in the objective: the internal refactorings of CFFI that are -needed to get it done cleanly. The goal is to avoid simply adding -another layer on top of the old unchanged CFFI. - -This work may happen eventually in any case, but support from the PSF -would help make it happen sooner rather than later. - - -Grant size ----------- - -2'500 US$ for supporting the development time. This would cover 2.5 -weeks of full-time work at the part-time cost of 25 US$ per hour. - -The estimated work time until the CFFI 1.0 release is a bit larger -than that (I estimate it at roughly 4 weeks), but 2.5 weeks should -cover all the basics. An extended grant size of 4'000 US$ would be -appreciated but not required ``:-)`` - - -Grant beneficiaries -------------------- - -Armin Rigo, main author of CFFI, committing 2.5 weeks of full-time -work. - - -Grant follow-up ---------------- - -I will report on the success of the grant on the CFFI mailing list and -on the blog I usually post to (the PyPy blog) and mention the PSF as -providing the grant. The PSF will receive an email pointing to these -postings once they are out. Moreover a full CFFI 1.0 release should -follow (likely starting with beta versions); the PSF will receive -another email pointing to it. diff --git a/doc/misc/parse_c_type.rst b/doc/misc/parse_c_type.rst deleted file mode 100644 index 1d1029d..0000000 --- a/doc/misc/parse_c_type.rst +++ /dev/null @@ -1,72 +0,0 @@ -================================================== -CPython C extension module produced by recompile() -================================================== - -Global variable:: - - _cffi_opcode_t _cffi_types[]; - -Every _cffi_types entry is initially an odd integer. At runtime, it -is fixed to be a `CTypeDescrObject *` when the odd integer is -interpreted and turned into a real <ctype> object. - -The generated C functions are listed in _cffi_globals, a sorted array -of entries which get turned lazily into real <builtin function -objects>. Each entry in this array has an index in the _cffi_types -array, which describe the function type (OP_FUNCTION opcode, see -below). We turn the odd integers describing argument and return types -into real CTypeDescrObjects at the point where the entry is turned -into a real builtin function object. - -The odd integers are "opcodes" that contain a type info in the lowest -byte. The remaining high bytes of the integer is an "arg" that depends -on the type info: - -OP_PRIMITIVE - the arg tells which primitive type it is (an index in some list) - -OP_POINTER - the arg is the index of the item type in the _cffi_types array. - -OP_ARRAY - the arg is the index of the item type in the _cffi_types array. - followed by another opcode that contains (uintptr_t)length_of_array. - -OP_OPEN_ARRAY - for syntax like "int[]". same as OP_ARRAY but without the length - -OP_STRUCT_UNION - the arg is the index of the struct/union in _cffi_structs_unions - -OP_ENUM - the arg is the index of the enum in _cffi_enums - -OP_TYPENAME - the arg is the index of the typename in _cffi_typenames - -OP_FUNCTION - the arg is the index of the result type in _cffi_types. - followed by other opcodes for the arguments. - terminated by OP_FUNCTION_END. - -OP_FUNCTION_END - the arg's lowest bit is set if there is a "..." argument. - -OP_NOOP - simple indirection: the arg is the index to look further in - -There are other opcodes, used not inside _cffi_types but in other -individual ``type_op`` fields. Most importantly, these are used -on _cffi_globals entries: - -OP_CPYTHON_BLTN_* - declare a function - -OP_CONSTANT - declare a non-integral constant - -OP_CONSTANT_INT - declare an int constant - -OP_GLOBAL_VAR - declare a global var diff --git a/doc/source/cdef.rst b/doc/source/cdef.rst deleted file mode 100644 index 0662668..0000000 --- a/doc/source/cdef.rst +++ /dev/null @@ -1,1012 +0,0 @@ -====================================== -Preparing and Distributing modules -====================================== - -.. contents:: - -There are three or four different ways to use CFFI in a project. -In order of complexity: - -* The **"in-line", "ABI mode"**: - - .. code-block:: python - - import cffi - - ffi = cffi.FFI() - ffi.cdef("C-like declarations") - lib = ffi.dlopen("libpath") - - # use ffi and lib here - -.. _out-of-line-abi: - -* The **"out-of-line",** but still **"ABI mode",** useful to organize - the code and reduce the import time: - - .. code-block:: python - - # in a separate file "package/foo_build.py" - import cffi - - ffibuilder = cffi.FFI() - ffibuilder.set_source("package._foo", None) - ffibuilder.cdef("C-like declarations") - - if __name__ == "__main__": - ffibuilder.compile() - - Running ``python foo_build.py`` produces a file ``_foo.py``, which - can then be imported in the main program: - - .. code-block:: python - - from package._foo import ffi - lib = ffi.dlopen("libpath") - - # use ffi and lib here - -.. _out-of-line-api: - -* The **"out-of-line", "API mode"** gives you the most flexibility - and speed to access a C library at the level of C, instead of at the - binary level: - - .. code-block:: python - - # in a separate file "package/foo_build.py" - import cffi - - ffibuilder = cffi.FFI() - ffibuilder.set_source("package._foo", r"""real C code""") # <= - ffibuilder.cdef("C-like declarations with '...'") - - if __name__ == "__main__": - ffibuilder.compile(verbose=True) - - Running ``python foo_build.py`` produces a file ``_foo.c`` and - invokes the C compiler to turn it into a file ``_foo.so`` (or - ``_foo.pyd`` or ``_foo.dylib``). It is a C extension module which - can be imported in the main program: - - .. code-block:: python - - from package._foo import ffi, lib - # no ffi.dlopen() - - # use ffi and lib here - -.. _distutils-setuptools: - -* Finally, you can (but don't have to) use CFFI's **Distutils** or - **Setuptools integration** when writing a ``setup.py``. For - Distutils (only in out-of-line API mode): - - .. code-block:: python - - # setup.py (requires CFFI to be installed first) - from distutils.core import setup - - import foo_build # possibly with sys.path tricks to find it - - setup( - ..., - ext_modules=[foo_build.ffibuilder.distutils_extension()], - ) - - For Setuptools (out-of-line, but works in ABI or API mode; - recommended): - - .. code-block:: python - - # setup.py (with automatic dependency tracking) - from setuptools import setup - - setup( - ..., - setup_requires=["cffi>=1.0.0"], - cffi_modules=["package/foo_build.py:ffibuilder"], - install_requires=["cffi>=1.0.0"], - ) - - Note again that the ``foo_build.py`` example contains the following - lines, which mean that the ``ffibuilder`` is not actually compiled - when ``package.foo_build`` is merely imported---it will be compiled - independently by the Setuptools logic, using compilation parameters - provided by Setuptools: - - .. code-block:: python - - if __name__ == "__main__": # not when running with setuptools - ffibuilder.compile(verbose=True) - -* Note that some bundler tools that try to find all modules used by a - project, like PyInstaller, will miss ``_cffi_backend`` in the - out-of-line mode because your program contains no explicit ``import - cffi`` or ``import _cffi_backend``. You need to add - ``_cffi_backend`` explicitly (as a "hidden import" in PyInstaller, - but it can also be done more generally by adding the line ``import - _cffi_backend`` in your main program). - -Note that CFFI actually contains two different ``FFI`` classes. The -page `Using the ffi/lib objects`_ describes the common functionality. -It is what you get in the ``from package._foo import ffi`` lines above. -On the other hand, the extended ``FFI`` class is the one you get from -``import cffi; ffi_or_ffibuilder = cffi.FFI()``. It has the same -functionality (for in-line use), but also the extra methods described -below (to prepare the FFI). NOTE: We use the name ``ffibuilder`` -instead of ``ffi`` in the out-of-line context, when the code is about -producing a ``_foo.so`` file; this is an attempt to distinguish it -from the different ``ffi`` object that you get by later saying -``from _foo import ffi``. - -.. _`Using the ffi/lib objects`: using.html - -The reason for this split of functionality is that a regular program -using CFFI out-of-line does not need to import the ``cffi`` pure -Python package at all. (Internally it still needs ``_cffi_backend``, -a C extension module that comes with CFFI; this is why CFFI is also -listed in ``install_requires=..`` above. In the future this might be -split into a different PyPI package that only installs -``_cffi_backend``.) - -Note that a few small differences do exist: notably, ``from _foo import -ffi`` returns an object of a type written in C, which does not let you -add random attributes to it (nor does it have all the -underscore-prefixed internal attributes of the Python version). -Similarly, the ``lib`` objects returned by the C version are read-only, -apart from writes to global variables. Also, ``lib.__dict__`` does -not work before version 1.2 or if ``lib`` happens to declare a name -called ``__dict__`` (use instead ``dir(lib)``). The same is true -for ``lib.__class__``, ``lib.__all__`` and ``lib.__name__`` added -in successive versions. - - -.. _cdef: - -ffi/ffibuilder.cdef(): declaring types and functions ----------------------------------------------------- - -**ffi/ffibuilder.cdef(source)**: parses the given C source. -It registers all the functions, types, constants and global variables in -the C source. The types can be used immediately in ``ffi.new()`` and -other functions. Before you can access the functions and global -variables, you need to give ``ffi`` another piece of information: where -they actually come from (which you do with either ``ffi.dlopen()`` or -``ffi.set_source()``). - -.. _`all types listed above`: - -The C source is parsed internally (using ``pycparser``). This code -cannot contain ``#include``. It should typically be a self-contained -piece of declarations extracted from a man page. The only things it -can assume to exist are the standard types: - -* char, short, int, long, long long (both signed and unsigned) - -* float, double, long double - -* intN_t, uintN_t (for N=8,16,32,64), intptr_t, uintptr_t, ptrdiff_t, - size_t, ssize_t - -* wchar_t (if supported by the backend). *New in version 1.11:* - char16_t and char32_t. - -* _Bool and bool (equivalent). If not directly supported by the C - compiler, this is declared with the size of ``unsigned char``. - -* FILE. `See here.`__ - -* all `common Windows types`_ are defined if you run - on Windows (``DWORD``, ``LPARAM``, etc.). Exception: - ``TBYTE TCHAR LPCTSTR PCTSTR LPTSTR PTSTR PTBYTE PTCHAR`` are - not automatically defined; see `ffi.set_unicode()`_. - -* the other standard integer types from - stdint.h, like ``intmax_t``, as long as they map to integers of 1, - 2, 4 or 8 bytes. Larger integers are not supported. - -.. __: ref.html#file -.. _`common Windows types`: http://msdn.microsoft.com/en-us/library/windows/desktop/aa383751%28v=vs.85%29.aspx - -The declarations can also contain "``...``" at various places; these are -placeholders that will be completed by the compiler. More information -about it below in `Letting the C compiler fill the gaps`_. - -Note that all standard type names listed above are handled as -*defaults* only (apart from the ones that are keywords in the C -language). If your ``cdef`` contains an explicit typedef that -redefines one of the types above, then the default described above is -ignored. (This is a bit hard to implement cleanly, so in some corner -cases it might fail, notably with the error ``Multiple type specifiers -with a type tag``. Please report it as a bug if it does.) - -Multiple calls to ``ffi.cdef()`` are possible. Beware that it can be -slow to call ``ffi.cdef()`` a lot of times, a consideration that is -important mainly in in-line mode. - -The ``ffi.cdef()`` call optionally takes an extra argument: either -``packed`` or ``pack``. If you pass ``packed=True``, -then all structs declared within -this cdef are "packed". (If you need both packed and non-packed -structs, use several cdefs in sequence.) This -has a meaning similar to ``__attribute__((packed))`` in GCC. It -specifies that all structure fields should have an alignment of one -byte. (Note that the packed attribute has no effect on bit fields so -far, which mean that they may be packed differently than on GCC. -Also, this has no effect on structs declared with ``"...;"``---more -about it later in `Letting the C compiler fill the gaps`_. In -particular, if your C source uses other attributes like -``__attribute__((aligned(16)))``, there is no way to declare this fact -in the ``cdef()``, but you can generally just declare the struct with -``"...;"`` as the last field.) - -*New in version 1.12:* In ABI mode, you can also pass ``pack=n``, -with an integer ``n`` which must be a power of two. Then the -alignment of any field is limited to ``n`` if it would otherwise be -greater than ``n``. Passing ``pack=1`` is equivalent to passing -``packed=True``. This is meant to emulate ``#pragma pack(n)`` from -the MSVC compiler. On Windows, the default is ``pack=8`` (from cffi -1.12 onwards); on other platforms, the default is ``pack=None``. - -Note that you can use the type-qualifiers ``const`` and ``restrict`` -(but not ``__restrict`` or ``__restrict__``) in the ``cdef()``, but -this has no effect on the cdata objects that you get at run-time (they -are never ``const``). The effect is limited to knowing if a global -variable is meant to be a constant or not. Also, *new in version -1.3:* when using ``set_source()`` or ``verify()``, these two -qualifiers are copied from the cdef to the generated C code; this -fixes warnings by the C compiler. - -Note a trick if you copy-paste code from sources in which there are -extra macros (for example, the Windows documentation uses SAL -annotations like ``_In_`` or ``_Out_``). These hints must be removed -in the string given to cdef(), but it can be done programmatically -like this:: - - ffi.cdef(re.sub(r"\b(_In_|_Inout_|_Out_|_Outptr_)(opt_)?\b", " ", - """ - DWORD WINAPI GetModuleFileName( - _In_opt_ HMODULE hModule, - _Out_ LPTSTR lpFilename, - _In_ DWORD nSize - ); - """)) - -Note also that pycparser, the underlying C parser, recognizes -preprocessor-like directives in the following format: ``# NUMBER -"FILE"``. For example, if you put ``# 42 "foo.h"`` in the middle of the -string passed to ``cdef()`` and there is an error two lines later, then -it is reported with an error message that starts with ``foo.h:43:`` (the -line which is given the number 42 is the line immediately after the -directive). *New in version 1.10.1:* CFFI automatically puts the line -``# 1 "<cdef source string>"`` just before the string you give to -``cdef()``. - - -.. _`ffi.set_unicode()`: - -**ffi.set_unicode(enabled_flag)**: Windows: if ``enabled_flag`` is -True, enable the ``UNICODE`` and ``_UNICODE`` defines in C, and -declare the types ``TBYTE TCHAR LPCTSTR PCTSTR LPTSTR PTSTR PTBYTE -PTCHAR`` to be (pointers to) ``wchar_t``. If ``enabled_flag`` is -False, declare these types to be (pointers to) plain 8-bit characters. -(These types are not predeclared at all if you don't call -``set_unicode()``.) - -The reason behind this method is that a lot of standard functions have -two versions, like ``MessageBoxA()`` and ``MessageBoxW()``. The -official interface is ``MessageBox()`` with arguments like -``LPTCSTR``. Depending on whether ``UNICODE`` is defined or not, the -standard header renames the generic function name to one of the two -specialized versions, and declares the correct (unicode or not) types. - -Usually, the right thing to do is to call this method with True. Be -aware (particularly on Python 2) that, afterwards, you need to pass unicode -strings as arguments instead of byte strings. - - -.. _loading-libraries: -.. _dlopen: - -ffi.dlopen(): loading libraries in ABI mode -------------------------------------------- - -``ffi.dlopen(libpath, [flags])``: this function opens a shared library and -returns a module-like library object. Use this when you are fine with -the limitations of ABI-level access to the system (dependency on ABI -details, getting crashes instead of C compiler errors/warnings, and -higher overhead to call the C functions). In case of doubt, read again -`ABI versus API`_ in the overview. - -.. _`ABI versus API`: overview.html#abi-versus-api - -You can use the library object to call the functions previously -declared by ``ffi.cdef()``, to read constants, and to read or write -global variables. Note that you can use a single ``cdef()`` to -declare functions from multiple libraries, as long as you load each of -them with ``dlopen()`` and access the functions from the correct one. - -The ``libpath`` is the file name of the shared library, which can -contain a full path or not (in which case it is searched in standard -locations, as described in ``man dlopen``), with extensions or not. -Alternatively, if ``libpath`` is None, it returns the standard C library -(which can be used to access the functions of glibc, on Linux). Note -that ``libpath`` `cannot be None`__ on Windows with Python 3. - -.. __: http://bugs.python.org/issue23606 - -Let me state it again: this gives ABI-level access to the library, so -you need to have all types declared manually exactly as they were -while the library was made. No checking is done. Mismatches can -cause random crashes. API-level access, on the other hand, is safer. -Speed-wise, API-level access is much faster (it is common to have -the opposite misconception about performance). - -Note that only functions and global variables live in library objects; -the types exist in the ``ffi`` instance independently of library objects. -This is due to the C model: the types you declare in C are not tied to a -particular library, as long as you ``#include`` their headers; but you -cannot call functions from a library without linking it in your program, -as ``dlopen()`` does dynamically in C. - -For the optional ``flags`` argument, see ``man dlopen`` (ignored on -Windows). It defaults to ``ffi.RTLD_NOW``. - -This function returns a "library" object that gets closed when it goes -out of scope. Make sure you keep the library object around as long as -needed. (Alternatively, the out-of-line FFIs have a method -``ffi.dlclose(lib)``.) - -.. _dlopen-note: - -Note: the old version of ``ffi.dlopen()`` from the in-line ABI mode -tries to use ``ctypes.util.find_library()`` if it cannot directly find -the library. The newer out-of-line ``ffi.dlopen()`` no longer does it -automatically; it simply passes the argument it receives to the -underlying ``dlopen()`` or ``LoadLibrary()`` function. If needed, it -is up to you to use ``ctypes.util.find_library()`` or any other way to -look for the library's filename. This also means that -``ffi.dlopen(None)`` no longer work on Windows; try instead -``ffi.dlopen(ctypes.util.find_library('c'))``. - -*New in version 1.14:* ``ffi.dlopen(handle)``: instead of a file path, -you can give an already-opened library handle, as a cdata of type -``void *``. Such a call converts this handle into a regular FFI object -with the functions and global variables declared by ``ffi.cdef()``. -Useful if you have special needs (e.g. you need the GNU extension -``dlmopen()``, which you can itself declare and call using a different -``ffi`` object). Note that in this variant, ``dlclose()`` is not called -automatically if the FFI object is garbage-collected (but you can still -call ``ffi.dlclose()`` explicitly if needed). - - -.. _set_source: - -ffibuilder.set_source(): preparing out-of-line modules ------------------------------------------------------- - -**ffibuilder.set_source(module_name, c_header_source, [\*\*keywords...])**: -prepare the ffi for producing out-of-line an external module called -``module_name``. - -``ffibuilder.set_source()`` by itself does not write any file, but merely -records its arguments for later. It can therefore be called before or -after ``ffibuilder.cdef()``. - -In **ABI mode,** you call ``ffibuilder.set_source(module_name, None)``. The -argument is the name (or dotted name inside a package) of the Python -module to generate. In this mode, no C compiler is called. - -In **API mode,** the ``c_header_source`` argument is a string that -will be pasted into the .c file generated. Typically, it is specified as -``r""" ...multiple lines of C code... """`` (the ``r`` prefix allows these -lines to contain a literal ``\n``, for example). This piece of C code -typically contains some ``#include``, but may also contain more, -like definitions for custom "wrapper" C functions. The goal is that -the .c file can be generated like this:: - - // C file "module_name.c" - #include <Python.h> - - ...c_header_source... - - ...magic code... - -where the "magic code" is automatically generated from the ``cdef()``. -For example, if the ``cdef()`` contains ``int foo(int x);`` then the -magic code will contain logic to call the function ``foo()`` with an -integer argument, itself wrapped inside some CPython or PyPy-specific -code. - -The keywords arguments to ``set_source()`` control how the C compiler -will be called. They are passed directly to distutils_ or setuptools_ -and include at least ``sources``, ``include_dirs``, ``define_macros``, -``undef_macros``, ``libraries``, ``library_dirs``, ``extra_objects``, -``extra_compile_args`` and ``extra_link_args``. You typically need at -least ``libraries=['foo']`` in order to link with ``libfoo.so`` or -``libfoo.so.X.Y``, or ``foo.dll`` on Windows. The ``sources`` is a -list of extra .c files compiled and linked together (the file -``module_name.c`` shown above is always generated and automatically added as the -first argument to ``sources``). See the distutils documentations for -`more information about the other arguments`__. - -.. __: http://docs.python.org/distutils/setupscript.html#library-options -.. _distutils: http://docs.python.org/distutils/setupscript.html#describing-extension-modules -.. _setuptools: https://pythonhosted.org/setuptools/setuptools.html - -An extra keyword argument processed internally is -``source_extension``, defaulting to ``".c"``. The file generated will -be actually called ``module_name + source_extension``. Example for -C++ (but note that there are still a few known issues of C-versus-C++ -compatibility): - -.. code-block:: python - - ffibuilder.set_source("mymodule", r''' - extern "C" { - int somefunc(int somearg) { return real_cpp_func(somearg); } - } - ''', source_extension='.cpp') - -.. _pkgconfig: - -**ffibuilder.set_source_pkgconfig(module_name, pkgconfig_libs, -c_header_source, [\*\*keywords...])**: - -*New in version 1.12.* This is equivalent to ``set_source()`` but it -first calls the system utility ``pkg-config`` with the package names -given in the list ``pkgconfig_libs``. It collects the information -obtained in this way and adds it to the explicitly-provided -``**keywords`` (if any). This should probably not be used on Windows. - -If the ``pkg-config`` program is not installed or does not know about -the requested library, the call fails with ``cffi.PkgConfigError``. If -necessary, you can catch this error and try to call ``set_source()`` -directly. (Ideally, you should also do that if the ``ffibuilder`` -instance has no method ``set_source_pkgconfig()``, to support older -versions of cffi.) - - -Letting the C compiler fill the gaps ------------------------------------- - -If you are using a C compiler ("API mode"), then: - -* functions taking or returning integer or float-point arguments can be - misdeclared: if e.g. a function is declared by ``cdef()`` as taking a - ``int``, but actually takes a ``long``, then the C compiler handles the - difference. - -* other arguments are checked: you get a compilation warning or error - if you pass a ``int *`` argument to a function expecting a ``long *``. - -* similarly, most other things declared in the ``cdef()`` are checked, - to the best we implemented so far; mistakes give compilation - warnings or errors. - -Moreover, you can use "``...``" (literally, dot-dot-dot) in the -``cdef()`` at various places, in order to ask the C compiler to fill -in the details. These places are: - -* structure declarations: any ``struct { }`` or ``union { }`` that ends - with "``...;``" as the last "field" is partial: it may be missing - fields, have them declared out of order, use non-standard alignment, - etc. Precisely, the field offsets, total struct size, and total - struct alignment deduced by looking at the ``cdef`` are not relied - upon and will instead be corrected by the compiler. (But note that you - can only access fields that you declared, not others.) Any ``struct`` - declaration which doesn't use "``...``" is assumed to be exact, but this is - checked: you get an error if it is not correct. - -* integer types: the syntax "``typedef - int... foo_t;``" declares the type ``foo_t`` as an integer type - whose exact size and signedness is not specified. The compiler will - figure it out. (Note that this requires ``set_source()``; it does - not work with ``verify()``.) The ``int...`` can be replaced with - ``long...`` or ``unsigned long long...`` or any other primitive - integer type, with no effect. The type will always map to one of - ``(u)int(8,16,32,64)_t`` in Python, but in the generated C code, - only ``foo_t`` is used. - -* *New in version 1.3:* floating-point types: "``typedef - float... foo_t;``" (or equivalently "``typedef double... foo_t;``") - declares ``foo_t`` as a-float-or-a-double; the compiler will figure - out which it is. Note that if the actual C type is even larger - (``long double`` on some platforms), then compilation will fail. - The problem is that the Python "float" type cannot be used to store - the extra precision. (Use the non-dot-dot-dot syntax ``typedef long - double foo_t;`` as usual, which returns values that are not Python - floats at all but cdata "long double" objects.) - -* unknown types: the syntax "``typedef ... foo_t;``" declares the type - ``foo_t`` as opaque. Useful mainly for when the API takes and returns - ``foo_t *`` without you needing to look inside the ``foo_t``. Also - works with "``typedef ... *foo_p;``" which declares the pointer type - ``foo_p`` without giving a name to the opaque type itself. Note that - such an opaque struct has no known size, which prevents some operations - from working (mostly like in C). *You cannot use this syntax to - declare a specific type, like an integer type! It declares opaque - struct-like types only.* In some cases you need to say that - ``foo_t`` is not opaque, but just a struct where you don't know any - field; then you would use "``typedef struct { ...; } foo_t;``". - -* array lengths: when used as structure fields or in global variables, - arrays can have an unspecified length, as in "``extern int n[...];``". The - length is completed by the C compiler. - This is slightly different from "``extern int n[];``", because the latter - means that the length is not known even to the C compiler, and thus - no attempt is made to complete it. This supports - multidimensional arrays: "``extern int n[...][...];``". - - *New in version 1.2:* "``extern int m[][...];``", i.e. ``...`` can be used - in the innermost dimensions without being also used in the outermost - dimension. In the example given, the length of the ``m`` array is - assumed not to be known to the C compiler, but the length of every - item (like the sub-array ``m[0]``) is always known the C compiler. - In other words, only the outermost dimension can be specified as - ``[]``, both in C and in CFFI, but any dimension can be given as - ``[...]`` in CFFI. - -* enums: if you don't know the exact order (or values) of the declared - constants, then use this syntax: "``enum foo { A, B, C, ... };``" - (with a trailing "``...``"). The C compiler will be used to figure - out the exact values of the constants. An alternative syntax is - "``enum foo { A=..., B, C };``" or even - "``enum foo { A=..., B=..., C=... };``". Like - with structs, an ``enum`` without "``...``" is assumed to - be exact, and this is checked. - -* integer constants and macros: you can write in the ``cdef`` the line - "``#define FOO ...``", with any macro name FOO but with ``...`` as - a value. Provided the macro - is defined to be an integer value, this value will be available via - an attribute of the library object. The - same effect can be achieved by writing a declaration - ``static const int FOO;``. The latter is more general because it - supports other types than integer types (note: the C syntax is then - to write the ``const`` together with the variable name, as in - ``static char *const FOO;``). - -Currently, it is not supported to find automatically which of the -various integer or float types you need at which place---except in the -following case: if such a type is explicitly named. For an integer -type, use ``typedef int... the_type_name;``, or another type like -``typedef unsigned long... the_type_name;``. Both are equivalent and -replaced by the real C type, which must be an integer type. -Similarly, for floating-point types, use ``typedef float... -the_type_name;`` or equivalently ``typedef double... the_type_name;``. -Note that ``long double`` cannot be detected this way. - -In the case of function arguments or return types, when it is a simple -integer/float type, you can simply misdeclare it. If you misdeclare a -function ``void f(long)`` as ``void f(int)``, it still works (but you -have to call it with arguments that fit an int). It works because the C -compiler will do the casting for us. This C-level casting of arguments -and return types only works for regular function, and not for function -pointer types; currently, it also does not work for variadic functions. - -For more complex types, you have no choice but be precise. For example, -you cannot misdeclare a ``int *`` argument as ``long *``, or a global -array ``extern int a[5];`` as ``extern long a[5];``. CFFI considers `all types listed -above`_ as primitive (so ``extern long long a[5];`` and ``extern int64_t a[5]`` are -different declarations). The reason for that is detailed in `a comment -about an issue.`__ - -.. __: https://foss.heptapod.net/pypy/cffi/-/issues/265#note_50393 - - -ffibuilder.compile() etc.: compiling out-of-line modules --------------------------------------------------------- - -You can use one of the following functions to actually generate the -.py or .c file prepared with ``ffibuilder.set_source()`` and -``ffibuilder.cdef()``. - -Note that these function won't overwrite a .py/.c file with exactly -the same content, to preserve the mtime. In some cases where you need -the mtime to be updated anyway, delete the file before calling the -functions. - -*New in version 1.8:* the C code produced by ``emit_c_code()`` or -``compile()`` contains ``#define Py_LIMITED_API``. This means that on -CPython >= 3.2, compiling this source produces a binary .so/.dll that -should work for any version of CPython >= 3.2 (as opposed to only for -the same version of CPython x.y). However, the standard ``distutils`` -package will still produce a file called e.g. -``NAME.cpython-35m-x86_64-linux-gnu.so``. You can manually rename it to -``NAME.abi3.so``, or use setuptools version 26 or later. Also, note -that compiling with a debug version of Python will not actually define -``Py_LIMITED_API``, as doing so makes ``Python.h`` unhappy. - -*New in version 1.12:* ``Py_LIMITED_API`` is now defined on Windows too. -If you use ``virtualenv``, you need a recent version of it: versions -older than 16.0.0 forgot to copy ``python3.dll`` into the virtual -environment. In case upgrading ``virtualenv`` is a real problem, you -can manually edit the C code to remove the first line ``# define -Py_LIMITED_API``. - -**ffibuilder.compile(tmpdir='.', verbose=False, debug=None):** -explicitly generate the .py or .c file, -and (if .c) compile it. The output file is (or are) put in the -directory given by ``tmpdir``. In the examples given here, we use -``if __name__ == "__main__": ffibuilder.compile()`` in the build scripts---if -they are directly executed, this makes them rebuild the .py/.c file in -the current directory. (Note: if a package is specified in the call -to ``set_source()``, then a corresponding subdirectory of the ``tmpdir`` -is used.) - -*New in version 1.4:* ``verbose`` argument. If True, it prints the -usual distutils output, including the command lines that call the -compiler. (This parameter might be changed to True by default in a -future release.) - -*New in version 1.8.1:* ``debug`` argument. If set to a bool, it -controls whether the C code is compiled in debug mode or not. The -default None means to use the host Python's ``sys.flags.debug``. -Starting with version 1.8.1, if you are running a debug-mode Python, the -C code is thus compiled in debug mode by default (note that it is anyway -necessary to do so on Windows). - -**ffibuilder.emit_python_code(filename):** generate the given .py file (same -as ``ffibuilder.compile()`` for ABI mode, with an explicitly-named file to -write). If you choose, you can include this .py file pre-packaged in -your own distributions: it is identical for any Python version (2 or -3). - -**ffibuilder.emit_c_code(filename):** generate the given .c file (for API -mode) without compiling it. Can be used if you have some other method -to compile it, e.g. if you want to integrate with some larger build -system that will compile this file for you. You can also distribute -the .c file: unless the build script you used depends on the OS or -platform, the .c file itself is generic (it would be exactly the same -if produced on a different OS, with a different version of CPython, or -with PyPy; it is done with generating the appropriate ``#ifdef``). - -**ffibuilder.distutils_extension(tmpdir='build', verbose=True):** for -distutils-based ``setup.py`` files. Calling this creates the .c file -if needed in the given ``tmpdir``, and returns a -``distutils.core.Extension`` instance. - -For Setuptools, you use instead the line -``cffi_modules=["path/to/foo_build.py:ffibuilder"]`` in ``setup.py``. This -line asks Setuptools to import and use a helper provided by CFFI, -which in turn executes the file ``path/to/foo_build.py`` (as with -``execfile()``) and looks up its global variable called ``ffibuilder``. You -can also say ``cffi_modules=["path/to/foo_build.py:maker"]``, where -``maker`` names a global function; it is called with no argument and -is supposed to return a ``FFI`` object. - - -ffi/ffibuilder.include(): combining multiple CFFI interfaces ------------------------------------------------------------- - -**ffi/ffibuilder.include(other_ffi)**: includes the typedefs, structs, unions, -enums and constants defined in another FFI instance. This is meant -for large projects where one CFFI-based interface depends on some -types declared in a different CFFI-based interface. - -*Note that you should only use one ffi object per library; the intended -usage of ffi.include() is if you want to interface with several -inter-dependent libraries.* For only one library, make one ``ffi`` -object. (You can write several ``cdef()`` calls over the same ``ffi`` -from several Python files, if one file would be too large.) - -For out-of-line modules, the ``ffibuilder.include(other_ffibuilder)`` -line should -occur in the build script, and the ``other_ffibuilder`` argument should be -another FFI instance that comes from another build script. When the two build -scripts are turned into generated files, say ``_ffi.so`` and -``_other_ffi.so``, then importing ``_ffi.so`` will internally cause -``_other_ffi.so`` to be imported. At that point, the real -declarations from ``_other_ffi.so`` are combined with the real -declarations from ``_ffi.so``. - -The usage of ``ffi.include()`` is the cdef-level equivalent of a -``#include`` in C, where a part of the program might include types and -functions defined in another part for its own usage. You can see on -the ``ffi`` object (and associated ``lib`` objects on the *including* -side) the types and constants declared on the included side. In API -mode, you can also see the functions and global variables directly. -In ABI mode, these must be accessed via the original ``other_lib`` -object returned by the ``dlopen()`` method on ``other_ffi``. - - -ffi.cdef() limitations ----------------------- - -All of the ANSI C *declarations* should be supported in ``cdef()``, -and some of C99. (This excludes any ``#include`` or ``#ifdef``.) -Known missing features that are either in C99, or are GCC or MSVC -extensions: - -* Any ``__attribute__`` or ``#pragma pack(n)`` - -* Additional types: special-size floating and fixed - point types, vector types, and so on. - -* The C99 types ``float _Complex`` and ``double _Complex`` are supported - by cffi since version 1.11, but not libffi: you cannot call C - functions with complex arguments or return value, except if they are - directly API-mode functions. The type ``long double _Complex`` is not - supported at all (declare and use it as if it were an array of two - ``long double``, and write wrapper functions in C with set_source()). - -* ``__restrict__`` or ``__restrict`` are extensions of, respectively, - GCC and MSVC. They are not recognized. But ``restrict`` is a C - keyword and is accepted (and ignored). - -Note that declarations like ``int field[];`` in -structures are interpreted as variable-length structures. Declarations -like ``int field[...];`` on the other hand are arrays whose length is -going to be completed by the compiler. You can use ``int field[];`` -for array fields that are not, in fact, variable-length; it works too, -but in this case, as CFFI -believes it cannot ask the C compiler for the length of the array, you -get reduced safety checks: for example, you risk overwriting the -following fields by passing too many array items in the constructor. - -*New in version 1.2:* -Thread-local variables (``__thread``) can be accessed, as well as -variables defined as dynamic macros (``#define myvar (*fetchme())``). -Before version 1.2, you need to write getter/setter functions. - -Note that if you declare a variable in ``cdef()`` without using -``const``, CFFI assumes it is a read-write variable and generates two -pieces of code, one to read it and one to write it. If the variable -cannot in fact be written to in C code, for one reason or another, it -will not compile. In this case, you can declare it as a constant: for -example, instead of ``foo_t *myglob;`` you would use ``foo_t *const -myglob;``. Note also that ``const foo_t *myglob;`` is a *variable;* it -contains a variable pointer to a constant ``foo_t``. - - -Debugging dlopen'ed C libraries -------------------------------- - -A few C libraries are actually hard to use correctly in a ``dlopen()`` -setting. This is because most C libraries are intended for, and tested -with, a situation where they are *linked* with another program, using -either static linking or dynamic linking --- but from a program written -in C, at start-up, using the linker's capabilities instead of -``dlopen()``. - -This can occasionally create issues. You would have the same issues in -another setting than CFFI, like with ``ctypes`` or even plain C code that -calls ``dlopen()``. This section contains a few generally useful -environment variables (on Linux) that can help when debugging these -issues. - -**export LD_TRACE_LOADED_OBJECTS=all** - - provides a lot of information, sometimes too much depending on the - setting. Output verbose debugging information about the dynamic - linker. If set to ``all`` prints all debugging information it has, if - set to ``help`` prints a help message about which categories can be - specified in this environment variable - -**export LD_VERBOSE=1** - - (glibc since 2.1) If set to a nonempty string, output symbol - versioning information about the program if querying information - about the program (i.e., either ``LD_TRACE_LOADED_OBJECTS`` has been set, - or ``--list`` or ``--verify`` options have been given to the dynamic - linker). - -**export LD_WARN=1** - - (ELF only)(glibc since 2.1.3) If set to a nonempty string, warn - about unresolved symbols. - - -ffi.verify(): in-line API-mode ------------------------------- - -**ffi.verify()** is supported for backward compatibility, but is -deprecated. ``ffi.verify(c_header_source, tmpdir=.., ext_package=.., -modulename=.., flags=.., **kwargs)`` makes and compiles a C file from -the ``ffi.cdef()``, like ``ffi.set_source()`` in API mode, and then -immediately loads and returns the dynamic library object. Some -non-trivial logic is used to decide if the dynamic library must be -recompiled or not; see below for ways to control it. - -The ``c_header_source`` and the extra keyword arguments have the -same meaning as in ``ffi.set_source()``. - -One remaining use case for ``ffi.verify()`` would be the following -hack to find explicitly the size of any type, in bytes, and have it -available in Python immediately (e.g. because it is needed in order to -write the rest of the build script): - -.. code-block:: python - - ffi = cffi.FFI() - ffi.cdef("const int mysize;") - lib = ffi.verify("const int mysize = sizeof(THE_TYPE);") - print lib.mysize - -Extra arguments to ``ffi.verify()``: - -* ``tmpdir`` controls where the C - files are created and compiled. Unless the ``CFFI_TMPDIR`` environment - variable is set, the default is - ``directory_containing_the_py_file/__pycache__`` using the - directory name of the .py file that contains the actual call to - ``ffi.verify()``. (This is a bit of a hack but is generally - consistent with the location of the .pyc files for your library. - The name ``__pycache__`` itself comes from Python 3.) - -* ``ext_package`` controls in which package the - compiled extension module should be looked from. This is - only useful after distributing ffi.verify()-based modules. - -* The ``tag`` argument gives an extra string inserted in the - middle of the extension module's name: ``_cffi_<tag>_<hash>``. - Useful to give a bit more context, e.g. when debugging. - -* The ``modulename`` argument can be used to force a specific module - name, overriding the name ``_cffi_<tag>_<hash>``. Use with care, - e.g. if you are passing variable information to ``verify()`` but - still want the module name to be always the same (e.g. absolute - paths to local files). In this case, no hash is computed and if - the module name already exists it will be reused without further - check. Be sure to have other means of clearing the ``tmpdir`` - whenever you change your sources. - -* ``source_extension`` has the same meaning as in ``ffibuilder.set_source()``. - -* The optional ``flags`` argument (ignored on Windows) defaults to - ``ffi.RTLD_NOW``; see ``man dlopen``. (With - ``ffibuilder.set_source()``, you would use ``sys.setdlopenflags()``.) - -* The optional ``relative_to`` argument is useful if you need to list - local files passed to the C compiler:: - - ext = ffi.verify(..., sources=['foo.c'], relative_to=__file__) - - The line above is roughly the same as:: - - ext = ffi.verify(..., sources=['/path/to/this/file/foo.c']) - - except that the default name of the produced library is built from - the CRC checkum of the argument ``sources``, as well as most other - arguments you give to ``ffi.verify()`` -- but not ``relative_to``. - So if you used the second line, it would stop finding the - already-compiled library after your project is installed, because - the ``'/path/to/this/file'`` suddenly changed. The first line does - not have this problem. - -Note that during development, every time you change the C sources that -you pass to ``cdef()`` or ``verify()``, then the latter will create a -new module file name, based on two CRC32 hashes computed from these -strings. This creates more and more files in the ``__pycache__`` -directory. It is recommended that you clean it up from time to time. -A nice way to do that is to add, in your test suite, a call to -``cffi.verifier.cleanup_tmpdir()``. Alternatively, you can manually -remove the whole ``__pycache__`` directory. - -An alternative cache directory can be given as the ``tmpdir`` argument -to ``verify()``, via the environment variable ``CFFI_TMPDIR``, or by -calling ``cffi.verifier.set_tmpdir(path)`` prior to calling -``verify``. - - -Upgrading from CFFI 0.9 to CFFI 1.0 ------------------------------------ - -CFFI 1.0 is backward-compatible, but it is still a good idea to -consider moving to the out-of-line approach new in 1.0. Here are the -steps. - -**ABI mode** if your CFFI project uses ``ffi.dlopen()``: - -.. code-block:: python - - import cffi - - ffi = cffi.FFI() - ffi.cdef("stuff") - lib = ffi.dlopen("libpath") - -and *if* the "stuff" part is big enough that import time is a concern, -then rewrite it as described in `the out-of-line but still ABI mode`__ -above. Optionally, see also the `setuptools integration`__ paragraph. - -.. __: out-of-line-abi_ -.. __: distutils-setuptools_ - - -**API mode** if your CFFI project uses ``ffi.verify()``: - -.. code-block:: python - - import cffi - - ffi = cffi.FFI() - ffi.cdef("stuff") - lib = ffi.verify("real C code") - -then you should really rewrite it as described in `the out-of-line, -API mode`__ above. It avoids a number of issues that have caused -``ffi.verify()`` to grow a number of extra arguments over time. Then -see the `distutils or setuptools`__ paragraph. Also, remember to -remove the ``ext_package=".."`` from your ``setup.py``, which was -sometimes needed with ``verify()`` but is just creating confusion with -``set_source()``. - -.. __: out-of-line-api_ -.. __: distutils-setuptools_ - -The following example should work both with old (pre-1.0) and new -versions of CFFI---supporting both is important to run on old -versions of PyPy (CFFI 1.0 does not work in PyPy < 2.6): - -.. code-block:: python - - # in a separate file "package/foo_build.py" - import cffi - - ffi = cffi.FFI() - C_HEADER_SRC = r''' - #include "somelib.h" - ''' - C_KEYWORDS = dict(libraries=['somelib']) - - if hasattr(ffi, 'set_source'): - ffi.set_source("package._foo", C_HEADER_SRC, **C_KEYWORDS) - - ffi.cdef(''' - int foo(int); - ''') - - if __name__ == "__main__": - ffi.compile() - -And in the main program: - -.. code-block:: python - - try: - from package._foo import ffi, lib - except ImportError: - from package.foo_build import ffi, C_HEADER_SRC, C_KEYWORDS - lib = ffi.verify(C_HEADER_SRC, **C_KEYWORDS) - -(FWIW, this latest trick can be used more generally to allow the -import to "work" even if the ``_foo`` module was not generated.) - -Writing a ``setup.py`` script that works both with CFFI 0.9 and 1.0 -requires explicitly checking the version of CFFI that we can have---it -is hard-coded as a built-in module in PyPy: - -.. code-block:: python - - if '_cffi_backend' in sys.builtin_module_names: # PyPy - import _cffi_backend - requires_cffi = "cffi==" + _cffi_backend.__version__ - else: - requires_cffi = "cffi>=1.0.0" - -Then we use the ``requires_cffi`` variable to give different arguments to -``setup()`` as needed, e.g.: - -.. code-block:: python - - if requires_cffi.startswith("cffi==0."): - # backward compatibility: we have "cffi==0.*" - from package.foo_build import ffi - extra_args = dict( - ext_modules=[ffi.verifier.get_extension()], - ext_package="...", # if needed - ) - else: - extra_args = dict( - setup_requires=[requires_cffi], - cffi_modules=['package/foo_build.py:ffi'], - ) - setup( - name=..., - ..., - install_requires=[requires_cffi], - **extra_args - ) diff --git a/doc/source/conf.py b/doc/source/conf.py deleted file mode 100644 index 33e8c11..0000000 --- a/doc/source/conf.py +++ /dev/null @@ -1,194 +0,0 @@ -# -*- coding: utf-8 -*- -# -# CFFI documentation build configuration file, created by -# sphinx-quickstart on Thu Jun 14 16:37:47 2012. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sys, os - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.append(os.path.abspath('.')) - -# -- General configuration ----------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'CFFI' -copyright = u'2012-2018, Armin Rigo, Maciej Fijalkowski' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '1.15' -# The full version, including alpha/beta/rc tags. -release = '1.15.0' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of documents that shouldn't be included in the build. -#unused_docs = [] - -# List of directories, relative to source directory, that shouldn't be searched -# for source files. -exclude_trees = [] - -# The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - - -# -- Options for HTML output --------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. Major themes that come with -# Sphinx are currently 'default' and 'sphinxdoc'. -#html_theme = 'default' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# "<project> v<release> documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -#html_static_path = ['_static'] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_use_modindex = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a <link> tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = '' - -# Output file base name for HTML help builder. -htmlhelp_basename = 'CFFIdoc' - - -# -- Options for LaTeX output -------------------------------------------------- - -# The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' - -# The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]). -latex_documents = [ - ('index', 'CFFI.tex', u'CFFI Documentation', - u'Armin Rigo, Maciej Fijalkowski', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# Additional stuff for the LaTeX preamble. -#latex_preamble = '' - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_use_modindex = True diff --git a/doc/source/embedding.rst b/doc/source/embedding.rst deleted file mode 100644 index 8020f21..0000000 --- a/doc/source/embedding.rst +++ /dev/null @@ -1,531 +0,0 @@ -================================ -Using CFFI for embedding -================================ - -.. contents:: - -You can use CFFI to generate C code which exports the API of your choice -to any C application that wants to link with this C code. This API, -which you define yourself, ends up as the API of a ``.so/.dll/.dylib`` -library---or you can statically link it within a larger application. - -Possible use cases: - -* Exposing a library written in Python directly to C/C++ programs. - -* Using Python to make a "plug-in" for an existing C/C++ program that is - already written to load them. - -* Using Python to implement part of a larger C/C++ application (with - static linking). - -* Writing a small C/C++ wrapper around Python, hiding the fact that the - application is actually written in Python (to make a custom - command-line interface; for distribution purposes; or simply to make - it a bit harder to reverse-engineer the application). - -The general idea is as follows: - -* You write and execute a Python script, which produces a ``.c`` file - with the API of your choice (and optionally compile it into a - ``.so/.dll/.dylib``). The script also gives some Python code to be - "frozen" inside the ``.so``. - -* At runtime, the C application loads this ``.so/.dll/.dylib`` (or is - statically linked with the ``.c`` source) without having to know that - it was produced from Python and CFFI. - -* The first time a C function is called, Python is initialized and - the frozen Python code is executed. - -* The frozen Python code defines more Python functions that implement the - C functions of your API, which are then used for all subsequent C - function calls. - -One of the goals of this approach is to be entirely independent from -the CPython C API: no ``Py_Initialize()`` nor ``PyRun_SimpleString()`` -nor even ``PyObject``. It works identically on CPython and PyPy. - -This is entirely *new in version 1.5.* (PyPy contains CFFI 1.5 since -release 5.0.) - - -Usage ------ - -.. __: overview.html#embedding - -See the `paragraph in the overview page`__ for a quick introduction. -In this section, we explain every step in more details. We will use -here this slightly expanded example: - -.. code-block:: c - - /* file plugin.h */ - typedef struct { int x, y; } point_t; - extern int do_stuff(point_t *); - -.. code-block:: c - - /* file plugin.h, Windows-friendly version */ - typedef struct { int x, y; } point_t; - - /* When including this file from ffibuilder.set_source(), the - following macro is defined to '__declspec(dllexport)'. When - including this file directly from your C program, we define - it to 'extern __declspec(dllimport)' instead. - - With non-MSVC compilers we simply define it to 'extern'. - (The 'extern' is needed for sharing global variables; - functions would be fine without it. The macros always - include 'extern': you must not repeat it when using the - macros later.) - */ - #ifndef CFFI_DLLEXPORT - # if defined(_MSC_VER) - # define CFFI_DLLEXPORT extern __declspec(dllimport) - # else - # define CFFI_DLLEXPORT extern - # endif - #endif - - CFFI_DLLEXPORT int do_stuff(point_t *); - -.. code-block:: python - - # file plugin_build.py - import cffi - ffibuilder = cffi.FFI() - - with open('plugin.h') as f: - # read plugin.h and pass it to embedding_api(), manually - # removing the '#' directives and the CFFI_DLLEXPORT - data = ''.join([line for line in f if not line.startswith('#')]) - data = data.replace('CFFI_DLLEXPORT', '') - ffibuilder.embedding_api(data) - - ffibuilder.set_source("my_plugin", r''' - #include "plugin.h" - ''') - - ffibuilder.embedding_init_code(""" - from my_plugin import ffi - - @ffi.def_extern() - def do_stuff(p): - print("adding %d and %d" % (p.x, p.y)) - return p.x + p.y - """) - - ffibuilder.compile(target="plugin-1.5.*", verbose=True) - # or: ffibuilder.emit_c_code("my_plugin.c") - -Running the code above produces a *DLL*, i,e, a dynamically-loadable -library. It is a file with the extension ``.dll`` on Windows, -``.dylib`` on Mac OS/X, or ``.so`` on other platforms. As usual, it -is produced by generating some intermediate ``.c`` code and then -calling the regular platform-specific C compiler. See below__ for -some pointers to C-level issues with using the produced library. - -.. __: `Issues about using the .so`_ - -Here are some details about the methods used above: - -* **ffibuilder.embedding_api(source):** parses the given C source, which - declares functions that you want to be exported by the DLL. It can - also declare types, constants and global variables that are part of - the C-level API of your DLL. - - The functions that are found in ``source`` will be automatically - defined in the ``.c`` file: they will contain code that initializes - the Python interpreter the first time any of them is called, - followed by code to call the attached Python function (with - ``@ffi.def_extern()``, see next point). - - The global variables, on the other hand, are not automatically - produced. You have to write their definition explicitly in - ``ffibuilder.set_source()``, as regular C code (see the point after next). - -* **ffibuilder.embedding_init_code(python_code):** this gives - initialization-time Python source code. This code is copied - ("frozen") inside the DLL. At runtime, the code is executed when - the DLL is first initialized, just after Python itself is - initialized. This newly initialized Python interpreter has got an - extra "built-in" module that can be loaded magically without - accessing any files, with a line like "``from my_plugin import ffi, - lib``". The name ``my_plugin`` comes from the first argument to - ``ffibuilder.set_source()``. This module represents "the caller's C world" - from the point of view of Python. - - The initialization-time Python code can import other modules or - packages as usual. You may have typical Python issues like needing - to set up ``sys.path`` somehow manually first. - - For every function declared within ``ffibuilder.embedding_api()``, the - initialization-time Python code or one of the modules it imports - should use the decorator ``@ffi.def_extern()`` to attach a - corresponding Python function to it. - - If the initialization-time Python code fails with an exception, then - you get a traceback printed to stderr, along with more information - to help you identify problems like wrong ``sys.path``. If some - function remains unattached at the time where the C code tries to - call it, an error message is also printed to stderr and the function - returns zero/null. - - Note that the CFFI module never calls ``exit()``, but CPython itself - contains code that calls ``exit()``, for example if importing - ``site`` fails. This may be worked around in the future. - -* **ffibuilder.set_source(c_module_name, c_code):** set the name of the - module from Python's point of view. It also gives more C code which - will be included in the generated C code. In trivial examples it - can be an empty string. It is where you would ``#include`` some - other files, define global variables, and so on. The macro - ``CFFI_DLLEXPORT`` is available to this C code: it expands to the - platform-specific way of saying "the following declaration should be - exported from the DLL". For example, you would put "``extern int - my_glob;``" in ``ffibuilder.embedding_api()`` and "``CFFI_DLLEXPORT int - my_glob = 42;``" in ``ffibuilder.set_source()``. - - Currently, any *type* declared in ``ffibuilder.embedding_api()`` must also - be present in the ``c_code``. This is automatic if this code - contains a line like ``#include "plugin.h"`` in the example above. - -* **ffibuilder.compile([target=...] [, verbose=True]):** make the C code and - compile it. By default, it produces a file called - ``c_module_name.dll``, ``c_module_name.dylib`` or - ``c_module_name.so``, but the default can be changed with the - optional ``target`` keyword argument. You can use - ``target="foo.*"`` with a literal ``*`` to ask for a file called - ``foo.dll`` on Windows, ``foo.dylib`` on OS/X and ``foo.so`` - elsewhere. One reason for specifying an alternate ``target`` is to - include characters not usually allowed in Python module names, like - "``plugin-1.5.*``". - - For more complicated cases, you can call instead - ``ffibuilder.emit_c_code("foo.c")`` and compile the resulting ``foo.c`` - file using other means. CFFI's compilation logic is based on the - standard library ``distutils`` package, which is really developed - and tested for the purpose of making CPython extension modules; it - might not always be appropriate for making general DLLs. Also, just - getting the C code is what you need if you do not want to make a - stand-alone ``.so/.dll/.dylib`` file: this C file can be compiled - and statically linked as part of a larger application. - - -More reading ------------- - -If you're reading this page about embedding and you are not familiar -with CFFI already, here are a few pointers to what you could read -next: - -* For the ``@ffi.def_extern()`` functions, integer C types are passed - simply as Python integers; and simple pointers-to-struct and basic - arrays are all straightforward enough. However, sooner or later you - will need to read about this topic in more details here__. - -* ``@ffi.def_extern()``: see `documentation here,`__ notably on what - happens if the Python function raises an exception. - -* To create Python objects attached to C data, one common solution is - to use ``ffi.new_handle()``. See documentation here__. - -* In embedding mode, the major direction is C code that calls Python - functions. This is the opposite of the regular extending mode of - CFFI, in which the major direction is Python code calling C. That's - why the page `Using the ffi/lib objects`_ talks first about the - latter, and why the direction "C code that calls Python" is - generally referred to as "callbacks" in that page. If you also - need to have your Python code call C code, read more about - `Embedding and Extending`_ below. - -* ``ffibuilder.embedding_api(source)``: follows the same syntax as - ``ffibuilder.cdef()``, `documented here.`__ You can use the "``...``" - syntax as well, although in practice it may be less useful than it - is for ``cdef()``. On the other hand, it is expected that often the - C sources that you need to give to ``ffibuilder.embedding_api()`` would be - exactly the same as the content of some ``.h`` file that you want to - give to users of your DLL. That's why the example above does this:: - - with open('foo.h') as f: - ffibuilder.embedding_api(f.read()) - - Note that a drawback of this approach is that ``ffibuilder.embedding_api()`` - doesn't support ``#ifdef`` directives. You may have to use a more - convoluted expression like:: - - with open('foo.h') as f: - lines = [line for line in f if not line.startswith('#')] - ffibuilder.embedding_api(''.join(lines)) - - As in the example above, you can also use the same ``foo.h`` from - ``ffibuilder.set_source()``:: - - ffibuilder.set_source('module_name', r''' - #include "foo.h" - ''') - - -.. __: using.html#working -.. __: using.html#def-extern -.. __: ref.html#ffi-new-handle -.. __: cdef.html#cdef - -.. _`Using the ffi/lib objects`: using.html - - -Troubleshooting ---------------- - -* The error message - - cffi extension module 'c_module_name' has unknown version 0x2701 - - means that the running Python interpreter located a CFFI version older - than 1.5. CFFI 1.5 or newer must be installed in the running Python. - -* On PyPy, the error message - - debug: pypy_setup_home: directories 'lib-python' and 'lib_pypy' not - found in pypy's shared library location or in any parent directory - - means that the ``libpypy-c.so`` file was found, but the standard library - was not found from this location. This occurs at least on some Linux - distributions, because they put ``libpypy-c.so`` inside ``/usr/lib/``, - instead of the way we recommend, which is: keep that file inside - ``/opt/pypy/bin/`` and put a symlink to there from ``/usr/lib/``. - The quickest fix is to do that change manually. - - -Issues about using the .so --------------------------- - -This paragraph describes issues that are not necessarily specific to -CFFI. It assumes that you have obtained the ``.so/.dylib/.dll`` file as -described above, but that you have troubles using it. (In summary: it -is a mess. This is my own experience, slowly built by using Google and -by listening to reports from various platforms. Please report any -inaccuracies in this paragraph or better ways to do things.) - -* The file produced by CFFI should follow this naming pattern: - ``libmy_plugin.so`` on Linux, ``libmy_plugin.dylib`` on Mac, or - ``my_plugin.dll`` on Windows (no ``lib`` prefix on Windows). - -* First note that this file does not contain the Python interpreter - nor the standard library of Python. You still need it to be - somewhere. There are ways to compact it to a smaller number of files, - but this is outside the scope of CFFI (please report if you used some - of these ways successfully so that I can add some links here). - -* In what we'll call the "main program", the ``.so`` can be either - used dynamically (e.g. by calling ``dlopen()`` or ``LoadLibrary()`` - inside the main program), or at compile-time (e.g. by compiling it - with ``gcc -lmy_plugin``). The former case is always used if you're - building a plugin for a program, and the program itself doesn't need - to be recompiled. The latter case is for making a CFFI library that - is more tightly integrated inside the main program. - -* In the case of compile-time usage: you can add the gcc - option ``-Lsome/path/`` before ``-lmy_plugin`` to describe where the - ``libmy_plugin.so`` is. On some platforms, notably Linux, ``gcc`` - will complain if it can find ``libmy_plugin.so`` but not - ``libpython27.so`` or ``libpypy-c.so``. To fix it, you need to call - ``LD_LIBRARY_PATH=/some/path/to/libpypy gcc``. - -* When actually executing the main program, it needs to find the - ``libmy_plugin.so`` but also ``libpython27.so`` or ``libpypy-c.so``. - For PyPy, unpack a PyPy distribution and you get a full directory - structure with ``libpypy-c.so`` inside a ``bin`` subdirectory, or on - Windows ``pypy-c.dll`` inside the top directory; you must not move - this file around, but just point to it. One way to point to it is by - running the main program with some environment variable: - ``LD_LIBRARY_PATH=/some/path/to/libpypy`` on Linux, - ``DYLD_LIBRARY_PATH=/some/path/to/libpypy`` on OS/X. - -* You can avoid the ``LD_LIBRARY_PATH`` issue if you compile - ``libmy_plugin.so`` with the path hard-coded inside in the first - place. On Linux, this is done by ``gcc -Wl,-rpath=/some/path``. You - would put this option in ``ffibuilder.set_source("my_plugin", ..., - extra_link_args=['-Wl,-rpath=/some/path/to/libpypy'])``. The path can - start with ``$ORIGIN`` to mean "the directory where - ``libmy_plugin.so`` is". You can then specify a path relative to that - place, like ``extra_link_args=['-Wl,-rpath=$ORIGIN/../venv/bin']``. - Use ``ldd libmy_plugin.so`` to look at what path is currently compiled - in after the expansion of ``$ORIGIN``.) - - After this, you don't need ``LD_LIBRARY_PATH`` any more to locate - ``libpython27.so`` or ``libpypy-c.so`` at runtime. In theory it - should also cover the call to ``gcc`` for the main program. I wasn't - able to make ``gcc`` happy without ``LD_LIBRARY_PATH`` on Linux if - the rpath starts with ``$ORIGIN``, though. - -* The same rpath trick might be used to let the main program find - ``libmy_plugin.so`` in the first place without ``LD_LIBRARY_PATH``. - (This doesn't apply if the main program uses ``dlopen()`` to load it - as a dynamic plugin.) You'd make the main program with ``gcc - -Wl,-rpath=/path/to/libmyplugin``, possibly with ``$ORIGIN``. The - ``$`` in ``$ORIGIN`` causes various shell problems on its own: if - using a common shell you need to say ``gcc - -Wl,-rpath=\$ORIGIN``. From a Makefile, you need to say - something like ``gcc -Wl,-rpath=\$$ORIGIN``. - -* On some Linux distributions, notably Debian, the ``.so`` files of - CPython C extension modules may be compiled without saying that they - depend on ``libpythonX.Y.so``. This makes such Python systems - unsuitable for embedding if the embedder uses ``dlopen(..., - RTLD_LOCAL)``. You get an ``undefined symbol`` error. See - `issue #264`__. A workaround is to first call - ``dlopen("libpythonX.Y.so", RTLD_LAZY|RTLD_GLOBAL)``, which will - force ``libpythonX.Y.so`` to be loaded first. - -.. __: https://foss.heptapod.net/pypy/cffi/-/issues/264 - - -Using multiple CFFI-made DLLs ------------------------------ - -Multiple CFFI-made DLLs can be used by the same process. - -Note that all CFFI-made DLLs in a process share a single Python -interpreter. The effect is the same as the one you get by trying to -build a large Python application by assembling a lot of unrelated -packages. Some of these might be libraries that monkey-patch some -functions from the standard library, for example, which might be -unexpected from other parts. - - -Multithreading --------------- - -Multithreading should work transparently, based on Python's standard -Global Interpreter Lock. - -If two threads both try to call a C function when Python is not yet -initialized, then locking occurs. One thread proceeds with -initialization and blocks the other thread. The other thread will be -allowed to continue only when the execution of the initialization-time -Python code is done. - -If the two threads call two *different* CFFI-made DLLs, the Python -initialization itself will still be serialized, but the two pieces of -initialization-time Python code will not. The idea is that there is a -priori no reason for one DLL to wait for initialization of the other -DLL to be complete. - -After initialization, Python's standard Global Interpreter Lock kicks -in. The end result is that when one CPU progresses on executing -Python code, no other CPU can progress on executing more Python code -from another thread of the same process. At regular intervals, the -lock switches to a different thread, so that no single thread should -appear to block indefinitely. - - -Testing -------- - -For testing purposes, a CFFI-made DLL can be imported in a running -Python interpreter instead of being loaded like a C shared library. - -You might have some issues with the file name: for example, on -Windows, Python expects the file to be called ``c_module_name.pyd``, -but the CFFI-made DLL is called ``target.dll`` instead. The base name -``target`` is the one specified in ``ffibuilder.compile()``, and on Windows -the extension is ``.dll`` instead of ``.pyd``. You have to rename or -copy the file, or on POSIX use a symlink. - -The module then works like a regular CFFI extension module. It is -imported with "``from c_module_name import ffi, lib``" and exposes on -the ``lib`` object all C functions. You can test it by calling these -C functions. The initialization-time Python code frozen inside the -DLL is executed the first time such a call is done. - - -Embedding and Extending ------------------------ - -The embedding mode is not incompatible with the non-embedding mode of -CFFI. - -You can use *both* ``ffibuilder.embedding_api()`` and -``ffibuilder.cdef()`` in the -same build script. You put in the former the declarations you want to -be exported by the DLL; you put in the latter only the C functions and -types that you want to share between C and Python, but not export from -the DLL. - -As an example of that, consider the case where you would like to have -a DLL-exported C function written in C directly, maybe to handle some -cases before calling Python functions. To do that, you must *not* put -the function's signature in ``ffibuilder.embedding_api()``. (Note that this -requires more hacks if you use ``ffibuilder.embedding_api(f.read())``.) -You must only write the custom function definition in -``ffibuilder.set_source()``, and prefix it with the macro CFFI_DLLEXPORT: - -.. code-block:: c - - CFFI_DLLEXPORT int myfunc(int a, int b) - { - /* implementation here */ - } - -This function can, if it wants, invoke Python functions using the -general mechanism of "callbacks"---called this way because it is a -call from C to Python, although in this case it is not calling -anything back: - -.. code-block:: python - - ffibuilder.cdef(""" - extern "Python" int mycb(int); - """) - - ffibuilder.set_source("my_plugin", r""" - - static int mycb(int); /* the callback: forward declaration, to make - it accessible from the C code that follows */ - - CFFI_DLLEXPORT int myfunc(int a, int b) - { - int product = a * b; /* some custom C code */ - return mycb(product); - } - """) - -and then the Python initialization code needs to contain the lines: - -.. code-block:: python - - @ffi.def_extern() - def mycb(x): - print "hi, I'm called with x =", x - return x * 10 - -This ``@ffi.def_extern`` is attaching a Python function to the C -callback ``mycb()``, which in this case is not exported from the DLL. -Nevertheless, the automatic initialization of Python occurs when -``mycb()`` is called, if it happens to be the first function called -from C. More precisely, it does not happen when ``myfunc()`` is -called: this is just a C function, with no extra code magically -inserted around it. It only happens when ``myfunc()`` calls -``mycb()``. - -As the above explanation hints, this is how ``ffibuilder.embedding_api()`` -actually implements function calls that directly invoke Python code; -here, we have merely decomposed it explicitly, in order to add some -custom C code in the middle. - -In case you need to force, from C code, Python to be initialized -before the first ``@ffi.def_extern()`` is called, you can do so by -calling the C function ``cffi_start_python()`` with no argument. It -returns an integer, 0 or -1, to tell if the initialization succeeded -or not. Currently there is no way to prevent a failing initialization -from also dumping a traceback and more information to stderr. -Note that the function ``cffi_start_python()`` is static: it must be -called from C source written inside ``ffibuilder.set_source()``. To -call it from somewhere else, you need to make a function (with a -different non-static name) in the ``ffibuilder.set_source()`` that just -calls ``cffi_start_python()``. The reason it is static is to avoid -naming conflicts in case you are ultimately trying to link a large C -program with more than one cffi embedded module in it. diff --git a/doc/source/goals.rst b/doc/source/goals.rst deleted file mode 100644 index df4877c..0000000 --- a/doc/source/goals.rst +++ /dev/null @@ -1,69 +0,0 @@ -Goals ------ - -The interface is based on `LuaJIT's FFI`_, and follows a few principles: - -* The goal is to call C code from Python without learning a 3rd language: - existing alternatives require users to learn domain specific language - (Cython_, SWIG_) or API (ctypes_). The CFFI design requires users to know - only C and Python, minimizing the extra bits of API that need to be learned. - -* Keep all the Python-related logic in Python so that you don't need to - write much C code (unlike `CPython native C extensions`_). - -* The preferred way is to work at the level of the API (Application - Programming Interface): the C compiler is called from the declarations - you write to validate and link to the C language constructs. - Alternatively, it is also possible to work at the ABI level - (Application Binary Interface), the way ctypes_ work. - However, on non-Windows platforms, C libraries typically - have a specified C API but not an ABI (e.g. they may - document a "struct" as having at least these fields, but maybe more). - -* Try to be complete. For now some C99 constructs are not supported, - but all C89 should be, including macros (and including macro "abuses", - which you can `manually wrap`_ in saner-looking C functions). - -* Attempt to support both PyPy and CPython, with a reasonable path - for other Python implementations like IronPython and Jython. - -* Note that this project is **not** about embedding executable C code in - Python, unlike `Weave`_. This is about calling existing C libraries - from Python. - -* There is no C++ support. Sometimes, it is reasonable to write a C - wrapper around the C++ code and then call this C API with CFFI. - Otherwise, look at other projects. I would recommend cppyy_, which - has got some similarities (and also works efficiently on both CPython - and PyPy). - -.. _`LuaJIT's FFI`: http://luajit.org/ext_ffi.html -.. _`Cython`: http://www.cython.org -.. _`SWIG`: http://www.swig.org/ -.. _`CPython native C extensions`: http://docs.python.org/extending/extending.html -.. _`native C extensions`: http://docs.python.org/extending/extending.html -.. _`ctypes`: http://docs.python.org/library/ctypes.html -.. _`Weave`: http://wiki.scipy.org/Weave -.. _`cppyy`: http://cppyy.readthedocs.io/en/latest/ -.. _`manually wrap`: overview.html#abi-versus-api - -Get started by reading `the overview`__. - -.. __: overview.html - - -Comments and bugs ------------------ - -The best way to contact us is on the IRC ``#cffi`` or ``#pypy`` channels of -``irc.libera.chat``. Feel free to discuss matters either there or in -the `mailing list`_. Please report to the `issue tracker`_ any bugs. - -As a general rule, when there is a design issue to resolve, we pick the -solution that is the "most C-like". We hope that this module has got -everything you need to access C code and nothing more. - ---- the authors, Armin Rigo and Maciej Fijalkowski - -.. _`issue tracker`: https://foss.heptapod.net/pypy/cffi/issues -.. _`mailing list`: https://groups.google.com/forum/#!forum/python-cffi diff --git a/doc/source/index.rst b/doc/source/index.rst deleted file mode 100644 index 54934f2..0000000 --- a/doc/source/index.rst +++ /dev/null @@ -1,19 +0,0 @@ -================================ -CFFI documentation -================================ - -C Foreign Function Interface for Python. Interact with almost any C -code from Python, based on C-like declarations that you can often -copy-paste from header files or documentation. - -.. toctree:: - :maxdepth: 2 - - goals - whatsnew - installation - overview - using - ref - cdef - embedding diff --git a/doc/source/installation.rst b/doc/source/installation.rst deleted file mode 100644 index 6d55eb5..0000000 --- a/doc/source/installation.rst +++ /dev/null @@ -1,190 +0,0 @@ -======================================================= -Installation and Status -======================================================= - -Quick installation for CPython (cffi is distributed with PyPy): - -* ``pip install cffi`` - -* or get the source code via the `Python Package Index`__. - -.. __: http://pypi.python.org/pypi/cffi - -In more details: - -This code has been developed on Linux, but should work on any POSIX -platform as well as on Windows 32 and 64. (It relies occasionally on -libffi, so it depends on libffi being bug-free; this may not be fully -the case on some of the more exotic platforms.) - -CFFI supports CPython 2.7, 3.x (tested with 3.6 to 3.9); and is -distributed with PyPy (CFFI 1.0 is distributed with and requires -PyPy 2.6). - -The core speed of CFFI is better than ctypes, with import times being -either lower if you use the post-1.0 features, or much higher if you -don't. The wrapper Python code you typically need to write around the -raw CFFI interface slows things down on CPython, but not unreasonably -so. On PyPy, this wrapper code has a minimal impact thanks to the JIT -compiler. This makes CFFI the recommended way to interface with C -libraries on PyPy. - -Requirements: - -* CPython 2.7 or 3.x, or PyPy (PyPy 2.0 for the earliest - versions of CFFI; or PyPy 2.6 for CFFI 1.0). - -* in some cases you need to be able to compile C extension modules. - On non-Windows platforms, this usually means installing the package - ``python-dev``. Refer to the appropriate docs for your OS. - -* on CPython, on non-Windows platforms, you also need to install - ``libffi-dev`` in order to compile CFFI itself. - -* pycparser >= 2.06: https://github.com/eliben/pycparser (automatically - tracked by ``pip install cffi``). - -* `py.test`_ is needed to run the tests of CFFI itself. - -.. _`py.test`: http://pypi.python.org/pypi/pytest - -Download and Installation: - -* https://pypi.python.org/pypi/cffi - -* Checksums of the "source" package version 1.15.0: - - - MD5: f3a3f26cd3335fc597479c9475da0a0b - - - SHA1: 9c51c29e35510adf7f94542e1f8e05611930b07b - - - SHA256: 920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954 - -* Or grab the most current version from the `Heptapod page`_: - ``hg clone https://foss.heptapod.net/pypy/cffi`` - -* ``python setup.py install`` or ``python setup_base.py install`` - (should work out of the box on Linux or Windows; see below for - `MacOS X`_.) - -* running the tests: ``py.test c/ testing/`` (if you didn't - install cffi yet, you need first ``python setup_base.py build_ext -f - -i``) - -.. _`Heptapod page`: https://foss.heptapod.net/pypy/cffi - -Demos: - -* The `demo`_ directory contains a number of small and large demos - of using ``cffi``. - -* The documentation below might be sketchy on details; for now the - ultimate reference is given by the tests, notably - `testing/cffi1/test_verify1.py`_ and `testing/cffi0/backend_tests.py`_. - -.. _`demo`: https://foss.heptapod.net/pypy/cffi/-/tree/branch/default/demo -.. _`testing/cffi1/test_verify1.py`: https://foss.heptapod.net/pypy/cffi/-/blob/branch/default/testing/cffi1/test_verify1.py -.. _`testing/cffi0/backend_tests.py`: https://foss.heptapod.net/pypy/cffi/-/blob/branch/default/testing/cffi0/backend_tests.py - - -Platform-specific instructions ------------------------------- - -``libffi`` is notoriously messy to install and use --- to the point that -CPython includes its own copy to avoid relying on external packages. -CFFI does the same for Windows, but not for other platforms (which should -have their own working libffi's). -Modern Linuxes work out of the box thanks to ``pkg-config``. Here are some -(user-supplied) instructions for other platforms. - - -MacOS X -+++++++ - -**Homebrew** (Thanks David Griffin for this) - -1) Install homebrew: http://brew.sh - -2) Run the following commands in a terminal - -:: - - brew install pkg-config libffi - PKG_CONFIG_PATH=/usr/local/opt/libffi/lib/pkgconfig pip install cffi - - -Alternatively, **on OS/X 10.6** (Thanks Juraj Sukop for this) - -For building libffi you can use the default install path, but then, in -``setup.py`` you need to change:: - - include_dirs = [] - -to:: - - include_dirs = ['/usr/local/lib/libffi-3.0.11/include'] - -Then running ``python setup.py build`` complains about "fatal error: error writing to -: Broken pipe", which can be fixed by running:: - - ARCHFLAGS="-arch i386 -arch x86_64" python setup.py build - -as described here_. - -.. _here: http://superuser.com/questions/259278/python-2-6-1-pycrypto-2-3-pypi-package-broken-pipe-during-build - - -Windows (32/64-bit) -+++++++++++++++++++ - -Win32 and Win64 work and are tested at least each official release. - -The recommended C compiler compatible with Python 2.7 is this one: -http://www.microsoft.com/en-us/download/details.aspx?id=44266 -There is a known problem with distutils on Python 2.7, as -explained in https://bugs.python.org/issue23246, and the same -problem applies whenever you want to run compile() to build a dll with -this specific compiler suite download. -``import setuptools`` might help, but YMMV - -For Python 3.4 and beyond: -https://www.visualstudio.com/en-us/downloads/visual-studio-2015-ctp-vs - - -Linux and OS/X: UCS2 versus UCS4 -++++++++++++++++++++++++++++++++ - -This is about getting an ImportError about ``_cffi_backend.so`` with a -message like ``Symbol not found: _PyUnicodeUCS2_AsASCIIString``. This -error occurs in Python 2 as soon as you mix "ucs2" and "ucs4" builds of -Python. It means that you are now running a Python compiled with -"ucs4", but the extension module ``_cffi_backend.so`` was compiled by a -different Python: one that was running "ucs2". (If the opposite problem -occurs, you get an error about ``_PyUnicodeUCS4_AsASCIIString`` -instead.) - -If you are using ``pyenv``, then see -https://github.com/yyuu/pyenv/issues/257. - -More generally, the solution that should always work is to download the -sources of CFFI (instead of a prebuilt binary) and make sure that you -build it with the same version of Python than the one that will use it. -For example, with virtualenv: - -* ``virtualenv ~/venv`` - -* ``cd ~/path/to/sources/of/cffi`` - -* ``~/venv/bin/python setup.py build --force`` # forcing a rebuild to - make sure - -* ``~/venv/bin/python setup.py install`` - -This will compile and install CFFI in this virtualenv, using the -Python from this virtualenv. - - -NetBSD -++++++ - -You need to make sure you have an up-to-date version of libffi, which -fixes some bugs. diff --git a/doc/source/overview.rst b/doc/source/overview.rst deleted file mode 100644 index dbc3540..0000000 --- a/doc/source/overview.rst +++ /dev/null @@ -1,651 +0,0 @@ -======================================================= -Overview -======================================================= - -.. contents:: - - -The first section presents a simple working -example of using CFFI to call a C function in a compiled shared object -(DLL) from Python. CFFI is -flexible and covers several other use cases presented in the second -section. The third section shows how to export Python functions -to a Python interpreter embedded in a C or C++ application. The last -two sections delve deeper in the CFFI library. - -Make sure you have `cffi installed`__. - -.. __: installation.html - -.. _out-of-line-api-level: -.. _real-example: - - -Main mode of usage ------------------- - -The main way to use CFFI is as an interface to some already-compiled -shared object which is provided by other means. Imagine that you have a -system-installed shared object called ``piapprox.dll`` (Windows) or -``libpiapprox.so`` (Linux and others) or ``libpiapprox.dylib`` (OS X), -exporting a function ``float pi_approx(int n);`` that computes some -approximation of pi given a number of iterations. You want to call -this function from Python. Note this method works equally well with a -static library ``piapprox.lib`` (Windows) or ``libpiapprox.a``. - -Create the file ``piapprox_build.py``: - -.. code-block:: python - - from cffi import FFI - ffibuilder = FFI() - - # cdef() expects a single string declaring the C types, functions and - # globals needed to use the shared object. It must be in valid C syntax. - ffibuilder.cdef(""" - float pi_approx(int n); - """) - - # set_source() gives the name of the python extension module to - # produce, and some C source code as a string. This C code needs - # to make the declarated functions, types and globals available, - # so it is often just the "#include". - ffibuilder.set_source("_pi_cffi", - """ - #include "pi.h" // the C header of the library - """, - libraries=['piapprox']) # library name, for the linker - - if __name__ == "__main__": - ffibuilder.compile(verbose=True) - -Execute this script. If everything is OK, it should produce -``_pi_cffi.c``, and then invoke the compiler on it. The produced -``_pi_cffi.c`` contains a copy of the string given in :ref:`set_source() <set_source>`, -in this example the ``#include "pi.h"``. Afterwards, it contains glue code -for all the functions, types and globals declared in the :ref:`cdef() <cdef>` above. - -At runtime, you use the extension module like this: - -.. code-block:: python - - from _pi_cffi import ffi, lib - print(lib.pi_approx(5000)) - -That's all! In the rest of this page, we describe some more advanced -examples and other CFFI modes. In particular, there is a complete -example `if you don't have an already-installed C library to call`_. - -For more information about the ``cdef()`` and ``set_source()`` methods -of the ``FFI`` class, see `Preparing and Distributing modules`__. - -.. __: cdef.html - -When your example works, a common alternative to running the build -script manually is to have it run as part of a ``setup.py``. Here is -an example using the Setuptools distribution: - -.. code-block:: python - - from setuptools import setup - - setup( - ... - setup_requires=["cffi>=1.0.0"], - cffi_modules=["piapprox_build:ffibuilder"], # "filename:global" - install_requires=["cffi>=1.0.0"], - ) - - -Other CFFI modes ----------------- - -CFFI can be used in one of four modes: "ABI" versus "API" level, -each with "in-line" or "out-of-line" preparation (or compilation). - -The **ABI mode** accesses libraries at the binary level, whereas the -faster **API mode** accesses them with a C compiler. We explain the -difference in more details below__. - -.. __: `abi-versus-api`_ - -In the **in-line mode,** everything is set up every time you import -your Python code. In the **out-of-line mode,** you have a separate -step of preparation (and possibly C compilation) that produces a -module which your main program can then import. - - -Simple example (ABI level, in-line) -+++++++++++++++++++++++++++++++++++ - -May look familiar to those who have used ctypes_. - -.. code-block:: python - - >>> from cffi import FFI - >>> ffi = FFI() - >>> ffi.cdef(""" - ... int printf(const char *format, ...); // copy-pasted from the man page - ... """) - >>> C = ffi.dlopen(None) # loads the entire C namespace - >>> arg = ffi.new("char[]", b"world") # equivalent to C code: char arg[] = "world"; - >>> C.printf(b"hi there, %s.\n", arg) # call printf - hi there, world. - 17 # this is the return value - >>> - -Note that ``char *`` arguments expect a ``bytes`` object. If you have a -``str`` (or a ``unicode`` on Python 2) you need to encode it explicitly -with ``somestring.encode(myencoding)``. - -*Python 3 on Windows:* :ref:`ffi.dlopen(None) <dlopen>` does not work. This problem -is messy and not really fixable. The problem does not occur if you try -to call a function from a specific DLL that exists on your system: then -you use ``ffi.dlopen("path.dll")``. - -*This example does not call any C compiler. It works in the so-called -ABI mode, which means that it will crash if you call some function or -access some fields of a structure that was slightly misdeclared in the -cdef().* - -If using a C compiler to install your module is an option, it is highly -recommended to use the API mode instead. (It is also faster.) - - -Struct/Array Example (minimal, in-line) -+++++++++++++++++++++++++++++++++++++++ - -.. code-block:: python - - from cffi import FFI - ffi = FFI() - ffi.cdef(""" - typedef struct { - unsigned char r, g, b; - } pixel_t; - """) - image = ffi.new("pixel_t[]", 800*600) - - f = open('data', 'rb') # binary mode -- important - f.readinto(ffi.buffer(image)) - f.close() - - image[100].r = 255 - image[100].g = 192 - image[100].b = 128 - - f = open('data', 'wb') - f.write(ffi.buffer(image)) - f.close() - -This can be used as a more flexible replacement of the struct_ and -array_ modules, and replaces ctypes_. You could also call :ref:`ffi.new("pixel_t[600][800]") <new>` -and get a two-dimensional array. - -.. _struct: http://docs.python.org/library/struct.html -.. _array: http://docs.python.org/library/array.html -.. _ctypes: http://docs.python.org/library/ctypes.html - -*This example does not call any C compiler.* - -This example also admits an out-of-line equivalent. It is similar to -the first example `Main mode of usage`_ above, -but passing ``None`` as the second argument to -:ref:`ffibuilder.set_source() <set_source>`. Then in the main program you write -``from _simple_example import ffi`` and then the same content as the -in-line example above starting from the line ``image = -ffi.new("pixel_t[]", 800*600)``. - - -API Mode, calling the C standard library -++++++++++++++++++++++++++++++++++++++++ - -.. code-block:: python - - # file "example_build.py" - - # Note: we instantiate the same 'cffi.FFI' class as in the previous - # example, but call the result 'ffibuilder' now instead of 'ffi'; - # this is to avoid confusion with the other 'ffi' object you get below - - from cffi import FFI - ffibuilder = FFI() - - ffibuilder.set_source("_example", - r""" // passed to the real C compiler, - // contains implementation of things declared in cdef() - #include <sys/types.h> - #include <pwd.h> - - // We can also define custom wrappers or other functions - // here (this is an example only): - static struct passwd *get_pw_for_root(void) { - return getpwuid(0); - } - """, - libraries=[]) # or a list of libraries to link with - # (more arguments like setup.py's Extension class: - # include_dirs=[..], extra_objects=[..], and so on) - - ffibuilder.cdef(""" - // declarations that are shared between Python and C - struct passwd { - char *pw_name; - ...; // literally dot-dot-dot - }; - struct passwd *getpwuid(int uid); // defined in <pwd.h> - struct passwd *get_pw_for_root(void); // defined in set_source() - """) - - if __name__ == "__main__": - ffibuilder.compile(verbose=True) - -You need to run the ``example_build.py`` script once to generate -"source code" into the file ``_example.c`` and compile this to a -regular C extension module. (CFFI selects either Python or C for the -module to generate based on whether the second argument to -:ref:`set_source() <set_source>` is ``None`` or not.) - -*You need a C compiler for this single step. It produces a file called -e.g. _example.so or _example.pyd. If needed, it can be distributed in -precompiled form like any other extension module.* - -Then, in your main program, you use: - -.. code-block:: python - - from _example import ffi, lib - - p = lib.getpwuid(0) - assert ffi.string(p.pw_name) == b'root' - p = lib.get_pw_for_root() - assert ffi.string(p.pw_name) == b'root' - -Note that this works independently of the exact C layout of ``struct -passwd`` (it is "API level", as opposed to "ABI level"). It requires -a C compiler in order to run ``example_build.py``, but it is much more -portable than trying to get the details of the fields of ``struct -passwd`` exactly right. Similarly, in the :ref:`cdef() <cdef>` we declared -``getpwuid()`` as taking an ``int`` argument; on some platforms this -might be slightly incorrect---but it does not matter. - -Note also that at runtime, the API mode is faster than the ABI mode. - -To integrate it inside a ``setup.py`` distribution with Setuptools: - -.. code-block:: python - - from setuptools import setup - - setup( - ... - setup_requires=["cffi>=1.0.0"], - cffi_modules=["example_build.py:ffibuilder"], - install_requires=["cffi>=1.0.0"], - ) - - -.. _`if you don't have an already-installed C library to call`: - -API Mode, calling C sources instead of a compiled library -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -If you want to call some library that is not precompiled, but for which -you have C sources, then the easiest solution is to make a single -extension module that is compiled from both the C sources of this -library, and the additional CFFI wrappers. For example, say you start -with the files ``pi.c`` and ``pi.h``: - - .. code-block:: C - - /* filename: pi.c*/ - # include <stdlib.h> - # include <math.h> - - /* Returns a very crude approximation of Pi - given a int: a number of iteration */ - float pi_approx(int n){ - - double i,x,y,sum=0; - - for(i=0;i<n;i++){ - - x=rand(); - y=rand(); - - if (sqrt(x*x+y*y) < sqrt((double)RAND_MAX*RAND_MAX)) - sum++; } - - return 4*(float)sum/(float)n; } - - .. code-block:: C - - /* filename: pi.h*/ - float pi_approx(int n); - -Create a script named ``pi_extension_build.py``, building -the C extension: - - .. code-block:: python - - from cffi import FFI - ffibuilder = FFI() - - ffibuilder.cdef("float pi_approx(int n);") - - ffibuilder.set_source("_pi", # name of the output C extension - """ - #include "pi.h" - """, - sources=['pi.c'], # includes pi.c as additional sources - libraries=['m']) # on Unix, link with the math library - - if __name__ == "__main__": - ffibuilder.compile(verbose=True) - -Build the extension: - - .. code-block:: shell - - python pi_extension_build.py - -Observe, in the working directory, the generated output files: -``_pi.c``, ``_pi.o`` and the compiled C extension (called ``_pi.so`` on -Linux for example). It can be called from Python: - - .. code-block:: python - - from _pi.lib import pi_approx - - approx = pi_approx(10) - assert str(approx).startswith("3.") - - approx = pi_approx(10000) - assert str(approx).startswith("3.1") - - -.. _performance: - -Purely for performance (API level, out-of-line) -+++++++++++++++++++++++++++++++++++++++++++++++ - -A variant of the `section above`__ where the goal is not to call an -existing C library, but to compile and call some C function written -directly in the build script: - -.. __: real-example_ - -.. code-block:: python - - # file "example_build.py" - - from cffi import FFI - ffibuilder = FFI() - - ffibuilder.cdef("int foo(int *, int *, int);") - - ffibuilder.set_source("_example", - r""" - static int foo(int *buffer_in, int *buffer_out, int x) - { - /* some algorithm that is seriously faster in C than in Python */ - } - """) - - if __name__ == "__main__": - ffibuilder.compile(verbose=True) - -.. code-block:: python - - # file "example.py" - - from _example import ffi, lib - - buffer_in = ffi.new("int[]", 1000) - # initialize buffer_in here... - - # easier to do all buffer allocations in Python and pass them to C, - # even for output-only arguments - buffer_out = ffi.new("int[]", 1000) - - result = lib.foo(buffer_in, buffer_out, 1000) - -*You need a C compiler to run example_build.py, once. It produces a -file called e.g. _example.so or _example.pyd. If needed, it can be -distributed in precompiled form like any other extension module.* - - -.. _out-of-line-abi-level: - -Out-of-line, ABI level -++++++++++++++++++++++ - -The out-of-line ABI mode is a mixture of the regular (API) out-of-line -mode and the in-line ABI mode. It lets you use the ABI mode, with its -advantages (not requiring a C compiler) and problems (crashes more -easily). - -This mixture mode lets you massively reduces the import times, because -it is slow to parse a large C header. It also allows you to do more -detailed checkings during build-time without worrying about performance -(e.g. calling :ref:`cdef() <cdef>` many times with small pieces of declarations, -based on the version of libraries detected on the system). - -.. code-block:: python - - # file "simple_example_build.py" - - from cffi import FFI - - ffibuilder = FFI() - # Note that the actual source is None - ffibuilder.set_source("_simple_example", None) - ffibuilder.cdef(""" - int printf(const char *format, ...); - """) - - if __name__ == "__main__": - ffibuilder.compile(verbose=True) - -Running it once produces ``_simple_example.py``. Your main program -only imports this generated module, not ``simple_example_build.py`` -any more: - -.. code-block:: python - - from _simple_example import ffi - - lib = ffi.dlopen(None) # Unix: open the standard C library - #import ctypes.util # or, try this on Windows: - #lib = ffi.dlopen(ctypes.util.find_library("c")) - - lib.printf(b"hi there, number %d\n", ffi.cast("int", 2)) - -Note that this :ref:`ffi.dlopen() <dlopen>`, unlike the one from in-line mode, -does not invoke any additional magic to locate the library: it must be -a path name (with or without a directory), as required by the C -``dlopen()`` or ``LoadLibrary()`` functions. This means that -``ffi.dlopen("libfoo.so")`` is ok, but ``ffi.dlopen("foo")`` is not. -In the latter case, you could replace it with -``ffi.dlopen(ctypes.util.find_library("foo"))``. Also, None is only -recognized on Unix to open the standard C library. - -For distribution purposes, remember that there is a new -``_simple_example.py`` file generated. You can either include it -statically within your project's source files, or, with Setuptools, -you can say in the ``setup.py``: - -.. code-block:: python - - from setuptools import setup - - setup( - ... - setup_requires=["cffi>=1.0.0"], - cffi_modules=["simple_example_build.py:ffibuilder"], - install_requires=["cffi>=1.0.0"], - ) - -In summary, this mode is useful when you wish to declare many C structures but -do not need fast interaction with a shared object. It is useful for parsing -binary files, for instance. - - -In-line, API level -++++++++++++++++++ - -The "API level + in-line" mode combination exists but is long -deprecated. It used to be done with ``lib = ffi.verify("C header")``. -The out-of-line variant with :ref:`set_source("modname", "C header") <set_source>` is -preferred and avoids a number of problems when the project grows in -size. - - -.. _embedding: - -Embedding ---------- - -*New in version 1.5.* - -CFFI can be used for embedding__: creating a standard -dynamically-linked library (``.dll`` under Windows, ``.so`` elsewhere) -which can be used from a C application. - -.. code-block:: python - - import cffi - ffibuilder = cffi.FFI() - - ffibuilder.embedding_api(""" - int do_stuff(int, int); - """) - - ffibuilder.set_source("my_plugin", "") - - ffibuilder.embedding_init_code(""" - from my_plugin import ffi - - @ffi.def_extern() - def do_stuff(x, y): - print("adding %d and %d" % (x, y)) - return x + y - """) - - ffibuilder.compile(target="plugin-1.5.*", verbose=True) - -This simple example creates ``plugin-1.5.dll`` or ``plugin-1.5.so`` as -a DLL with a single exported function, ``do_stuff()``. You execute -the script above once, with the interpreter you want to have -internally used; it can be CPython 2.x or 3.x or PyPy. This DLL can -then be used "as usual" from an application; the application doesn't -need to know that it is talking with a library made with Python and -CFFI. At runtime, when the application calls ``int do_stuff(int, -int)``, the Python interpreter is automatically initialized and ``def -do_stuff(x, y):`` gets called. `See the details in the documentation -about embedding.`__ - -.. __: embedding.html -.. __: embedding.html - - -What actually happened? ------------------------ - -The CFFI interface operates on the same level as C - you declare types -and functions using the same syntax as you would define them in C. This -means that most of the documentation or examples can be copied straight -from the man pages. - -The declarations can contain **types, functions, constants** -and **global variables.** What you pass to the :ref:`cdef() <cdef>` must not -contain more than that; in particular, ``#ifdef`` or ``#include`` -directives are not supported. The cdef in the above examples are just -that - they declared "there is a function in the C level with this -given signature", or "there is a struct type with this shape". - -In the ABI examples, the :ref:`dlopen() <dlopen>` calls load libraries manually. -At the binary level, a program is split into multiple namespaces---a -global one (on some platforms), plus one namespace per library. So -``dlopen()`` returns a ``<FFILibrary>`` object, and this object has -got as attributes all function, constant and variable symbols that are -coming from this library and that have been declared in the -``cdef()``. If you have several interdependent libraries to load, -you would call ``cdef()`` only once but ``dlopen()`` several times. - -By opposition, the API mode works more closely like a C program: the C -linker (static or dynamic) is responsible for finding any symbol used. -You name the libraries in the ``libraries`` keyword argument to -:ref:`set_source() <set_source>`, but never need to say which symbol comes -from which library. -Other common arguments to ``set_source()`` include ``library_dirs`` and -``include_dirs``; all these arguments are passed to the standard -distutils/setuptools. - -The :ref:`ffi.new() <new>` lines allocate C objects. They are filled -with zeroes initially, unless the optional second argument is used. -If specified, this argument gives an "initializer", like you can use -with C code to initialize global variables. - -The actual ``lib.*()`` function calls should be obvious: it's like C. - - -.. _abi-versus-api: - -ABI versus API --------------- - -Accessing the C library at the binary level ("ABI") is fraught -with problems, particularly on non-Windows platforms. - -The most immediate drawback of the ABI level is that calling functions -needs to go through the very general *libffi* library, which is slow -(and not always perfectly tested on non-standard platforms). The API -mode instead compiles a CPython C wrapper that directly invokes the -target function. It can be massively faster (and works -better than libffi ever will). - -The more fundamental reason to prefer the API mode is that *the C -libraries are typically meant to be used with a C compiler.* You are not -supposed to do things like guess where fields are in the structures. -The "real example" above shows how CFFI uses a C compiler under the -hood: this example uses :ref:`set_source(..., "C source...") <set_source>` and never -:ref:`dlopen() <dlopen>`. When using this approach, -we have the advantage that we can use literally "``...``" at various places in -the :ref:`cdef() <cdef>`, and the missing information will be completed with the -help of the C compiler. CFFI will turn this into a single C source file, -which contains the "C source" part unmodified, followed by some -"magic" C code and declarations derived from the ``cdef()``. When -this C file is compiled, the resulting C extension module will contain -all the information we need---or the C compiler will give warnings or -errors, as usual e.g. if we misdeclare some function's signature. - -Note that the "C source" part from ``set_source()`` can contain -arbitrary C code. You can use this to declare some -more helper functions written in C. To export -these helpers to Python, put their signature in the ``cdef()`` too. -(You can use the ``static`` C keyword in the "C source" part, -as in ``static int myhelper(int x) { return x * 42; }``, -because these helpers are only -referenced from the "magic" C code that is generated afterwards in the -same C file.) - -This can be used for example to wrap "crazy" macros into more standard -C functions. The extra layer of C can be useful for other reasons -too, like calling functions that expect some complicated argument -structures that you prefer to build in C rather than in Python. (On -the other hand, if all you need is to call "function-like" macros, -then you can directly declare them in the ``cdef()`` as if they were -functions.) - -The generated piece of C code should be the same independently on the -platform on which you run it (or the Python version), so in simple cases -you can directly distribute the pre-generated C code and treat it as a -regular C extension module (which depends on the ``_cffi_backend`` -module, on CPython). The special Setuptools lines in the `example -above`__ are meant for the more complicated cases where we need to -regenerate the C sources as well---e.g. because the Python script that -regenerates this file will itself look around the system to know what it -should include or not. - -.. __: real-example_ diff --git a/doc/source/ref.rst b/doc/source/ref.rst deleted file mode 100644 index 05c0f7c..0000000 --- a/doc/source/ref.rst +++ /dev/null @@ -1,1028 +0,0 @@ -================================ -CFFI Reference -================================ - -.. contents:: - - -FFI Interface -------------- - -*This page documents the runtime interface of the two types "FFI" and -"CompiledFFI". These two types are very similar to each other. You get -a CompiledFFI object if you import an out-of-line module. You get a FFI -object from explicitly writing cffi.FFI(). Unlike CompiledFFI, the type -FFI has also got additional methods documented on the* `next page`__. - -.. __: cdef.html - - -ffi.NULL -++++++++ - -**ffi.NULL**: a constant NULL of type ``<cdata 'void *'>``. - - -ffi.error -+++++++++ - -**ffi.error**: the Python exception raised in various cases. (Don't -confuse it with ``ffi.errno``.) - - -.. _new: - -ffi.new() -+++++++++ - -**ffi.new(cdecl, init=None)**: -allocate an instance according to the specified C type and return a -pointer to it. The specified C type must be either a pointer or an -array: ``new('X *')`` allocates an X and returns a pointer to it, -whereas ``new('X[10]')`` allocates an array of 10 X'es and returns an -array referencing it (which works mostly like a pointer, like in C). -You can also use ``new('X[]', n)`` to allocate an array of a -non-constant length n. See the `detailed documentation`__ for other -valid initializers. - -.. __: using.html#working - -When the returned ``<cdata>`` object goes out of scope, the memory is -freed. In other words the returned ``<cdata>`` object has ownership of -the value of type ``cdecl`` that it points to. This means that the raw -data can be used as long as this object is kept alive, but must not be -used for a longer time. Be careful about that when copying the -pointer to the memory somewhere else, e.g. into another structure. -Also, this means that a line like ``x = ffi.cast("B *", ffi.new("A *"))`` -or ``x = ffi.new("struct s[1]")[0]`` is wrong: the newly allocated object -goes out of scope instantly, and so is freed immediately, and ``x`` is -garbage. The only case where this is fine comes from a special case for -pointers-to-struct and pointers-to-union types: after -``p = ffi.new("struct-or-union *", ..)``, then either ``p`` or ``p[0]`` -keeps the memory alive. - -The returned memory is initially cleared (filled with zeroes), before -the optional initializer is applied. For performance, see -`ffi.new_allocator()`_ for a way to allocate non-zero-initialized -memory. - -*New in version 1.12:* see also ``ffi.release()``. - - -ffi.cast() -++++++++++ - -**ffi.cast("C type", value)**: similar to a C cast: returns an -instance of the named C type initialized with the given value. The -value is casted between integers or pointers of any type. - - -.. _ffi-errno: -.. _ffi-getwinerror: - -ffi.errno, ffi.getwinerror() -++++++++++++++++++++++++++++ - -**ffi.errno**: the value of ``errno`` received from the most recent C call -in this thread, and passed to the following C call. (This is a thread-local -read-write property.) - -**ffi.getwinerror(code=-1)**: on Windows, in addition to ``errno`` we -also save and restore the ``GetLastError()`` value across function -calls. This function returns this error code as a tuple ``(code, -message)``, adding a readable message like Python does when raising -WindowsError. If the argument ``code`` is given, format that code into -a message instead of using ``GetLastError()``. -(Note that it is also possible to declare and call the ``GetLastError()`` -function as usual.) - - -.. _ffi-string: -.. _ffi-unpack: - -ffi.string(), ffi.unpack() -++++++++++++++++++++++++++ - -**ffi.string(cdata, [maxlen])**: return a Python string (or unicode -string) from the 'cdata'. - -- If 'cdata' is a pointer or array of characters or bytes, returns the - null-terminated string. The returned string extends until the first - null character. The 'maxlen' argument limits how far we look for a - null character. If 'cdata' is an - array then 'maxlen' defaults to its length. See ``ffi.unpack()`` below - for a way to continue past the first null character. *Python 3:* this - returns a ``bytes``, not a ``str``. - -- If 'cdata' is a pointer or array of wchar_t, returns a unicode string - following the same rules. *New in version 1.11:* can also be - char16_t or char32_t. - -- If 'cdata' is a single character or byte or a wchar_t or charN_t, - returns it as a byte string or unicode string. (Note that in some - situation a single wchar_t or char32_t may require a Python unicode - string of length 2.) - -- If 'cdata' is an enum, returns the value of the enumerator as a string. - If the value is out of range, it is simply returned as the stringified - integer. - -**ffi.unpack(cdata, length)**: unpacks an array of C data of the given -length, returning a Python string/unicode/list. The 'cdata' should be -a pointer; if it is an array it is first converted to the pointer -type. *New in version 1.6.* - -- If 'cdata' is a pointer to 'char', returns a byte string. It does - not stop at the first null. (An equivalent way to do that is - ``ffi.buffer(cdata, length)[:]``.) - -- If 'cdata' is a pointer to 'wchar_t', returns a unicode string. - ('length' is measured in number of wchar_t; it is not the size in - bytes.) *New in version 1.11:* can also be char16_t or char32_t. - -- If 'cdata' is a pointer to anything else, returns a list, of the - given 'length'. (A slower way to do that is ``[cdata[i] for i in - range(length)]``.) - - -.. _ffi-buffer: -.. _ffi-from-buffer: - -ffi.buffer(), ffi.from_buffer() -+++++++++++++++++++++++++++++++ - -**ffi.buffer(cdata, [size])**: return a buffer object that references -the raw C data pointed to by the given 'cdata', of 'size' bytes. What -Python calls "a buffer", or more precisely "an object supporting the -buffer interface", is an object that represents some raw memory and -that can be passed around to various built-in or extension functions; -these built-in functions read from or write to the raw memory directly, -without needing an extra copy. - -The 'cdata' argument -must be a pointer or an array. If unspecified, the size of the -buffer is either the size of what ``cdata`` points to, or the whole size -of the array. - -Here are a few examples of where buffer() would be useful: - -- use ``file.write()`` and ``file.readinto()`` with - such a buffer (for files opened in binary mode) - -- overwrite the content of a struct: if ``p`` is a cdata pointing to - it, use ``ffi.buffer(p)[:] = newcontent``, where ``newcontent`` is - a bytes object (``str`` in Python 2). - -Remember that like in C, you can use ``array + index`` to get the pointer -to the index'th item of an array. (In C you might more naturally write -``&array[index]``, but that is equivalent.) - -The returned object's type is not the builtin ``buffer`` nor ``memoryview`` -types, because these types' API changes too much across Python versions. -Instead it has the following Python API (a subset of Python 2's ``buffer``) -in addition to supporting the buffer interface: - -- ``buf[:]`` or ``bytes(buf)``: copy data out of the buffer, returning a - regular byte string (or ``buf[start:end]`` for a part) - -- ``buf[:] = newstr``: copy data into the buffer (or ``buf[start:end] - = newstr``) - -- ``len(buf)``, ``buf[index]``, ``buf[index] = newchar``: access as a sequence - of characters. - -The buffer object returned by ``ffi.buffer(cdata)`` keeps alive the -``cdata`` object: if it was originally an owning cdata, then its -owned memory will not be freed as long as the buffer is alive. - -Python 2/3 compatibility note: you should avoid using ``str(buf)``, -because it gives inconsistent results between Python 2 and Python 3. -(This is similar to how ``str()`` gives inconsistent results on regular -byte strings). Use ``buf[:]`` instead. - -*New in version 1.10:* ``ffi.buffer`` is now the type of the returned -buffer objects; ``ffi.buffer()`` actually calls the constructor. - -**ffi.from_buffer([cdecl,] python_buffer, require_writable=False)**: -return an array cdata (by default a ``<cdata 'char[]'>``) that -points to the data of the given Python object, which must support the -buffer interface. Note that ``ffi.from_buffer()`` turns a generic -Python buffer object into a cdata object, whereas ``ffi.buffer()`` does -the opposite conversion. Both calls don't actually copy any data. - -``ffi.from_buffer()`` is meant to be used on objects -containing large quantities of raw data, like bytearrays -or ``array.array`` or numpy -arrays. It supports both the old *buffer* API (in Python 2.x) and the -new *memoryview* API. Note that if you pass a read-only buffer object, -you still get a regular ``<cdata 'char[]'>``; it is your responsibility -not to write there if the original buffer doesn't expect you to. -*In particular, never modify byte strings!* - -The original object is kept alive (and, in case -of memoryview, locked) as long as the cdata object returned by -``ffi.from_buffer()`` is alive. - -A common use case is calling a C function with some ``char *`` that -points to the internal buffer of a Python object; for this case you -can directly pass ``ffi.from_buffer(python_buffer)`` as argument to -the call. - -*New in version 1.10:* the ``python_buffer`` can be anything supporting -the buffer/memoryview interface (except unicode strings). Previously, -bytearray objects were supported in version 1.7 onwards (careful, if you -resize the bytearray, the ``<cdata>`` object will point to freed -memory); and byte strings were supported in version 1.8 onwards. - -*New in version 1.12:* added the optional *first* argument ``cdecl``, and -the keyword argument ``require_writable``: - -* ``cdecl`` defaults to ``"char[]"``, but a different array - or (from version 1.13) pointer type can be - specified for the result. A value like ``"int[]"`` will return an array of - ints instead of chars, and its length will be set to the number of ints - that fit in the buffer (rounded down if the division is not exact). Values - like ``"int[42]"`` or ``"int[2][3]"`` will return an array of exactly 42 - (resp. 2-by-3) ints, raising a ValueError if the buffer is too small. The - difference between specifying ``"int[]"`` and using the older code ``p1 = - ffi.from_buffer(x); p2 = ffi.cast("int *", p1)`` is that the older code - needs to keep ``p1`` alive as long as ``p2`` is in use, because only ``p1`` - keeps the underlying Python object alive and locked. (In addition, - ``ffi.from_buffer("int[]", x)`` gives better array bound checking.) - - *New in version 1.13:* ``cdecl`` can be a pointer type. If it points - to a struct or union, you can, as usual, write ``p.field`` instead of - ``p[0].field``. You can also access ``p[n]``; note that CFFI does not - perform any bounds checking in this case. Note also that ``p[0]`` cannot - be used to keep the buffer alive (unlike what occurs with ``ffi.new()``). - -* if ``require_writable`` is set to True, the function fails if the buffer - obtained from ``python_buffer`` is read-only (e.g. if ``python_buffer`` is - a byte string). The exact exception is raised by the object itself, and - for things like bytes it varies with the Python version, so don't rely on - it. (Before version 1.12, the same effect can be achieved with a hack: - call ``ffi.memmove(python_buffer, b"", 0)``. This has no effect if the - object is writable, but fails if it is read-only.) Please keep in mind - that CFFI does not implement the C keyword ``const``: even if you set - ``require_writable`` to False explicitly, you still get a regular - read-write cdata pointer. - -*New in version 1.12:* see also ``ffi.release()``. - - -ffi.memmove() -+++++++++++++ - -**ffi.memmove(dest, src, n)**: copy ``n`` bytes from memory area -``src`` to memory area ``dest``. See examples below. Inspired by the -C functions ``memcpy()`` and ``memmove()``---like the latter, the -areas can overlap. Each of ``dest`` and ``src`` can be either a cdata -pointer or a Python object supporting the buffer/memoryview interface. -In the case of ``dest``, the buffer/memoryview must be writable. -*New in version 1.3.* Examples: - -* ``ffi.memmove(myptr, b"hello", 5)`` copies the 5 bytes of - ``b"hello"`` to the area that ``myptr`` points to. - -* ``ba = bytearray(100); ffi.memmove(ba, myptr, 100)`` copies 100 - bytes from ``myptr`` into the bytearray ``ba``. - -* ``ffi.memmove(myptr + 1, myptr, 100)`` shifts 100 bytes from - the memory at ``myptr`` to the memory at ``myptr + 1``. - -In versions before 1.10, ``ffi.from_buffer()`` had restrictions on the -type of buffer, which made ``ffi.memmove()`` more general. - -.. _ffi-typeof: -.. _ffi-sizeof: -.. _ffi-alignof: - -ffi.typeof(), ffi.sizeof(), ffi.alignof() -+++++++++++++++++++++++++++++++++++++++++ - -**ffi.typeof("C type" or cdata object)**: return an object of type -``<ctype>`` corresponding to the parsed string, or to the C type of the -cdata instance. Usually you don't need to call this function or to -explicitly manipulate ``<ctype>`` objects in your code: any place that -accepts a C type can receive either a string or a pre-parsed ``ctype`` -object (and because of caching of the string, there is no real -performance difference). It can still be useful in writing typechecks, -e.g.: - -.. code-block:: python - - def myfunction(ptr): - assert ffi.typeof(ptr) is ffi.typeof("foo_t*") - ... - -Note also that the mapping from strings like ``"foo_t*"`` to the -``<ctype>`` objects is stored in some internal dictionary. This -guarantees that there is only one ``<ctype 'foo_t *'>`` object, so you -can use the ``is`` operator to compare it. The downside is that the -dictionary entries are immortal for now. In the future, we may add -transparent reclamation of old, unused entries. In the meantime, note -that using strings like ``"int[%d]" % length`` to name a type will -create many immortal cached entries if called with many different -lengths. - -**ffi.sizeof("C type" or cdata object)**: return the size of the -argument in bytes. The argument can be either a C type, or a cdata object, -like in the equivalent ``sizeof`` operator in C. - -For ``array = ffi.new("T[]", n)``, then ``ffi.sizeof(array)`` returns -``n * ffi.sizeof("T")``. *New in version 1.9:* Similar rules apply for -structures with a variable-sized array at the end. More precisely, if -``p`` was returned by ``ffi.new("struct foo *", ...)``, then -``ffi.sizeof(p[0])`` now returns the total allocated size. In previous -versions, it used to just return ``ffi.sizeof(ffi.typeof(p[0]))``, which -is the size of the structure ignoring the variable-sized part. (Note -that due to alignment, it is possible for ``ffi.sizeof(p[0])`` to return -a value smaller than ``ffi.sizeof(ffi.typeof(p[0]))``.) - -**ffi.alignof("C type")**: return the natural alignment size in bytes of -the argument. Corresponds to the ``__alignof__`` operator in GCC. - - -.. _ffi-offsetof: -.. _ffi-addressof: - -ffi.offsetof(), ffi.addressof() -+++++++++++++++++++++++++++++++ - -**ffi.offsetof("C struct or array type", \*fields_or_indexes)**: return the -offset within the struct of the given field. Corresponds to ``offsetof()`` -in C. - -You can give several field names in case of nested structures. You -can also give numeric values which correspond to array items, in case -of a pointer or array type. For example, ``ffi.offsetof("int[5]", 2)`` -is equal to the size of two integers, as is ``ffi.offsetof("int *", 2)``. - - -**ffi.addressof(cdata, \*fields_or_indexes)**: limited equivalent to -the '&' operator in C: - -1. ``ffi.addressof(<cdata 'struct-or-union'>)`` returns a cdata that -is a pointer to this struct or union. The returned pointer is only -valid as long as the original ``cdata`` object is; be sure to keep it -alive if it was obtained directly from ``ffi.new()``. - -2. ``ffi.addressof(<cdata>, field-or-index...)`` returns the address -of a field or array item inside the given structure or array. In case -of nested structures or arrays, you can give more than one field or -index to look recursively. Note that ``ffi.addressof(array, index)`` -can also be expressed as ``array + index``: this is true both in CFFI -and in C, where ``&array[index]`` is just ``array + index``. - -3. ``ffi.addressof(<library>, "name")`` returns the address of the -named function or global variable from the given library object. -For functions, it returns a regular cdata -object containing a pointer to the function. - -Note that the case 1. cannot be used to take the address of a -primitive or pointer, but only a struct or union. It would be -difficult to implement because only structs and unions are internally -stored as an indirect pointer to the data. If you need a C int whose -address can be taken, use ``ffi.new("int[1]")`` in the first place; -similarly, for a pointer, use ``ffi.new("foo_t *[1]")``. - - -.. _ffi-cdata: -.. _ffi-ctype: - -ffi.CData, ffi.CType -++++++++++++++++++++ - -**ffi.CData, ffi.CType**: the Python type of the objects referred to -as ``<cdata>`` and ``<ctype>`` in the rest of this document. Note -that some cdata objects may be actually of a subclass of -``ffi.CData``, and similarly with ctype, so you should check with -``if isinstance(x, ffi.CData)``. Also, ``<ctype>`` objects have -a number of attributes for introspection: ``kind`` and ``cname`` are -always present, and depending on the kind they may also have -``item``, ``length``, ``fields``, ``args``, ``result``, ``ellipsis``, -``abi``, ``elements`` and ``relements``. - -*New in version 1.10:* ``ffi.buffer`` is now `a type`__ as well. - -.. __: #ffi-buffer - - -.. _ffi-gc: - -ffi.gc() -++++++++ - -**ffi.gc(cdata, destructor, size=0)**: -return a new cdata object that points to the -same data. Later, when this new cdata object is garbage-collected, -``destructor(old_cdata_object)`` will be called. Example of usage: -``ptr = ffi.gc(lib.custom_malloc(42), lib.custom_free)``. -Note that like objects -returned by ``ffi.new()``, the returned pointer objects have *ownership*, -which means the destructor is called as soon as *this* exact returned -object is garbage-collected. - -*New in version 1.12:* see also ``ffi.release()``. - -**ffi.gc(ptr, None, size=0)**: -removes the ownership on a object returned by a -regular call to ``ffi.gc``, and no destructor will be called when it -is garbage-collected. The object is modified in-place, and the -function returns ``None``. *New in version 1.7: ffi.gc(ptr, None)* - -Note that ``ffi.gc()`` should be avoided for limited resources, or (with -cffi below 1.11) for large memory allocations. This is particularly -true on PyPy: its GC does not know how much memory or how many resources -the returned ``ptr`` holds. It will only run its GC when enough memory -it knows about has been allocated (and thus run the destructor possibly -later than you would expect). Moreover, the destructor is called in -whatever thread PyPy is at that moment, which might be a problem for -some C libraries. In these cases, consider writing a wrapper class with -custom ``__enter__()`` and ``__exit__()`` methods, allocating and -freeing the C data at known points in time, and using it in a ``with`` -statement. In cffi 1.12, see also ``ffi.release()``. - -*New in version 1.11:* the ``size`` argument. If given, this should be -an estimate of the size (in bytes) that ``ptr`` keeps alive. This -information is passed on to the garbage collector, fixing part of the -problem described above. The ``size`` argument is most important on -PyPy; on CPython, it is ignored so far, but in the future it could be -used to trigger more eagerly the cyclic reference GC, too (see CPython -`issue 31105`__). - -The form ``ffi.gc(ptr, None, size=0)`` can be called with a negative -``size``, to cancel the estimate. It is not mandatory, though: -nothing gets out of sync if the size estimates do not match. It only -makes the next GC start more or less early. - -Note that if you have several ``ffi.gc()`` objects, the corresponding -destructors will be called in a random order. If you need a particular -order, see the discussion in `issue 340`__. - -.. __: http://bugs.python.org/issue31105 -.. __: https://foss.heptapod.net/pypy/cffi/-/issues/340 - - -.. _ffi-new-handle: -.. _ffi-from-handle: - -ffi.new_handle(), ffi.from_handle() -+++++++++++++++++++++++++++++++++++ - -**ffi.new_handle(python_object)**: return a non-NULL cdata of type -``void *`` that contains an opaque reference to ``python_object``. You -can pass it around to C functions or store it into C structures. Later, -you can use **ffi.from_handle(p)** to retrieve the original -``python_object`` from a value with the same ``void *`` pointer. -*Calling ffi.from_handle(p) is invalid and will likely crash if -the cdata object returned by new_handle() is not kept alive!* - -See a `typical usage example`_ below. - -(In case you are wondering, this ``void *`` is not the ``PyObject *`` -pointer. This wouldn't make sense on PyPy anyway.) - -The ``ffi.new_handle()/from_handle()`` functions *conceptually* work -like this: - -* ``new_handle()`` returns cdata objects that contains references to - the Python objects; we call them collectively the "handle" cdata - objects. The ``void *`` value in these handle cdata objects are - random but unique. - -* ``from_handle(p)`` searches all live "handle" cdata objects for the - one that has the same value ``p`` as its ``void *`` value. It then - returns the Python object referenced by that handle cdata object. - If none is found, you get "undefined behavior" (i.e. crashes). - -The "handle" cdata object keeps the Python object alive, similar to -how ``ffi.new()`` returns a cdata object that keeps a piece of memory -alive. If the handle cdata object *itself* is not alive any more, -then the association ``void * -> python_object`` is dead and -``from_handle()`` will crash. - -*New in version 1.4:* two calls to ``new_handle(x)`` are guaranteed to -return cdata objects with different ``void *`` values, even with the -same ``x``. This is a useful feature that avoids issues with unexpected -duplicates in the following trick: if you need to keep alive the -"handle" until explicitly asked to free it, but don't have a natural -Python-side place to attach it to, then the easiest is to ``add()`` it -to a global set. It can later be removed from the set by -``global_set.discard(p)``, with ``p`` any cdata object whose ``void *`` -value compares equal. - -.. _`typical usage example`: - -Usage example: suppose you have a C library where you must call a -``lib.process_document()`` function which invokes some callback. The -``process_document()`` function receives a pointer to a callback and a -``void *`` argument. The callback is then invoked with the ``void -*data`` argument that is equal to the provided value. In this typical -case, you can implement it like this (out-of-line API mode):: - - class MyDocument: - ... - - def process(self): - h = ffi.new_handle(self) - lib.process_document(lib.my_callback, # the callback - h, # 'void *data' - args...) - # 'h' stays alive until here, which means that the - # ffi.from_handle() done in my_callback() during - # the call to process_document() is safe - - def callback(self, arg1, arg2): - ... - - # the actual callback is this one-liner global function: - @ffi.def_extern() - def my_callback(arg1, arg2, data): - return ffi.from_handle(data).callback(arg1, arg2) - - -.. _ffi-dlopen: -.. _ffi-dlclose: - -ffi.dlopen(), ffi.dlclose() -+++++++++++++++++++++++++++ - -**ffi.dlopen(libpath, [flags])**: opens and returns a "handle" to a -dynamic library, as a ``<lib>`` object. See `Preparing and -Distributing modules`_. - -**ffi.dlclose(lib)**: explicitly closes a ``<lib>`` object returned -by ``ffi.dlopen()``. - -**ffi.RLTD_...**: constants: flags for ``ffi.dlopen()``. - - -ffi.new_allocator() -+++++++++++++++++++ - -**ffi.new_allocator(alloc=None, free=None, should_clear_after_alloc=True)**: -returns a new allocator. An "allocator" is a callable that behaves like -``ffi.new()`` but uses the provided low-level ``alloc`` and ``free`` -functions. *New in version 1.2.* - -``alloc()`` is invoked with the size as sole argument. If it returns -NULL, a MemoryError is raised. Later, if ``free`` is not None, it will -be called with the result of ``alloc()`` as argument. Both can be either -Python function or directly C functions. If only ``free`` is None, then no -free function is called. If both ``alloc`` and ``free`` are None, the -default alloc/free combination is used. (In other words, the call -``ffi.new(*args)`` is equivalent to ``ffi.new_allocator()(*args)``.) - -If ``should_clear_after_alloc`` is set to False, then the memory -returned by ``alloc()`` is assumed to be already cleared (or you are -fine with garbage); otherwise CFFI will clear it. Example: for -performance, if you are using ``ffi.new()`` to allocate large chunks of -memory where the initial content can be left uninitialized, you can do:: - - # at module level - new_nonzero = ffi.new_allocator(should_clear_after_alloc=False) - - # then replace `p = ffi.new("char[]", bigsize)` with: - p = new_nonzero("char[]", bigsize) - -**NOTE:** the following is a general warning that applies particularly -(but not only) to PyPy versions 5.6 or older (PyPy > 5.6 attempts to -account for the memory returned by ``ffi.new()`` or a custom allocator; -and CPython uses reference counting). If you do large allocations, then -there is no hard guarantee about when the memory will be freed. You -should avoid both ``new()`` and ``new_allocator()()`` if you want to be -sure that the memory is promptly released, e.g. before you allocate more -of it. - -An alternative is to declare and call the C ``malloc()`` and ``free()`` -functions, or some variant like ``mmap()`` and ``munmap()``. Then you -control exactly when the memory is allocated and freed. For example, -add these two lines to your existing ``ffibuilder.cdef()``:: - - void *malloc(size_t size); - void free(void *ptr); - -and then call these two functions manually:: - - p = lib.malloc(n * ffi.sizeof("int")) - try: - my_array = ffi.cast("int *", p) - ... - finally: - lib.free(p) - -In cffi version 1.12 you can indeed use ``ffi.new_allocator()`` but use the -``with`` statement (see ``ffi.release()``) to force the free function to be -called at a known point. The above is equivalent to this code:: - - my_new = ffi.new_allocator(lib.malloc, lib.free) # at global level - ... - with my_new("int[]", n) as my_array: - ... - -**Warning:** due to a bug, ``p = ffi.new_allocator(..)("struct-or-union *")`` -might not follow the rule that either ``p`` or ``p[0]`` keeps the memory -alive, which holds for the normal ``ffi.new("struct-or-union *")`` allocator. -It may sometimes be the case that if there is only a reference to ``p[0]``, -the memory is freed. The cause is that the rule doesn't hold for -``ffi.gc()``, which is sometimes used in the implementation of -``ffi.new_allocator()()``; this might be fixed in a future release. - - -.. _ffi-release: - -ffi.release() and the context manager -+++++++++++++++++++++++++++++++++++++ - -**ffi.release(cdata)**: release the resources held by a cdata object from -``ffi.new()``, ``ffi.gc()``, ``ffi.from_buffer()`` or -``ffi.new_allocator()()``. The cdata object must not be used afterwards. -The normal Python destructor of the cdata object releases the same resources, -but this allows the releasing to occur at a known time, as opposed as at an -unspecified point in the future. -*New in version 1.12.* - -``ffi.release(cdata)`` is equivalent to ``cdata.__exit__()``, which means that -you can use the ``with`` statement to ensure that the cdata is released at the -end of a block (in version 1.12 and above):: - - with ffi.from_buffer(...) as p: - do something with p - -The effect is more precisely as follows: - -* on an object returned from ``ffi.gc(destructor)``, ``ffi.release()`` will - cause the ``destructor`` to be called immediately. - -* on an object returned from a custom allocator, the custom free function - is called immediately. - -* on CPython, ``ffi.from_buffer(buf)`` locks the buffer, so ``ffi.release()`` - can be used to unlock it at a known time. On PyPy, there is no locking - (so far); the effect of ``ffi.release()`` is limited to removing the link, - allowing the original buffer object to be garbage-collected even if the - cdata object stays alive. - -* on CPython this method has no effect (so far) on objects returned by - ``ffi.new()``, because the memory is allocated inline with the cdata object - and cannot be freed independently. It might be fixed in future releases of - cffi. - -* on PyPy, ``ffi.release()`` frees the ``ffi.new()`` memory immediately. It is - useful because otherwise the memory is kept alive until the next GC occurs. - If you allocate large amounts of memory with ``ffi.new()`` and don't free - them with ``ffi.release()``, PyPy (>= 5.7) runs its GC more often to - compensate, so the total memory allocated should be kept within bounds - anyway; but calling ``ffi.release()`` explicitly should improve performance - by reducing the frequency of GC runs. - -After ``ffi.release(x)``, do not use anything pointed to by ``x`` any longer. -As an exception to this rule, you can call ``ffi.release(x)`` several times -for the exact same cdata object ``x``; the calls after the first one are -ignored. - - -ffi.init_once() -+++++++++++++++ - -**ffi.init_once(function, tag)**: run ``function()`` once. The -``tag`` should be a primitive object, like a string, that identifies -the function: ``function()`` is only called the first time we see the -``tag``. The return value of ``function()`` is remembered and -returned by the current and all future ``init_once()`` with the same -tag. If ``init_once()`` is called from multiple threads in parallel, -all calls block until the execution of ``function()`` is done. If -``function()`` raises an exception, it is propagated and nothing is -cached (i.e. ``function()`` will be called again, in case we catch the -exception and try ``init_once()`` again). *New in version 1.4.* - -Example:: - - from _xyz_cffi import ffi, lib - - def initlib(): - lib.init_my_library() - - def make_new_foo(): - ffi.init_once(initlib, "init") - return lib.make_foo() - -``init_once()`` is optimized to run very quickly if ``function()`` has -already been called. (On PyPy, the cost is zero---the JIT usually -removes everything in the machine code it produces.) - -*Note:* one motivation__ for ``init_once()`` is the CPython notion of -"subinterpreters" in the embedded case. If you are using the -out-of-line API mode, ``function()`` is called only once even in the -presence of multiple subinterpreters, and its return value is shared -among all subinterpreters. The goal is to mimic the way traditional -CPython C extension modules have their init code executed only once in -total even if there are subinterpreters. In the example above, the C -function ``init_my_library()`` is called once in total, not once per -subinterpreter. For this reason, avoid Python-level side-effects in -``function()`` (as they will only be applied in the first -subinterpreter to run); instead, return a value, as in the following -example:: - - def init_get_max(): - return lib.initialize_once_and_get_some_maximum_number() - - def process(i): - if i > ffi.init_once(init_get_max, "max"): - raise IndexError("index too large!") - ... - -.. __: https://foss.heptapod.net/pypy/cffi/-/issues/233 - - -.. _ffi-getctype: -.. _ffi-list-types: - -ffi.getctype(), ffi.list_types() -++++++++++++++++++++++++++++++++ - -**ffi.getctype("C type" or <ctype>, extra="")**: return the string -representation of the given C type. If non-empty, the "extra" string is -appended (or inserted at the right place in more complicated cases); it -can be the name of a variable to declare, or an extra part of the type -like ``"*"`` or ``"[5]"``. For example -``ffi.getctype(ffi.typeof(x), "*")`` returns the string representation -of the C type "pointer to the same type than x"; and -``ffi.getctype("char[80]", "a") == "char a[80]"``. - -**ffi.list_types()**: Returns the user type names known to this FFI -instance. This returns a tuple containing three lists of names: -``(typedef_names, names_of_structs, names_of_unions)``. *New in -version 1.6.* - - -.. _`Preparing and Distributing modules`: cdef.html#loading-libraries - - -Conversions ------------ - -This section documents all the conversions that are allowed when -*writing into* a C data structure (or passing arguments to a function -call), and *reading from* a C data structure (or getting the result of a -function call). The last column gives the type-specific operations -allowed. - -+---------------+------------------------+------------------+----------------+ -| C type | writing into | reading from |other operations| -+===============+========================+==================+================+ -| integers | an integer or anything | a Python int or | int(), bool() | -| and enums | on which int() works | long, depending | `[6]`, | -| `[5]` | (but not a float!). | on the type | ``<`` | -| | Must be within range. | (ver. 1.10: or a | | -| | | bool) | | -+---------------+------------------------+------------------+----------------+ -| ``char`` | a string of length 1 | a string of | int(), bool(), | -| | or another <cdata char>| length 1 | ``<`` | -+---------------+------------------------+------------------+----------------+ -| ``wchar_t``, | a unicode of length 1 | a unicode of | | -| ``char16_t``, | (or maybe 2 if | length 1 | int(), | -| ``char32_t`` | surrogates) or | (or maybe 2 if | bool(), ``<`` | -| `[8]` | another similar <cdata>| surrogates) | | -+---------------+------------------------+------------------+----------------+ -| ``float``, | a float or anything on | a Python float | float(), int(),| -| ``double`` | which float() works | | bool(), ``<`` | -+---------------+------------------------+------------------+----------------+ -|``long double``| another <cdata> with | a <cdata>, to | float(), int(),| -| | a ``long double``, or | avoid loosing | bool() | -| | anything on which | precision `[3]` | | -| | float() works | | | -+---------------+------------------------+------------------+----------------+ -| ``float`` | a complex number | a Python complex | complex(), | -| ``_Complex``, | or anything on which | number | bool() | -| ``double`` | complex() works | | `[7]` | -| ``_Complex`` | | | | -+---------------+------------------------+------------------+----------------+ -| pointers | another <cdata> with | a <cdata> |``[]`` `[4]`, | -| | a compatible type (i.e.| |``+``, ``-``, | -| | same type | |bool() | -| | or ``void*``, or as an | | | -| | array instead) `[1]` | | | -+---------------+------------------------+ | | -| ``void *`` | another <cdata> with | | | -| | any pointer or array | | | -| | type | | | -+---------------+------------------------+ +----------------+ -| pointers to | same as pointers | | ``[]``, ``+``, | -| structure or | | | ``-``, bool(), | -| union | | | and read/write | -| | | | struct fields | -+---------------+------------------------+ +----------------+ -| function | same as pointers | | bool(), | -| pointers | | | call `[2]` | -+---------------+------------------------+------------------+----------------+ -| arrays | a list or tuple of | a <cdata> |len(), iter(), | -| | items | |``[]`` `[4]`, | -| | | |``+``, ``-`` | -+---------------+------------------------+ +----------------+ -| ``char[]``, | same as arrays, or a | | len(), iter(), | -| ``un/signed`` | Python byte string | | ``[]``, ``+``, | -| ``char[]``, | | | ``-`` | -| ``_Bool[]`` | | | | -+---------------+------------------------+ +----------------+ -|``wchar_t[]``, | same as arrays, or a | | len(), iter(), | -|``char16_t[]``,| Python unicode string | | ``[]``, | -|``char32_t[]`` | | | ``+``, ``-`` | -| | | | | -+---------------+------------------------+------------------+----------------+ -| structure | a list or tuple or | a <cdata> | read/write | -| | dict of the field | | fields | -| | values, or a same-type | | | -| | <cdata> | | | -+---------------+------------------------+ +----------------+ -| union | same as struct, but | | read/write | -| | with at most one field | | fields | -+---------------+------------------------+------------------+----------------+ - -`[1]` ``item *`` is ``item[]`` in function arguments: - - In a function declaration, as per the C standard, a ``item *`` - argument is identical to a ``item[]`` argument (and ``ffi.cdef()`` - doesn't record the difference). So when you call such a function, - you can pass an argument that is accepted by either C type, like - for example passing a Python string to a ``char *`` argument - (because it works for ``char[]`` arguments) or a list of integers - to a ``int *`` argument (it works for ``int[]`` arguments). Note - that even if you want to pass a single ``item``, you need to - specify it in a list of length 1; for example, a ``struct point_s - *`` argument might be passed as ``[[x, y]]`` or ``[{'x': 5, 'y': - 10}]``. - - As an optimization, CFFI assumes that a - function with a ``char *`` argument to which you pass a Python - string will not actually modify the array of characters passed in, - and so passes directly a pointer inside the Python string object. - (On PyPy, this optimization is only available since PyPy 5.4 - with CFFI 1.8.) - -`[2]` C function calls are done with the GIL released. - - Note that we assume that the called functions are *not* using the - Python API from Python.h. For example, we don't check afterwards - if they set a Python exception. You may work around it, but mixing - CFFI with ``Python.h`` is not recommended. (If you do that, on - PyPy and on some platforms like Windows, you may need to explicitly - link to ``libpypy-c.dll`` to access the CPython C API compatibility - layer; indeed, CFFI-generated modules on PyPy don't link to - ``libpypy-c.dll`` on their own. But really, don't do that in the - first place.) - -`[3]` ``long double`` support: - - We keep ``long double`` values inside a cdata object to avoid - loosing precision. Normal Python floating-point numbers only - contain enough precision for a ``double``. If you really want to - convert such an object to a regular Python float (i.e. a C - ``double``), call ``float()``. If you need to do arithmetic on - such numbers without any precision loss, you need instead to define - and use a family of C functions like ``long double add(long double - a, long double b);``. - -`[4]` Slicing with ``x[start:stop]``: - - Slicing is allowed, as long as you specify explicitly both ``start`` - and ``stop`` (and don't give any ``step``). It gives a cdata - object that is a "view" of all items from ``start`` to ``stop``. - It is a cdata of type "array" (so e.g. passing it as an argument to a - C function would just convert it to a pointer to the ``start`` item). - As with indexing, negative bounds mean really negative indices, like in - C. As for slice assignment, it accepts any iterable, including a list - of items or another array-like cdata object, but the length must match. - (Note that this behavior differs from initialization: e.g. you can - say ``chararray[10:15] = "hello"``, but the assigned string must be of - exactly the correct length; no implicit null character is added.) - -`[5]` Enums are handled like ints: - - Like C, enum types are mostly int types (unsigned or signed, int or - long; note that GCC's first choice is unsigned). Reading an enum - field of a structure, for example, returns you an integer. To - compare their value symbolically, use code like ``if x.field == - lib.FOO``. If you really want to get their value as a string, use - ``ffi.string(ffi.cast("the_enum_type", x.field))``. - -`[6]` bool() on a primitive cdata: - - *New in version 1.7.* In previous versions, it only worked on - pointers; for primitives it always returned True. - - *New in version 1.10:* The C type ``_Bool`` or ``bool`` converts to - Python booleans now. You get an exception if a C ``_Bool`` happens - to contain a value different from 0 and 1 (this case triggers - undefined behavior in C; if you really have to interface with a - library relying on this, don't use ``_Bool`` in the CFFI side). - Also, when converting from a byte string to a ``_Bool[]``, only the - bytes ``\x00`` and ``\x01`` are accepted. - -`[7]` libffi does not support complex numbers: - - *New in version 1.11:* CFFI now supports complex numbers directly. - Note however that libffi does not. This means that C functions that - take directly as argument types or return type a complex type cannot - be called by CFFI, unless they are directly using the API mode. - -`[8]` ``wchar_t``, ``char16_t`` and ``char32_t`` - - See `Unicode character types`_ below. - - -.. _file: - -Support for FILE -++++++++++++++++ - -You can declare C functions taking a ``FILE *`` argument and -call them with a Python file object. If needed, you can also do ``c_f -= ffi.cast("FILE *", fileobj)`` and then pass around ``c_f``. - -Note, however, that CFFI does this by a best-effort approach. If you -need finer control over buffering, flushing, and timely closing of the -``FILE *``, then you should not use this special support for ``FILE *``. -Instead, you can handle regular ``FILE *`` cdata objects that you -explicitly make using fdopen(), like this: - -.. code-block:: python - - ffi.cdef(''' - FILE *fdopen(int, const char *); // from the C <stdio.h> - int fclose(FILE *); - ''') - - myfile.flush() # make sure the file is flushed - newfd = os.dup(myfile.fileno()) # make a copy of the file descriptor - fp = lib.fdopen(newfd, "w") # make a cdata 'FILE *' around newfd - lib.write_stuff_to_file(fp) # invoke the external function - lib.fclose(fp) # when you're done, close fp (and newfd) - -The special support for ``FILE *`` is anyway implemented in a similar manner -on CPython 3.x and on PyPy, because these Python implementations' files are -not natively based on ``FILE *``. Doing it explicity offers more control. - - -.. _unichar: - -Unicode character types -+++++++++++++++++++++++ - -The ``wchar_t`` type has the same signedness as the underlying -platform's. For example, on Linux, it is a signed 32-bit integer. -However, the types ``char16_t`` and ``char32_t`` (*new in version 1.11*) -are always unsigned. - -Note that CFFI assumes that these types are meant to contain UTF-16 or -UTF-32 characters in the native endianness. More precisely: - -* ``char32_t`` is assumed to contain UTF-32, or UCS4, which is just the - unicode codepoint; - -* ``char16_t`` is assumed to contain UTF-16, i.e. UCS2 plus surrogates; - -* ``wchar_t`` is assumed to contain either UTF-32 or UTF-16 based on its - actual platform-defined size of 4 or 2 bytes. - -Whether this assumption is true or not is unspecified by the C language. -In theory, the C library you are interfacing with could use one of these -types with a different meaning. You would then need to handle it -yourself---for example, by using ``uint32_t`` instead of ``char32_t`` in -the ``cdef()``, and building the expected arrays of ``uint32_t`` -manually. - -Python itself can be compiled with ``sys.maxunicode == 65535`` or -``sys.maxunicode == 1114111`` (Python >= 3.3 is always 1114111). This -changes the handling of surrogates (which are pairs of 16-bit -"characters" which actually stand for a single codepoint whose value is -greater than 65535). If your Python is ``sys.maxunicode == 1114111``, -then it can store arbitrary unicode codepoints; surrogates are -automatically inserted when converting from Python unicodes to UTF-16, -and automatically removed when converting back. On the other hand, if -your Python is ``sys.maxunicode == 65535``, then it is the other way -around: surrogates are removed when converting from Python unicodes -to UTF-32, and added when converting back. In other words, surrogate -conversion is done only when there is a size mismatch. - -Note that Python's internal representations is not specified. For -example, on CPython >= 3.3, it will use 1- or 2- or 4-bytes arrays -depending on what the string actually contains. With CFFI, when you -pass a Python byte string to a C function expecting a ``char*``, then -we pass directly a pointer to the existing data without needing a -temporary buffer; however, the same cannot cleanly be done with -*unicode* string arguments and the ``wchar_t*`` / ``char16_t*`` / -``char32_t*`` types, because of the changing internal -representation. As a result, and for consistency, CFFI always allocates -a temporary buffer for unicode strings. - -**Warning:** for now, if you use ``char16_t`` and ``char32_t`` with -``set_source()``, you have to make sure yourself that the types are -declared by the C source you provide to ``set_source()``. They would be -declared if you ``#include`` a library that explicitly uses them, for -example, or when using C++11. Otherwise, you need ``#include -<uchar.h>`` on Linux, or more generally something like ``typedef -uint16_t char16_t;``. This is not done automatically by CFFI because -``uchar.h`` is not standard across platforms, and writing a ``typedef`` -like above would crash if the type happens to be already defined. diff --git a/doc/source/using.rst b/doc/source/using.rst deleted file mode 100644 index 38c96ba..0000000 --- a/doc/source/using.rst +++ /dev/null @@ -1,1043 +0,0 @@ -================================ -Using the ffi/lib objects -================================ - -.. contents:: - -Keep this page under your pillow. - - -.. _working: - -Working with pointers, structures and arrays --------------------------------------------- - -The C code's integers and floating-point values are mapped to Python's -regular ``int``, ``long`` and ``float``. Moreover, the C type ``char`` -corresponds to single-character strings in Python. (If you want it to -map to small integers, use either ``signed char`` or ``unsigned char``.) - -Similarly, the C type ``wchar_t`` corresponds to single-character -unicode strings. Note that in some situations (a narrow Python build -with an underlying 4-bytes wchar_t type), a single wchar_t character -may correspond to a pair of surrogates, which is represented as a -unicode string of length 2. If you need to convert such a 2-chars -unicode string to an integer, ``ord(x)`` does not work; use instead -``int(ffi.cast('wchar_t', x))``. - -*New in version 1.11:* in addition to ``wchar_t``, the C types -``char16_t`` and ``char32_t`` work the same but with a known fixed size. -In previous versions, this could be achieved using ``uint16_t`` and -``int32_t`` but without automatic conversion to Python unicodes. - -Pointers, structures and arrays are more complex: they don't have an -obvious Python equivalent. Thus, they correspond to objects of type -``cdata``, which are printed for example as -``<cdata 'struct foo_s *' 0xa3290d8>``. - -``ffi.new(ctype, [initializer])``: this function builds and returns a -new cdata object of the given ``ctype``. The ctype is usually some -constant string describing the C type. It must be a pointer or array -type. If it is a pointer, e.g. ``"int *"`` or ``struct foo *``, then -it allocates the memory for one ``int`` or ``struct foo``. If it is -an array, e.g. ``int[10]``, then it allocates the memory for ten -``int``. In both cases the returned cdata is of type ``ctype``. - -The memory is initially filled with zeros. An initializer can be given -too, as described later. - -Example:: - - >>> ffi.new("int *") - <cdata 'int *' owning 4 bytes> - >>> ffi.new("int[10]") - <cdata 'int[10]' owning 40 bytes> - - >>> ffi.new("char *") # allocates only one char---not a C string! - <cdata 'char *' owning 1 bytes> - >>> ffi.new("char[]", "foobar") # this allocates a C string, ending in \0 - <cdata 'char[]' owning 7 bytes> - -Unlike C, the returned pointer object has *ownership* on the allocated -memory: when this exact object is garbage-collected, then the memory is -freed. If, at the level of C, you store a pointer to the memory -somewhere else, then make sure you also keep the object alive for as -long as needed. (This also applies if you immediately cast the returned -pointer to a pointer of a different type: only the original object has -ownership, so you must keep it alive. As soon as you forget it, then -the casted pointer will point to garbage! In other words, the ownership -rules are attached to the *wrapper* cdata objects: they are not, and -cannot, be attached to the underlying raw memory.) Example: - -.. code-block:: python - - global_weakkeydict = weakref.WeakKeyDictionary() - - def make_foo(): - s1 = ffi.new("struct foo *") - fld1 = ffi.new("struct bar *") - fld2 = ffi.new("struct bar *") - s1.thefield1 = fld1 - s1.thefield2 = fld2 - # here the 'fld1' and 'fld2' object must not go away, - # otherwise 's1.thefield1/2' will point to garbage! - global_weakkeydict[s1] = (fld1, fld2) - # now 's1' keeps alive 'fld1' and 'fld2'. When 's1' goes - # away, then the weak dictionary entry will be removed. - return s1 - -Usually you don't need a weak dict: for example, to call a function with -a ``char * *`` argument that contains a pointer to a ``char *`` pointer, -it is enough to do this: - -.. code-block:: python - - p = ffi.new("char[]", "hello, world") # p is a 'char *' - q = ffi.new("char **", p) # q is a 'char **' - lib.myfunction(q) - # p is alive at least until here, so that's fine - -However, this is always wrong (usage of freed memory): - -.. code-block:: python - - p = ffi.new("char **", ffi.new("char[]", "hello, world")) - # WRONG! as soon as p is built, the inner ffi.new() gets freed! - -This is wrong too, for the same reason: - -.. code-block:: python - - p = ffi.new("struct my_stuff") - p.foo = ffi.new("char[]", "hello, world") - # WRONG! as soon as p.foo is set, the ffi.new() gets freed! - - -The cdata objects support mostly the same operations as in C: you can -read or write from pointers, arrays and structures. Dereferencing a -pointer is done usually in C with the syntax ``*p``, which is not valid -Python, so instead you have to use the alternative syntax ``p[0]`` -(which is also valid C). Additionally, the ``p.x`` and ``p->x`` -syntaxes in C both become ``p.x`` in Python. - -We have ``ffi.NULL`` to use in the same places as the C ``NULL``. -Like the latter, it is actually defined to be ``ffi.cast("void *", -0)``. For example, reading a NULL pointer returns a ``<cdata 'type *' -NULL>``, which you can check for e.g. by comparing it with -``ffi.NULL``. - -There is no general equivalent to the ``&`` operator in C (because it -would not fit nicely in the model, and it does not seem to be needed -here). There is `ffi.addressof()`__, but only for some cases. You -cannot take the "address" of a number in Python, for example; similarly, -you cannot take the address of a CFFI pointer. If you have this kind -of C code:: - - int x, y; - fetch_size(&x, &y); - - opaque_t *handle; // some opaque pointer - init_stuff(&handle); // initializes the variable 'handle' - more_stuff(handle); // pass the handle around to more functions - -then you need to rewrite it like this, replacing the variables in C -with what is logically pointers to the variables: - -.. code-block:: python - - px = ffi.new("int *") - py = ffi.new("int *") arr = ffi.new("int[2]") - lib.fetch_size(px, py) -OR- lib.fetch_size(arr, arr + 1) - x = px[0] x = arr[0] - y = py[0] y = arr[1] - - p_handle = ffi.new("opaque_t **") - lib.init_stuff(p_handle) # pass the pointer to the 'handle' pointer - handle = p_handle[0] # now we can read 'handle' out of 'p_handle' - lib.more_stuff(handle) - -.. __: ref.html#ffi-addressof - - -Any operation that would in C return a pointer or array or struct type -gives you a fresh cdata object. Unlike the "original" one, these fresh -cdata objects don't have ownership: they are merely references to -existing memory. - -As an exception to the above rule, dereferencing a pointer that owns a -*struct* or *union* object returns a cdata struct or union object -that "co-owns" the same memory. Thus in this case there are two -objects that can keep the same memory alive. This is done for cases where -you really want to have a struct object but don't have any convenient -place to keep alive the original pointer object (returned by -``ffi.new()``). - -Example: - -.. code-block:: python - - # void somefunction(int *); - - x = ffi.new("int *") # allocate one int, and return a pointer to it - x[0] = 42 # fill it - lib.somefunction(x) # call the C function - print x[0] # read the possibly-changed value - -The equivalent of C casts are provided with ``ffi.cast("type", value)``. -They should work in the same cases as they do in C. Additionally, this -is the only way to get cdata objects of integer or floating-point type:: - - >>> x = ffi.cast("int", 42) - >>> x - <cdata 'int' 42> - >>> int(x) - 42 - -To cast a pointer to an int, cast it to ``intptr_t`` or ``uintptr_t``, -which are defined by C to be large enough integer types (example on 32 -bits):: - - >>> int(ffi.cast("intptr_t", pointer_cdata)) # signed - -1340782304 - >>> int(ffi.cast("uintptr_t", pointer_cdata)) # unsigned - 2954184992L - -The initializer given as the optional second argument to ``ffi.new()`` -can be mostly anything that you would use as an initializer for C code, -with lists or tuples instead of using the C syntax ``{ .., .., .. }``. -Example:: - - typedef struct { int x, y; } foo_t; - - foo_t v = { 1, 2 }; // C syntax - v = ffi.new("foo_t *", [1, 2]) # CFFI equivalent - - foo_t v = { .y=1, .x=2 }; // C99 syntax - v = ffi.new("foo_t *", {'y': 1, 'x': 2}) # CFFI equivalent - -Like C, arrays of chars can also be initialized from a string, in -which case a terminating null character is appended implicitly:: - - >>> x = ffi.new("char[]", "hello") - >>> x - <cdata 'char[]' owning 6 bytes> - >>> len(x) # the actual size of the array - 6 - >>> x[5] # the last item in the array - '\x00' - >>> x[0] = 'H' # change the first item - >>> ffi.string(x) # interpret 'x' as a regular null-terminated string - 'Hello' - -Similarly, arrays of wchar_t or char16_t or char32_t can be initialized -from a unicode string, -and calling ``ffi.string()`` on the cdata object returns the current unicode -string stored in the source array (adding surrogates if necessary). -See the `Unicode character types`__ section for more details. - -.. __: ref.html#unichar - -Note that unlike Python lists or tuples, but like C, you *cannot* index in -a C array from the end using negative numbers. - -More generally, the C array types can have their length unspecified in C -types, as long as their length can be derived from the initializer, like -in C:: - - int array[] = { 1, 2, 3, 4 }; // C syntax - array = ffi.new("int[]", [1, 2, 3, 4]) # CFFI equivalent - -As an extension, the initializer can also be just a number, giving -the length (in case you just want zero-initialization):: - - int array[1000]; // C syntax - array = ffi.new("int[1000]") # CFFI 1st equivalent - array = ffi.new("int[]", 1000) # CFFI 2nd equivalent - -This is useful if the length is not actually a constant, to avoid things -like ``ffi.new("int[%d]" % x)``. Indeed, this is not recommended: -``ffi`` normally caches the string ``"int[]"`` to not need to re-parse -it all the time. - -The C99 variable-sized structures are supported too, as long as the -initializer says how long the array should be: - -.. code-block:: python - - # typedef struct { int x; int y[]; } foo_t; - - p = ffi.new("foo_t *", [5, [6, 7, 8]]) # length 3 - p = ffi.new("foo_t *", [5, 3]) # length 3 with 0 in the array - p = ffi.new("foo_t *", {'y': 3}) # length 3 with 0 everywhere - -Finally, note that any Python object used as initializer can also be -used directly without ``ffi.new()`` in assignments to array items or -struct fields. In fact, ``p = ffi.new("T*", initializer)`` is -equivalent to ``p = ffi.new("T*"); p[0] = initializer``. Examples: - -.. code-block:: python - - # if 'p' is a <cdata 'int[5][5]'> - p[2] = [10, 20] # writes to p[2][0] and p[2][1] - - # if 'p' is a <cdata 'foo_t *'>, and foo_t has fields x, y and z - p[0] = {'x': 10, 'z': 20} # writes to p.x and p.z; p.y unmodified - - # if, on the other hand, foo_t has a field 'char a[5]': - p.a = "abc" # writes 'a', 'b', 'c' and '\0'; p.a[4] unmodified - -In function calls, when passing arguments, these rules can be used too; -see `Function calls`_. - - -Python 3 support ----------------- - -Python 3 is supported, but the main point to note is that the ``char`` C -type corresponds to the ``bytes`` Python type, and not ``str``. It is -your responsibility to encode/decode all Python strings to bytes when -passing them to or receiving them from CFFI. - -This only concerns the ``char`` type and derivative types; other parts -of the API that accept strings in Python 2 continue to accept strings in -Python 3. - - -An example of calling a main-like thing ---------------------------------------- - -Imagine we have something like this: - -.. code-block:: python - - from cffi import FFI - ffi = FFI() - ffi.cdef(""" - int main_like(int argv, char *argv[]); - """) - lib = ffi.dlopen("some_library.so") - -Now, everything is simple, except, how do we create the ``char**`` argument -here? -The first idea: - -.. code-block:: python - - lib.main_like(2, ["arg0", "arg1"]) - -does not work, because the initializer receives two Python ``str`` objects -where it was expecting ``<cdata 'char *'>`` objects. You need to use -``ffi.new()`` explicitly to make these objects: - -.. code-block:: python - - lib.main_like(2, [ffi.new("char[]", "arg0"), - ffi.new("char[]", "arg1")]) - -Note that the two ``<cdata 'char[]'>`` objects are kept alive for the -duration of the call: they are only freed when the list itself is freed, -and the list is only freed when the call returns. - -If you want instead to build an "argv" variable that you want to reuse, -then more care is needed: - -.. code-block:: python - - # DOES NOT WORK! - argv = ffi.new("char *[]", [ffi.new("char[]", "arg0"), - ffi.new("char[]", "arg1")]) - -In the above example, the inner "arg0" string is deallocated as soon -as "argv" is built. You have to make sure that you keep a reference -to the inner "char[]" objects, either directly or by keeping the list -alive like this: - -.. code-block:: python - - argv_keepalive = [ffi.new("char[]", "arg0"), - ffi.new("char[]", "arg1")] - argv = ffi.new("char *[]", argv_keepalive) - - -Function calls --------------- - -When calling C functions, passing arguments follows mostly the same -rules as assigning to structure fields, and the return value follows the -same rules as reading a structure field. For example: - -.. code-block:: python - - # int foo(short a, int b); - - n = lib.foo(2, 3) # returns a normal integer - lib.foo(40000, 3) # raises OverflowError - -You can pass to ``char *`` arguments a normal Python string (but don't -pass a normal Python string to functions that take a ``char *`` -argument and may mutate it!): - -.. code-block:: python - - # size_t strlen(const char *); - - assert lib.strlen("hello") == 5 - -You can also pass unicode strings as ``wchar_t *`` or ``char16_t *`` or -``char32_t *`` arguments. Note that -the C language makes no difference between argument declarations that -use ``type *`` or ``type[]``. For example, ``int *`` is fully -equivalent to ``int[]`` (or even ``int[5]``; the 5 is ignored). For CFFI, -this means that you can always pass arguments that can be converted to -either ``int *`` or ``int[]``. For example: - -.. code-block:: python - - # void do_something_with_array(int *array); - - lib.do_something_with_array([1, 2, 3, 4, 5]) # works for int[] - -See `Reference: conversions`__ for a similar way to pass ``struct foo_s -*`` arguments---but in general, it is clearer in this case to pass -``ffi.new('struct foo_s *', initializer)``. - -__ ref.html#conversions - -CFFI supports passing and returning structs and unions to functions and -callbacks. Example: - -.. code-block:: python - - # struct foo_s { int a, b; }; - # struct foo_s function_returning_a_struct(void); - - myfoo = lib.function_returning_a_struct() - # `myfoo`: <cdata 'struct foo_s' owning 8 bytes> - -For performance, non-variadic API-level functions that you get by -writing ``lib.some_function`` are not ``<cdata>`` -objects, but an object of a different type (on CPython, ``<built-in -function>``). This means you cannot pass them directly to some other C -function expecting a function pointer argument. Only ``ffi.typeof()`` -works on them. To get a cdata containing a regular function pointer, -use ``ffi.addressof(lib, "name")``. - -There are a few (obscure) limitations to the supported argument and -return types. These limitations come from libffi and apply only to -calling ``<cdata>`` function pointers; in other words, they don't -apply to non-variadic ``cdef()``-declared functions if you are using -the API mode. The limitations are that you cannot pass directly as -argument or return type: - -* a union (but a *pointer* to a union is fine); - -* a struct which uses bitfields (but a *pointer* to such a struct is - fine); - -* a struct that was declared with "``...``" in the ``cdef()``. - -In API mode, you can work around these limitations: for example, if you -need to call such a function pointer from Python, you can instead write -a custom C function that accepts the function pointer and the real -arguments and that does the call from C. Then declare that custom C -function in the ``cdef()`` and use it from Python. - - -Variadic function calls ------------------------ - -Variadic functions in C (which end with "``...``" as their last -argument) can be declared and called normally, with the exception that -all the arguments passed in the variable part *must* be cdata objects. -This is because it would not be possible to guess, if you wrote this:: - - lib.printf("hello, %d\n", 42) # doesn't work! - -that you really meant the 42 to be passed as a C ``int``, and not a -``long`` or ``long long``. The same issue occurs with ``float`` versus -``double``. So you have to force cdata objects of the C type you want, -if necessary with ``ffi.cast()``: - -.. code-block:: python - - lib.printf("hello, %d\n", ffi.cast("int", 42)) - lib.printf("hello, %ld\n", ffi.cast("long", 42)) - lib.printf("hello, %f\n", ffi.cast("double", 42)) - -But of course: - -.. code-block:: python - - lib.printf("hello, %s\n", ffi.new("char[]", "world")) - -Note that if you are using ``dlopen()``, the function declaration in the -``cdef()`` must match the original one in C exactly, as usual --- in -particular, if this function is variadic in C, then its ``cdef()`` -declaration must also be variadic. You cannot declare it in the -``cdef()`` with fixed arguments instead, even if you plan to only call -it with these argument types. The reason is that some architectures -have a different calling convention depending on whether the function -signature is fixed or not. (On x86-64, the difference can sometimes be -seen in PyPy's JIT-generated code if some arguments are ``double``.) - -Note that the function signature ``int foo();`` is interpreted by CFFI -as equivalent to ``int foo(void);``. This differs from the C standard, -in which ``int foo();`` is really like ``int foo(...);`` and can be -called with any arguments. (This feature of C is a pre-C89 relic: the -arguments cannot be accessed at all in the body of ``foo()`` without -relying on compiler-specific extensions. Nowadays virtually all code -with ``int foo();`` really means ``int foo(void);``.) - - -Memory pressure (PyPy) ----------------------- - -This paragraph applies only to PyPy, because its garbage collector (GC) -is different from CPython's. It is very common in C code to have pairs -of functions, one which performs memory allocations or acquires other -resources, and the other which frees them again. Depending on how you -structure your Python code, the freeing function is only called when the -GC decides a particular (Python) object can be freed. This occurs -notably in these cases: - -* If you use a ``__del__()`` method to call the freeing function. - -* If you use ``ffi.gc()`` without also using ``ffi.release()``. - -* This does not occur if you call the freeing function at a - deterministic time, like in a regular ``try: finally:`` block. It - does however occur *inside a generator---* if the generator is not - explicitly exhausted but forgotten at a ``yield`` point, then the code - in the enclosing ``finally`` block is only invoked at the next GC. - -In these cases, you may have to use the built-in function -``__pypy__.add_memory_pressure(n)``. Its argument ``n`` is an estimate -of how much memory pressure to add. For example, if the pair of C -functions that we are talking about is ``malloc(n)`` and ``free()`` or -similar, you would call ``__pypy__.add_memory_pressure(n)`` after -``malloc(n)``. Doing so is not always a complete answer to the problem, -but it makes the next GC occur earlier, which is often enough. - -The same applies if the memory allocations are indirect, e.g. the C -function allocates some internal data structures. In that case, call -``__pypy__.add_memory_pressure(n)`` with an argument ``n`` that is an -rough estimation. Knowing the exact size is not important, and memory -pressure doesn't have to be manually brought down again after calling -the freeing function. If you are writing wrappers for the allocating / -freeing pair of functions, you should probably call -``__pypy__.add_memory_pressure()`` in the former even if the user may -invoke the latter at a known point with a ``finally:`` block. - -In case this solution is not sufficient, or if the acquired resource is -not memory but something else more limited (like file descriptors), then -there is no better way than restructuring your code to make sure the -freeing function is called at a known point and not indirectly by the -GC. - -Note that in PyPy <= 5.6 the discussion above also applies to -``ffi.new()``. In more recent versions of PyPy, both ``ffi.new()`` and -``ffi.new_allocator()()`` automatically account for the memory pressure -they create. (In case you need to support both older and newer PyPy's, -try calling ``__pypy__.add_memory_pressure()`` anyway; it is better to -overestimate than not account for the memory pressure.) - - -.. _extern-python: -.. _`extern "Python"`: - -Extern "Python" (new-style callbacks) -------------------------------------- - -When the C code needs a pointer to a function which invokes back a -Python function of your choice, here is how you do it in the -out-of-line API mode. The next section about Callbacks_ describes the -ABI-mode solution. - -This is *new in version 1.4.* Use old-style Callbacks_ if backward -compatibility is an issue. (The original callbacks are slower to -invoke and have the same issue as libffi's callbacks; notably, see the -warning__. The new style described in the present section does not -use libffi's callbacks at all.) - -.. __: Callbacks_ - -In the builder script, declare in the cdef a function prefixed with -``extern "Python"``:: - - ffibuilder.cdef(""" - extern "Python" int my_callback(int, int); - - void library_function(int(*callback)(int, int)); - """) - ffibuilder.set_source("_my_example", r""" - #include <some_library.h> - """) - -The function ``my_callback()`` is then implemented in Python inside -your application's code:: - - from _my_example import ffi, lib - - @ffi.def_extern() - def my_callback(x, y): - return 42 - -You obtain a ``<cdata>`` pointer-to-function object by getting -``lib.my_callback``. This ``<cdata>`` can be passed to C code and -then works like a callback: when the C code calls this function -pointer, the Python function ``my_callback`` is called. (You need -to pass ``lib.my_callback`` to C code, and not ``my_callback``: the -latter is just the Python function above, which cannot be passed to C.) - -CFFI implements this by defining ``my_callback`` as a static C -function, written after the ``set_source()`` code. The ``<cdata>`` -then points to this function. What this function does is invoke the -Python function object that is, at runtime, attached with -``@ffi.def_extern()``. - -The ``@ffi.def_extern()`` decorator should be applied to **global -functions,** one for each ``extern "Python"`` function of the same -name. - -To support some corner cases, it is possible to redefine the attached -Python function by calling ``@ffi.def_extern()`` again for the same -name---but this is not recommended! Better attach a single global -Python function for this name, and write it more flexibly in the first -place. This is because each ``extern "Python"`` function turns into -only one C function. Calling ``@ffi.def_extern()`` again changes this -function's C logic to call the new Python function; the old Python -function is not callable any more. The C function pointer you get -from ``lib.my_function`` is always this C function's address, i.e. it -remains the same. - -Extern "Python" and ``void *`` arguments -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -As described just before, you cannot use ``extern "Python"`` to make a -variable number of C function pointers. However, achieving that -result is not possible in pure C code either. For this reason, it is -usual for C to define callbacks with a ``void *data`` argument. You -can use ``ffi.new_handle()`` and ``ffi.from_handle()`` to pass a -Python object through this ``void *`` argument. For example, if the C -type of the callbacks is:: - - typedef void (*event_cb_t)(event_t *evt, void *userdata); - -and you register events by calling this function:: - - void event_cb_register(event_cb_t cb, void *userdata); - -Then you would write this in the build script:: - - ffibuilder.cdef(""" - typedef ... event_t; - typedef void (*event_cb_t)(event_t *evt, void *userdata); - void event_cb_register(event_cb_t cb, void *userdata); - - extern "Python" void my_event_callback(event_t *, void *); - """) - ffibuilder.set_source("_demo_cffi", r""" - #include <the_event_library.h> - """) - -and in your main application you register events like this:: - - from _demo_cffi import ffi, lib - - class Widget(object): - def __init__(self): - userdata = ffi.new_handle(self) - self._userdata = userdata # must keep this alive! - lib.event_cb_register(lib.my_event_callback, userdata) - - def process_event(self, evt): - print "got event!" - - @ffi.def_extern() - def my_event_callback(evt, userdata): - widget = ffi.from_handle(userdata) - widget.process_event(evt) - -Some other libraries don't have an explicit ``void *`` argument, but -let you attach the ``void *`` to an existing structure. For example, -the library might say that ``widget->userdata`` is a generic field -reserved for the application. If the event's signature is now this:: - - typedef void (*event_cb_t)(widget_t *w, event_t *evt); - -Then you can use the ``void *`` field in the low-level -``widget_t *`` like this:: - - from _demo_cffi import ffi, lib - - class Widget(object): - def __init__(self): - ll_widget = lib.new_widget(500, 500) - self.ll_widget = ll_widget # <cdata 'struct widget *'> - userdata = ffi.new_handle(self) - self._userdata = userdata # must still keep this alive! - ll_widget.userdata = userdata # this makes a copy of the "void *" - lib.event_cb_register(ll_widget, lib.my_event_callback) - - def process_event(self, evt): - print "got event!" - - @ffi.def_extern() - def my_event_callback(ll_widget, evt): - widget = ffi.from_handle(ll_widget.userdata) - widget.process_event(evt) - -Extern "Python" accessed from C directly -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In case you want to access some ``extern "Python"`` function directly -from the C code written in ``set_source()``, you need to write a -forward declaration. (By default it needs to be static, but see -`next paragraph`__.) The real implementation of this function -is added by CFFI *after* the C code---this is needed because the -declaration might use types defined by ``set_source()`` -(e.g. ``event_t`` above, from the ``#include``), so it cannot be -generated before. - -.. __: `extern-python-c`_ - -:: - - ffibuilder.set_source("_demo_cffi", r""" - #include <the_event_library.h> - - static void my_event_callback(widget_t *, event_t *); - - /* here you can write C code which uses '&my_event_callback' */ - """) - -This can also be used to write custom C code which calls Python -directly. Here is an example (inefficient in this case, but might be -useful if the logic in ``my_algo()`` is much more complex):: - - ffibuilder.cdef(""" - extern "Python" int f(int); - int my_algo(int); - """) - ffibuilder.set_source("_example_cffi", r""" - static int f(int); /* the forward declaration */ - - static int my_algo(int n) { - int i, sum = 0; - for (i = 0; i < n; i++) - sum += f(i); /* call f() here */ - return sum; - } - """) - -.. _extern-python-c: - -Extern "Python+C" -~~~~~~~~~~~~~~~~~ - -Functions declared with ``extern "Python"`` are generated as -``static`` functions in the C source. However, in some cases it is -convenient to make them non-static, typically when you want to make -them directly callable from other C source files. To do that, you can -say ``extern "Python+C"`` instead of just ``extern "Python"``. *New -in version 1.6.* - -+------------------------------------+--------------------------------------+ -| if the cdef contains | then CFFI generates | -+------------------------------------+--------------------------------------+ -| ``extern "Python" int f(int);`` | ``static int f(int) { /* code */ }`` | -+------------------------------------+--------------------------------------+ -| ``extern "Python+C" int f(int);`` | ``int f(int) { /* code */ }`` | -+------------------------------------+--------------------------------------+ - -The name ``extern "Python+C"`` comes from the fact that we want an -extern function in both senses: as an ``extern "Python"``, and as a -C function that is not static. - -You cannot make CFFI generate additional macros or other -compiler-specific stuff like the GCC ``__attribute__``. You can only -control whether the function should be ``static`` or not. But often, -these attributes must be written alongside the function *header*, and -it is fine if the function *implementation* does not repeat them:: - - ffibuilder.cdef(""" - extern "Python+C" int f(int); /* not static */ - """) - ffibuilder.set_source("_example_cffi", r""" - /* the forward declaration, setting a gcc attribute - (this line could also be in some .h file, to be included - both here and in the other C files of the project) */ - int f(int) __attribute__((visibility("hidden"))); - """) - - -Extern "Python": reference -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -``extern "Python"`` must appear in the cdef(). Like the C++ ``extern -"C"`` syntax, it can also be used with braces around a group of -functions:: - - extern "Python" { - int foo(int); - int bar(int); - } - -The ``extern "Python"`` functions cannot be variadic for now. This -may be implemented in the future. (`This demo`__ shows how to do it -anyway, but it is a bit lengthy.) - -.. __: https://foss.heptapod.net/pypy/cffi/-/blob/branch/default/demo/extern_python_varargs.py - -Each corresponding Python callback function is defined with the -``@ffi.def_extern()`` decorator. Be careful when writing this -function: if it raises an exception, or tries to return an object of -the wrong type, then the exception cannot be propagated. Instead, the -exception is printed to stderr and the C-level callback is made to -return a default value. This can be controlled with ``error`` and -``onerror``, described below. - -.. _def-extern: - -The ``@ffi.def_extern()`` decorator takes these optional arguments: - -* ``name``: the name of the function as written in the cdef. By default - it is taken from the name of the Python function you decorate. - -.. _error_onerror: - -* ``error``: the returned value in case the Python function raises an - exception. It is 0 or null by default. The exception is still - printed to stderr, so this should be used only as a last-resort - solution. - -* ``onerror``: if you want to be sure to catch all exceptions, use - ``@ffi.def_extern(onerror=my_handler)``. If an exception occurs and - ``onerror`` is specified, then ``onerror(exception, exc_value, - traceback)`` is called. This is useful in some situations where you - cannot simply write ``try: except:`` in the main callback function, - because it might not catch exceptions raised by signal handlers: if - a signal occurs while in C, the Python signal handler is called as - soon as possible, which is after entering the callback function but - *before* executing even the ``try:``. If the signal handler raises, - we are not in the ``try: except:`` yet. - - If ``onerror`` is called and returns normally, then it is assumed - that it handled the exception on its own and nothing is printed to - stderr. If ``onerror`` raises, then both tracebacks are printed. - Finally, ``onerror`` can itself provide the result value of the - callback in C, but doesn't have to: if it simply returns None---or - if ``onerror`` itself fails---then the value of ``error`` will be - used, if any. - - Note the following hack: in ``onerror``, you can access the original - callback arguments as follows. First check if ``traceback`` is not - None (it is None e.g. if the whole function ran successfully but - there was an error converting the value returned: this occurs after - the call). If ``traceback`` is not None, then - ``traceback.tb_frame`` is the frame of the outermost function, - i.e. directly the frame of the function decorated with - ``@ffi.def_extern()``. So you can get the value of ``argname`` in - that frame by reading ``traceback.tb_frame.f_locals['argname']``. - - -.. _Callbacks: - -Callbacks (old style) ---------------------- - -Here is how to make a new ``<cdata>`` object that contains a pointer -to a function, where that function invokes back a Python function of -your choice:: - - >>> @ffi.callback("int(int, int)") - >>> def myfunc(x, y): - ... return x + y - ... - >>> myfunc - <cdata 'int(*)(int, int)' calling <function myfunc at 0xf757bbc4>> - -Note that ``"int(*)(int, int)"`` is a C *function pointer* type, whereas -``"int(int, int)"`` is a C *function* type. Either can be specified to -ffi.callback() and the result is the same. - -.. warning:: - - Callbacks are provided for the ABI mode or for backward - compatibility. If you are using the out-of-line API mode, it is - recommended to use the `extern "Python"`_ mechanism instead of - callbacks: it gives faster and cleaner code. It also avoids several - issues with old-style callbacks: - - - On less common architecture, libffi is more likely to crash on - callbacks (`e.g. on NetBSD`__); - - - On hardened systems like PAX and SELinux, the extra memory - protections can interfere (for example, on SELinux you need to - run with ``deny_execmem`` set to ``off``). - - - `On Mac OS X,`__ you need to give your application the entitlement - ``com.apple.security.cs.allow-unsigned-executable-memory``. - - Note also that a cffi fix for this issue was attempted---see - the ``ffi_closure_alloc`` branch---but was not merged because it - creates potential `memory corruption`__ with ``fork()``. - - In other words: yes, it is dangerous to allow write+execute memory in your - program; that's why the various "hardening" options above exist. But at - the same time, these options open wide the door to another attack: if the - program forks and then attempts to call any of the ``ffi.callback()``, then - this immediately results in a crash---or, with a minimal amount of work - from an attacker, arbitrary code execution. To me it sounds even more - dangerous than the original problem, and that's why cffi is not playing - along. - - To fix the issue once and for all on the affected platforms, you need - to refactor the involved code so that it no longer uses ``ffi.callback()``. - -.. __: https://github.com/pyca/pyopenssl/issues/596 -.. __: https://foss.heptapod.net/pypy/cffi/-/issues/391 -.. __: https://bugzilla.redhat.com/show_bug.cgi?id=1249685 - -Warning: like ffi.new(), ffi.callback() returns a cdata that has -ownership of its C data. (In this case, the necessary C data contains -the libffi data structures to do a callback.) This means that the -callback can only be invoked as long as this cdata object is alive. -If you store the function pointer into C code, then make sure you also -keep this object alive for as long as the callback may be invoked. -The easiest way to do that is to always use ``@ffi.callback()`` at -module-level only, and to pass "context" information around with -`ffi.new_handle()`__, if possible. Example: - -.. __: ref.html#new-handle - -.. code-block:: python - - # a good way to use this decorator is once at global level - @ffi.callback("int(int, void *)") - def my_global_callback(x, handle): - return ffi.from_handle(handle).some_method(x) - - - class Foo(object): - - def __init__(self): - handle = ffi.new_handle(self) - self._handle = handle # must be kept alive - lib.register_stuff_with_callback_and_voidp_arg(my_global_callback, handle) - - def some_method(self, x): - print "method called!" - -(See also the section about `extern "Python"`_ above, where the same -general style is used.) - -Note that callbacks of a variadic function type are not supported. A -workaround is to add custom C code. In the following example, a -callback gets a first argument that counts how many extra ``int`` -arguments are passed: - -.. code-block:: python - - # file "example_build.py" - - import cffi - - ffibuilder = cffi.FFI() - ffibuilder.cdef(""" - int (*python_callback)(int how_many, int *values); - void *const c_callback; /* pass this const ptr to C routines */ - """) - ffibuilder.set_source("_example", r""" - #include <stdarg.h> - #include <alloca.h> - static int (*python_callback)(int how_many, int *values); - static int c_callback(int how_many, ...) { - va_list ap; - /* collect the "..." arguments into the values[] array */ - int i, *values = alloca(how_many * sizeof(int)); - va_start(ap, how_many); - for (i=0; i<how_many; i++) - values[i] = va_arg(ap, int); - va_end(ap); - return python_callback(how_many, values); - } - """) - ffibuilder.compile(verbose=True) - -.. code-block:: python - - # file "example.py" - - from _example import ffi, lib - - @ffi.callback("int(int, int *)") - def python_callback(how_many, values): - print ffi.unpack(values, how_many) - return 0 - lib.python_callback = python_callback - -Deprecated: you can also use ``ffi.callback()`` not as a decorator but -directly as ``ffi.callback("int(int, int)", myfunc)``. This is -discouraged: using this a style, we are more likely to forget the -callback object too early, when it is still in use. - -The ``ffi.callback()`` decorator also accepts the optional argument -``error``, and from CFFI version 1.2 the optional argument ``onerror``. -These two work in the same way as `described above for extern "Python".`__ - -.. __: error_onerror_ - - - -Windows: calling conventions ----------------------------- - -On Win32, functions can have two main calling conventions: either -"cdecl" (the default), or "stdcall" (also known as "WINAPI"). There -are also other rare calling conventions, but these are not supported. -*New in version 1.3.* - -When you issue calls from Python to C, the implementation is such that -it works with any of these two main calling conventions; you don't -have to specify it. However, if you manipulate variables of type -"function pointer" or declare callbacks, then the calling convention -must be correct. This is done by writing ``__cdecl`` or ``__stdcall`` -in the type, like in C:: - - @ffi.callback("int __stdcall(int, int)") - def AddNumbers(x, y): - return x + y - -or:: - - ffibuilder.cdef(""" - struct foo_s { - int (__stdcall *MyFuncPtr)(int, int); - }; - """) - -``__cdecl`` is supported but is always the default so it can be left -out. In the ``cdef()``, you can also use ``WINAPI`` as equivalent to -``__stdcall``. As mentioned above, it is mostly not needed (but doesn't -hurt) to say ``WINAPI`` or ``__stdcall`` when declaring a plain -function in the ``cdef()``. (The difference can still be seen if you -take explicitly a pointer to this function with ``ffi.addressof()``, -or if the function is ``extern "Python"``.) - -These calling convention specifiers are accepted but ignored on any -platform other than 32-bit Windows. - -In CFFI versions before 1.3, the calling convention specifiers are not -recognized. In API mode, you could work around it by using an -indirection, like in the example in the section about Callbacks_ -(``"example_build.py"``). There was no way to use stdcall callbacks -in ABI mode. - - -FFI Interface -------------- - -(The reference for the FFI interface has been moved to the `next page`__.) - -.. __: ref.html diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst deleted file mode 100644 index aa7f2fe..0000000 --- a/doc/source/whatsnew.rst +++ /dev/null @@ -1,887 +0,0 @@ -====================== -What's New -====================== - -v1.15.0 -======= - -* Fixed MANIFEST.in to include missing file for Windows arm64 support - -* Fixed Linux wheel build to use gcc default ISA for libffi - -* Updated setup.py Python trove specifiers to currently-tested Python versions - -* CPython 3.10 support (including wheels) - -* MacOS arm64 support (including wheels) - -* Initial Windows arm64 support - -* Misc. doc and test updates - -v1.14.6 -======= - -* Test fixes for CPython 3.10.0b3 - -* Support for `sys.unraisablehook()` on Python >= 3.8 - -* Fix two minor memory leaks (thanks Sebastian!) - -* Like many projects that had an IRC channel on freenode, we moved it to - ``irc.libera.chat``. - -v1.14.5 -======= - -* Source fix for old gcc versions - -* This and future releases should include wheels on more platforms, - thanks to our new release managers Matt and Matt! - -v1.14.4 -======= - -Release done for pip reasons. - -v1.14.3 -======= - -Release done for pip reasons. - -v1.14.2 -======= - -* CPython 3 on Windows: we again try to compile with ``Py_LIMITED_API`` - by default. This flag is not added if you run the compilation with - CPython 3.4, as it only works with CPython >= 3.5, but by now this - version of Python is quite old (and we no longer distribute cffi - wheels for it). - - This may require that you upgrade ``virtualenv`` (requires version 16 - or newer) or at least copy manually ``python3.dll`` into your existing - virtualenvs. For distributing wheels with your cffi modules, you may - also need to upgrade ``wheel`` to the just-released version 0.35. - - You can manually disable ``Py_LIMITED_API`` by calling - ``ffi.set_source(..., py_limited_api=False)``. - - -v1.14.1 -======= - -* CFFI source code is now `hosted on Heptapod`_. - -* Improved support for ``typedef int my_array_t[...];`` with an explicit - dot-dot-dot in API mode (`issue #453`_) - -* Windows (32 and 64 bits): multiple fixes for ABI-mode call to functions - that return a structure. - -* Experimental support for MacOS 11 on aarch64. - -* and a few other minor changes and bug fixes. - -.. _`hosted on Heptapod`: https://foss.heptapod.net/pypy/cffi/ -.. _`issue #453`: https://foss.heptapod.net/pypy/cffi/issues/453 - - -v1.14 -===== - -* ``ffi.dlopen()`` can now be called with a handle (as a ``void *``) to an - already-opened C library. - -* CPython only: fixed a stack overflow issue for calls like - ``lib.myfunc([large list])``. If the function is declared as taking a - ``float *`` argument, for example, then the array is temporarily converted - into a C array of floats---however, the code used to use ``alloca()`` for - this temporary storage, no matter how large. This is now fixed. - - The fix concerns all modes: in-line/out-of-line API/ABI. Also note that your - API-mode C extension modules need to be regenerated with cffi 1.14 in order - to get the fix; i.e. for API mode, the fix is in the generated C sources. - (The C sources generated from cffi 1.14 should also work when running in - a different environment in which we have an older version of cffi. Also, - this change makes no difference on PyPy.) - - As a workaround that works on all versions of cffi, you can write - ``lib.myfunc(ffi.new("float[]", [large list]))``, which is - equivalent but explicity builds the intermediate array as a regular - Python object on the heap. - -* fixed a memory leak inside ``ffi.getwinerror()`` on CPython 3.x. - - -v1.13.2 -======= - -* re-release because the Linux wheels came with an attached version of libffi - that was very old and buggy (`issue #432`_). - -.. _`issue #432`: https://foss.heptapod.net/pypy/cffi/-/issues/432 - - - -v1.13.1 -======= - -* deprecate the way to declare in ``cdef()`` a global variable with only - ``void *foo;``. You should always use a storage class, like ``extern void - *foo;`` or maybe ``static void *foo;``. These are all equivalent for - the purposes of ``cdef()``, but the reason for deprecating the bare version - is that (as far as I know) it would always be mistake in a real C header. - -* fix the regression ``RuntimeError: found a situation in which we try - to build a type recursively`` (`issue #429`_). - -* fixed `issue #427`_ where a multithreading mistake in the embedding logic - initialization code would cause deadlocks on CPython 3.7. - -.. _`issue #429`: https://foss.heptapod.net/pypy/cffi/-/issues/429 -.. _`issue #427`: https://foss.heptapod.net/pypy/cffi/-/issues/427 - - -v1.13 -===== - -* ``ffi.from_buffer("type *", ..)`` is now supported, in addition to - ``"type[]"``. You can then write ``p.field`` to access the items, instead - of only ``p[0].field``. Be careful that no bounds checking is performed, so - ``p[n]`` might access data out of bounds. - -* fix for structs containing unnamed bitfields like ``int : 1;``. - -* when calling cdata of "function pointer" type, give a RuntimeError instead - of a crash if the pointer happens to be NULL - -* support some more binary operations between constants in enum definitions - (PR #96) - -* silence a warning incorrectly emitted if you use a quote in a preprocessor - line - -* detect a corner case that would throw the C code into an infinite - recursion, with ``ffi.cdef("""struct X { void(*fnptr)(struct X); };""")`` - - -Older Versions -============== - -v1.12.3 -------- - -* Fix for nested struct types that end in a var-sized array (#405). - -* Add support for using ``U`` and ``L`` characters at the end of integer - constants in ``ffi.cdef()`` (thanks Guillaume). - -* More 3.8 fixes. - - -v1.12.2 -------- - -* Added temporary workaround to compile on CPython 3.8.0a2. - - -v1.12.1 -------- - -* CPython 3 on Windows: we again no longer compile with ``Py_LIMITED_API`` - by default because such modules *still* cannot be used with virtualenv. - The problem is that it doesn't work in CPython <= 3.4, and for - technical reason we can't enable this flag automatically based on the - version of Python. - - Like before, `Issue #350`_ mentions a workaround if you still want - the ``Py_LIMITED_API`` flag and *either* you are not concerned about - virtualenv *or* you are sure your module will not be used on CPython - <= 3.4: pass ``define_macros=[("Py_LIMITED_API", None)]`` as a keyword to the - ``ffibuilder.set_source()`` call. - - -v1.12 ------ - -* `Direct support for pkg-config`__. - -* ``ffi.from_buffer()`` takes a new optional *first* argument that gives - the array type of the result. It also takes an optional keyword argument - ``require_writable`` to refuse read-only Python buffers. - -* ``ffi.new()``, ``ffi.gc()`` or ``ffi.from_buffer()`` cdata objects - can now be released at known times, either by using the ``with`` - keyword or by calling the new ``ffi.release()``. - -* Windows, CPython 3.x: cffi modules are linked with ``python3.dll`` - again. This makes them independant on the exact CPython version, - like they are on other platforms. **It requires virtualenv 16.0.0.** - -* Accept an expression like ``ffi.new("int[4]", p)`` if ``p`` is itself - another cdata ``int[4]``. - -* CPython 2.x: ``ffi.dlopen()`` failed with non-ascii file names on Posix - -* CPython: if a thread is started from C and then runs Python code (with - callbacks or with the embedding solution), then previous versions of - cffi would contain possible crashes and/or memory leaks. Hopefully, - this has been fixed (see `issue #362`_). - -* Support for ``ffi.cdef(..., pack=N)`` where N is a power of two. - Means to emulate ``#pragma pack(N)`` on MSVC. Also, the default on - Windows is now ``pack=8``, like on MSVC. This might make a difference - in corner cases, although I can't think of one in the context of CFFI. - The old way ``ffi.cdef(..., packed=True)`` remains and is equivalent - to ``pack=1`` (saying e.g. that fields like ``int`` should be aligned - to 1 byte instead of 4). - -.. __: cdef.html#pkgconfig -.. _`issue #362`: https://foss.heptapod.net/pypy/cffi/-/issues/362 - - -v1.11.5 -------- - -* `Issue #357`_: fix ``ffi.emit_python_code()`` which generated a buggy - Python file if you are using a ``struct`` with an anonymous ``union`` - field or vice-versa. - -* Windows: ``ffi.dlopen()`` should now handle unicode filenames. - -* ABI mode: implemented ``ffi.dlclose()`` for the in-line case (it used - to be present only in the out-of-line case). - -* Fixed a corner case for ``setup.py install --record=xx --root=yy`` - with an out-of-line ABI module. Also fixed `Issue #345`_. - -* More hacks on Windows for running CFFI's own ``setup.py``. - -* `Issue #358`_: in embedding, to protect against (the rare case of) - Python initialization from several threads in parallel, we have to use - a spin-lock. On CPython 3 it is worse because it might spin-lock for - a long time (execution of ``Py_InitializeEx()``). Sadly, recent - changes to CPython make that solution needed on CPython 2 too. - -* CPython 3 on Windows: we no longer compile with ``Py_LIMITED_API`` - by default because such modules cannot be used with virtualenv. - `Issue #350`_ mentions a workaround if you still want that and are not - concerned about virtualenv: pass ``define_macros=[("Py_LIMITED_API", - None)]`` as a keyword to the ``ffibuilder.set_source()`` call. - -.. _`Issue #345`: https://foss.heptapod.net/pypy/cffi/-/issues/345 -.. _`Issue #350`: https://foss.heptapod.net/pypy/cffi/-/issues/350 -.. _`Issue #358`: https://foss.heptapod.net/pypy/cffi/-/issues/358 -.. _`Issue #357`: https://foss.heptapod.net/pypy/cffi/-/issues/357 - - -v1.11.4 -------- - -* Windows: reverted linking with ``python3.dll``, because - virtualenv does not make this DLL available to virtual environments - for now. See `Issue #355`_. On Windows only, the C extension - modules created by cffi follow for now the standard naming scheme - ``foo.cp36-win32.pyd``, to make it clear that they are regular - CPython modules depending on ``python36.dll``. - -.. _`Issue #355`: https://foss.heptapod.net/pypy/cffi/-/issues/355 - - -v1.11.3 -------- - -* Fix on CPython 3.x: reading the attributes ``__loader__`` or - ``__spec__`` from the cffi-generated lib modules gave a buggy - SystemError. (These attributes are always None, and provided only to - help compatibility with tools that expect them in all modules.) - -* More Windows fixes: workaround for MSVC not supporting large - literal strings in C code (from - ``ffi.embedding_init_code(large_string)``); and an issue with - ``Py_LIMITED_API`` linking with ``python35.dll/python36.dll`` instead - of ``python3.dll``. - -* Small documentation improvements. - - -v1.11.2 -------- - -* Fix Windows issue with managing the thread-state on CPython 3.0 to 3.5 - - -v1.11.1 -------- - -* Fix tests, remove deprecated C API usage - -* Fix (hack) for 3.6.0/3.6.1/3.6.2 giving incompatible binary extensions - (cpython issue `#29943`_) - -* Fix for 3.7.0a1+ - -.. _`#29943`: https://bugs.python.org/issue29943 - - -v1.11 ------ - -* Support the modern standard types ``char16_t`` and ``char32_t``. - These work like ``wchar_t``: they represent one unicode character, or - when used as ``charN_t *`` or ``charN_t[]`` they represent a unicode - string. The difference with ``wchar_t`` is that they have a known, - fixed size. They should work at all places that used to work with - ``wchar_t`` (please report an issue if I missed something). Note - that with ``set_source()``, you need to make sure that these types are - actually defined by the C source you provide (if used in ``cdef()``). - -* Support the C99 types ``float _Complex`` and ``double _Complex``. - Note that libffi doesn't support them, which means that in the ABI - mode you still cannot call C functions that take complex numbers - directly as arguments or return type. - -* Fixed a rare race condition when creating multiple ``FFI`` instances - from multiple threads. (Note that you aren't meant to create many - ``FFI`` instances: in inline mode, you should write ``ffi = - cffi.FFI()`` at module level just after ``import cffi``; and in - out-of-line mode you don't instantiate ``FFI`` explicitly at all.) - -* Windows: using callbacks can be messy because the CFFI internal error - messages show up to stderr---but stderr goes nowhere in many - applications. This makes it particularly hard to get started with the - embedding mode. (Once you get started, you can at least use - ``@ffi.def_extern(onerror=...)`` and send the error logs where it - makes sense for your application, or record them in log files, and so - on.) So what is new in CFFI is that now, on Windows CFFI will try to - open a non-modal MessageBox (in addition to sending raw messages to - stderr). The MessageBox is only visible if the process stays alive: - typically, console applications that crash close immediately, but that - is also the situation where stderr should be visible anyway. - -* Progress on support for `callbacks in NetBSD`__. - -* Functions returning booleans would in some case still return 0 or 1 - instead of False or True. Fixed. - -* `ffi.gc()`__ now takes an optional third parameter, which gives an - estimate of the size (in bytes) of the object. So far, this is only - used by PyPy, to make the next GC occur more quickly (`issue #320`__). - In the future, this might have an effect on CPython too (provided - the CPython `issue 31105`__ is addressed). - -* Add a note to the documentation: the ABI mode gives function objects - that are *slower* to call than the API mode does. For some reason it - is often thought to be faster. It is not! - -.. __: https://foss.heptapod.net/pypy/cffi/-/issues/321 -.. __: ref.html#ffi-gc -.. __: https://foss.heptapod.net/pypy/cffi/-/issues/320 -.. __: http://bugs.python.org/issue31105 - - -v1.10.1 -------- - -(only released inside PyPy 5.8.0) - -* Fixed the line numbers reported in case of ``cdef()`` errors. - Also, I just noticed, but pycparser always supported the preprocessor - directive ``# 42 "foo.h"`` to mean "from the next line, we're in file - foo.h starting from line 42", which it puts in the error messages. - - -v1.10 ------ - -* Issue #295: use calloc() directly instead of - PyObject_Malloc()+memset() to handle ffi.new() with a default - allocator. Speeds up ``ffi.new(large-array)`` where most of the time - you never touch most of the array. - -* Some OS/X build fixes ("only with Xcode but without CLT"). - -* Improve a couple of error messages: when getting mismatched versions - of cffi and its backend; and when calling functions which cannot be - called with libffi because an argument is a struct that is "too - complicated" (and not a struct *pointer*, which always works). - -* Add support for some unusual compilers (non-msvc, non-gcc, non-icc, - non-clang) - -* Implemented the remaining cases for ``ffi.from_buffer``. Now all - buffer/memoryview objects can be passed. The one remaining check is - against passing unicode strings in Python 2. (They support the buffer - interface, but that gives the raw bytes behind the UTF16/UCS4 storage, - which is most of the times not what you expect. In Python 3 this has - been fixed and the unicode strings don't support the memoryview - interface any more.) - -* The C type ``_Bool`` or ``bool`` now converts to a Python boolean - when reading, instead of the content of the byte as an integer. The - potential incompatibility here is what occurs if the byte contains a - value different from 0 and 1. Previously, it would just return it; - with this change, CFFI raises an exception in this case. But this - case means "undefined behavior" in C; if you really have to interface - with a library relying on this, don't use ``bool`` in the CFFI side. - Also, it is still valid to use a byte string as initializer for a - ``bool[]``, but now it must only contain ``\x00`` or ``\x01``. As an - aside, ``ffi.string()`` no longer works on ``bool[]`` (but it never - made much sense, as this function stops at the first zero). - -* ``ffi.buffer`` is now the name of cffi's buffer type, and - ``ffi.buffer()`` works like before but is the constructor of that type. - -* ``ffi.addressof(lib, "name")`` now works also in in-line mode, not - only in out-of-line mode. This is useful for taking the address of - global variables. - -* Issue #255: ``cdata`` objects of a primitive type (integers, floats, - char) are now compared and ordered by value. For example, ``<cdata - 'int' 42>`` compares equal to ``42`` and ``<cdata 'char' b'A'>`` - compares equal to ``b'A'``. Unlike C, ``<cdata 'int' -1>`` does not - compare equal to ``ffi.cast("unsigned int", -1)``: it compares - smaller, because ``-1 < 4294967295``. - -* PyPy: ``ffi.new()`` and ``ffi.new_allocator()()`` did not record - "memory pressure", causing the GC to run too infrequently if you call - ``ffi.new()`` very often and/or with large arrays. Fixed in PyPy 5.7. - -* Support in ``ffi.cdef()`` for numeric expressions with ``+`` or - ``-``. Assumes that there is no overflow; it should be fixed first - before we add more general support for arbitrary arithmetic on - constants. - - -v1.9 ----- - -* Structs with variable-sized arrays as their last field: now we track - the length of the array after ``ffi.new()`` is called, just like we - always tracked the length of ``ffi.new("int[]", 42)``. This lets us - detect out-of-range accesses to array items. This also lets us - display a better ``repr()``, and have the total size returned by - ``ffi.sizeof()`` and ``ffi.buffer()``. Previously both functions - would return a result based on the size of the declared structure - type, with an assumed empty array. (Thanks andrew for starting this - refactoring.) - -* Add support in ``cdef()/set_source()`` for unspecified-length arrays - in typedefs: ``typedef int foo_t[...];``. It was already supported - for global variables or structure fields. - -* I turned in v1.8 a warning from ``cffi/model.py`` into an error: - ``'enum xxx' has no values explicitly defined: refusing to guess which - integer type it is meant to be (unsigned/signed, int/long)``. Now I'm - turning it back to a warning again; it seems that guessing that the - enum has size ``int`` is a 99%-safe bet. (But not 100%, so it stays - as a warning.) - -* Fix leaks in the code handling ``FILE *`` arguments. In CPython 3 - there is a remaining issue that is hard to fix: if you pass a Python - file object to a ``FILE *`` argument, then ``os.dup()`` is used and - the new file descriptor is only closed when the GC reclaims the Python - file object---and not at the earlier time when you call ``close()``, - which only closes the original file descriptor. If this is an issue, - you should avoid this automatic convertion of Python file objects: - instead, explicitly manipulate file descriptors and call ``fdopen()`` - from C (...via cffi). - - -v1.8.3 ------- - -* When passing a ``void *`` argument to a function with a different - pointer type, or vice-versa, the cast occurs automatically, like in C. - The same occurs for initialization with ``ffi.new()`` and a few other - places. However, I thought that ``char *`` had the same - property---but I was mistaken. In C you get the usual warning if you - try to give a ``char *`` to a ``char **`` argument, for example. - Sorry about the confusion. This has been fixed in CFFI by giving for - now a warning, too. It will turn into an error in a future version. - - -v1.8.2 ------- - -* Issue #283: fixed ``ffi.new()`` on structures/unions with nested - anonymous structures/unions, when there is at least one union in - the mix. When initialized with a list or a dict, it should now - behave more closely like the ``{ }`` syntax does in GCC. - - -v1.8.1 ------- - -* CPython 3.x: experimental: the generated C extension modules now use - the "limited API", which means that, as a compiled .so/.dll, it should - work directly on any version of CPython >= 3.2. The name produced by - distutils is still version-specific. To get the version-independent - name, you can rename it manually to ``NAME.abi3.so``, or use the very - recent setuptools 26. - -* Added ``ffi.compile(debug=...)``, similar to ``python setup.py build - --debug`` but defaulting to True if we are running a debugging - version of Python itself. - - -v1.8 ----- - -* Removed the restriction that ``ffi.from_buffer()`` cannot be used on - byte strings. Now you can get a ``char *`` out of a byte string, - which is valid as long as the string object is kept alive. (But - don't use it to *modify* the string object! If you need this, use - ``bytearray`` or other official techniques.) - -* PyPy 5.4 can now pass a byte string directly to a ``char *`` - argument (in older versions, a copy would be made). This used to be - a CPython-only optimization. - - -v1.7 ----- - -* ``ffi.gc(p, None)`` removes the destructor on an object previously - created by another call to ``ffi.gc()`` - -* ``bool(ffi.cast("primitive type", x))`` now returns False if the - value is zero (including ``-0.0``), and True otherwise. Previously - this would only return False for cdata objects of a pointer type when - the pointer is NULL. - -* bytearrays: ``ffi.from_buffer(bytearray-object)`` is now supported. - (The reason it was not supported was that it was hard to do in PyPy, - but it works since PyPy 5.3.) To call a C function with a ``char *`` - argument from a buffer object---now including bytearrays---you write - ``lib.foo(ffi.from_buffer(x))``. Additionally, this is now supported: - ``p[0:length] = bytearray-object``. The problem with this was that a - iterating over bytearrays gives *numbers* instead of *characters*. - (Now it is implemented with just a memcpy, of course, not actually - iterating over the characters.) - -* C++: compiling the generated C code with C++ was supposed to work, - but failed if you make use the ``bool`` type (because that is rendered - as the C ``_Bool`` type, which doesn't exist in C++). - -* ``help(lib)`` and ``help(lib.myfunc)`` now give useful information, - as well as ``dir(p)`` where ``p`` is a struct or pointer-to-struct. - - -v1.6 ----- - -* `ffi.list_types()`_ - -* `ffi.unpack()`_ - -* `extern "Python+C"`_ - -* in API mode, ``lib.foo.__doc__`` contains the C signature now. On - CPython you can say ``help(lib.foo)``, but for some reason - ``help(lib)`` (or ``help(lib.foo)`` on PyPy) is still useless; I - haven't yet figured out the hacks needed to convince ``pydoc`` to - show more. (You can use ``dir(lib)`` but it is not most helpful.) - -* Yet another attempt at robustness of ``ffi.def_extern()`` against - CPython's interpreter shutdown logic. - -.. _`ffi.list_types()`: ref.html#ffi-list-types -.. _`ffi.unpack()`: ref.html#ffi-unpack -.. _`extern "Python+C"`: using.html#extern-python-c - - -v1.5.2 ------- - -* Fix 1.5.1 for Python 2.6. - - -v1.5.1 ------- - -* A few installation-time tweaks (thanks Stefano!) - -* Issue #245: Win32: ``__stdcall`` was never generated for - ``extern "Python"`` functions - -* Issue #246: trying to be more robust against CPython's fragile - interpreter shutdown logic - - -v1.5.0 ------- - -* Support for `using CFFI for embedding`__. - -.. __: embedding.html - - -v1.4.2 ------- - -Nothing changed from v1.4.1. - - -v1.4.1 ------- - -* Fix the compilation failure of cffi on CPython 3.5.0. (3.5.1 works; - some detail changed that makes some underscore-starting macros - disappear from view of extension modules, and I worked around it, - thinking it changed in all 3.5 versions---but no: it was only in - 3.5.1.) - - -v1.4.0 ------- - -* A `better way to do callbacks`__ has been added (faster and more - portable, and usually cleaner). It is a mechanism for the - out-of-line API mode that replaces the dynamic creation of callback - objects (i.e. C functions that invoke Python) with the static - declaration in ``cdef()`` of which callbacks are needed. This is - more C-like, in that you have to structure your code around the idea - that you get a fixed number of function pointers, instead of - creating them on-the-fly. - -* ``ffi.compile()`` now takes an optional ``verbose`` argument. When - ``True``, distutils prints the calls to the compiler. - -* ``ffi.compile()`` used to fail if given ``sources`` with a path that - includes ``".."``. Fixed. - -* ``ffi.init_once()`` added. See docs__. - -* ``dir(lib)`` now works on libs returned by ``ffi.dlopen()`` too. - -* Cleaned up and modernized the content of the ``demo`` subdirectory - in the sources (thanks matti!). - -* ``ffi.new_handle()`` is now guaranteed to return unique ``void *`` - values, even if called twice on the same object. Previously, in - that case, CPython would return two ``cdata`` objects with the same - ``void *`` value. This change is useful to add and remove handles - from a global dict (or set) without worrying about duplicates. - It already used to work like that on PyPy. - *This change can break code that used to work on CPython by relying - on the object to be kept alive by other means than keeping the - result of ffi.new_handle() alive.* (The corresponding `warning in - the docs`__ of ``ffi.new_handle()`` has been here since v0.8!) - -.. __: using.html#extern-python -.. __: ref.html#ffi-init-once -.. __: ref.html#ffi-new-handle - - -v1.3.1 ------- - -* The optional typedefs (``bool``, ``FILE`` and all Windows types) were - not always available from out-of-line FFI objects. - -* Opaque enums are phased out from the cdefs: they now give a warning, - instead of (possibly wrongly) being assumed equal to ``unsigned int``. - Please report if you get a reasonable use case for them. - -* Some parsing details, notably ``volatile`` is passed along like - ``const`` and ``restrict``. Also, older versions of pycparser - mis-parse some pointer-to-pointer types like ``char * const *``: the - "const" ends up at the wrong place. Added a workaround. - - -v1.3.0 ------- - -* Added `ffi.memmove()`_. - -* Pull request #64: out-of-line API mode: we can now declare - floating-point types with ``typedef float... foo_t;``. This only - works if ``foo_t`` is a float or a double, not ``long double``. - -* Issue #217: fix possible unaligned pointer manipulation, which crashes - on some architectures (64-bit, non-x86). - -* Issues #64 and #126: when using ``set_source()`` or ``verify()``, - the ``const`` and ``restrict`` keywords are copied from the cdef - to the generated C code; this fixes warnings by the C compiler. - It also fixes corner cases like ``typedef const int T; T a;`` - which would previously not consider ``a`` as a constant. (The - cdata objects themselves are never ``const``.) - -* Win32: support for ``__stdcall``. For callbacks and function - pointers; regular C functions still don't need to have their `calling - convention`_ declared. - -* Windows: CPython 2.7 distutils doesn't work with Microsoft's official - Visual Studio for Python, and I'm told this is `not a bug`__. For - ffi.compile(), we `removed a workaround`__ that was inside cffi but - which had unwanted side-effects. Try saying ``import setuptools`` - first, which patches distutils... - -.. _`ffi.memmove()`: ref.html#ffi-memmove -.. __: https://bugs.python.org/issue23246 -.. __: https://bitbucket.org/cffi/cffi/pull-requests/65/remove-_hack_at_distutils-which-imports/diff -.. _`calling convention`: using.html#windows-calling-conventions - - -v1.2.1 ------- - -Nothing changed from v1.2.0. - - -v1.2.0 ------- - -* Out-of-line mode: ``int a[][...];`` can be used to declare a structure - field or global variable which is, simultaneously, of total length - unknown to the C compiler (the ``a[]`` part) and each element is - itself an array of N integers, where the value of N *is* known to the - C compiler (the ``int`` and ``[...]`` parts around it). Similarly, - ``int a[5][...];`` is supported (but probably less useful: remember - that in C it means ``int (a[5])[...];``). - -* PyPy: the ``lib.some_function`` objects were missing the attributes - ``__name__``, ``__module__`` and ``__doc__`` that are expected e.g. by - some decorators-management functions from ``functools``. - -* Out-of-line API mode: you can now do ``from _example.lib import x`` - to import the name ``x`` from ``_example.lib``, even though the - ``lib`` object is not a standard module object. (Also works in ``from - _example.lib import *``, but this is even more of a hack and will fail - if ``lib`` happens to declare a name called ``__all__``. Note that - ``*`` excludes the global variables; only the functions and constants - make sense to import like this.) - -* ``lib.__dict__`` works again and gives you a copy of the - dict---assuming that ``lib`` has got no symbol called precisely - ``__dict__``. (In general, it is safer to use ``dir(lib)``.) - -* Out-of-line API mode: global variables are now fetched on demand at - every access. It fixes issue #212 (Windows DLL variables), and also - allows variables that are defined as dynamic macros (like ``errno``) - or ``__thread`` -local variables. (This change might also tighten - the C compiler's check on the variables' type.) - -* Issue #209: dereferencing NULL pointers now raises RuntimeError - instead of segfaulting. Meant as a debugging aid. The check is - only for NULL: if you dereference random or dead pointers you might - still get segfaults. - -* Issue #152: callbacks__: added an argument ``ffi.callback(..., - onerror=...)``. If the main callback function raises an exception - and ``onerror`` is provided, then ``onerror(exception, exc_value, - traceback)`` is called. This is similar to writing a ``try: - except:`` in the main callback function, but in some cases (e.g. a - signal) an exception can occur at the very start of the callback - function---before it had time to enter the ``try: except:`` block. - -* Issue #115: added ``ffi.new_allocator()``, which officializes - support for `alternative allocators`__. - -.. __: using.html#callbacks -.. __: ref.html#ffi-new-allocator - - -v1.1.2 ------- - -* ``ffi.gc()``: fixed a race condition in multithreaded programs - introduced in 1.1.1 - - -v1.1.1 ------- - -* Out-of-line mode: ``ffi.string()``, ``ffi.buffer()`` and - ``ffi.getwinerror()`` didn't accept their arguments as keyword - arguments, unlike their in-line mode equivalent. (It worked in PyPy.) - -* Out-of-line ABI mode: documented a restriction__ of ``ffi.dlopen()`` - when compared to the in-line mode. - -* ``ffi.gc()``: when called several times with equal pointers, it was - accidentally registering only the last destructor, or even none at - all depending on details. (It was correctly registering all of them - only in PyPy, and only with the out-of-line FFIs.) - -.. __: cdef.html#dlopen-note - - -v1.1.0 ------- - -* Out-of-line API mode: we can now declare integer types with - ``typedef int... foo_t;``. The exact size and signedness of ``foo_t`` - is figured out by the compiler. - -* Out-of-line API mode: we can now declare multidimensional arrays - (as fields or as globals) with ``int n[...][...]``. Before, only the - outermost dimension would support the ``...`` syntax. - -* Out-of-line ABI mode: we now support any constant declaration, - instead of only integers whose value is given in the cdef. Such "new" - constants, i.e. either non-integers or without a value given in the - cdef, must correspond to actual symbols in the lib. At runtime they - are looked up the first time we access them. This is useful if the - library defines ``extern const sometype somename;``. - -* ``ffi.addressof(lib, "func_name")`` now returns a regular cdata object - of type "pointer to function". You can use it on any function from a - library in API mode (in ABI mode, all functions are already regular - cdata objects). To support this, you need to recompile your cffi - modules. - -* Issue #198: in API mode, if you declare constants of a ``struct`` - type, what you saw from lib.CONSTANT was corrupted. - -* Issue #196: ``ffi.set_source("package._ffi", None)`` would - incorrectly generate the Python source to ``package._ffi.py`` instead - of ``package/_ffi.py``. Also fixed: in some cases, if the C file was - in ``build/foo.c``, the .o file would be put in ``build/build/foo.o``. - - -v1.0.3 ------- - -* Same as 1.0.2, apart from doc and test fixes on some platforms. - - -v1.0.2 ------- - -* Variadic C functions (ending in a "..." argument) were not supported - in the out-of-line ABI mode. This was a bug---there was even a - (non-working) example__ doing exactly that! - -.. __: overview.html#out-of-line-abi-level - - -v1.0.1 ------- - -* ``ffi.set_source()`` crashed if passed a ``sources=[..]`` argument. - Fixed by chrippa on pull request #60. - -* Issue #193: if we use a struct between the first cdef() where it is - declared and another cdef() where its fields are defined, then this - definition was ignored. - -* Enums were buggy if you used too many "..." in their definition. - - -v1.0.0 ------- - -* The main news item is out-of-line module generation: - - * `for ABI level`_, with ``ffi.dlopen()`` - - * `for API level`_, which used to be with ``ffi.verify()``, now deprecated - -* (this page will list what is new from all versions from 1.0.0 - forward.) - -.. _`for ABI level`: overview.html#out-of-line-abi-level -.. _`for API level`: overview.html#out-of-line-api-level diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index a97f028..0000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -pycparser -pytest diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 0c9e0fc..0000000 --- a/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[metadata] -license_file = LICENSE diff --git a/setup.py b/setup.py deleted file mode 100644 index 5fd1a1c..0000000 --- a/setup.py +++ /dev/null @@ -1,238 +0,0 @@ -import sys, os, platform -import subprocess -import errno - -# on Windows we give up and always import setuptools early to fix things for us -if sys.platform == "win32": - import setuptools - - -sources = ['c/_cffi_backend.c'] -libraries = ['ffi'] -include_dirs = ['/usr/include/ffi', - '/usr/include/libffi'] # may be changed by pkg-config -define_macros = [] -library_dirs = [] -extra_compile_args = [] -extra_link_args = [] - - -def _ask_pkg_config(resultlist, option, result_prefix='', sysroot=False): - pkg_config = os.environ.get('PKG_CONFIG','pkg-config') - try: - p = subprocess.Popen([pkg_config, option, 'libffi'], - stdout=subprocess.PIPE) - except OSError as e: - if e.errno not in [errno.ENOENT, errno.EACCES]: - raise - else: - t = p.stdout.read().decode().strip() - p.stdout.close() - if p.wait() == 0: - res = t.split() - # '-I/usr/...' -> '/usr/...' - for x in res: - assert x.startswith(result_prefix) - res = [x[len(result_prefix):] for x in res] - #print 'PKG_CONFIG:', option, res - # - sysroot = sysroot and os.environ.get('PKG_CONFIG_SYSROOT_DIR', '') - if sysroot: - # old versions of pkg-config don't support this env var, - # so here we emulate its effect if needed - res = [path if path.startswith(sysroot) - else sysroot + path - for path in res] - # - resultlist[:] = res - -no_compiler_found = False -def no_working_compiler_found(): - sys.stderr.write(""" - No working compiler found, or bogus compiler options passed to - the compiler from Python's standard "distutils" module. See - the error messages above. Likely, the problem is not related - to CFFI but generic to the setup.py of any Python package that - tries to compile C code. (Hints: on OS/X 10.8, for errors about - -mno-fused-madd see http://stackoverflow.com/questions/22313407/ - Otherwise, see https://wiki.python.org/moin/CompLangPython or - the IRC channel #python on irc.libera.chat.) - - Trying to continue anyway. If you are trying to install CFFI from - a build done in a different context, you can ignore this warning. - \n""") - global no_compiler_found - no_compiler_found = True - -def get_config(): - from distutils.core import Distribution - from distutils.sysconfig import get_config_vars - get_config_vars() # workaround for a bug of distutils, e.g. on OS/X - config = Distribution().get_command_obj('config') - return config - -def ask_supports_thread(): - config = get_config() - ok = (sys.platform != 'win32' and - config.try_compile('__thread int some_threadlocal_variable_42;')) - if ok: - define_macros.append(('USE__THREAD', None)) - else: - ok1 = config.try_compile('int some_regular_variable_42;') - if not ok1: - no_working_compiler_found() - else: - sys.stderr.write("Note: will not use '__thread' in the C code\n") - _safe_to_ignore() - -def ask_supports_sync_synchronize(): - if sys.platform == 'win32' or no_compiler_found: - return - config = get_config() - ok = config.try_link('int main(void) { __sync_synchronize(); return 0; }') - if ok: - define_macros.append(('HAVE_SYNC_SYNCHRONIZE', None)) - else: - sys.stderr.write("Note: will not use '__sync_synchronize()'" - " in the C code\n") - _safe_to_ignore() - -def _safe_to_ignore(): - sys.stderr.write("***** The above error message can be safely ignored.\n\n") - -def uses_msvc(): - config = get_config() - return config.try_compile('#ifndef _MSC_VER\n#error "not MSVC"\n#endif') - -def use_pkg_config(): - if sys.platform == 'darwin' and os.path.exists('/usr/local/bin/brew'): - use_homebrew_for_libffi() - - _ask_pkg_config(include_dirs, '--cflags-only-I', '-I', sysroot=True) - _ask_pkg_config(extra_compile_args, '--cflags-only-other') - _ask_pkg_config(library_dirs, '--libs-only-L', '-L', sysroot=True) - _ask_pkg_config(extra_link_args, '--libs-only-other') - _ask_pkg_config(libraries, '--libs-only-l', '-l') - -def use_homebrew_for_libffi(): - # We can build by setting: - # PKG_CONFIG_PATH = $(brew --prefix libffi)/lib/pkgconfig - with os.popen('brew --prefix libffi') as brew_prefix_cmd: - prefix = brew_prefix_cmd.read().strip() - pkgconfig = os.path.join(prefix, 'lib', 'pkgconfig') - os.environ['PKG_CONFIG_PATH'] = ( - os.environ.get('PKG_CONFIG_PATH', '') + ':' + pkgconfig) - -if sys.platform == "win32" and uses_msvc(): - if platform.machine() == "ARM64": - include_dirs.append(os.path.join("c/libffi_arm64/include")) - library_dirs.append(os.path.join("c/libffi_arm64")) - else: - COMPILE_LIBFFI = 'c/libffi_x86_x64' # from the CPython distribution - assert os.path.isdir(COMPILE_LIBFFI), "directory not found!" - include_dirs[:] = [COMPILE_LIBFFI] - libraries[:] = [] - _filenames = [filename.lower() for filename in os.listdir(COMPILE_LIBFFI)] - _filenames = [filename for filename in _filenames - if filename.endswith('.c')] - if sys.maxsize > 2**32: - # 64-bit: unlist win32.c, and add instead win64.obj. If the obj - # happens to get outdated at some point in the future, you need to - # rebuild it manually from win64.asm. - _filenames.remove('win32.c') - extra_link_args.append(os.path.join(COMPILE_LIBFFI, 'win64.obj')) - sources.extend(os.path.join(COMPILE_LIBFFI, filename) - for filename in _filenames) -else: - use_pkg_config() - ask_supports_thread() - ask_supports_sync_synchronize() - -if 'darwin' in sys.platform: - # priority is given to `pkg_config`, but always fall back on SDK's libffi. - extra_compile_args += ['-iwithsysroot/usr/include/ffi'] - -if 'freebsd' in sys.platform: - include_dirs.append('/usr/local/include') - library_dirs.append('/usr/local/lib') - -if __name__ == '__main__': - from setuptools import setup, Distribution, Extension - - class CFFIDistribution(Distribution): - def has_ext_modules(self): - # Event if we don't have extension modules (e.g. on PyPy) we want to - # claim that we do so that wheels get properly tagged as Python - # specific. (thanks dstufft!) - return True - - # On PyPy, cffi is preinstalled and it is not possible, at least for now, - # to install a different version. We work around it by making the setup() - # arguments mostly empty in this case. - cpython = ('_cffi_backend' not in sys.builtin_module_names) - - setup( - name='cffi', - description='Foreign Function Interface for Python calling C code.', - long_description=""" -CFFI -==== - -Foreign Function Interface for Python calling C code. -Please see the `Documentation <http://cffi.readthedocs.org/>`_. - -Contact -------- - -`Mailing list <https://groups.google.com/forum/#!forum/python-cffi>`_ -""", - version='1.15.0', - packages=['cffi'] if cpython else [], - package_data={'cffi': ['_cffi_include.h', 'parse_c_type.h', - '_embedding.h', '_cffi_errors.h']} - if cpython else {}, - zip_safe=False, - - url='http://cffi.readthedocs.org', - author='Armin Rigo, Maciej Fijalkowski', - author_email='python-cffi@googlegroups.com', - - license='MIT', - - distclass=CFFIDistribution, - ext_modules=[Extension( - name='_cffi_backend', - include_dirs=include_dirs, - sources=sources, - libraries=libraries, - define_macros=define_macros, - library_dirs=library_dirs, - extra_compile_args=extra_compile_args, - extra_link_args=extra_link_args, - )] if cpython else [], - - install_requires=[ - 'pycparser' if sys.version_info >= (2, 7) else 'pycparser<2.19', - ] if cpython else [], - - entry_points = { - "distutils.setup_keywords": [ - "cffi_modules = cffi.setuptools_ext:cffi_modules", - ], - }, - - classifiers=[ - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: Implementation :: CPython', - 'Programming Language :: Python :: Implementation :: PyPy', - 'License :: OSI Approved :: MIT License', - ], - ) diff --git a/setup_base.py b/setup_base.py deleted file mode 100644 index 4cf6ea5..0000000 --- a/setup_base.py +++ /dev/null @@ -1,23 +0,0 @@ -import sys, os - - -from setup import include_dirs, sources, libraries, define_macros -from setup import library_dirs, extra_compile_args, extra_link_args - - -if __name__ == '__main__': - from distutils.core import setup - from distutils.extension import Extension - - standard = '__pypy__' not in sys.builtin_module_names - setup(packages=['cffi'], - requires=['pycparser'], - ext_modules=[Extension(name = '_cffi_backend', - include_dirs=include_dirs, - sources=sources, - libraries=libraries, - define_macros=define_macros, - library_dirs=library_dirs, - extra_compile_args=extra_compile_args, - extra_link_args=extra_link_args, - )] * standard) diff --git a/testing/__init__.py b/testing/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/testing/__init__.py +++ /dev/null diff --git a/testing/cffi0/__init__.py b/testing/cffi0/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/testing/cffi0/__init__.py +++ /dev/null diff --git a/testing/cffi0/backend_tests.py b/testing/cffi0/backend_tests.py deleted file mode 100644 index ab013a1..0000000 --- a/testing/cffi0/backend_tests.py +++ /dev/null @@ -1,2027 +0,0 @@ -import py -import pytest -import platform -import sys, ctypes, ctypes.util -from cffi import FFI, CDefError, FFIError, VerificationMissing -from testing.support import * - -SIZE_OF_INT = ctypes.sizeof(ctypes.c_int) -SIZE_OF_LONG = ctypes.sizeof(ctypes.c_long) -SIZE_OF_SHORT = ctypes.sizeof(ctypes.c_short) -SIZE_OF_PTR = ctypes.sizeof(ctypes.c_void_p) -SIZE_OF_WCHAR = ctypes.sizeof(ctypes.c_wchar) - -def needs_dlopen_none(): - if sys.platform == 'win32' and not ctypes.util.find_library('c'): - py.test.skip("dlopen(None) cannot work on Windows with this runtime") - - -class BackendTests: - - def test_integer_ranges(self): - ffi = FFI(backend=self.Backend()) - for (c_type, size) in [('char', 1), - ('short', 2), - ('short int', 2), - ('', 4), - ('int', 4), - ('long', SIZE_OF_LONG), - ('long int', SIZE_OF_LONG), - ('long long', 8), - ('long long int', 8), - ]: - for unsigned in [None, False, True]: - c_decl = {None: '', - False: 'signed ', - True: 'unsigned '}[unsigned] + c_type - if c_decl == 'char' or c_decl == '': - continue - self._test_int_type(ffi, c_decl, size, unsigned) - - def test_fixedsize_int(self): - ffi = FFI(backend=self.Backend()) - for size in [1, 2, 4, 8]: - self._test_int_type(ffi, 'int%d_t' % (8*size), size, False) - self._test_int_type(ffi, 'uint%d_t' % (8*size), size, True) - self._test_int_type(ffi, 'intptr_t', SIZE_OF_PTR, False) - self._test_int_type(ffi, 'uintptr_t', SIZE_OF_PTR, True) - self._test_int_type(ffi, 'ptrdiff_t', SIZE_OF_PTR, False) - self._test_int_type(ffi, 'size_t', SIZE_OF_PTR, True) - self._test_int_type(ffi, 'ssize_t', SIZE_OF_PTR, False) - - def _test_int_type(self, ffi, c_decl, size, unsigned): - if unsigned: - min = 0 - max = (1 << (8*size)) - 1 - else: - min = -(1 << (8*size-1)) - max = (1 << (8*size-1)) - 1 - min = int(min) - max = int(max) - p = ffi.cast(c_decl, min) - assert p == min - assert hash(p) == hash(min) - assert bool(p) is bool(min) - assert int(p) == min - p = ffi.cast(c_decl, max) - assert int(p) == max - p = ffi.cast(c_decl, long(max)) - assert int(p) == max - q = ffi.cast(c_decl, min - 1) - assert ffi.typeof(q) is ffi.typeof(p) and int(q) == max - q = ffi.cast(c_decl, long(min - 1)) - assert ffi.typeof(q) is ffi.typeof(p) and int(q) == max - assert q == p - assert int(q) == int(p) - assert hash(q) == hash(p) - c_decl_ptr = '%s *' % c_decl - py.test.raises(OverflowError, ffi.new, c_decl_ptr, min - 1) - py.test.raises(OverflowError, ffi.new, c_decl_ptr, max + 1) - py.test.raises(OverflowError, ffi.new, c_decl_ptr, long(min - 1)) - py.test.raises(OverflowError, ffi.new, c_decl_ptr, long(max + 1)) - assert ffi.new(c_decl_ptr, min)[0] == min - assert ffi.new(c_decl_ptr, max)[0] == max - assert ffi.new(c_decl_ptr, long(min))[0] == min - assert ffi.new(c_decl_ptr, long(max))[0] == max - - def test_new_unsupported_type(self): - ffi = FFI(backend=self.Backend()) - e = py.test.raises(TypeError, ffi.new, "int") - assert str(e.value) == "expected a pointer or array ctype, got 'int'" - - def test_new_single_integer(self): - ffi = FFI(backend=self.Backend()) - p = ffi.new("int *") # similar to ffi.new("int[1]") - assert p[0] == 0 - p[0] = -123 - assert p[0] == -123 - p = ffi.new("int *", -42) - assert p[0] == -42 - assert repr(p) == "<cdata 'int *' owning %d bytes>" % SIZE_OF_INT - - def test_new_array_no_arg(self): - ffi = FFI(backend=self.Backend()) - p = ffi.new("int[10]") - # the object was zero-initialized: - for i in range(10): - assert p[i] == 0 - - def test_array_indexing(self): - ffi = FFI(backend=self.Backend()) - p = ffi.new("int[10]") - p[0] = 42 - p[9] = 43 - assert p[0] == 42 - assert p[9] == 43 - with pytest.raises(IndexError): - p[10] - with pytest.raises(IndexError): - p[10] = 44 - with pytest.raises(IndexError): - p[-1] - with pytest.raises(IndexError): - p[-1] = 44 - - def test_new_array_args(self): - ffi = FFI(backend=self.Backend()) - # this tries to be closer to C: where we say "int x[5] = {10, 20, ..}" - # then here we must enclose the items in a list - p = ffi.new("int[5]", [10, 20, 30, 40, 50]) - assert p[0] == 10 - assert p[1] == 20 - assert p[2] == 30 - assert p[3] == 40 - assert p[4] == 50 - p = ffi.new("int[4]", [25]) - assert p[0] == 25 - assert p[1] == 0 # follow C convention rather than LuaJIT's - assert p[2] == 0 - assert p[3] == 0 - p = ffi.new("int[4]", [ffi.cast("int", -5)]) - assert p[0] == -5 - assert repr(p) == "<cdata 'int[4]' owning %d bytes>" % (4*SIZE_OF_INT) - - def test_new_array_varsize(self): - ffi = FFI(backend=self.Backend()) - p = ffi.new("int[]", 10) # a single integer is the length - assert p[9] == 0 - with pytest.raises(IndexError): - p[10] - # - py.test.raises(TypeError, ffi.new, "int[]") - # - p = ffi.new("int[]", [-6, -7]) # a list is all the items, like C - assert p[0] == -6 - assert p[1] == -7 - with pytest.raises(IndexError): - p[2] - assert repr(p) == "<cdata 'int[]' owning %d bytes>" % (2*SIZE_OF_INT) - # - p = ffi.new("int[]", 0) - with pytest.raises(IndexError): - p[0] - py.test.raises(ValueError, ffi.new, "int[]", -1) - assert repr(p) == "<cdata 'int[]' owning 0 bytes>" - - def test_pointer_init(self): - ffi = FFI(backend=self.Backend()) - n = ffi.new("int *", 24) - a = ffi.new("int *[10]", [ffi.NULL, ffi.NULL, n, n, ffi.NULL]) - for i in range(10): - if i not in (2, 3): - assert a[i] == ffi.NULL - assert a[2] == a[3] == n - - def test_cannot_cast(self): - ffi = FFI(backend=self.Backend()) - a = ffi.new("short int[10]") - e = py.test.raises(TypeError, ffi.new, "long int **", a) - msg = str(e.value) - assert "'short[10]'" in msg and "'long *'" in msg - - def test_new_pointer_to_array(self): - ffi = FFI(backend=self.Backend()) - a = ffi.new("int[4]", [100, 102, 104, 106]) - p = ffi.new("int **", a) - assert p[0] == ffi.cast("int *", a) - assert p[0][2] == 104 - p = ffi.cast("int *", a) - assert p[0] == 100 - assert p[1] == 102 - assert p[2] == 104 - assert p[3] == 106 - # keepalive: a - - def test_pointer_direct(self): - ffi = FFI(backend=self.Backend()) - p = ffi.cast("int*", 0) - assert p is not None - assert bool(p) is False - assert p == ffi.cast("int*", 0) - assert p != None - assert repr(p) == "<cdata 'int *' NULL>" - a = ffi.new("int[]", [123, 456]) - p = ffi.cast("int*", a) - assert bool(p) is True - assert p == ffi.cast("int*", a) - assert p != ffi.cast("int*", 0) - assert p[0] == 123 - assert p[1] == 456 - - def test_repr(self): - typerepr = self.TypeRepr - ffi = FFI(backend=self.Backend()) - ffi.cdef("struct foo { short a, b, c; };") - p = ffi.cast("short unsigned int", 0) - assert repr(p) == "<cdata 'unsigned short' 0>" - assert repr(ffi.typeof(p)) == typerepr % "unsigned short" - p = ffi.cast("unsigned short int", 0) - assert repr(p) == "<cdata 'unsigned short' 0>" - assert repr(ffi.typeof(p)) == typerepr % "unsigned short" - p = ffi.cast("int*", 0) - assert repr(p) == "<cdata 'int *' NULL>" - assert repr(ffi.typeof(p)) == typerepr % "int *" - # - p = ffi.new("int*") - assert repr(p) == "<cdata 'int *' owning %d bytes>" % SIZE_OF_INT - assert repr(ffi.typeof(p)) == typerepr % "int *" - p = ffi.new("int**") - assert repr(p) == "<cdata 'int * *' owning %d bytes>" % SIZE_OF_PTR - assert repr(ffi.typeof(p)) == typerepr % "int * *" - p = ffi.new("int [2]") - assert repr(p) == "<cdata 'int[2]' owning %d bytes>" % (2*SIZE_OF_INT) - assert repr(ffi.typeof(p)) == typerepr % "int[2]" - p = ffi.new("int*[2][3]") - assert repr(p) == "<cdata 'int *[2][3]' owning %d bytes>" % ( - 6*SIZE_OF_PTR) - assert repr(ffi.typeof(p)) == typerepr % "int *[2][3]" - p = ffi.new("struct foo *") - assert repr(p) == "<cdata 'struct foo *' owning %d bytes>" % ( - 3*SIZE_OF_SHORT) - assert repr(ffi.typeof(p)) == typerepr % "struct foo *" - # - q = ffi.cast("short", -123) - assert repr(q) == "<cdata 'short' -123>" - assert repr(ffi.typeof(q)) == typerepr % "short" - p = ffi.new("int*") - q = ffi.cast("short*", p) - assert repr(q).startswith("<cdata 'short *' 0x") - assert repr(ffi.typeof(q)) == typerepr % "short *" - p = ffi.new("int [2]") - q = ffi.cast("int*", p) - assert repr(q).startswith("<cdata 'int *' 0x") - assert repr(ffi.typeof(q)) == typerepr % "int *" - p = ffi.new("struct foo*") - q = ffi.cast("struct foo *", p) - assert repr(q).startswith("<cdata 'struct foo *' 0x") - assert repr(ffi.typeof(q)) == typerepr % "struct foo *" - prevrepr = repr(q) - q = q[0] - assert repr(q) == prevrepr.replace(' *', ' &') - assert repr(ffi.typeof(q)) == typerepr % "struct foo" - - def test_new_array_of_array(self): - ffi = FFI(backend=self.Backend()) - p = ffi.new("int[3][4]") - p[0][0] = 10 - p[2][3] = 33 - assert p[0][0] == 10 - assert p[2][3] == 33 - with pytest.raises(IndexError): - p[1][-1] - - def test_constructor_array_of_array(self): - ffi = FFI(backend=self.Backend()) - p = ffi.new("int[3][2]", [[10, 11], [12, 13], [14, 15]]) - assert p[2][1] == 15 - - def test_new_array_of_pointer_1(self): - ffi = FFI(backend=self.Backend()) - n = ffi.new("int*", 99) - p = ffi.new("int*[4]") - p[3] = n - a = p[3] - assert repr(a).startswith("<cdata 'int *' 0x") - assert a[0] == 99 - - def test_new_array_of_pointer_2(self): - ffi = FFI(backend=self.Backend()) - n = ffi.new("int[1]", [99]) - p = ffi.new("int*[4]") - p[3] = n - a = p[3] - assert repr(a).startswith("<cdata 'int *' 0x") - assert a[0] == 99 - - def test_char(self): - ffi = FFI(backend=self.Backend()) - assert ffi.new("char*", b"\xff")[0] == b'\xff' - assert ffi.new("char*")[0] == b'\x00' - assert int(ffi.cast("char", 300)) == 300 - 256 - assert not bool(ffi.cast("char", 0)) - assert bool(ffi.cast("char", 1)) - assert bool(ffi.cast("char", 255)) - py.test.raises(TypeError, ffi.new, "char*", 32) - py.test.raises(TypeError, ffi.new, "char*", u+"x") - py.test.raises(TypeError, ffi.new, "char*", b"foo") - # - p = ffi.new("char[]", [b'a', b'b', b'\x9c']) - assert len(p) == 3 - assert p[0] == b'a' - assert p[1] == b'b' - assert p[2] == b'\x9c' - p[0] = b'\xff' - assert p[0] == b'\xff' - p = ffi.new("char[]", b"abcd") - assert len(p) == 5 - assert p[4] == b'\x00' # like in C, with: char[] p = "abcd"; - # - p = ffi.new("char[4]", b"ab") - assert len(p) == 4 - assert [p[i] for i in range(4)] == [b'a', b'b', b'\x00', b'\x00'] - p = ffi.new("char[2]", b"ab") - assert len(p) == 2 - assert [p[i] for i in range(2)] == [b'a', b'b'] - py.test.raises(IndexError, ffi.new, "char[2]", b"abc") - - def check_wchar_t(self, ffi): - try: - ffi.cast("wchar_t", 0) - except NotImplementedError: - py.test.skip("NotImplementedError: wchar_t") - - def test_wchar_t(self): - ffi = FFI(backend=self.Backend()) - self.check_wchar_t(ffi) - assert ffi.new("wchar_t*", u+'x')[0] == u+'x' - assert ffi.new("wchar_t*", u+'\u1234')[0] == u+'\u1234' - if SIZE_OF_WCHAR > 2: - assert ffi.new("wchar_t*", u+'\U00012345')[0] == u+'\U00012345' - else: - py.test.raises(TypeError, ffi.new, "wchar_t*", u+'\U00012345') - assert ffi.new("wchar_t*")[0] == u+'\x00' - assert int(ffi.cast("wchar_t", 300)) == 300 - assert not bool(ffi.cast("wchar_t", 0)) - assert bool(ffi.cast("wchar_t", 1)) - assert bool(ffi.cast("wchar_t", 65535)) - if SIZE_OF_WCHAR > 2: - assert bool(ffi.cast("wchar_t", 65536)) - py.test.raises(TypeError, ffi.new, "wchar_t*", 32) - py.test.raises(TypeError, ffi.new, "wchar_t*", "foo") - # - p = ffi.new("wchar_t[]", [u+'a', u+'b', u+'\u1234']) - assert len(p) == 3 - assert p[0] == u+'a' - assert p[1] == u+'b' and type(p[1]) is unicode - assert p[2] == u+'\u1234' - p[0] = u+'x' - assert p[0] == u+'x' and type(p[0]) is unicode - p[1] = u+'\u1357' - assert p[1] == u+'\u1357' - p = ffi.new("wchar_t[]", u+"abcd") - assert len(p) == 5 - assert p[4] == u+'\x00' - p = ffi.new("wchar_t[]", u+"a\u1234b") - assert len(p) == 4 - assert p[1] == u+'\u1234' - # - p = ffi.new("wchar_t[]", u+'\U00023456') - if SIZE_OF_WCHAR == 2: - assert len(p) == 3 - assert p[0] == u+'\ud84d' - assert p[1] == u+'\udc56' - assert p[2] == u+'\x00' - else: - assert len(p) == 2 - assert p[0] == u+'\U00023456' - assert p[1] == u+'\x00' - # - p = ffi.new("wchar_t[4]", u+"ab") - assert len(p) == 4 - assert [p[i] for i in range(4)] == [u+'a', u+'b', u+'\x00', u+'\x00'] - p = ffi.new("wchar_t[2]", u+"ab") - assert len(p) == 2 - assert [p[i] for i in range(2)] == [u+'a', u+'b'] - py.test.raises(IndexError, ffi.new, "wchar_t[2]", u+"abc") - - def test_none_as_null_doesnt_work(self): - ffi = FFI(backend=self.Backend()) - p = ffi.new("int*[1]") - assert p[0] is not None - assert p[0] != None - assert p[0] == ffi.NULL - assert repr(p[0]) == "<cdata 'int *' NULL>" - # - n = ffi.new("int*", 99) - p = ffi.new("int*[]", [n]) - assert p[0][0] == 99 - with pytest.raises(TypeError): - p[0] = None - p[0] = ffi.NULL - assert p[0] == ffi.NULL - - def test_float(self): - ffi = FFI(backend=self.Backend()) - p = ffi.new("float[]", [-2, -2.5]) - assert p[0] == -2.0 - assert p[1] == -2.5 - p[1] += 17.75 - assert p[1] == 15.25 - # - p = ffi.new("float*", 15.75) - assert p[0] == 15.75 - py.test.raises(TypeError, int, p) - py.test.raises(TypeError, float, p) - p[0] = 0.0 - assert bool(p) is True - # - p = ffi.new("float*", 1.1) - f = p[0] - assert f != 1.1 # because of rounding effect - assert abs(f - 1.1) < 1E-7 - # - INF = 1E200 * 1E200 - assert 1E200 != INF - p[0] = 1E200 - assert p[0] == INF # infinite, not enough precision - - def test_struct_simple(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("struct foo { int a; short b, c; };") - s = ffi.new("struct foo*") - assert s.a == s.b == s.c == 0 - s.b = -23 - assert s.b == -23 - with pytest.raises(OverflowError): - s.b = 32768 - # - s = ffi.new("struct foo*", [-2, -3]) - assert s.a == -2 - assert s.b == -3 - assert s.c == 0 - with pytest.raises((AttributeError, TypeError)): - del s.a - assert repr(s) == "<cdata 'struct foo *' owning %d bytes>" % ( - SIZE_OF_INT + 2 * SIZE_OF_SHORT) - # - py.test.raises(ValueError, ffi.new, "struct foo*", [1, 2, 3, 4]) - - def test_constructor_struct_from_dict(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("struct foo { int a; short b, c; };") - s = ffi.new("struct foo*", {'b': 123, 'c': 456}) - assert s.a == 0 - assert s.b == 123 - assert s.c == 456 - py.test.raises(KeyError, ffi.new, "struct foo*", {'d': 456}) - - def test_struct_pointer(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("struct foo { int a; short b, c; };") - s = ffi.new("struct foo*") - assert s[0].a == s[0].b == s[0].c == 0 - s[0].b = -23 - assert s[0].b == s.b == -23 - with pytest.raises(OverflowError): - s[0].b = -32769 - with pytest.raises(IndexError): - s[1] - - def test_struct_opaque(self): - ffi = FFI(backend=self.Backend()) - py.test.raises(TypeError, ffi.new, "struct baz*") - p = ffi.new("struct baz **") # this works - assert p[0] == ffi.NULL - - def test_pointer_to_struct(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("struct foo { int a; short b, c; };") - s = ffi.new("struct foo *") - s.a = -42 - assert s[0].a == -42 - p = ffi.new("struct foo **", s) - assert p[0].a == -42 - assert p[0][0].a == -42 - p[0].a = -43 - assert s.a == -43 - assert s[0].a == -43 - p[0][0].a = -44 - assert s.a == -44 - assert s[0].a == -44 - s.a = -45 - assert p[0].a == -45 - assert p[0][0].a == -45 - s[0].a = -46 - assert p[0].a == -46 - assert p[0][0].a == -46 - - def test_constructor_struct_of_array(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("struct foo { int a[2]; char b[3]; };") - s = ffi.new("struct foo *", [[10, 11], [b'a', b'b', b'c']]) - assert s.a[1] == 11 - assert s.b[2] == b'c' - s.b[1] = b'X' - assert s.b[0] == b'a' - assert s.b[1] == b'X' - assert s.b[2] == b'c' - - def test_recursive_struct(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("struct foo { int value; struct foo *next; };") - s = ffi.new("struct foo*") - t = ffi.new("struct foo*") - s.value = 123 - s.next = t - t.value = 456 - assert s.value == 123 - assert s.next.value == 456 - - def test_union_simple(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("union foo { int a; short b, c; };") - u = ffi.new("union foo*") - assert u.a == u.b == u.c == 0 - u.b = -23 - assert u.b == -23 - assert u.a != 0 - with pytest.raises(OverflowError): - u.b = 32768 - # - u = ffi.new("union foo*", [-2]) - assert u.a == -2 - with pytest.raises((AttributeError, TypeError)): - del u.a - assert repr(u) == "<cdata 'union foo *' owning %d bytes>" % SIZE_OF_INT - - def test_union_opaque(self): - ffi = FFI(backend=self.Backend()) - py.test.raises(TypeError, ffi.new, "union baz *") - u = ffi.new("union baz **") # this works - assert u[0] == ffi.NULL - - def test_union_initializer(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("union foo { char a; int b; };") - py.test.raises(TypeError, ffi.new, "union foo*", b'A') - py.test.raises(TypeError, ffi.new, "union foo*", 5) - py.test.raises(ValueError, ffi.new, "union foo*", [b'A', 5]) - u = ffi.new("union foo*", [b'A']) - assert u.a == b'A' - py.test.raises(TypeError, ffi.new, "union foo*", [1005]) - u = ffi.new("union foo*", {'b': 12345}) - assert u.b == 12345 - u = ffi.new("union foo*", []) - assert u.a == b'\x00' - assert u.b == 0 - - def test_sizeof_type(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - struct foo { int a; short b, c, d; }; - union foo { int a; short b, c, d; }; - """) - for c_type, expected_size in [ - ('char', 1), - ('unsigned int', 4), - ('char *', SIZE_OF_PTR), - ('int[5]', 20), - ('struct foo', 12), - ('union foo', 4), - ]: - size = ffi.sizeof(c_type) - assert size == expected_size, (size, expected_size, ctype) - - def test_sizeof_cdata(self): - ffi = FFI(backend=self.Backend()) - assert ffi.sizeof(ffi.new("short*")) == SIZE_OF_PTR - assert ffi.sizeof(ffi.cast("short", 123)) == SIZE_OF_SHORT - # - a = ffi.new("int[]", [10, 11, 12, 13, 14]) - assert len(a) == 5 - assert ffi.sizeof(a) == 5 * SIZE_OF_INT - - def test_string_from_char_pointer(self): - ffi = FFI(backend=self.Backend()) - x = ffi.new("char*", b"x") - assert str(x) == repr(x) - assert ffi.string(x) == b"x" - assert ffi.string(ffi.new("char*", b"\x00")) == b"" - py.test.raises(TypeError, ffi.new, "char*", unicode("foo")) - - def test_unicode_from_wchar_pointer(self): - ffi = FFI(backend=self.Backend()) - self.check_wchar_t(ffi) - x = ffi.new("wchar_t*", u+"x") - assert unicode(x) == unicode(repr(x)) - assert ffi.string(x) == u+"x" - assert ffi.string(ffi.new("wchar_t*", u+"\x00")) == u+"" - - def test_string_from_char_array(self): - ffi = FFI(backend=self.Backend()) - p = ffi.new("char[]", b"hello.") - p[5] = b'!' - assert ffi.string(p) == b"hello!" - p[6] = b'?' - assert ffi.string(p) == b"hello!?" - p[3] = b'\x00' - assert ffi.string(p) == b"hel" - assert ffi.string(p, 2) == b"he" - with pytest.raises(IndexError): - p[7] = b'X' - # - a = ffi.new("char[]", b"hello\x00world") - assert len(a) == 12 - p = ffi.cast("char *", a) - assert ffi.string(p) == b'hello' - - def test_string_from_wchar_array(self): - ffi = FFI(backend=self.Backend()) - self.check_wchar_t(ffi) - assert ffi.string(ffi.cast("wchar_t", "x")) == u+"x" - assert ffi.string(ffi.cast("wchar_t", u+"x")) == u+"x" - x = ffi.cast("wchar_t", "x") - assert str(x) == repr(x) - assert ffi.string(x) == u+"x" - # - p = ffi.new("wchar_t[]", u+"hello.") - p[5] = u+'!' - assert ffi.string(p) == u+"hello!" - p[6] = u+'\u04d2' - assert ffi.string(p) == u+"hello!\u04d2" - p[3] = u+'\x00' - assert ffi.string(p) == u+"hel" - assert ffi.string(p, 123) == u+"hel" - with pytest.raises(IndexError): - p[7] = u+'X' - # - a = ffi.new("wchar_t[]", u+"hello\x00world") - assert len(a) == 12 - p = ffi.cast("wchar_t *", a) - assert ffi.string(p) == u+'hello' - assert ffi.string(p, 123) == u+'hello' - assert ffi.string(p, 5) == u+'hello' - assert ffi.string(p, 2) == u+'he' - - def test_fetch_const_char_p_field(self): - # 'const' is ignored so far - ffi = FFI(backend=self.Backend()) - ffi.cdef("struct foo { const char *name; };") - t = ffi.new("const char[]", b"testing") - s = ffi.new("struct foo*", [t]) - assert type(s.name) not in (bytes, str, unicode) - assert ffi.string(s.name) == b"testing" - with pytest.raises(TypeError): - s.name = None - s.name = ffi.NULL - assert s.name == ffi.NULL - - def test_fetch_const_wchar_p_field(self): - # 'const' is ignored so far - ffi = FFI(backend=self.Backend()) - self.check_wchar_t(ffi) - ffi.cdef("struct foo { const wchar_t *name; };") - t = ffi.new("const wchar_t[]", u+"testing") - s = ffi.new("struct foo*", [t]) - assert type(s.name) not in (bytes, str, unicode) - assert ffi.string(s.name) == u+"testing" - s.name = ffi.NULL - assert s.name == ffi.NULL - - def test_voidp(self): - ffi = FFI(backend=self.Backend()) - py.test.raises(TypeError, ffi.new, "void*") - p = ffi.new("void **") - assert p[0] == ffi.NULL - a = ffi.new("int[]", [10, 11, 12]) - p = ffi.new("void **", a) - vp = p[0] - with pytest.raises(TypeError): - vp[0] - py.test.raises(TypeError, ffi.new, "short **", a) - # - ffi.cdef("struct foo { void *p; int *q; short *r; };") - s = ffi.new("struct foo *") - s.p = a # works - s.q = a # works - with pytest.raises(TypeError): - s.r = a # fails - b = ffi.cast("int *", a) - s.p = b # works - s.q = b # works - with pytest.raises(TypeError): - s.r = b # fails - - def test_functionptr_simple(self): - ffi = FFI(backend=self.Backend()) - py.test.raises(TypeError, ffi.callback, "int(*)(int)", 0) - def cb(n): - return n + 1 - cb.__qualname__ = 'cb' - p = ffi.callback("int(*)(int)", cb) - res = p(41) # calling an 'int(*)(int)', i.e. a function pointer - assert res == 42 and type(res) is int - res = p(ffi.cast("int", -41)) - assert res == -40 and type(res) is int - assert repr(p).startswith( - "<cdata 'int(*)(int)' calling <function cb at 0x") - assert ffi.typeof(p) is ffi.typeof("int(*)(int)") - q = ffi.new("int(**)(int)", p) - assert repr(q) == "<cdata 'int(* *)(int)' owning %d bytes>" % ( - SIZE_OF_PTR) - with pytest.raises(TypeError): - q(43) - res = q[0](43) - assert res == 44 - q = ffi.cast("int(*)(int)", p) - assert repr(q).startswith("<cdata 'int(*)(int)' 0x") - res = q(45) - assert res == 46 - - def test_functionptr_advanced(self): - ffi = FFI(backend=self.Backend()) - t = ffi.typeof("int(*(*)(int))(int)") - assert repr(t) == self.TypeRepr % "int(*(*)(int))(int)" - - def test_functionptr_voidptr_return(self): - ffi = FFI(backend=self.Backend()) - def cb(): - return ffi.NULL - p = ffi.callback("void*(*)()", cb) - res = p() - assert res is not None - assert res == ffi.NULL - int_ptr = ffi.new('int*') - void_ptr = ffi.cast('void*', int_ptr) - def cb(): - return void_ptr - p = ffi.callback("void*(*)()", cb) - res = p() - assert res == void_ptr - - def test_functionptr_intptr_return(self): - ffi = FFI(backend=self.Backend()) - def cb(): - return ffi.NULL - p = ffi.callback("int*(*)()", cb) - res = p() - assert res == ffi.NULL - int_ptr = ffi.new('int*') - def cb(): - return int_ptr - p = ffi.callback("int*(*)()", cb) - res = p() - assert repr(res).startswith("<cdata 'int *' 0x") - assert res == int_ptr - int_array_ptr = ffi.new('int[1]') - def cb(): - return int_array_ptr - p = ffi.callback("int*(*)()", cb) - res = p() - assert repr(res).startswith("<cdata 'int *' 0x") - assert res == int_array_ptr - - def test_functionptr_void_return(self): - ffi = FFI(backend=self.Backend()) - def foo(): - pass - foo_cb = ffi.callback("void foo()", foo) - result = foo_cb() - assert result is None - - def test_char_cast(self): - ffi = FFI(backend=self.Backend()) - p = ffi.cast("int", b'\x01') - assert ffi.typeof(p) is ffi.typeof("int") - assert int(p) == 1 - p = ffi.cast("int", ffi.cast("char", b"a")) - assert int(p) == ord("a") - p = ffi.cast("int", ffi.cast("char", b"\x80")) - assert int(p) == 0x80 # "char" is considered unsigned in this case - p = ffi.cast("int", b"\x81") - assert int(p) == 0x81 - - def test_wchar_cast(self): - ffi = FFI(backend=self.Backend()) - self.check_wchar_t(ffi) - p = ffi.cast("int", ffi.cast("wchar_t", u+'\u1234')) - assert int(p) == 0x1234 - p = ffi.cast("long long", ffi.cast("wchar_t", -1)) - if SIZE_OF_WCHAR == 2: # 2 bytes, unsigned - assert int(p) == 0xffff - elif (sys.platform.startswith('linux') and - platform.machine().startswith('x86')): # known to be signed - assert int(p) == -1 - else: # in general, it can be either signed or not - assert int(p) in [-1, 0xffffffff] # e.g. on arm, both cases occur - p = ffi.cast("int", u+'\u1234') - assert int(p) == 0x1234 - - def test_cast_array_to_charp(self): - ffi = FFI(backend=self.Backend()) - a = ffi.new("short int[]", [0x1234, 0x5678]) - p = ffi.cast("char*", a) - data = b''.join([p[i] for i in range(4)]) - if sys.byteorder == 'little': - assert data == b'\x34\x12\x78\x56' - else: - assert data == b'\x12\x34\x56\x78' - - def test_cast_between_pointers(self): - ffi = FFI(backend=self.Backend()) - a = ffi.new("short int[]", [0x1234, 0x5678]) - p = ffi.cast("short*", a) - p2 = ffi.cast("int*", p) - q = ffi.cast("char*", p2) - data = b''.join([q[i] for i in range(4)]) - if sys.byteorder == 'little': - assert data == b'\x34\x12\x78\x56' - else: - assert data == b'\x12\x34\x56\x78' - - def test_cast_pointer_and_int(self): - ffi = FFI(backend=self.Backend()) - a = ffi.new("short int[]", [0x1234, 0x5678]) - l1 = ffi.cast("intptr_t", a) - p = ffi.cast("short*", a) - l2 = ffi.cast("intptr_t", p) - assert int(l1) == int(l2) != 0 - q = ffi.cast("short*", l1) - assert q == ffi.cast("short*", int(l1)) - assert q[0] == 0x1234 - assert int(ffi.cast("intptr_t", ffi.NULL)) == 0 - - def test_cast_functionptr_and_int(self): - ffi = FFI(backend=self.Backend()) - def cb(n): - return n + 1 - a = ffi.callback("int(*)(int)", cb) - p = ffi.cast("void *", a) - assert p - b = ffi.cast("int(*)(int)", p) - assert b(41) == 42 - assert a == b - assert hash(a) == hash(b) - - def test_callback_crash(self): - ffi = FFI(backend=self.Backend()) - def cb(n): - raise Exception - a = ffi.callback("int(*)(int)", cb, error=42) - res = a(1) # and the error reported to stderr - assert res == 42 - - def test_structptr_argument(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("struct foo_s { int a, b; };") - def cb(p): - return p[0].a * 1000 + p[0].b * 100 + p[1].a * 10 + p[1].b - a = ffi.callback("int(*)(struct foo_s[])", cb) - res = a([[5, 6], {'a': 7, 'b': 8}]) - assert res == 5678 - res = a([[5], {'b': 8}]) - assert res == 5008 - - def test_array_argument_as_list(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("struct foo_s { int a, b; };") - seen = [] - def cb(argv): - seen.append(ffi.string(argv[0])) - seen.append(ffi.string(argv[1])) - a = ffi.callback("void(*)(char *[])", cb) - a([ffi.new("char[]", b"foobar"), ffi.new("char[]", b"baz")]) - assert seen == [b"foobar", b"baz"] - - def test_cast_float(self): - ffi = FFI(backend=self.Backend()) - a = ffi.cast("float", 12) - assert float(a) == 12.0 - a = ffi.cast("float", 12.5) - assert float(a) == 12.5 - a = ffi.cast("float", b"A") - assert float(a) == ord("A") - a = ffi.cast("int", 12.9) - assert int(a) == 12 - a = ffi.cast("char", 66.9 + 256) - assert ffi.string(a) == b"B" - # - a = ffi.cast("float", ffi.cast("int", 12)) - assert float(a) == 12.0 - a = ffi.cast("float", ffi.cast("double", 12.5)) - assert float(a) == 12.5 - a = ffi.cast("float", ffi.cast("char", b"A")) - assert float(a) == ord("A") - a = ffi.cast("int", ffi.cast("double", 12.9)) - assert int(a) == 12 - a = ffi.cast("char", ffi.cast("double", 66.9 + 256)) - assert ffi.string(a) == b"B" - - def test_enum(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("enum foo { A0, B0, CC0, D0 };") - assert ffi.string(ffi.cast("enum foo", 0)) == "A0" - assert ffi.string(ffi.cast("enum foo", 2)) == "CC0" - assert ffi.string(ffi.cast("enum foo", 3)) == "D0" - assert ffi.string(ffi.cast("enum foo", 4)) == "4" - ffi.cdef("enum bar { A1, B1=-2, CC1, D1, E1 };") - assert ffi.string(ffi.cast("enum bar", 0)) == "A1" - assert ffi.string(ffi.cast("enum bar", -2)) == "B1" - assert ffi.string(ffi.cast("enum bar", -1)) == "CC1" - assert ffi.string(ffi.cast("enum bar", 1)) == "E1" - assert ffi.cast("enum bar", -2) == ffi.cast("enum bar", -2) - assert ffi.cast("enum foo", 0) == ffi.cast("enum bar", 0) - assert ffi.cast("enum bar", 0) == ffi.cast("int", 0) - assert repr(ffi.cast("enum bar", -1)) == "<cdata 'enum bar' -1: CC1>" - assert repr(ffi.cast("enum foo", -1)) == ( # enums are unsigned, if - "<cdata 'enum foo' 4294967295>") # they contain no neg value - ffi.cdef("enum baz { A2=0x1000, B2=0x2000 };") - assert ffi.string(ffi.cast("enum baz", 0x1000)) == "A2" - assert ffi.string(ffi.cast("enum baz", 0x2000)) == "B2" - - def test_enum_in_struct(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("enum foo { A, B, C, D }; struct bar { enum foo e; };") - s = ffi.new("struct bar *") - s.e = 0 - assert s.e == 0 - s.e = 3 - assert s.e == 3 - assert s[0].e == 3 - s[0].e = 2 - assert s.e == 2 - assert s[0].e == 2 - s.e = ffi.cast("enum foo", -1) - assert s.e == 4294967295 - assert s[0].e == 4294967295 - s.e = s.e - with pytest.raises(TypeError): - s.e = 'B' - with pytest.raises(TypeError): - s.e = '2' - with pytest.raises(TypeError): - s.e = '#2' - with pytest.raises(TypeError): - s.e = '#7' - - def test_enum_non_contiguous(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("enum foo { A, B=42, C };") - assert ffi.string(ffi.cast("enum foo", 0)) == "A" - assert ffi.string(ffi.cast("enum foo", 42)) == "B" - assert ffi.string(ffi.cast("enum foo", 43)) == "C" - invalid_value = ffi.cast("enum foo", 2) - assert int(invalid_value) == 2 - assert ffi.string(invalid_value) == "2" - - def test_enum_char_hex_oct(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef(r"enum foo{A='!', B='\'', C=0x10, D=010, E=- 0x10, F=-010};") - assert ffi.string(ffi.cast("enum foo", ord('!'))) == "A" - assert ffi.string(ffi.cast("enum foo", ord("'"))) == "B" - assert ffi.string(ffi.cast("enum foo", 16)) == "C" - assert ffi.string(ffi.cast("enum foo", 8)) == "D" - assert ffi.string(ffi.cast("enum foo", -16)) == "E" - assert ffi.string(ffi.cast("enum foo", -8)) == "F" - - def test_enum_partial(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef(r"enum foo {A, ...}; enum bar { B, C };") - needs_dlopen_none() - lib = ffi.dlopen(None) - assert lib.B == 0 - py.test.raises(VerificationMissing, getattr, lib, "A") - assert lib.C == 1 - - def test_array_of_struct(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("struct foo { int a, b; };") - s = ffi.new("struct foo[1]") - with pytest.raises(AttributeError): - s.b - with pytest.raises(AttributeError): - s.b = 412 - s[0].b = 412 - assert s[0].b == 412 - with pytest.raises(IndexError): - s[1] - - def test_pointer_to_array(self): - ffi = FFI(backend=self.Backend()) - p = ffi.new("int(**)[5]") - assert repr(p) == "<cdata 'int(* *)[5]' owning %d bytes>" % SIZE_OF_PTR - - def test_iterate_array(self): - ffi = FFI(backend=self.Backend()) - a = ffi.new("char[]", b"hello") - assert list(a) == [b"h", b"e", b"l", b"l", b"o", b"\0"] - assert list(iter(a)) == [b"h", b"e", b"l", b"l", b"o", b"\0"] - # - py.test.raises(TypeError, iter, ffi.cast("char *", a)) - py.test.raises(TypeError, list, ffi.cast("char *", a)) - py.test.raises(TypeError, iter, ffi.new("int *")) - py.test.raises(TypeError, list, ffi.new("int *")) - - def test_offsetof(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("struct foo { int a, b, c; };") - assert ffi.offsetof("struct foo", "a") == 0 - assert ffi.offsetof("struct foo", "b") == 4 - assert ffi.offsetof("struct foo", "c") == 8 - - def test_offsetof_nested(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("struct foo { int a, b, c; };" - "struct bar { struct foo d, e; };") - assert ffi.offsetof("struct bar", "e") == 12 - py.test.raises(KeyError, ffi.offsetof, "struct bar", "e.a") - assert ffi.offsetof("struct bar", "e", "a") == 12 - assert ffi.offsetof("struct bar", "e", "b") == 16 - assert ffi.offsetof("struct bar", "e", "c") == 20 - - def test_offsetof_array(self): - ffi = FFI(backend=self.Backend()) - assert ffi.offsetof("int[]", 51) == 51 * ffi.sizeof("int") - assert ffi.offsetof("int *", 51) == 51 * ffi.sizeof("int") - ffi.cdef("struct bar { int a, b; int c[99]; };") - assert ffi.offsetof("struct bar", "c") == 2 * ffi.sizeof("int") - assert ffi.offsetof("struct bar", "c", 0) == 2 * ffi.sizeof("int") - assert ffi.offsetof("struct bar", "c", 51) == 53 * ffi.sizeof("int") - - def test_alignof(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("struct foo { char a; short b; char c; };") - assert ffi.alignof("int") == 4 - assert ffi.alignof("double") in (4, 8) - assert ffi.alignof("struct foo") == 2 - - def test_bitfield(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("struct foo { int a:10, b:20, c:3; };") - assert ffi.sizeof("struct foo") == 8 - s = ffi.new("struct foo *") - s.a = 511 - with pytest.raises(OverflowError): - s.a = 512 - with pytest.raises(OverflowError): - s[0].a = 512 - assert s.a == 511 - s.a = -512 - with pytest.raises(OverflowError): - s.a = -513 - with pytest.raises(OverflowError): - s[0].a = -513 - assert s.a == -512 - s.c = 3 - assert s.c == 3 - with pytest.raises(OverflowError): - s.c = 4 - with pytest.raises(OverflowError): - s[0].c = 4 - s.c = -4 - assert s.c == -4 - - def test_bitfield_enum(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - typedef enum { AA, BB, CC } foo_e; - typedef struct { foo_e f:2; } foo_s; - """) - s = ffi.new("foo_s *") - s.f = 2 - assert s.f == 2 - - def test_anonymous_struct(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("typedef struct { int a; } foo_t;") - ffi.cdef("typedef struct { char b, c; } bar_t;") - f = ffi.new("foo_t *", [12345]) - b = ffi.new("bar_t *", [b"B", b"C"]) - assert f.a == 12345 - assert b.b == b"B" - assert b.c == b"C" - assert repr(b).startswith("<cdata 'bar_t *'") - - def test_struct_with_two_usages(self): - for name in ['foo_s', '']: # anonymous or not - ffi = FFI(backend=self.Backend()) - ffi.cdef("typedef struct %s { int a; } foo_t, *foo_p;" % name) - f = ffi.new("foo_t *", [12345]) - ps = ffi.new("foo_p[]", [f]) - - def test_pointer_arithmetic(self): - ffi = FFI(backend=self.Backend()) - s = ffi.new("short[]", list(range(100, 110))) - p = ffi.cast("short *", s) - assert p[2] == 102 - assert p+1 == p+1 - assert p+1 != p+0 - assert p == p+0 == p-0 - assert (p+1)[0] == 101 - assert (p+19)[-10] == 109 - assert (p+5) - (p+1) == 4 - assert p == s+0 - assert p+1 == s+1 - - def test_pointer_comparison(self): - ffi = FFI(backend=self.Backend()) - s = ffi.new("short[]", list(range(100))) - p = ffi.cast("short *", s) - assert (p < s) is False - assert (p <= s) is True - assert (p == s) is True - assert (p != s) is False - assert (p > s) is False - assert (p >= s) is True - assert (s < p) is False - assert (s <= p) is True - assert (s == p) is True - assert (s != p) is False - assert (s > p) is False - assert (s >= p) is True - q = p + 1 - assert (q < s) is False - assert (q <= s) is False - assert (q == s) is False - assert (q != s) is True - assert (q > s) is True - assert (q >= s) is True - assert (s < q) is True - assert (s <= q) is True - assert (s == q) is False - assert (s != q) is True - assert (s > q) is False - assert (s >= q) is False - assert (q < p) is False - assert (q <= p) is False - assert (q == p) is False - assert (q != p) is True - assert (q > p) is True - assert (q >= p) is True - assert (p < q) is True - assert (p <= q) is True - assert (p == q) is False - assert (p != q) is True - assert (p > q) is False - assert (p >= q) is False - # - assert (None == s) is False - assert (None != s) is True - assert (s == None) is False - assert (s != None) is True - assert (None == q) is False - assert (None != q) is True - assert (q == None) is False - assert (q != None) is True - - def test_integer_comparison(self): - ffi = FFI(backend=self.Backend()) - x = ffi.cast("int", 123) - y = ffi.cast("int", 456) - assert x < y - # - z = ffi.cast("double", 78.9) - assert x > z - assert y > z - - def test_ffi_buffer_ptr(self): - ffi = FFI(backend=self.Backend()) - a = ffi.new("short *", 100) - try: - b = ffi.buffer(a) - except NotImplementedError as e: - py.test.skip(str(e)) - assert type(b) is ffi.buffer - content = b[:] - assert len(content) == len(b) == 2 - if sys.byteorder == 'little': - assert content == b'\x64\x00' - assert b[0] == b'\x64' - b[0] = b'\x65' - else: - assert content == b'\x00\x64' - assert b[1] == b'\x64' - b[1] = b'\x65' - assert a[0] == 101 - - def test_ffi_buffer_array(self): - ffi = FFI(backend=self.Backend()) - a = ffi.new("int[]", list(range(100, 110))) - try: - b = ffi.buffer(a) - except NotImplementedError as e: - py.test.skip(str(e)) - content = b[:] - if sys.byteorder == 'little': - assert content.startswith(b'\x64\x00\x00\x00\x65\x00\x00\x00') - b[4] = b'\x45' - else: - assert content.startswith(b'\x00\x00\x00\x64\x00\x00\x00\x65') - b[7] = b'\x45' - assert len(content) == 4 * 10 - assert a[1] == 0x45 - - def test_ffi_buffer_ptr_size(self): - ffi = FFI(backend=self.Backend()) - a = ffi.new("short *", 0x4243) - try: - b = ffi.buffer(a, 1) - except NotImplementedError as e: - py.test.skip(str(e)) - content = b[:] - assert len(content) == 1 - if sys.byteorder == 'little': - assert content == b'\x43' - b[0] = b'\x62' - assert a[0] == 0x4262 - else: - assert content == b'\x42' - b[0] = b'\x63' - assert a[0] == 0x6343 - - def test_ffi_buffer_array_size(self): - ffi = FFI(backend=self.Backend()) - a1 = ffi.new("int[]", list(range(100, 110))) - a2 = ffi.new("int[]", list(range(100, 115))) - try: - ffi.buffer(a1) - except NotImplementedError as e: - py.test.skip(str(e)) - assert ffi.buffer(a1)[:] == ffi.buffer(a2, 4*10)[:] - - def test_ffi_buffer_with_file(self): - ffi = FFI(backend=self.Backend()) - import tempfile, os, array - fd, filename = tempfile.mkstemp() - f = os.fdopen(fd, 'r+b') - a = ffi.new("int[]", list(range(1005))) - try: - ffi.buffer(a, 512) - except NotImplementedError as e: - py.test.skip(str(e)) - f.write(ffi.buffer(a, 1000 * ffi.sizeof("int"))) - f.seek(0) - assert f.read() == arraytostring(array.array('i', range(1000))) - f.seek(0) - b = ffi.new("int[]", 1005) - f.readinto(ffi.buffer(b, 1000 * ffi.sizeof("int"))) - assert list(a)[:1000] + [0] * (len(a)-1000) == list(b) - f.close() - os.unlink(filename) - - def test_ffi_buffer_with_io(self): - ffi = FFI(backend=self.Backend()) - import io, array - f = io.BytesIO() - a = ffi.new("int[]", list(range(1005))) - try: - ffi.buffer(a, 512) - except NotImplementedError as e: - py.test.skip(str(e)) - f.write(ffi.buffer(a, 1000 * ffi.sizeof("int"))) - f.seek(0) - assert f.read() == arraytostring(array.array('i', range(1000))) - f.seek(0) - b = ffi.new("int[]", 1005) - f.readinto(ffi.buffer(b, 1000 * ffi.sizeof("int"))) - assert list(a)[:1000] + [0] * (len(a)-1000) == list(b) - f.close() - - def test_ffi_buffer_comparisons(self): - ffi = FFI(backend=self.Backend()) - ba = bytearray(range(100, 110)) - if sys.version_info >= (2, 7): - assert ba == memoryview(ba) # justification for the following - a = ffi.new("uint8_t[]", list(ba)) - c = ffi.new("uint8_t[]", [99] + list(ba)) - try: - b_full = ffi.buffer(a) - b_short = ffi.buffer(a, 3) - b_mid = ffi.buffer(a, 6) - b_other = ffi.buffer(c, 6) - except NotImplementedError as e: - py.test.skip(str(e)) - else: - content = b_full[:] - assert content == b_full == ba - assert b_other < b_short < b_mid < b_full - assert ba > b_mid > ba[0:2] - assert b_short != ba[1:4] - - def test_array_in_struct(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("struct foo_s { int len; short data[5]; };") - p = ffi.new("struct foo_s *") - p.data[3] = 5 - assert p.data[3] == 5 - assert repr(p.data).startswith("<cdata 'short[5]' 0x") - - def test_struct_containing_array_varsize_workaround(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("struct foo_s { int len; short data[0]; };") - p = ffi.new("char[]", ffi.sizeof("struct foo_s") + 7 * SIZE_OF_SHORT) - q = ffi.cast("struct foo_s *", p) - assert q.len == 0 - # 'q.data' gets not a 'short[0]', but just a 'short *' instead - assert repr(q.data).startswith("<cdata 'short *' 0x") - assert q.data[6] == 0 - q.data[6] = 15 - assert q.data[6] == 15 - - def test_new_struct_containing_array_varsize(self): - py.test.skip("later?") - ffi = FFI(backend=self.Backend()) - ffi.cdef("struct foo_s { int len; short data[]; };") - p = ffi.new("struct foo_s *", 10) # a single integer is the length - assert p.len == 0 - assert p.data[9] == 0 - with pytest.raises(IndexError): - p.data[10] - - def test_ffi_typeof_getcname(self): - ffi = FFI(backend=self.Backend()) - assert ffi.getctype("int") == "int" - assert ffi.getctype("int", 'x') == "int x" - assert ffi.getctype("int*") == "int *" - assert ffi.getctype("int*", '') == "int *" - assert ffi.getctype("int*", 'x') == "int * x" - assert ffi.getctype("int", '*') == "int *" - assert ffi.getctype("int", ' * x ') == "int * x" - assert ffi.getctype(ffi.typeof("int*"), '*') == "int * *" - assert ffi.getctype("int", '[5]') == "int[5]" - assert ffi.getctype("int[5]", '[6]') == "int[6][5]" - assert ffi.getctype("int[5]", '(*)') == "int(*)[5]" - # special-case for convenience: automatically put '()' around '*' - assert ffi.getctype("int[5]", '*') == "int(*)[5]" - assert ffi.getctype("int[5]", '*foo') == "int(*foo)[5]" - assert ffi.getctype("int[5]", ' ** foo ') == "int(** foo)[5]" - - def test_array_of_func_ptr(self): - ffi = FFI(backend=self.Backend()) - f = ffi.cast("int(*)(int)", 42) - assert f != ffi.NULL - py.test.raises(CDefError, ffi.cast, "int(int)", 42) - py.test.raises(CDefError, ffi.new, "int([5])(int)") - a = ffi.new("int(*[5])(int)", [f]) - assert ffi.getctype(ffi.typeof(a)) == "int(*[5])(int)" - assert len(a) == 5 - assert a[0] == f - assert a[1] == ffi.NULL - py.test.raises(TypeError, ffi.cast, "int(*)(int)[5]", 0) - # - def cb(n): - return n + 1 - f = ffi.callback("int(*)(int)", cb) - a = ffi.new("int(*[5])(int)", [f, f]) - assert a[1](42) == 43 - - def test_callback_as_function_argument(self): - # In C, function arguments can be declared with a function type, - # which is automatically replaced with the ptr-to-function type. - ffi = FFI(backend=self.Backend()) - def cb(a, b): - return chr(ord(a) + ord(b)).encode() - f = ffi.callback("char cb(char, char)", cb) - assert f(b'A', b'\x01') == b'B' - def g(callback): - return callback(b'A', b'\x01') - g = ffi.callback("char g(char cb(char, char))", g) - assert g(f) == b'B' - - def test_vararg_callback(self): - py.test.skip("callback with '...'") - ffi = FFI(backend=self.Backend()) - def cb(i, va_list): - j = ffi.va_arg(va_list, "int") - k = ffi.va_arg(va_list, "long long") - return i * 2 + j * 3 + k * 5 - f = ffi.callback("long long cb(long i, ...)", cb) - res = f(10, ffi.cast("int", 100), ffi.cast("long long", 1000)) - assert res == 20 + 300 + 5000 - - def test_callback_decorator(self): - ffi = FFI(backend=self.Backend()) - # - @ffi.callback("long(long, long)", error=42) - def cb(a, b): - return a - b - # - assert cb(-100, -10) == -90 - sz = ffi.sizeof("long") - assert cb((1 << (sz*8-1)) - 1, -10) == 42 - - def test_unique_types(self): - ffi1 = FFI(backend=self.Backend()) - ffi2 = FFI(backend=self.Backend()) - assert ffi1.typeof("char") is ffi2.typeof("char ") - assert ffi1.typeof("long") is ffi2.typeof("signed long int") - assert ffi1.typeof("double *") is ffi2.typeof("double*") - assert ffi1.typeof("int ***") is ffi2.typeof(" int * * *") - assert ffi1.typeof("int[]") is ffi2.typeof("signed int[]") - assert ffi1.typeof("signed int*[17]") is ffi2.typeof("int *[17]") - assert ffi1.typeof("void") is ffi2.typeof("void") - assert ffi1.typeof("int(*)(int,int)") is ffi2.typeof("int(*)(int,int)") - # - # these depend on user-defined data, so should not be shared - assert ffi1.typeof("struct foo") is not ffi2.typeof("struct foo") - assert ffi1.typeof("union foo *") is not ffi2.typeof("union foo*") - # the following test is an opaque enum, which we no longer support - #assert ffi1.typeof("enum foo") is not ffi2.typeof("enum foo") - # sanity check: twice 'ffi1' - assert ffi1.typeof("struct foo*") is ffi1.typeof("struct foo *") - - def test_anonymous_enum(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("typedef enum { Value0 = 0 } e, *pe;\n" - "typedef enum { Value1 = 1 } e1;") - assert ffi.getctype("e*") == 'e *' - assert ffi.getctype("pe") == 'e *' - assert ffi.getctype("e1*") == 'e1 *' - - def test_opaque_enum(self): - import warnings - ffi = FFI(backend=self.Backend()) - ffi.cdef("enum foo;") - with warnings.catch_warnings(record=True) as log: - warnings.simplefilter("always") - n = ffi.cast("enum foo", -1) - assert int(n) == 0xffffffff - assert str(log[0].message) == ( - "'enum foo' has no values explicitly defined; " - "guessing that it is equivalent to 'unsigned int'") - - def test_new_ctype(self): - ffi = FFI(backend=self.Backend()) - p = ffi.new("int *") - py.test.raises(TypeError, ffi.new, p) - p = ffi.new(ffi.typeof("int *"), 42) - assert p[0] == 42 - - def test_enum_with_non_injective_mapping(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("enum e { AA=0, BB=0, CC=0, DD=0 };") - e = ffi.cast("enum e", 0) - assert ffi.string(e) == "AA" # pick the first one arbitrarily - - def test_enum_refer_previous_enum_value(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("enum e { AA, BB=2, CC=4, DD=BB, EE, FF=CC, GG=FF };") - assert ffi.string(ffi.cast("enum e", 2)) == "BB" - assert ffi.string(ffi.cast("enum e", 3)) == "EE" - assert ffi.sizeof("char[DD]") == 2 - assert ffi.sizeof("char[EE]") == 3 - assert ffi.sizeof("char[FF]") == 4 - assert ffi.sizeof("char[GG]") == 4 - - def test_nested_anonymous_struct(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - struct foo_s { - struct { int a, b; }; - union { int c, d; }; - }; - """) - assert ffi.sizeof("struct foo_s") == 3 * SIZE_OF_INT - p = ffi.new("struct foo_s *", [1, 2, 3]) - assert p.a == 1 - assert p.b == 2 - assert p.c == 3 - assert p.d == 3 - p.d = 17 - assert p.c == 17 - p.b = 19 - assert p.a == 1 - assert p.b == 19 - assert p.c == 17 - assert p.d == 17 - p = ffi.new("struct foo_s *", {'b': 12, 'd': 14}) - assert p.a == 0 - assert p.b == 12 - assert p.c == 14 - assert p.d == 14 - py.test.raises(ValueError, ffi.new, "struct foo_s *", [0, 0, 0, 0]) - - def test_nested_field_offset_align(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - struct foo_s { - struct { int a; char b; }; - union { char c; }; - }; - """) - assert ffi.offsetof("struct foo_s", "c") == 2 * SIZE_OF_INT - assert ffi.sizeof("struct foo_s") == 3 * SIZE_OF_INT - - def test_nested_anonymous_union(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - union foo_u { - struct { int a, b; }; - union { int c, d; }; - }; - """) - assert ffi.sizeof("union foo_u") == 2 * SIZE_OF_INT - p = ffi.new("union foo_u *", [5]) - assert p.a == 5 - assert p.b == 0 - assert p.c == 5 - assert p.d == 5 - p.d = 17 - assert p.c == 17 - assert p.a == 17 - p.b = 19 - assert p.a == 17 - assert p.b == 19 - assert p.c == 17 - assert p.d == 17 - p = ffi.new("union foo_u *", {'d': 14}) - assert p.a == 14 - assert p.b == 0 - assert p.c == 14 - assert p.d == 14 - p = ffi.new("union foo_u *", {'a': -63, 'b': 12}) - assert p.a == -63 - assert p.b == 12 - assert p.c == -63 - assert p.d == -63 - p = ffi.new("union foo_u *", [123, 456]) - assert p.a == 123 - assert p.b == 456 - assert p.c == 123 - assert p.d == 123 - py.test.raises(ValueError, ffi.new, "union foo_u *", [0, 0, 0]) - - def test_nested_anonymous_struct_2(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - struct foo_s { - int a; - union { int b; union { int c, d; }; }; - int e; - }; - """) - assert ffi.sizeof("struct foo_s") == 3 * SIZE_OF_INT - p = ffi.new("struct foo_s *", [11, 22, 33]) - assert p.a == 11 - assert p.b == p.c == p.d == 22 - assert p.e == 33 - py.test.raises(ValueError, ffi.new, "struct foo_s *", [11, 22, 33, 44]) - FOO = ffi.typeof("struct foo_s") - fields = [(name, fld.offset, fld.flags) for (name, fld) in FOO.fields] - assert fields == [ - ('a', 0 * SIZE_OF_INT, 0), - ('b', 1 * SIZE_OF_INT, 0), - ('c', 1 * SIZE_OF_INT, 1), - ('d', 1 * SIZE_OF_INT, 1), - ('e', 2 * SIZE_OF_INT, 0), - ] - - def test_cast_to_array_type(self): - ffi = FFI(backend=self.Backend()) - p = ffi.new("int[4]", [-5]) - q = ffi.cast("int[3]", p) - assert q[0] == -5 - assert repr(q).startswith("<cdata 'int[3]' 0x") - - def test_gc(self): - ffi = FFI(backend=self.Backend()) - p = ffi.new("int *", 123) - seen = [] - def destructor(p1): - assert p1 is p - assert p1[0] == 123 - seen.append(1) - q = ffi.gc(p, destructor) - assert ffi.typeof(q) is ffi.typeof(p) - import gc; gc.collect() - assert seen == [] - del q - import gc; gc.collect(); gc.collect(); gc.collect() - assert seen == [1] - - def test_gc_2(self): - ffi = FFI(backend=self.Backend()) - p = ffi.new("int *", 123) - seen = [] - q1 = ffi.gc(p, lambda p: seen.append(1)) - q2 = ffi.gc(q1, lambda p: seen.append(2)) - import gc; gc.collect() - assert seen == [] - del q1, q2 - import gc; gc.collect(); gc.collect(); gc.collect(); gc.collect() - assert seen == [2, 1] - - def test_gc_3(self): - ffi = FFI(backend=self.Backend()) - p = ffi.new("int *", 123) - r = ffi.new("int *", 123) - seen = [] - seen_r = [] - q1 = ffi.gc(p, lambda p: seen.append(1)) - s1 = ffi.gc(r, lambda r: seen_r.append(4)) - q2 = ffi.gc(q1, lambda p: seen.append(2)) - s2 = ffi.gc(s1, lambda r: seen_r.append(5)) - q3 = ffi.gc(q2, lambda p: seen.append(3)) - import gc; gc.collect() - assert seen == [] - assert seen_r == [] - del q1, q2, q3, s2, s1 - import gc; gc.collect(); gc.collect(); gc.collect(); gc.collect() - assert seen == [3, 2, 1] - assert seen_r == [5, 4] - - def test_gc_4(self): - ffi = FFI(backend=self.Backend()) - p = ffi.new("int *", 123) - seen = [] - q1 = ffi.gc(p, lambda p: seen.append(1)) - q2 = ffi.gc(q1, lambda p: seen.append(2)) - q3 = ffi.gc(q2, lambda p: seen.append(3)) - import gc; gc.collect() - assert seen == [] - del q1, q3 # q2 remains, and has a hard ref to q1 - import gc; gc.collect(); gc.collect(); gc.collect() - assert seen == [3] - - def test_gc_disable(self): - ffi = FFI(backend=self.Backend()) - p = ffi.new("int *", 123) - py.test.raises(TypeError, ffi.gc, p, None) - seen = [] - q1 = ffi.gc(p, lambda p: seen.append(1)) - q2 = ffi.gc(q1, lambda p: seen.append(2)) - import gc; gc.collect() - assert seen == [] - assert ffi.gc(q1, None) is None - del q1, q2 - import gc; gc.collect(); gc.collect(); gc.collect() - assert seen == [2] - - def test_gc_finite_list(self): - ffi = FFI(backend=self.Backend()) - p = ffi.new("int *", 123) - keepalive = [] - for i in range(10): - keepalive.append(ffi.gc(p, lambda p: None)) - del keepalive[:] - import gc; gc.collect(); gc.collect() - for i in range(10): - keepalive.append(ffi.gc(p, lambda p: None)) - - def test_CData_CType(self): - ffi = FFI(backend=self.Backend()) - assert isinstance(ffi.cast("int", 0), ffi.CData) - assert isinstance(ffi.new("int *"), ffi.CData) - assert not isinstance(ffi.typeof("int"), ffi.CData) - assert not isinstance(ffi.cast("int", 0), ffi.CType) - assert not isinstance(ffi.new("int *"), ffi.CType) - - def test_CData_CType_2(self): - ffi = FFI(backend=self.Backend()) - assert isinstance(ffi.typeof("int"), ffi.CType) - - def test_bool(self): - ffi = FFI(backend=self.Backend()) - assert int(ffi.cast("_Bool", 0.1)) == 1 - assert int(ffi.cast("_Bool", -0.0)) == 0 - assert int(ffi.cast("_Bool", b'\x02')) == 1 - assert int(ffi.cast("_Bool", b'\x00')) == 0 - assert int(ffi.cast("_Bool", b'\x80')) == 1 - assert ffi.new("_Bool *", False)[0] == 0 - assert ffi.new("_Bool *", 1)[0] == 1 - py.test.raises(OverflowError, ffi.new, "_Bool *", 2) - py.test.raises(TypeError, ffi.string, ffi.cast("_Bool", 2)) - - def test_use_own_bool(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("""typedef int bool;""") - - def test_ordering_bug1(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - struct foo_s { - struct bar_s *p; - }; - struct bar_s { - struct foo_s foo; - }; - """) - q = ffi.new("struct foo_s *") - bar = ffi.new("struct bar_s *") - q.p = bar - assert q.p.foo.p == ffi.NULL - - def test_ordering_bug2(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - struct bar_s; - - struct foo_s { - void (*foo)(struct bar_s[]); - }; - - struct bar_s { - struct foo_s foo; - }; - """) - q = ffi.new("struct foo_s *") - - def test_addressof(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("struct foo_s { int x, y; };") - p = ffi.new("struct foo_s *") - a = ffi.addressof(p[0]) - assert repr(a).startswith("<cdata 'struct foo_s *' 0x") - assert a == p - py.test.raises(TypeError, ffi.addressof, p) - py.test.raises((AttributeError, TypeError), ffi.addressof, 5) - py.test.raises(TypeError, ffi.addressof, ffi.cast("int", 5)) - - def test_addressof_field(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("struct foo_s { int x, y; };") - p = ffi.new("struct foo_s *") - a = ffi.addressof(p[0], 'y') - assert repr(a).startswith("<cdata 'int *' 0x") - assert int(ffi.cast("uintptr_t", a)) == ( - int(ffi.cast("uintptr_t", p)) + ffi.sizeof("int")) - assert a == ffi.addressof(p, 'y') - assert a != ffi.addressof(p, 'x') - - def test_addressof_field_nested(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("struct foo_s { int x, y; };" - "struct bar_s { struct foo_s a, b; };") - p = ffi.new("struct bar_s *") - py.test.raises(KeyError, ffi.addressof, p[0], 'b.y') - a = ffi.addressof(p[0], 'b', 'y') - assert int(ffi.cast("uintptr_t", a)) == ( - int(ffi.cast("uintptr_t", p)) + - ffi.sizeof("struct foo_s") + ffi.sizeof("int")) - - def test_addressof_anonymous_struct(self): - ffi = FFI() - ffi.cdef("typedef struct { int x; } foo_t;") - p = ffi.new("foo_t *") - a = ffi.addressof(p[0]) - assert a == p - - def test_addressof_array(self): - ffi = FFI() - p = ffi.new("int[52]") - p0 = ffi.addressof(p) - assert p0 == p - assert ffi.typeof(p0) is ffi.typeof("int(*)[52]") - py.test.raises(TypeError, ffi.addressof, p0) - # - p1 = ffi.addressof(p, 25) - assert ffi.typeof(p1) is ffi.typeof("int *") - assert (p1 - p) == 25 - assert ffi.addressof(p, 0) == p - - def test_addressof_pointer(self): - ffi = FFI() - array = ffi.new("int[50]") - p = ffi.cast("int *", array) - py.test.raises(TypeError, ffi.addressof, p) - assert ffi.addressof(p, 0) == p - assert ffi.addressof(p, 25) == p + 25 - assert ffi.typeof(ffi.addressof(p, 25)) == ffi.typeof(p) - # - ffi.cdef("struct foo { int a, b; };") - array = ffi.new("struct foo[50]") - p = ffi.cast("int *", array) - py.test.raises(TypeError, ffi.addressof, p) - assert ffi.addressof(p, 0) == p - assert ffi.addressof(p, 25) == p + 25 - assert ffi.typeof(ffi.addressof(p, 25)) == ffi.typeof(p) - - def test_addressof_array_in_struct(self): - ffi = FFI() - ffi.cdef("struct foo { int a, b; int c[50]; };") - p = ffi.new("struct foo *") - p1 = ffi.addressof(p, "c", 25) - assert ffi.typeof(p1) is ffi.typeof("int *") - assert p1 == ffi.cast("int *", p) + 27 - assert ffi.addressof(p, "c") == ffi.cast("int *", p) + 2 - assert ffi.addressof(p, "c", 0) == ffi.cast("int *", p) + 2 - p2 = ffi.addressof(p, 1) - assert ffi.typeof(p2) is ffi.typeof("struct foo *") - assert p2 == p + 1 - - def test_multiple_independent_structs(self): - ffi1 = FFI(); ffi1.cdef("struct foo { int x; };") - ffi2 = FFI(); ffi2.cdef("struct foo { int y, z; };") - foo1 = ffi1.new("struct foo *", [10]) - foo2 = ffi2.new("struct foo *", [20, 30]) - assert foo1.x == 10 - assert foo2.y == 20 - assert foo2.z == 30 - - def test_missing_include(self): - backend = self.Backend() - ffi1 = FFI(backend=backend) - ffi2 = FFI(backend=backend) - ffi1.cdef("typedef signed char schar_t;") - py.test.raises(CDefError, ffi2.cast, "schar_t", 142) - - def test_include_typedef(self): - backend = self.Backend() - ffi1 = FFI(backend=backend) - ffi2 = FFI(backend=backend) - ffi1.cdef("typedef signed char schar_t;") - ffi2.include(ffi1) - p = ffi2.cast("schar_t", 142) - assert int(p) == 142 - 256 - - def test_include_struct(self): - backend = self.Backend() - ffi1 = FFI(backend=backend) - ffi2 = FFI(backend=backend) - ffi1.cdef("struct foo { int x; };") - ffi2.include(ffi1) - p = ffi2.new("struct foo *", [142]) - assert p.x == 142 - - def test_include_union(self): - backend = self.Backend() - ffi1 = FFI(backend=backend) - ffi2 = FFI(backend=backend) - ffi1.cdef("union foo { int x; };") - ffi2.include(ffi1) - p = ffi2.new("union foo *", [142]) - assert p.x == 142 - - def test_include_enum(self): - backend = self.Backend() - ffi1 = FFI(backend=backend) - ffi2 = FFI(backend=backend) - ffi1.cdef("enum foo { FA, FB, FC };") - ffi2.include(ffi1) - p = ffi2.cast("enum foo", 1) - assert ffi2.string(p) == "FB" - assert ffi2.sizeof("char[FC]") == 2 - - def test_include_typedef_2(self): - backend = self.Backend() - ffi1 = FFI(backend=backend) - ffi2 = FFI(backend=backend) - ffi1.cdef("typedef struct { int x; } *foo_p;") - ffi2.include(ffi1) - p = ffi2.new("foo_p", [142]) - assert p.x == 142 - - def test_ignore_multiple_declarations_of_constant(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("#define FOO 42") - ffi.cdef("#define FOO 42") - py.test.raises(FFIError, ffi.cdef, "#define FOO 43") - - def test_struct_packed(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("struct nonpacked { char a; int b; };") - ffi.cdef("struct is_packed { char a; int b; };", packed=True) - ffi.cdef("struct is_packed1 { char a; int b; };", pack=1) - ffi.cdef("struct is_packed2 { char a; int b; };", pack=2) - ffi.cdef("struct is_packed4 { char a; int b; };", pack=4) - ffi.cdef("struct is_packed8 { char a; int b; };", pack=8) - assert ffi.sizeof("struct nonpacked") == 8 - assert ffi.sizeof("struct is_packed") == 5 - assert ffi.sizeof("struct is_packed1") == 5 - assert ffi.sizeof("struct is_packed2") == 6 - assert ffi.sizeof("struct is_packed4") == 8 - assert ffi.sizeof("struct is_packed8") == 8 - assert ffi.alignof("struct nonpacked") == 4 - assert ffi.alignof("struct is_packed") == 1 - assert ffi.alignof("struct is_packed1") == 1 - assert ffi.alignof("struct is_packed2") == 2 - assert ffi.alignof("struct is_packed4") == 4 - assert ffi.alignof("struct is_packed8") == 4 - for name in ['is_packed', 'is_packed1', 'is_packed2', - 'is_packed4', 'is_packed8']: - s = ffi.new("struct %s[2]" % name) - s[0].b = 42623381 - s[0].a = b'X' - s[1].b = -4892220 - s[1].a = b'Y' - assert s[0].b == 42623381 - assert s[0].a == b'X' - assert s[1].b == -4892220 - assert s[1].a == b'Y' - - def test_pack_valueerror(self): - ffi = FFI(backend=self.Backend()) - py.test.raises(ValueError, ffi.cdef, "", pack=3) - py.test.raises(ValueError, ffi.cdef, "", packed=2) - py.test.raises(ValueError, ffi.cdef, "", packed=True, pack=1) - - def test_define_integer_constant(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - #define DOT_0 0 - #define DOT 100 - #define DOT_OCT 0100l - #define DOT_HEX 0x100u - #define DOT_HEX2 0X10 - #define DOT_UL 1000UL - enum foo {AA, BB=DOT, CC}; - """) - needs_dlopen_none() - lib = ffi.dlopen(None) - assert ffi.string(ffi.cast("enum foo", 100)) == "BB" - assert lib.DOT_0 == 0 - assert lib.DOT == 100 - assert lib.DOT_OCT == 0o100 - assert lib.DOT_HEX == 0x100 - assert lib.DOT_HEX2 == 0x10 - assert lib.DOT_UL == 1000 - - def test_opaque_struct_becomes_nonopaque(self): - # Issue #193: if we use a struct between the first cdef() where it is - # declared and another cdef() where its fields are defined, then the - # definition was ignored. - ffi = FFI(backend=self.Backend()) - ffi.cdef("struct foo_s;") - py.test.raises(TypeError, ffi.new, "struct foo_s *") - ffi.cdef("struct foo_s { int x; };") - ffi.new("struct foo_s *") - - def test_ffi_self_include(self): - ffi = FFI(backend=self.Backend()) - py.test.raises(ValueError, ffi.include, ffi) - - def test_anonymous_enum_include(self): - ffi1 = FFI() - ffi1.cdef("enum { EE1 };") - ffi = FFI() - ffi.include(ffi1) - ffi.cdef("enum { EE2, EE3 };") - needs_dlopen_none() - lib = ffi.dlopen(None) - assert lib.EE1 == 0 - assert lib.EE2 == 0 - assert lib.EE3 == 1 - - def test_init_once(self): - def do_init(): - seen.append(1) - return 42 - ffi = FFI() - seen = [] - for i in range(3): - res = ffi.init_once(do_init, "tag1") - assert res == 42 - assert seen == [1] - for i in range(3): - res = ffi.init_once(do_init, "tag2") - assert res == 42 - assert seen == [1, 1] - - def test_init_once_multithread(self): - import sys, time - if sys.version_info < (3,): - import thread - else: - import _thread as thread - # - def do_init(): - seen.append('init!') - time.sleep(1) - seen.append('init done') - return 7 - ffi = FFI() - seen = [] - for i in range(6): - def f(): - res = ffi.init_once(do_init, "tag") - seen.append(res) - thread.start_new_thread(f, ()) - time.sleep(1.5) - assert seen == ['init!', 'init done'] + 6 * [7] - - def test_sizeof_struct_directly(self): - # only works with the Python FFI instances - ffi = FFI(backend=self.Backend()) - assert ffi.sizeof("struct{int a;}") == ffi.sizeof("int") - - def test_callback_large_struct(self): - ffi = FFI(backend=self.Backend()) - # more than 8 bytes - ffi.cdef("struct foo_s { unsigned long a, b, c; };") - # - @ffi.callback("void(struct foo_s)") - def cb(s): - seen.append(ffi.typeof(s)) - s.a += 1 - s.b += 2 - s.c += 3 - seen.append(s.a) - seen.append(s.b) - seen.append(s.c) - # - s1 = ffi.new("struct foo_s *", {'a': 100, 'b': 200, 'c': 300}) - seen = [] - cb(s1[0]) - assert len(seen) == 4 - assert s1.a == 100 # unmodified - assert s1.b == 200 - assert s1.c == 300 - assert seen[0] == ffi.typeof("struct foo_s") - assert seen[1] == 101 - assert seen[2] == 202 - assert seen[3] == 303 - - def test_ffi_array_as_init(self): - ffi = FFI(backend=self.Backend()) - p = ffi.new("int[4]", [10, 20, 30, 400]) - q = ffi.new("int[4]", p) - assert list(q) == [10, 20, 30, 400] - py.test.raises(TypeError, ffi.new, "int[3]", p) - py.test.raises(TypeError, ffi.new, "int[5]", p) - py.test.raises(TypeError, ffi.new, "int16_t[4]", p) - s = ffi.new("struct {int i[4];}*", {'i': p}) - assert list(s.i) == [10, 20, 30, 400] - - def test_too_many_initializers(self): - ffi = FFI(backend=self.Backend()) - py.test.raises(IndexError, ffi.new, "int[4]", [10, 20, 30, 40, 50]) diff --git a/testing/cffi0/callback_in_thread.py b/testing/cffi0/callback_in_thread.py deleted file mode 100644 index c98605c..0000000 --- a/testing/cffi0/callback_in_thread.py +++ /dev/null @@ -1,42 +0,0 @@ -import sys, time -sys.path.insert(0, sys.argv[1]) -from cffi import FFI - -def _run_callback_in_thread(): - ffi = FFI() - ffi.cdef(""" - typedef int (*mycallback_func_t)(int, int); - int threaded_ballback_test(mycallback_func_t mycb); - """) - lib = ffi.verify(""" - #include <pthread.h> - typedef int (*mycallback_func_t)(int, int); - void *my_wait_function(void *ptr) { - mycallback_func_t cbfunc = (mycallback_func_t)ptr; - cbfunc(10, 10); - cbfunc(12, 15); - return NULL; - } - int threaded_ballback_test(mycallback_func_t mycb) { - pthread_t thread; - pthread_create(&thread, NULL, my_wait_function, (void*)mycb); - return 0; - } - """, extra_compile_args=['-pthread']) - seen = [] - @ffi.callback('int(*)(int,int)') - def mycallback(x, y): - time.sleep(0.022) - seen.append((x, y)) - return 0 - lib.threaded_ballback_test(mycallback) - count = 300 - while len(seen) != 2: - time.sleep(0.01) - count -= 1 - assert count > 0, "timeout" - assert seen == [(10, 10), (12, 15)] - -print('STARTING') -_run_callback_in_thread() -print('DONE') diff --git a/testing/cffi0/snippets/distutils_module/setup.py b/testing/cffi0/snippets/distutils_module/setup.py deleted file mode 100644 index a4d5551..0000000 --- a/testing/cffi0/snippets/distutils_module/setup.py +++ /dev/null @@ -1,7 +0,0 @@ - -from distutils.core import setup -import snip_basic_verify - -setup( - py_modules=['snip_basic_verify'], - ext_modules=[snip_basic_verify.ffi.verifier.get_extension()]) diff --git a/testing/cffi0/snippets/distutils_module/snip_basic_verify.py b/testing/cffi0/snippets/distutils_module/snip_basic_verify.py deleted file mode 100644 index e8a867e..0000000 --- a/testing/cffi0/snippets/distutils_module/snip_basic_verify.py +++ /dev/null @@ -1,17 +0,0 @@ - -from cffi import FFI -import sys - -ffi = FFI() -ffi.cdef(""" // some declarations from the man page - struct passwd { - char *pw_name; - ...; - }; - struct passwd *getpwuid(int uid); -""") -C = ffi.verify(""" // passed to the real C compiler -#include <sys/types.h> -#include <pwd.h> -""", libraries=[], # or a list of libraries to link with - force_generic_engine=hasattr(sys, '_force_generic_engine_')) diff --git a/testing/cffi0/snippets/distutils_package_1/setup.py b/testing/cffi0/snippets/distutils_package_1/setup.py deleted file mode 100644 index e3d28a5..0000000 --- a/testing/cffi0/snippets/distutils_package_1/setup.py +++ /dev/null @@ -1,7 +0,0 @@ - -from distutils.core import setup -import snip_basic_verify1 - -setup( - packages=['snip_basic_verify1'], - ext_modules=[snip_basic_verify1.ffi.verifier.get_extension()]) diff --git a/testing/cffi0/snippets/distutils_package_1/snip_basic_verify1/__init__.py b/testing/cffi0/snippets/distutils_package_1/snip_basic_verify1/__init__.py deleted file mode 100644 index e8a867e..0000000 --- a/testing/cffi0/snippets/distutils_package_1/snip_basic_verify1/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ - -from cffi import FFI -import sys - -ffi = FFI() -ffi.cdef(""" // some declarations from the man page - struct passwd { - char *pw_name; - ...; - }; - struct passwd *getpwuid(int uid); -""") -C = ffi.verify(""" // passed to the real C compiler -#include <sys/types.h> -#include <pwd.h> -""", libraries=[], # or a list of libraries to link with - force_generic_engine=hasattr(sys, '_force_generic_engine_')) diff --git a/testing/cffi0/snippets/distutils_package_2/setup.py b/testing/cffi0/snippets/distutils_package_2/setup.py deleted file mode 100644 index 6d8f72a..0000000 --- a/testing/cffi0/snippets/distutils_package_2/setup.py +++ /dev/null @@ -1,8 +0,0 @@ - -from distutils.core import setup -import snip_basic_verify2 - -setup( - packages=['snip_basic_verify2'], - ext_package='snip_basic_verify2', - ext_modules=[snip_basic_verify2.ffi.verifier.get_extension()]) diff --git a/testing/cffi0/snippets/distutils_package_2/snip_basic_verify2/__init__.py b/testing/cffi0/snippets/distutils_package_2/snip_basic_verify2/__init__.py deleted file mode 100644 index b4ee686..0000000 --- a/testing/cffi0/snippets/distutils_package_2/snip_basic_verify2/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ - -from cffi import FFI -import sys - -ffi = FFI() -ffi.cdef(""" // some declarations from the man page - struct passwd { - char *pw_name; - ...; - }; - struct passwd *getpwuid(int uid); -""") -C = ffi.verify(""" // passed to the real C compiler -#include <sys/types.h> -#include <pwd.h> -""", libraries=[], # or a list of libraries to link with - ext_package='snip_basic_verify2', - force_generic_engine=hasattr(sys, '_force_generic_engine_')) diff --git a/testing/cffi0/snippets/infrastructure/setup.py b/testing/cffi0/snippets/infrastructure/setup.py deleted file mode 100644 index ea89f50..0000000 --- a/testing/cffi0/snippets/infrastructure/setup.py +++ /dev/null @@ -1,5 +0,0 @@ - -from distutils.core import setup - -setup(packages=['snip_infrastructure'], - requires=['cffi']) diff --git a/testing/cffi0/snippets/infrastructure/snip_infrastructure/__init__.py b/testing/cffi0/snippets/infrastructure/snip_infrastructure/__init__.py deleted file mode 100644 index dad950d..0000000 --- a/testing/cffi0/snippets/infrastructure/snip_infrastructure/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ - -def func(): - return 42 diff --git a/testing/cffi0/snippets/setuptools_module/setup.py b/testing/cffi0/snippets/setuptools_module/setup.py deleted file mode 100644 index 30f2e04..0000000 --- a/testing/cffi0/snippets/setuptools_module/setup.py +++ /dev/null @@ -1,8 +0,0 @@ - -from setuptools import setup -import snip_setuptools_verify - -setup( - zip_safe=False, - py_modules=['snip_setuptools_verify'], - ext_modules=[snip_setuptools_verify.ffi.verifier.get_extension()]) diff --git a/testing/cffi0/snippets/setuptools_module/snip_setuptools_verify.py b/testing/cffi0/snippets/setuptools_module/snip_setuptools_verify.py deleted file mode 100644 index e8a867e..0000000 --- a/testing/cffi0/snippets/setuptools_module/snip_setuptools_verify.py +++ /dev/null @@ -1,17 +0,0 @@ - -from cffi import FFI -import sys - -ffi = FFI() -ffi.cdef(""" // some declarations from the man page - struct passwd { - char *pw_name; - ...; - }; - struct passwd *getpwuid(int uid); -""") -C = ffi.verify(""" // passed to the real C compiler -#include <sys/types.h> -#include <pwd.h> -""", libraries=[], # or a list of libraries to link with - force_generic_engine=hasattr(sys, '_force_generic_engine_')) diff --git a/testing/cffi0/snippets/setuptools_package_1/setup.py b/testing/cffi0/snippets/setuptools_package_1/setup.py deleted file mode 100644 index 18ea3f6..0000000 --- a/testing/cffi0/snippets/setuptools_package_1/setup.py +++ /dev/null @@ -1,8 +0,0 @@ - -from setuptools import setup -import snip_setuptools_verify1 - -setup( - zip_safe=False, - packages=['snip_setuptools_verify1'], - ext_modules=[snip_setuptools_verify1.ffi.verifier.get_extension()]) diff --git a/testing/cffi0/snippets/setuptools_package_1/snip_setuptools_verify1/__init__.py b/testing/cffi0/snippets/setuptools_package_1/snip_setuptools_verify1/__init__.py deleted file mode 100644 index e8a867e..0000000 --- a/testing/cffi0/snippets/setuptools_package_1/snip_setuptools_verify1/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ - -from cffi import FFI -import sys - -ffi = FFI() -ffi.cdef(""" // some declarations from the man page - struct passwd { - char *pw_name; - ...; - }; - struct passwd *getpwuid(int uid); -""") -C = ffi.verify(""" // passed to the real C compiler -#include <sys/types.h> -#include <pwd.h> -""", libraries=[], # or a list of libraries to link with - force_generic_engine=hasattr(sys, '_force_generic_engine_')) diff --git a/testing/cffi0/snippets/setuptools_package_2/setup.py b/testing/cffi0/snippets/setuptools_package_2/setup.py deleted file mode 100644 index 87fb22b..0000000 --- a/testing/cffi0/snippets/setuptools_package_2/setup.py +++ /dev/null @@ -1,9 +0,0 @@ - -from setuptools import setup -import snip_setuptools_verify2 - -setup( - zip_safe=False, - packages=['snip_setuptools_verify2'], - ext_package='snip_setuptools_verify2', - ext_modules=[snip_setuptools_verify2.ffi.verifier.get_extension()]) diff --git a/testing/cffi0/snippets/setuptools_package_2/snip_setuptools_verify2/__init__.py b/testing/cffi0/snippets/setuptools_package_2/snip_setuptools_verify2/__init__.py deleted file mode 100644 index 5f4bd13..0000000 --- a/testing/cffi0/snippets/setuptools_package_2/snip_setuptools_verify2/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ - -from cffi import FFI -import sys - -ffi = FFI() -ffi.cdef(""" // some declarations from the man page - struct passwd { - char *pw_name; - ...; - }; - struct passwd *getpwuid(int uid); -""") -C = ffi.verify(""" // passed to the real C compiler -#include <sys/types.h> -#include <pwd.h> -""", libraries=[], # or a list of libraries to link with - ext_package='snip_setuptools_verify2', - force_generic_engine=hasattr(sys, '_force_generic_engine_')) diff --git a/testing/cffi0/test_cdata.py b/testing/cffi0/test_cdata.py deleted file mode 100644 index 23989ab..0000000 --- a/testing/cffi0/test_cdata.py +++ /dev/null @@ -1,41 +0,0 @@ -import py -from cffi import FFI - -class FakeBackend(object): - - def nonstandard_integer_types(self): - return {} - - def sizeof(self, name): - return 1 - - def load_library(self, path): - return "fake library" - - def new_primitive_type(self, name): - return FakeType("primitive " + name) - - def new_void_type(self): - return FakeType("void") - def new_pointer_type(self, x): - return FakeType('ptr-to-%r' % (x,)) - def new_array_type(self, x, y): - return FakeType('array-from-%r-len-%r' % (x, y)) - def cast(self, x, y): - return 'casted!' - def _get_types(self): - return "CData", "CType" - - buffer = "buffer type" - - -class FakeType(object): - def __init__(self, cdecl): - self.cdecl = cdecl - - -def test_typeof(): - ffi = FFI(backend=FakeBackend()) - clong = ffi.typeof("signed long int") - assert isinstance(clong, FakeType) - assert clong.cdecl == 'primitive long' diff --git a/testing/cffi0/test_ctypes.py b/testing/cffi0/test_ctypes.py deleted file mode 100644 index a70c8f0..0000000 --- a/testing/cffi0/test_ctypes.py +++ /dev/null @@ -1,43 +0,0 @@ -import py, sys -from testing.cffi0 import backend_tests -from cffi.backend_ctypes import CTypesBackend - - -class TestCTypes(backend_tests.BackendTests): - # for individual tests see - # ====> backend_tests.py - - Backend = CTypesBackend - TypeRepr = "<class 'ffi.CData<%s>'>" - - def test_array_of_func_ptr(self): - py.test.skip("ctypes backend: not supported: " - "initializers for function pointers") - - def test_structptr_argument(self): - py.test.skip("ctypes backend: not supported: passing a list " - "for a pointer argument") - - def test_array_argument_as_list(self): - py.test.skip("ctypes backend: not supported: passing a list " - "for a pointer argument") - - def test_cast_to_array_type(self): - py.test.skip("ctypes backend: not supported: casting to array") - - def test_nested_anonymous_struct(self): - py.test.skip("ctypes backend: not supported: nested anonymous struct") - - def test_nested_field_offset_align(self): - py.test.skip("ctypes backend: not supported: nested anonymous struct") - - def test_nested_anonymous_union(self): - py.test.skip("ctypes backend: not supported: nested anonymous union") - - def test_nested_anonymous_struct_2(self): - py.test.skip("ctypes backend: not supported: nested anonymous union") - - def test_CData_CType_2(self): - if sys.version_info >= (3,): - py.test.skip("ctypes backend: not supported in Python 3: CType") - backend_tests.BackendTests.test_CData_CType_2(self) diff --git a/testing/cffi0/test_ffi_backend.py b/testing/cffi0/test_ffi_backend.py deleted file mode 100644 index 8e29bc4..0000000 --- a/testing/cffi0/test_ffi_backend.py +++ /dev/null @@ -1,620 +0,0 @@ -import py, sys, platform -import pytest -from testing.cffi0 import backend_tests, test_function, test_ownlib -from testing.support import u -from cffi import FFI -import _cffi_backend - - -class TestFFI(backend_tests.BackendTests, - test_function.TestFunction, - test_ownlib.TestOwnLib): - TypeRepr = "<ctype '%s'>" - - @staticmethod - def Backend(): - return _cffi_backend - - def test_not_supported_bitfield_in_result(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("struct foo_s { int a,b,c,d,e; int x:1; };") - e = py.test.raises(NotImplementedError, ffi.callback, - "struct foo_s foo(void)", lambda: 42) - assert str(e.value) == ("struct foo_s(*)(): " - "callback with unsupported argument or return type or with '...'") - - def test_inspecttype(self): - ffi = FFI(backend=self.Backend()) - assert ffi.typeof("long").kind == "primitive" - assert ffi.typeof("long(*)(long, long**, ...)").cname == ( - "long(*)(long, long * *, ...)") - assert ffi.typeof("long(*)(long, long**, ...)").ellipsis is True - - def test_new_handle(self): - ffi = FFI(backend=self.Backend()) - o = [2, 3, 4] - p = ffi.new_handle(o) - assert ffi.typeof(p) == ffi.typeof("void *") - assert ffi.from_handle(p) is o - assert ffi.from_handle(ffi.cast("char *", p)) is o - py.test.raises(RuntimeError, ffi.from_handle, ffi.NULL) - - def test_callback_onerror(self): - ffi = FFI(backend=self.Backend()) - seen = [] - def oops(*args): - seen.append(args) - def otherfunc(): - raise LookupError - def cb(n): - otherfunc() - a = ffi.callback("int(*)(int)", cb, error=42, onerror=oops) - res = a(234) - assert res == 42 - assert len(seen) == 1 - exc, val, tb = seen[0] - assert exc is LookupError - assert isinstance(val, LookupError) - assert tb.tb_frame.f_code.co_name == 'cb' - assert tb.tb_frame.f_locals['n'] == 234 - - def test_ffi_new_allocator_2(self): - ffi = FFI(backend=self.Backend()) - seen = [] - def myalloc(size): - seen.append(size) - return ffi.new("char[]", b"X" * size) - def myfree(raw): - seen.append(raw) - alloc1 = ffi.new_allocator(myalloc, myfree) - alloc2 = ffi.new_allocator(alloc=myalloc, free=myfree, - should_clear_after_alloc=False) - p1 = alloc1("int[10]") - p2 = alloc2("int[]", 10) - assert seen == [40, 40] - assert ffi.typeof(p1) == ffi.typeof("int[10]") - assert ffi.sizeof(p1) == 40 - assert ffi.typeof(p2) == ffi.typeof("int[]") - assert ffi.sizeof(p2) == 40 - assert p1[5] == 0 - assert p2[6] == ord('X') * 0x01010101 - raw1 = ffi.cast("char *", p1) - raw2 = ffi.cast("char *", p2) - del p1, p2 - retries = 0 - while len(seen) != 4: - retries += 1 - assert retries <= 5 - import gc; gc.collect() - assert seen == [40, 40, raw1, raw2] - assert repr(seen[2]) == "<cdata 'char[]' owning 41 bytes>" - assert repr(seen[3]) == "<cdata 'char[]' owning 41 bytes>" - - def test_ffi_new_allocator_3(self): - ffi = FFI(backend=self.Backend()) - seen = [] - def myalloc(size): - seen.append(size) - return ffi.new("char[]", b"X" * size) - alloc1 = ffi.new_allocator(myalloc) # no 'free' - p1 = alloc1("int[10]") - assert seen == [40] - assert ffi.typeof(p1) == ffi.typeof("int[10]") - assert ffi.sizeof(p1) == 40 - assert p1[5] == 0 - - def test_ffi_new_allocator_4(self): - ffi = FFI(backend=self.Backend()) - py.test.raises(TypeError, ffi.new_allocator, free=lambda x: None) - # - def myalloc2(size): - raise LookupError - alloc2 = ffi.new_allocator(myalloc2) - py.test.raises(LookupError, alloc2, "int[5]") - # - def myalloc3(size): - return 42 - alloc3 = ffi.new_allocator(myalloc3) - e = py.test.raises(TypeError, alloc3, "int[5]") - assert str(e.value) == "alloc() must return a cdata object (got int)" - # - def myalloc4(size): - return ffi.cast("int", 42) - alloc4 = ffi.new_allocator(myalloc4) - e = py.test.raises(TypeError, alloc4, "int[5]") - assert str(e.value) == "alloc() must return a cdata pointer, not 'int'" - # - def myalloc5(size): - return ffi.NULL - alloc5 = ffi.new_allocator(myalloc5) - py.test.raises(MemoryError, alloc5, "int[5]") - - def test_new_struct_containing_struct_containing_array_varsize(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - struct foo_s { int len[100]; short data[]; }; - struct bar_s { int abc[100]; struct foo_s tail; }; - """) - # loop to try to detect heap overwrites, if the size allocated - # is too small - for i in range(1, 501, 100): - p = ffi.new("struct bar_s *", [[10], [[20], [3,4,5,6,7,8,9] * i]]) - assert p.abc[0] == 10 - assert p.tail.len[0] == 20 - assert p.tail.data[0] == 3 - assert p.tail.data[6] == 9 - assert p.tail.data[7 * i - 1] == 9 - - def test_bogus_struct_containing_struct_containing_array_varsize(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - struct foo_s { signed char len; signed char data[]; }; - struct bar_s { struct foo_s foo; int bcd; }; - """) - p = ffi.new("struct bar_s *", [[123, [45, 56, 67, 78]], 9999999]) - assert p.foo.len == 123 - assert p.foo.data[0] == 45 - assert p.foo.data[1] == 56 - assert p.foo.data[2] == 67 - assert p.bcd == 9999999 - assert p.foo.data[3] != 78 # has been overwritten with 9999999 - - -class TestBitfield: - def check(self, source, expected_ofs_y, expected_align, expected_size): - # NOTE: 'expected_*' is the numbers expected from GCC. - # The numbers expected from MSVC are not explicitly written - # in this file, and will just be taken from the compiler. - ffi = FFI() - ffi.cdef("struct s1 { %s };" % source) - ctype = ffi.typeof("struct s1") - # verify the information with gcc - ffi1 = FFI() - ffi1.cdef(""" - static const int Gofs_y, Galign, Gsize; - struct s1 *try_with_value(int fieldnum, long long value); - """) - fnames = [name for name, cfield in ctype.fields - if name and cfield.bitsize > 0] - setters = ['case %d: s.%s = value; break;' % iname - for iname in enumerate(fnames)] - lib = ffi1.verify(""" - #include <string.h> - struct s1 { %s }; - struct sa { char a; struct s1 b; }; - #define Gofs_y offsetof(struct s1, y) - #define Galign offsetof(struct sa, b) - #define Gsize sizeof(struct s1) - struct s1 *try_with_value(int fieldnum, long long value) - { - static struct s1 s; - memset(&s, 0, sizeof(s)); - switch (fieldnum) { %s } - return &s; - } - """ % (source, ' '.join(setters))) - if sys.platform == 'win32': - expected_ofs_y = lib.Gofs_y - expected_align = lib.Galign - expected_size = lib.Gsize - else: - assert (lib.Gofs_y, lib.Galign, lib.Gsize) == ( - expected_ofs_y, expected_align, expected_size) - # the real test follows - assert ffi.offsetof("struct s1", "y") == expected_ofs_y - assert ffi.alignof("struct s1") == expected_align - assert ffi.sizeof("struct s1") == expected_size - # compare the actual storage of the two - for name, cfield in ctype.fields: - if cfield.bitsize < 0 or not name: - continue - if int(ffi.cast(cfield.type, -1)) == -1: # signed - min_value = -(1 << (cfield.bitsize-1)) - max_value = (1 << (cfield.bitsize-1)) - 1 - else: - min_value = 0 - max_value = (1 << cfield.bitsize) - 1 - for t in [1, 2, 4, 8, 16, 128, 2813, 89728, 981729, - -1,-2,-4,-8,-16,-128,-2813,-89728,-981729]: - if min_value <= t <= max_value: - self._fieldcheck(ffi, lib, fnames, name, t) - - def _fieldcheck(self, ffi, lib, fnames, name, value): - s = ffi.new("struct s1 *") - setattr(s, name, value) - assert getattr(s, name) == value - raw1 = ffi.buffer(s)[:] - buff1 = ffi.buffer(s) - t = lib.try_with_value(fnames.index(name), value) - raw2 = ffi.buffer(t, len(raw1))[:] - assert raw1 == raw2 - buff2 = ffi.buffer(t, len(buff1)) - assert buff1 == buff2 - - def test_bitfield_basic(self): - self.check("int a; int b:9; int c:20; int y;", 8, 4, 12) - self.check("int a; short b:9; short c:7; int y;", 8, 4, 12) - self.check("int a; short b:9; short c:9; int y;", 8, 4, 12) - - def test_bitfield_reuse_if_enough_space(self): - self.check("int a:2; char y;", 1, 4, 4) - self.check("int a:1; char b ; int c:1; char y;", 3, 4, 4) - self.check("int a:1; char b:8; int c:1; char y;", 3, 4, 4) - self.check("char a; int b:9; char y;", 3, 4, 4) - self.check("char a; short b:9; char y;", 4, 2, 6) - self.check("int a:2; char b:6; char y;", 1, 4, 4) - self.check("int a:2; char b:7; char y;", 2, 4, 4) - self.check("int a:2; short b:15; char c:2; char y;", 5, 4, 8) - self.check("int a:2; char b:1; char c:1; char y;", 1, 4, 4) - - @pytest.mark.skipif( - "not (sys.platform == 'darwin' and platform.machine() == 'arm64')" - " and " - "platform.machine().startswith(('arm', 'aarch64'))") - def test_bitfield_anonymous_no_align(self): - L = FFI().alignof("long long") - self.check("char y; int :1;", 0, 1, 2) - self.check("char x; int z:1; char y;", 2, 4, 4) - self.check("char x; int :1; char y;", 2, 1, 3) - self.check("char x; long long z:48; char y;", 7, L, 8) - self.check("char x; long long :48; char y;", 7, 1, 8) - self.check("char x; long long z:56; char y;", 8, L, 8 + L) - self.check("char x; long long :56; char y;", 8, 1, 9) - self.check("char x; long long z:57; char y;", L + 8, L, L + 8 + L) - self.check("char x; long long :57; char y;", L + 8, 1, L + 9) - - @pytest.mark.skipif( - "(sys.platform == 'darwin' and platform.machine() == 'arm64')" - " or " - "not platform.machine().startswith(('arm', 'aarch64'))") - def test_bitfield_anonymous_align_arm(self): - L = FFI().alignof("long long") - self.check("char y; int :1;", 0, 4, 4) - self.check("char x; int z:1; char y;", 2, 4, 4) - self.check("char x; int :1; char y;", 2, 4, 4) - self.check("char x; long long z:48; char y;", 7, L, 8) - self.check("char x; long long :48; char y;", 7, 8, 8) - self.check("char x; long long z:56; char y;", 8, L, 8 + L) - self.check("char x; long long :56; char y;", 8, L, 8 + L) - self.check("char x; long long z:57; char y;", L + 8, L, L + 8 + L) - self.check("char x; long long :57; char y;", L + 8, L, L + 8 + L) - - @pytest.mark.skipif( - "not (sys.platform == 'darwin' and platform.machine() == 'arm64')" - " and " - "platform.machine().startswith(('arm', 'aarch64'))") - def test_bitfield_zero(self): - L = FFI().alignof("long long") - self.check("char y; int :0;", 0, 1, 4) - self.check("char x; int :0; char y;", 4, 1, 5) - self.check("char x; int :0; int :0; char y;", 4, 1, 5) - self.check("char x; long long :0; char y;", L, 1, L + 1) - self.check("short x, y; int :0; int :0;", 2, 2, 4) - self.check("char x; int :0; short b:1; char y;", 5, 2, 6) - self.check("int a:1; int :0; int b:1; char y;", 5, 4, 8) - - @pytest.mark.skipif( - "(sys.platform == 'darwin' and platform.machine() == 'arm64')" - " or " - "not platform.machine().startswith(('arm', 'aarch64'))") - def test_bitfield_zero_arm(self): - L = FFI().alignof("long long") - self.check("char y; int :0;", 0, 4, 4) - self.check("char x; int :0; char y;", 4, 4, 8) - self.check("char x; int :0; int :0; char y;", 4, 4, 8) - self.check("char x; long long :0; char y;", L, 8, L + 8) - self.check("short x, y; int :0; int :0;", 2, 4, 4) - self.check("char x; int :0; short b:1; char y;", 5, 4, 8) - self.check("int a:1; int :0; int b:1; char y;", 5, 4, 8) - - def test_error_cases(self): - ffi = FFI() - ffi.cdef("struct s1 { float x:1; };") - with pytest.raises(TypeError): - ffi.new("struct s1 *") - ffi.cdef("struct s2 { char x:0; };") - with pytest.raises(TypeError): - ffi.new("struct s2 *") - ffi.cdef("struct s3 { char x:9; };") - with pytest.raises(TypeError): - ffi.new("struct s3 *") - - def test_struct_with_typedef(self): - ffi = FFI() - ffi.cdef("typedef struct { float x; } foo_t;") - p = ffi.new("foo_t *", [5.2]) - assert repr(p).startswith("<cdata 'foo_t *' ") - - def test_struct_array_no_length(self): - ffi = FFI() - ffi.cdef("struct foo_s { int x; int a[]; };") - p = ffi.new("struct foo_s *", [100, [200, 300, 400]]) - assert p.x == 100 - assert ffi.typeof(p.a) is ffi.typeof("int[]") - assert len(p.a) == 3 # length recorded - assert p.a[0] == 200 - assert p.a[1] == 300 - assert p.a[2] == 400 - assert list(p.a) == [200, 300, 400] - q = ffi.cast("struct foo_s *", p) - assert q.x == 100 - assert ffi.typeof(q.a) is ffi.typeof("int *") # no length recorded - py.test.raises(TypeError, len, q.a) - assert q.a[0] == 200 - assert q.a[1] == 300 - assert q.a[2] == 400 - py.test.raises(TypeError, list, q.a) - - @pytest.mark.skipif("sys.platform != 'win32'") - def test_getwinerror(self): - ffi = FFI() - code, message = ffi.getwinerror(1155) - assert code == 1155 - assert message == ("No application is associated with the " - "specified file for this operation") - ffi.cdef("void SetLastError(int);") - lib = ffi.dlopen("Kernel32.dll") - lib.SetLastError(2) - code, message = ffi.getwinerror() - assert code == 2 - assert message == "The system cannot find the file specified" - code, message = ffi.getwinerror(-1) - assert code == 2 - assert message == "The system cannot find the file specified" - - def test_from_buffer(self): - import array - ffi = FFI() - a = array.array('H', [10000, 20000, 30000]) - c = ffi.from_buffer(a) - assert ffi.typeof(c) is ffi.typeof("char[]") - assert len(c) == 6 - ffi.cast("unsigned short *", c)[1] += 500 - assert list(a) == [10000, 20500, 30000] - assert c == ffi.from_buffer("char[]", a, True) - assert c == ffi.from_buffer(a, require_writable=True) - # - c = ffi.from_buffer("unsigned short[]", a) - assert len(c) == 3 - assert c[1] == 20500 - # - p = ffi.from_buffer(b"abcd") - assert p[2] == b"c" - # - assert p == ffi.from_buffer(b"abcd", require_writable=False) - py.test.raises((TypeError, BufferError), ffi.from_buffer, - "char[]", b"abcd", True) - py.test.raises((TypeError, BufferError), ffi.from_buffer, b"abcd", - require_writable=True) - - def test_release(self): - ffi = FFI() - p = ffi.new("int[]", 123) - ffi.release(p) - # here, reading p[0] might give garbage or segfault... - ffi.release(p) # no effect - - def test_memmove(self): - ffi = FFI() - p = ffi.new("short[]", [-1234, -2345, -3456, -4567, -5678]) - ffi.memmove(p, p + 1, 4) - assert list(p) == [-2345, -3456, -3456, -4567, -5678] - p[2] = 999 - ffi.memmove(p + 2, p, 6) - assert list(p) == [-2345, -3456, -2345, -3456, 999] - ffi.memmove(p + 4, ffi.new("char[]", b"\x71\x72"), 2) - if sys.byteorder == 'little': - assert list(p) == [-2345, -3456, -2345, -3456, 0x7271] - else: - assert list(p) == [-2345, -3456, -2345, -3456, 0x7172] - - def test_memmove_buffer(self): - import array - ffi = FFI() - a = array.array('H', [10000, 20000, 30000]) - p = ffi.new("short[]", 5) - ffi.memmove(p, a, 6) - assert list(p) == [10000, 20000, 30000, 0, 0] - ffi.memmove(p + 1, a, 6) - assert list(p) == [10000, 10000, 20000, 30000, 0] - b = array.array('h', [-1000, -2000, -3000]) - ffi.memmove(b, a, 4) - assert b.tolist() == [10000, 20000, -3000] - assert a.tolist() == [10000, 20000, 30000] - p[0] = 999 - p[1] = 998 - p[2] = 997 - p[3] = 996 - p[4] = 995 - ffi.memmove(b, p, 2) - assert b.tolist() == [999, 20000, -3000] - ffi.memmove(b, p + 2, 4) - assert b.tolist() == [997, 996, -3000] - p[2] = -p[2] - p[3] = -p[3] - ffi.memmove(b, p + 2, 6) - assert b.tolist() == [-997, -996, 995] - - def test_memmove_readonly_readwrite(self): - ffi = FFI() - p = ffi.new("signed char[]", 5) - ffi.memmove(p, b"abcde", 3) - assert list(p) == [ord("a"), ord("b"), ord("c"), 0, 0] - ffi.memmove(p, bytearray(b"ABCDE"), 2) - assert list(p) == [ord("A"), ord("B"), ord("c"), 0, 0] - py.test.raises((TypeError, BufferError), ffi.memmove, b"abcde", p, 3) - ba = bytearray(b"xxxxx") - ffi.memmove(dest=ba, src=p, n=3) - assert ba == bytearray(b"ABcxx") - - def test_all_primitives(self): - ffi = FFI() - for name in [ - "char", - "short", - "int", - "long", - "long long", - "signed char", - "unsigned char", - "unsigned short", - "unsigned int", - "unsigned long", - "unsigned long long", - "float", - "double", - "long double", - "wchar_t", - "char16_t", - "char32_t", - "_Bool", - "int8_t", - "uint8_t", - "int16_t", - "uint16_t", - "int32_t", - "uint32_t", - "int64_t", - "uint64_t", - "int_least8_t", - "uint_least8_t", - "int_least16_t", - "uint_least16_t", - "int_least32_t", - "uint_least32_t", - "int_least64_t", - "uint_least64_t", - "int_fast8_t", - "uint_fast8_t", - "int_fast16_t", - "uint_fast16_t", - "int_fast32_t", - "uint_fast32_t", - "int_fast64_t", - "uint_fast64_t", - "intptr_t", - "uintptr_t", - "intmax_t", - "uintmax_t", - "ptrdiff_t", - "size_t", - "ssize_t", - ]: - x = ffi.sizeof(name) - assert 1 <= x <= 16 - - def test_ffi_def_extern(self): - ffi = FFI() - py.test.raises(ValueError, ffi.def_extern) - - def test_introspect_typedef(self): - ffi = FFI() - ffi.cdef("typedef int foo_t;") - assert ffi.list_types() == (['foo_t'], [], []) - assert ffi.typeof('foo_t').kind == 'primitive' - assert ffi.typeof('foo_t').cname == 'int' - # - ffi.cdef("typedef signed char a_t, c_t, g_t, b_t;") - assert ffi.list_types() == (['a_t', 'b_t', 'c_t', 'foo_t', 'g_t'], - [], []) - - def test_introspect_struct(self): - ffi = FFI() - ffi.cdef("struct foo_s { int a; };") - assert ffi.list_types() == ([], ['foo_s'], []) - assert ffi.typeof('struct foo_s').kind == 'struct' - assert ffi.typeof('struct foo_s').cname == 'struct foo_s' - - def test_introspect_union(self): - ffi = FFI() - ffi.cdef("union foo_s { int a; };") - assert ffi.list_types() == ([], [], ['foo_s']) - assert ffi.typeof('union foo_s').kind == 'union' - assert ffi.typeof('union foo_s').cname == 'union foo_s' - - def test_introspect_struct_and_typedef(self): - ffi = FFI() - ffi.cdef("typedef struct { int a; } foo_t;") - assert ffi.list_types() == (['foo_t'], [], []) - assert ffi.typeof('foo_t').kind == 'struct' - assert ffi.typeof('foo_t').cname == 'foo_t' - - def test_introspect_included_type(self): - ffi1 = FFI() - ffi2 = FFI() - ffi1.cdef("typedef signed char schar_t; struct sint_t { int x; };") - ffi2.include(ffi1) - assert ffi1.list_types() == ffi2.list_types() == ( - ['schar_t'], ['sint_t'], []) - - def test_introspect_order(self): - ffi = FFI() - ffi.cdef("union CFFIaaa { int a; }; typedef struct CFFIccc { int a; } CFFIb;") - ffi.cdef("union CFFIg { int a; }; typedef struct CFFIcc { int a; } CFFIbbb;") - ffi.cdef("union CFFIaa { int a; }; typedef struct CFFIa { int a; } CFFIbb;") - assert ffi.list_types() == (['CFFIb', 'CFFIbb', 'CFFIbbb'], - ['CFFIa', 'CFFIcc', 'CFFIccc'], - ['CFFIaa', 'CFFIaaa', 'CFFIg']) - - def test_unpack(self): - ffi = FFI() - p = ffi.new("char[]", b"abc\x00def") - assert ffi.unpack(p+1, 7) == b"bc\x00def\x00" - p = ffi.new("int[]", [-123456789]) - assert ffi.unpack(p, 1) == [-123456789] - - def test_negative_array_size(self): - ffi = FFI() - py.test.raises(ValueError, ffi.cast, "int[-5]", 0) - - def test_cannot_instantiate_manually(self): - ffi = FFI() - ct = type(ffi.typeof("void *")) - py.test.raises(TypeError, ct) - py.test.raises(TypeError, ct, ffi.NULL) - for cd in [type(ffi.cast("void *", 0)), - type(ffi.new("char[]", 3)), - type(ffi.gc(ffi.NULL, lambda x: None))]: - py.test.raises(TypeError, cd) - py.test.raises(TypeError, cd, ffi.NULL) - py.test.raises(TypeError, cd, ffi.typeof("void *")) - - def test_explicitly_defined_char16_t(self): - ffi = FFI() - ffi.cdef("typedef uint16_t char16_t;") - x = ffi.cast("char16_t", 1234) - assert ffi.typeof(x) is ffi.typeof("uint16_t") - - def test_char16_t(self): - ffi = FFI() - x = ffi.new("char16_t[]", 5) - assert len(x) == 5 and ffi.sizeof(x) == 10 - x[2] = u+'\u1324' - assert x[2] == u+'\u1324' - y = ffi.new("char16_t[]", u+'\u1234\u5678') - assert len(y) == 3 - assert list(y) == [u+'\u1234', u+'\u5678', u+'\x00'] - assert ffi.string(y) == u+'\u1234\u5678' - z = ffi.new("char16_t[]", u+'\U00012345') - assert len(z) == 3 - assert list(z) == [u+'\ud808', u+'\udf45', u+'\x00'] - assert ffi.string(z) == u+'\U00012345' - - def test_char32_t(self): - ffi = FFI() - x = ffi.new("char32_t[]", 5) - assert len(x) == 5 and ffi.sizeof(x) == 20 - x[3] = u+'\U00013245' - assert x[3] == u+'\U00013245' - y = ffi.new("char32_t[]", u+'\u1234\u5678') - assert len(y) == 3 - assert list(y) == [u+'\u1234', u+'\u5678', u+'\x00'] - py_uni = u+'\U00012345' - z = ffi.new("char32_t[]", py_uni) - assert len(z) == 2 - assert list(z) == [py_uni, u+'\x00'] # maybe a 2-unichars string - assert ffi.string(z) == py_uni - if len(py_uni) == 1: # 4-bytes unicodes in Python - s = ffi.new("char32_t[]", u+'\ud808\udf00') - assert len(s) == 3 - assert list(s) == [u+'\ud808', u+'\udf00', u+'\x00'] diff --git a/testing/cffi0/test_function.py b/testing/cffi0/test_function.py deleted file mode 100644 index b4bb23d..0000000 --- a/testing/cffi0/test_function.py +++ /dev/null @@ -1,548 +0,0 @@ -import py -import pytest -from cffi import FFI, CDefError -import math, os, sys -import ctypes.util -from cffi.backend_ctypes import CTypesBackend -from testing.udir import udir -from testing.support import FdWriteCapture, StdErrCapture -from .backend_tests import needs_dlopen_none - -try: - from StringIO import StringIO -except ImportError: - from io import StringIO - - -lib_m = 'm' -if sys.platform == 'win32': - #there is a small chance this fails on Mingw via environ $CC - import distutils.ccompiler - if distutils.ccompiler.get_default_compiler() == 'msvc': - lib_m = 'msvcrt' - -class TestFunction(object): - Backend = CTypesBackend - - def test_sin(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - double sin(double x); - """) - m = ffi.dlopen(lib_m) - x = m.sin(1.23) - assert x == math.sin(1.23) - - def test_sinf(self): - if sys.platform == 'win32': - py.test.skip("no sinf found in the Windows stdlib") - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - float sinf(float x); - """) - m = ffi.dlopen(lib_m) - x = m.sinf(1.23) - assert type(x) is float - assert x != math.sin(1.23) # rounding effects - assert abs(x - math.sin(1.23)) < 1E-6 - - def test_getenv_no_return_value(self): - # check that 'void'-returning functions work too - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - void getenv(char *); - """) - needs_dlopen_none() - m = ffi.dlopen(None) - x = m.getenv(b"FOO") - assert x is None - - def test_dlopen_filename(self): - path = ctypes.util.find_library(lib_m) - if not path: - py.test.skip("%s not found" % lib_m) - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - double cos(double x); - """) - m = ffi.dlopen(path) - x = m.cos(1.23) - assert x == math.cos(1.23) - - m = ffi.dlopen(os.path.basename(path)) - x = m.cos(1.23) - assert x == math.cos(1.23) - - def test_dlopen_flags(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - double cos(double x); - """) - m = ffi.dlopen(lib_m, ffi.RTLD_LAZY | ffi.RTLD_LOCAL) - x = m.cos(1.23) - assert x == math.cos(1.23) - - def test_dlopen_constant(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - #define FOOBAR 42 - static const float baz = 42.5; /* not visible */ - double sin(double x); - """) - m = ffi.dlopen(lib_m) - assert m.FOOBAR == 42 - with pytest.raises(NotImplementedError): - m.baz - - def test_tlsalloc(self): - if sys.platform != 'win32': - py.test.skip("win32 only") - if self.Backend is CTypesBackend: - py.test.skip("ctypes complains on wrong calling conv") - ffi = FFI(backend=self.Backend()) - ffi.cdef("long TlsAlloc(void); int TlsFree(long);") - lib = ffi.dlopen('KERNEL32.DLL') - x = lib.TlsAlloc() - assert x != 0 - y = lib.TlsFree(x) - assert y != 0 - - def test_fputs(self): - if not sys.platform.startswith('linux'): - py.test.skip("probably no symbol 'stderr' in the lib") - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - int fputs(const char *, void *); - extern void *stderr; - """) - needs_dlopen_none() - ffi.C = ffi.dlopen(None) - ffi.C.fputs # fetch before capturing, for easier debugging - with FdWriteCapture() as fd: - ffi.C.fputs(b"hello\n", ffi.C.stderr) - ffi.C.fputs(b" world\n", ffi.C.stderr) - res = fd.getvalue() - assert res == b'hello\n world\n' - - def test_fputs_without_const(self): - if not sys.platform.startswith('linux'): - py.test.skip("probably no symbol 'stderr' in the lib") - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - int fputs(char *, void *); - extern void *stderr; - """) - needs_dlopen_none() - ffi.C = ffi.dlopen(None) - ffi.C.fputs # fetch before capturing, for easier debugging - with FdWriteCapture() as fd: - ffi.C.fputs(b"hello\n", ffi.C.stderr) - ffi.C.fputs(b" world\n", ffi.C.stderr) - res = fd.getvalue() - assert res == b'hello\n world\n' - - def test_vararg(self): - if not sys.platform.startswith('linux'): - py.test.skip("probably no symbol 'stderr' in the lib") - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - int fprintf(void *, const char *format, ...); - extern void *stderr; - """) - needs_dlopen_none() - ffi.C = ffi.dlopen(None) - with FdWriteCapture() as fd: - ffi.C.fprintf(ffi.C.stderr, b"hello with no arguments\n") - ffi.C.fprintf(ffi.C.stderr, - b"hello, %s!\n", ffi.new("char[]", b"world")) - ffi.C.fprintf(ffi.C.stderr, - ffi.new("char[]", b"hello, %s!\n"), - ffi.new("char[]", b"world2")) - ffi.C.fprintf(ffi.C.stderr, - b"hello int %d long %ld long long %lld\n", - ffi.cast("int", 42), - ffi.cast("long", 84), - ffi.cast("long long", 168)) - ffi.C.fprintf(ffi.C.stderr, b"hello %p\n", ffi.NULL) - res = fd.getvalue() - assert res == (b"hello with no arguments\n" - b"hello, world!\n" - b"hello, world2!\n" - b"hello int 42 long 84 long long 168\n" - b"hello (nil)\n") - - def test_must_specify_type_of_vararg(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - int printf(const char *format, ...); - """) - needs_dlopen_none() - ffi.C = ffi.dlopen(None) - e = py.test.raises(TypeError, ffi.C.printf, b"hello %d\n", 42) - assert str(e.value) == ("argument 2 passed in the variadic part " - "needs to be a cdata object (got int)") - - def test_function_has_a_c_type(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - int puts(const char *); - """) - needs_dlopen_none() - ffi.C = ffi.dlopen(None) - fptr = ffi.C.puts - assert ffi.typeof(fptr) == ffi.typeof("int(*)(const char*)") - if self.Backend is CTypesBackend: - assert repr(fptr).startswith("<cdata 'int puts(char *)' 0x") - - def test_function_pointer(self): - ffi = FFI(backend=self.Backend()) - def cb(charp): - assert repr(charp).startswith("<cdata 'char *' 0x") - return 42 - fptr = ffi.callback("int(*)(const char *txt)", cb) - assert fptr != ffi.callback("int(*)(const char *)", cb) - assert repr(fptr) == "<cdata 'int(*)(char *)' calling %r>" % (cb,) - res = fptr(b"Hello") - assert res == 42 - # - if not sys.platform.startswith('linux'): - py.test.skip("probably no symbol 'stderr' in the lib") - ffi.cdef(""" - int fputs(const char *, void *); - extern void *stderr; - """) - needs_dlopen_none() - ffi.C = ffi.dlopen(None) - fptr = ffi.cast("int(*)(const char *txt, void *)", ffi.C.fputs) - assert fptr == ffi.C.fputs - assert repr(fptr).startswith("<cdata 'int(*)(char *, void *)' 0x") - with FdWriteCapture() as fd: - fptr(b"world\n", ffi.C.stderr) - res = fd.getvalue() - assert res == b'world\n' - - def test_callback_returning_void(self): - ffi = FFI(backend=self.Backend()) - for returnvalue in [None, 42]: - def cb(): - return returnvalue - fptr = ffi.callback("void(*)(void)", cb) - with StdErrCapture() as f: - returned = fptr() - printed = f.getvalue() - assert returned is None - if returnvalue is None: - assert printed == '' - else: - assert "None" in printed - - def test_callback_returning_struct_three_bytes(self): - if self.Backend is CTypesBackend: - py.test.skip("not supported with the ctypes backend") - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - typedef struct { - unsigned char a, b, c; - } THREEBYTES; - """) - def cb(): - return (12, 34, 56) - fptr = ffi.callback("THREEBYTES(*)(void)", cb) - tb = fptr() - assert tb.a == 12 - assert tb.b == 34 - assert tb.c == 56 - - def test_passing_array(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - int strlen(char[]); - """) - needs_dlopen_none() - ffi.C = ffi.dlopen(None) - p = ffi.new("char[]", b"hello") - res = ffi.C.strlen(p) - assert res == 5 - - def test_write_variable(self): - if not sys.platform.startswith('linux'): - py.test.skip("probably no symbol 'stdout' in the lib") - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - extern void *stdout; - """) - needs_dlopen_none() - C = ffi.dlopen(None) - pout = C.stdout - C.stdout = ffi.NULL - assert C.stdout == ffi.NULL - C.stdout = pout - assert C.stdout == pout - - def test_strchr(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - char *strchr(const char *s, int c); - """) - needs_dlopen_none() - ffi.C = ffi.dlopen(None) - p = ffi.new("char[]", b"hello world!") - q = ffi.C.strchr(p, ord('w')) - assert ffi.string(q) == b"world!" - - def test_function_with_struct_argument(self): - if sys.platform == 'win32': - py.test.skip("no 'inet_ntoa'") - if (self.Backend is CTypesBackend and - '__pypy__' in sys.builtin_module_names): - py.test.skip("ctypes limitation on pypy") - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - struct in_addr { unsigned int s_addr; }; - char *inet_ntoa(struct in_addr in); - """) - needs_dlopen_none() - ffi.C = ffi.dlopen(None) - ina = ffi.new("struct in_addr *", [0x04040404]) - a = ffi.C.inet_ntoa(ina[0]) - assert ffi.string(a) == b'4.4.4.4' - - def test_function_typedef(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - typedef double func_t(double); - func_t sin; - """) - m = ffi.dlopen(lib_m) - x = m.sin(1.23) - assert x == math.sin(1.23) - - def test_fputs_custom_FILE(self): - if self.Backend is CTypesBackend: - py.test.skip("FILE not supported with the ctypes backend") - filename = str(udir.join('fputs_custom_FILE')) - ffi = FFI(backend=self.Backend()) - ffi.cdef("int fputs(const char *, FILE *);") - needs_dlopen_none() - C = ffi.dlopen(None) - with open(filename, 'wb') as f: - f.write(b'[') - C.fputs(b"hello from custom file", f) - f.write(b'][') - C.fputs(b"some more output", f) - f.write(b']') - with open(filename, 'rb') as f: - res = f.read() - assert res == b'[hello from custom file][some more output]' - - def test_constants_on_lib(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("""enum foo_e { AA, BB, CC=5, DD }; - typedef enum { EE=-5, FF } some_enum_t;""") - needs_dlopen_none() - lib = ffi.dlopen(None) - assert lib.AA == 0 - assert lib.BB == 1 - assert lib.CC == 5 - assert lib.DD == 6 - assert lib.EE == -5 - assert lib.FF == -4 - - def test_void_star_accepts_string(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("""int strlen(const void *);""") - needs_dlopen_none() - lib = ffi.dlopen(None) - res = lib.strlen(b"hello") - assert res == 5 - - def test_signed_char_star_accepts_string(self): - if self.Backend is CTypesBackend: - py.test.skip("not supported by the ctypes backend") - ffi = FFI(backend=self.Backend()) - ffi.cdef("""int strlen(signed char *);""") - needs_dlopen_none() - lib = ffi.dlopen(None) - res = lib.strlen(b"hello") - assert res == 5 - - def test_unsigned_char_star_accepts_string(self): - if self.Backend is CTypesBackend: - py.test.skip("not supported by the ctypes backend") - ffi = FFI(backend=self.Backend()) - ffi.cdef("""int strlen(unsigned char *);""") - needs_dlopen_none() - lib = ffi.dlopen(None) - res = lib.strlen(b"hello") - assert res == 5 - - def test_missing_function(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - int nonexistent(); - """) - m = ffi.dlopen(lib_m) - assert not hasattr(m, 'nonexistent') - - def test_wraps_from_stdlib(self): - import functools - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - double sin(double x); - """) - def my_decorator(f): - @functools.wraps(f) - def wrapper(*args): - return f(*args) + 100 - return wrapper - m = ffi.dlopen(lib_m) - sin100 = my_decorator(m.sin) - x = sin100(1.23) - assert x == math.sin(1.23) + 100 - - def test_free_callback_cycle(self): - if self.Backend is CTypesBackend: - py.test.skip("seems to fail with the ctypes backend on windows") - import weakref - def make_callback(data): - container = [data] - callback = ffi.callback('int()', lambda: len(container)) - container.append(callback) - # Ref cycle: callback -> lambda (closure) -> container -> callback - return callback - - class Data(object): - pass - ffi = FFI(backend=self.Backend()) - data = Data() - callback = make_callback(data) - wr = weakref.ref(data) - del callback, data - for i in range(3): - if wr() is not None: - import gc; gc.collect() - assert wr() is None # 'data' does not leak - - def test_windows_stdcall(self): - if sys.platform != 'win32': - py.test.skip("Windows-only test") - if self.Backend is CTypesBackend: - py.test.skip("not with the ctypes backend") - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - BOOL QueryPerformanceFrequency(LONGLONG *lpFrequency); - """) - m = ffi.dlopen("Kernel32.dll") - p_freq = ffi.new("LONGLONG *") - res = m.QueryPerformanceFrequency(p_freq) - assert res != 0 - assert p_freq[0] != 0 - - def test_explicit_cdecl_stdcall(self): - if sys.platform != 'win32': - py.test.skip("Windows-only test") - if self.Backend is CTypesBackend: - py.test.skip("not with the ctypes backend") - win64 = (sys.maxsize > 2**32) - # - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - BOOL QueryPerformanceFrequency(LONGLONG *lpFrequency); - """) - m = ffi.dlopen("Kernel32.dll") - tp = ffi.typeof(m.QueryPerformanceFrequency) - assert str(tp) == "<ctype 'int(*)(long long *)'>" - # - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - BOOL __cdecl QueryPerformanceFrequency(LONGLONG *lpFrequency); - """) - m = ffi.dlopen("Kernel32.dll") - tpc = ffi.typeof(m.QueryPerformanceFrequency) - assert tpc is tp - # - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - BOOL WINAPI QueryPerformanceFrequency(LONGLONG *lpFrequency); - """) - m = ffi.dlopen("Kernel32.dll") - tps = ffi.typeof(m.QueryPerformanceFrequency) - if win64: - assert tps is tpc - else: - assert tps is not tpc - assert str(tps) == "<ctype 'int(__stdcall *)(long long *)'>" - # - ffi = FFI(backend=self.Backend()) - ffi.cdef("typedef int (__cdecl *fnc_t)(int);") - ffi.cdef("typedef int (__stdcall *fns_t)(int);") - tpc = ffi.typeof("fnc_t") - tps = ffi.typeof("fns_t") - assert str(tpc) == "<ctype 'int(*)(int)'>" - if win64: - assert tps is tpc - else: - assert str(tps) == "<ctype 'int(__stdcall *)(int)'>" - # - fnc = ffi.cast("fnc_t", 0) - fns = ffi.cast("fns_t", 0) - ffi.new("fnc_t[]", [fnc]) - if not win64: - py.test.raises(TypeError, ffi.new, "fnc_t[]", [fns]) - py.test.raises(TypeError, ffi.new, "fns_t[]", [fnc]) - ffi.new("fns_t[]", [fns]) - - def test_stdcall_only_on_windows(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef("double __stdcall sin(double x);") # stdcall ignored - m = ffi.dlopen(lib_m) - if (sys.platform == 'win32' and sys.maxsize < 2**32 and - self.Backend is not CTypesBackend): - assert "double(__stdcall *)(double)" in str(ffi.typeof(m.sin)) - else: - assert "double(*)(double)" in str(ffi.typeof(m.sin)) - x = m.sin(1.23) - assert x == math.sin(1.23) - - def test_dir_on_dlopen_lib(self): - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - typedef enum { MYE1, MYE2 } myenum_t; - double myfunc(double); - extern double myvar; - const double myconst; - #define MYFOO 42 - """) - m = ffi.dlopen(lib_m) - assert dir(m) == ['MYE1', 'MYE2', 'MYFOO', 'myconst', 'myfunc', 'myvar'] - - def test_dlclose(self): - if self.Backend is CTypesBackend: - py.test.skip("not with the ctypes backend") - ffi = FFI(backend=self.Backend()) - ffi.cdef("int foobar(void); extern int foobaz;") - lib = ffi.dlopen(lib_m) - ffi.dlclose(lib) - e = py.test.raises(ValueError, getattr, lib, 'foobar') - assert str(e.value).startswith("library '") - assert str(e.value).endswith("' has already been closed") - e = py.test.raises(ValueError, getattr, lib, 'foobaz') - assert str(e.value).startswith("library '") - assert str(e.value).endswith("' has already been closed") - e = py.test.raises(ValueError, setattr, lib, 'foobaz', 42) - assert str(e.value).startswith("library '") - assert str(e.value).endswith("' has already been closed") - ffi.dlclose(lib) # does not raise - - def test_passing_large_list(self): - if self.Backend is CTypesBackend: - py.test.skip("the ctypes backend doesn't support this") - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - void getenv(char *); - """) - needs_dlopen_none() - m = ffi.dlopen(None) - arg = [b"F", b"O", b"O"] + [b"\x00"] * 20000000 - x = m.getenv(arg) - assert x is None diff --git a/testing/cffi0/test_model.py b/testing/cffi0/test_model.py deleted file mode 100644 index bb653ca..0000000 --- a/testing/cffi0/test_model.py +++ /dev/null @@ -1,111 +0,0 @@ -from cffi.model import * - - -def test_void_type(): - assert void_type.get_c_name() == "void" - assert void_type.get_c_name("foo") == "void foo" - assert void_type.get_c_name("*foo") == "void *foo" - -def test_primitive_type(): - int_type = PrimitiveType("int") - assert int_type.get_c_name() == "int" - assert int_type.get_c_name("foo") == "int foo" - assert int_type.get_c_name("*foo") == "int *foo" - assert int_type.get_c_name("[5]") == "int[5]" - -def test_raw_function_type(): - int_type = PrimitiveType("int") - fn_type = RawFunctionType([], int_type, False) - assert fn_type.get_c_name() == "int()(void)" - assert fn_type.get_c_name("*") == "int( *)(void)" - assert fn_type.get_c_name("*foo") == "int( *foo)(void)" - fn_type = RawFunctionType([int_type], int_type, False) - assert fn_type.get_c_name() == "int()(int)" - fn_type = RawFunctionType([int_type] * 2, int_type, False) - assert fn_type.get_c_name() == "int()(int, int)" - # - fn_type = RawFunctionType([int_type], int_type, True) - assert fn_type.get_c_name() == "int()(int, ...)" - assert fn_type.get_c_name("*foo") == "int( *foo)(int, ...)" - # - res_type = FunctionPtrType([int_type], int_type, True) - fn_type = RawFunctionType([int_type], res_type, True) - assert fn_type.get_c_name("x") == "int(*( x)(int, ...))(int, ...)" - -def test_function_ptr_type(): - int_type = PrimitiveType("int") - fn_type = FunctionPtrType([], int_type, False) - assert fn_type.get_c_name() == "int(*)(void)" - assert fn_type.get_c_name("*") == "int(* *)(void)" - assert fn_type.get_c_name("*foo") == "int(* *foo)(void)" - fn_type = FunctionPtrType([int_type], int_type, False) - assert fn_type.get_c_name() == "int(*)(int)" - fn_type = FunctionPtrType([int_type] * 2, int_type, False) - assert fn_type.get_c_name() == "int(*)(int, int)" - # - fn_type = FunctionPtrType([int_type], int_type, True) - assert fn_type.get_c_name() == "int(*)(int, ...)" - -def test_pointer_type(): - ptr_type = PointerType(PrimitiveType("int")) - assert ptr_type.get_c_name("x") == "int * x" - -def test_const_pointer_type(): - ptr_type = ConstPointerType(PrimitiveType("int")) - assert ptr_type.get_c_name("x") == "int const * x" - ptr_type = ConstPointerType(ArrayType(PrimitiveType("int"), 5)) - assert ptr_type.get_c_name("") == "int(const *)[5]" - assert ptr_type.get_c_name("*x") == "int(const * *x)[5]" - -def test_qual_pointer_type(): - ptr_type = PointerType(PrimitiveType("long long"), Q_RESTRICT) - assert ptr_type.get_c_name("") == "long long __restrict *" - assert const_voidp_type.get_c_name("") == "void const *" - -def test_unknown_pointer_type(): - ptr_type = unknown_ptr_type("foo_p") - assert ptr_type.get_c_name("") == "foo_p" - assert ptr_type.get_c_name("x") == "foo_p x" - -def test_unknown_type(): - u_type = unknown_type("foo_t") - assert u_type.get_c_name("") == "foo_t" - assert u_type.get_c_name("x") == "foo_t x" - -def test_array_type(): - a_type = ArrayType(PrimitiveType("int"), None) - assert a_type.get_c_name("") == "int[]" - assert a_type.get_c_name("x") == "int x[]" - assert a_type.get_c_name("*x") == "int(*x)[]" - assert a_type.get_c_name(" *x") == "int(*x)[]" - assert a_type.get_c_name("[5]") == "int[5][]" - a_type = ArrayType(unknown_type("foo_t"), 5) - assert a_type.get_c_name("") == "foo_t[5]" - assert a_type.get_c_name("x") == "foo_t x[5]" - assert a_type.get_c_name("*x") == "foo_t(*x)[5]" - a_type = ArrayType(unknown_ptr_type("foo_p"), None) - assert a_type.get_c_name("") == "foo_p[]" - assert a_type.get_c_name("x") == "foo_p x[]" - assert a_type.get_c_name("*x") == "foo_p(*x)[]" - a_type = ArrayType(ConstPointerType(PrimitiveType("int")), None) - assert a_type.get_c_name("") == "int const *[]" - assert a_type.get_c_name("x") == "int const * x[]" - assert a_type.get_c_name("*x") == "int const *(*x)[]" - fn_type = FunctionPtrType([], PrimitiveType("int"), False) - a_type = ArrayType(fn_type, 5) - assert a_type.get_c_name("") == "int(*[5])(void)" - assert a_type.get_c_name("x") == "int(* x[5])(void)" - assert a_type.get_c_name("*x") == "int(*(*x)[5])(void)" - -def test_struct_type(): - struct_type = StructType("foo_s", None, None, None) - assert struct_type.get_c_name() == "struct foo_s" - assert struct_type.get_c_name("*x") == "struct foo_s *x" - -def test_union_type(): - union_type = UnionType("foo_s", None, None, None) - assert union_type.get_c_name() == "union foo_s" - -def test_enum_type(): - enum_type = EnumType("foo_e", [], []) - assert enum_type.get_c_name() == "enum foo_e" diff --git a/testing/cffi0/test_ownlib.py b/testing/cffi0/test_ownlib.py deleted file mode 100644 index ffad879..0000000 --- a/testing/cffi0/test_ownlib.py +++ /dev/null @@ -1,431 +0,0 @@ -import py, sys, os -import subprocess, weakref -from cffi import FFI -from cffi.backend_ctypes import CTypesBackend -from testing.support import u - - -SOURCE = """\ -#include <errno.h> - -#ifdef _WIN32 -#define EXPORT __declspec(dllexport) -#else -#define EXPORT -#endif - -EXPORT int test_getting_errno(void) { - errno = 123; - return -1; -} - -EXPORT int test_setting_errno(void) { - return errno; -}; - -typedef struct { - long x; - long y; -} POINT; - -typedef struct { - long left; - long top; - long right; - long bottom; -} RECT; - -typedef struct { - unsigned char a, b, c; -} THREEBYTES; - - -EXPORT int PointInRect(RECT *prc, POINT pt) -{ - if (pt.x < prc->left) - return 0; - if (pt.x > prc->right) - return 0; - if (pt.y < prc->top) - return 0; - if (pt.y > prc->bottom) - return 0; - return 1; -}; - -EXPORT long left = 10; -EXPORT long top = 20; -EXPORT long right = 30; -EXPORT long bottom = 40; - -EXPORT RECT ReturnRect(int i, RECT ar, RECT* br, POINT cp, RECT dr, - RECT *er, POINT fp, RECT gr) -{ - /*Check input */ - if (ar.left + br->left + dr.left + er->left + gr.left != left * 5) - { - ar.left = 100; - return ar; - } - if (ar.right + br->right + dr.right + er->right + gr.right != right * 5) - { - ar.right = 100; - return ar; - } - if (cp.x != fp.x) - { - ar.left = -100; - } - if (cp.y != fp.y) - { - ar.left = -200; - } - switch(i) - { - case 0: - return ar; - break; - case 1: - return dr; - break; - case 2: - return gr; - break; - - } - return ar; -} - -EXPORT int my_array[7] = {0, 1, 2, 3, 4, 5, 6}; - -EXPORT unsigned short foo_2bytes(unsigned short a) -{ - return (unsigned short)(a + 42); -} -EXPORT unsigned int foo_4bytes(unsigned int a) -{ - return (unsigned int)(a + 42); -} - -EXPORT void modify_struct_value(RECT r) -{ - r.left = r.right = r.top = r.bottom = 500; -} - -EXPORT THREEBYTES return_three_bytes(void) -{ - THREEBYTES result; - result.a = 12; - result.b = 34; - result.c = 56; - return result; -} -""" - -class TestOwnLib(object): - Backend = CTypesBackend - - def setup_class(cls): - cls.module = None - from testing.udir import udir - udir.join('testownlib.c').write(SOURCE) - if sys.platform == 'win32': - # did we already build it? - if cls.Backend is CTypesBackend: - dll_path = str(udir) + '\\testownlib1.dll' # only ascii for the ctypes backend - else: - dll_path = str(udir) + '\\' + (u+'testownlib\u03be.dll') # non-ascii char - if os.path.exists(dll_path): - cls.module = dll_path - return - # try (not too hard) to find the version used to compile this python - # no mingw - from distutils.msvc9compiler import get_build_version - version = get_build_version() - toolskey = "VS%0.f0COMNTOOLS" % version - toolsdir = os.environ.get(toolskey, None) - if toolsdir is None: - return - productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC") - productdir = os.path.abspath(productdir) - vcvarsall = os.path.join(productdir, "vcvarsall.bat") - # 64? - arch = 'x86' - if sys.maxsize > 2**32: - arch = 'amd64' - if os.path.isfile(vcvarsall): - cmd = '"%s" %s' % (vcvarsall, arch) + ' & cl.exe testownlib.c ' \ - ' /LD /Fetestownlib.dll' - subprocess.check_call(cmd, cwd = str(udir), shell=True) - os.rename(str(udir) + '\\testownlib.dll', dll_path) - cls.module = dll_path - else: - encoded = None - if cls.Backend is not CTypesBackend: - try: - unicode_name = u+'testownlibcaf\xe9' - encoded = unicode_name.encode(sys.getfilesystemencoding()) - if sys.version_info >= (3,): - encoded = str(unicode_name) - except UnicodeEncodeError: - pass - if encoded is None: - unicode_name = u+'testownlib' - encoded = str(unicode_name) - subprocess.check_call( - "cc testownlib.c -shared -fPIC -o '%s.so'" % (encoded,), - cwd=str(udir), shell=True) - cls.module = os.path.join(str(udir), unicode_name + (u+'.so')) - print(repr(cls.module)) - - def test_getting_errno(self): - if self.module is None: - py.test.skip("fix the auto-generation of the tiny test lib") - if sys.platform == 'win32': - py.test.skip("fails, errno at multiple addresses") - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - int test_getting_errno(void); - """) - ownlib = ffi.dlopen(self.module) - res = ownlib.test_getting_errno() - assert res == -1 - assert ffi.errno == 123 - - def test_setting_errno(self): - if self.module is None: - py.test.skip("fix the auto-generation of the tiny test lib") - if sys.platform == 'win32': - py.test.skip("fails, errno at multiple addresses") - if self.Backend is CTypesBackend and '__pypy__' in sys.modules: - py.test.skip("XXX errno issue with ctypes on pypy?") - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - int test_setting_errno(void); - """) - ownlib = ffi.dlopen(self.module) - ffi.errno = 42 - res = ownlib.test_setting_errno() - assert res == 42 - assert ffi.errno == 42 - - def test_my_array_7(self): - if self.module is None: - py.test.skip("fix the auto-generation of the tiny test lib") - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - extern int my_array[7]; - """) - ownlib = ffi.dlopen(self.module) - for i in range(7): - assert ownlib.my_array[i] == i - assert len(ownlib.my_array) == 7 - if self.Backend is CTypesBackend: - py.test.skip("not supported by the ctypes backend") - ownlib.my_array = list(range(10, 17)) - for i in range(7): - assert ownlib.my_array[i] == 10 + i - ownlib.my_array = list(range(7)) - for i in range(7): - assert ownlib.my_array[i] == i - - def test_my_array_no_length(self): - if self.module is None: - py.test.skip("fix the auto-generation of the tiny test lib") - if self.Backend is CTypesBackend: - py.test.skip("not supported by the ctypes backend") - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - extern int my_array[]; - """) - ownlib = ffi.dlopen(self.module) - for i in range(7): - assert ownlib.my_array[i] == i - py.test.raises(TypeError, len, ownlib.my_array) - ownlib.my_array = list(range(10, 17)) - for i in range(7): - assert ownlib.my_array[i] == 10 + i - ownlib.my_array = list(range(7)) - for i in range(7): - assert ownlib.my_array[i] == i - - def test_keepalive_lib(self): - if self.module is None: - py.test.skip("fix the auto-generation of the tiny test lib") - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - int test_getting_errno(void); - """) - ownlib = ffi.dlopen(self.module) - ffi_r = weakref.ref(ffi) - ownlib_r = weakref.ref(ownlib) - func = ownlib.test_getting_errno - del ffi - import gc; gc.collect() # ownlib stays alive - assert ownlib_r() is not None - assert ffi_r() is not None # kept alive by ownlib - res = func() - assert res == -1 - - def test_keepalive_ffi(self): - if self.module is None: - py.test.skip("fix the auto-generation of the tiny test lib") - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - int test_getting_errno(void); - """) - ownlib = ffi.dlopen(self.module) - ffi_r = weakref.ref(ffi) - ownlib_r = weakref.ref(ownlib) - func = ownlib.test_getting_errno - del ownlib - import gc; gc.collect() # ffi stays alive - assert ffi_r() is not None - assert ownlib_r() is not None # kept alive by ffi - res = func() - assert res == -1 - if sys.platform != 'win32': # else, errno at multiple addresses - assert ffi.errno == 123 - - def test_struct_by_value(self): - if self.module is None: - py.test.skip("fix the auto-generation of the tiny test lib") - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - typedef struct { - long x; - long y; - } POINT; - - typedef struct { - long left; - long top; - long right; - long bottom; - } RECT; - - extern long left, top, right, bottom; - - RECT ReturnRect(int i, RECT ar, RECT* br, POINT cp, RECT dr, - RECT *er, POINT fp, RECT gr); - """) - ownlib = ffi.dlopen(self.module) - - rect = ffi.new('RECT[1]') - pt = ffi.new('POINT[1]') - pt[0].x = 15 - pt[0].y = 25 - rect[0].left = ownlib.left - rect[0].right = ownlib.right - rect[0].top = ownlib.top - rect[0].bottom = ownlib.bottom - - for i in range(4): - ret = ownlib.ReturnRect(i, rect[0], rect, pt[0], rect[0], - rect, pt[0], rect[0]) - assert ret.left == ownlib.left - assert ret.right == ownlib.right - assert ret.top == ownlib.top - assert ret.bottom == ownlib.bottom - - def test_addressof_lib(self): - if self.module is None: - py.test.skip("fix the auto-generation of the tiny test lib") - if self.Backend is CTypesBackend: - py.test.skip("not implemented with the ctypes backend") - ffi = FFI(backend=self.Backend()) - ffi.cdef("extern long left; int test_getting_errno(void);") - lib = ffi.dlopen(self.module) - lib.left = 123456 - p = ffi.addressof(lib, "left") - assert ffi.typeof(p) == ffi.typeof("long *") - assert p[0] == 123456 - p[0] += 1 - assert lib.left == 123457 - pfn = ffi.addressof(lib, "test_getting_errno") - assert ffi.typeof(pfn) == ffi.typeof("int(*)(void)") - assert pfn == lib.test_getting_errno - - def test_char16_char32_t(self): - if self.module is None: - py.test.skip("fix the auto-generation of the tiny test lib") - if self.Backend is CTypesBackend: - py.test.skip("not implemented with the ctypes backend") - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - char16_t foo_2bytes(char16_t); - char32_t foo_4bytes(char32_t); - """) - lib = ffi.dlopen(self.module) - assert lib.foo_2bytes(u+'\u1234') == u+'\u125e' - assert lib.foo_4bytes(u+'\u1234') == u+'\u125e' - assert lib.foo_4bytes(u+'\U00012345') == u+'\U0001236f' - - def test_modify_struct_value(self): - if self.module is None: - py.test.skip("fix the auto-generation of the tiny test lib") - if self.Backend is CTypesBackend: - py.test.skip("fails with the ctypes backend on some architectures") - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - typedef struct { - long left; - long top; - long right; - long bottom; - } RECT; - - void modify_struct_value(RECT r); - """) - lib = ffi.dlopen(self.module) - s = ffi.new("RECT *", [11, 22, 33, 44]) - lib.modify_struct_value(s[0]) - assert s.left == 11 - assert s.top == 22 - assert s.right == 33 - assert s.bottom == 44 - - def test_dlopen_handle(self): - if self.module is None: - py.test.skip("fix the auto-generation of the tiny test lib") - if sys.platform == 'win32': - py.test.skip("uses 'dl' explicitly") - if self.__class__.Backend is CTypesBackend: - py.test.skip("not for the ctypes backend") - backend = self.Backend() - ffi1 = FFI(backend=backend) - ffi1.cdef("""void *dlopen(const char *filename, int flags); - int dlclose(void *handle);""") - lib1 = ffi1.dlopen('dl') - handle = lib1.dlopen(self.module.encode(sys.getfilesystemencoding()), - backend.RTLD_LAZY) - assert ffi1.typeof(handle) == ffi1.typeof("void *") - assert handle - - ffi = FFI(backend=backend) - ffi.cdef("""unsigned short foo_2bytes(unsigned short a);""") - lib = ffi.dlopen(handle) - x = lib.foo_2bytes(1000) - assert x == 1042 - - err = lib1.dlclose(handle) - assert err == 0 - - def test_return_three_bytes(self): - if self.module is None: - py.test.skip("fix the auto-generation of the tiny test lib") - if self.__class__.Backend is CTypesBackend: - py.test.skip("not working on win32 on the ctypes backend") - ffi = FFI(backend=self.Backend()) - ffi.cdef(""" - typedef struct { - unsigned char a, b, c; - } THREEBYTES; - - THREEBYTES return_three_bytes(void); - """) - lib = ffi.dlopen(self.module) - tb = lib.return_three_bytes() - assert tb.a == 12 - assert tb.b == 34 - assert tb.c == 56 diff --git a/testing/cffi0/test_parsing.py b/testing/cffi0/test_parsing.py deleted file mode 100644 index a5e4587..0000000 --- a/testing/cffi0/test_parsing.py +++ /dev/null @@ -1,610 +0,0 @@ -import py, sys, re -from cffi import FFI, FFIError, CDefError, VerificationError -from .backend_tests import needs_dlopen_none - - -class FakeBackend(object): - - def nonstandard_integer_types(self): - return {} - - def sizeof(self, name): - return 1 - - def load_library(self, name, flags): - if sys.platform == 'win32': - assert name is None or "msvcr" in name - else: - assert name is None or "libc" in name or "libm" in name - return FakeLibrary() - - def new_function_type(self, args, result, has_varargs): - args = [arg.cdecl for arg in args] - result = result.cdecl - return FakeType( - '<func (%s), %s, %s>' % (', '.join(args), result, has_varargs)) - - def new_primitive_type(self, name): - assert name == name.lower() - return FakeType('<%s>' % name) - - def new_pointer_type(self, itemtype): - return FakeType('<pointer to %s>' % (itemtype,)) - - def new_struct_type(self, name): - return FakeStruct(name) - - def complete_struct_or_union(self, s, fields, tp=None, - totalsize=-1, totalalignment=-1, sflags=0): - assert isinstance(s, FakeStruct) - s.fields = fields - - def new_array_type(self, ptrtype, length): - return FakeType('<array %s x %s>' % (ptrtype, length)) - - def new_void_type(self): - return FakeType("<void>") - def cast(self, x, y): - return 'casted!' - def _get_types(self): - return "CData", "CType" - - buffer = "buffer type" - -class FakeType(object): - def __init__(self, cdecl): - self.cdecl = cdecl - def __str__(self): - return self.cdecl - -class FakeStruct(object): - def __init__(self, name): - self.name = name - def __str__(self): - return ', '.join([str(y) + str(x) for x, y, z in self.fields]) - -class FakeLibrary(object): - - def load_function(self, BType, name): - return FakeFunction(BType, name) - -class FakeFunction(object): - - def __init__(self, BType, name): - self.BType = str(BType) - self.name = name - -lib_m = "m" -if sys.platform == 'win32': - #there is a small chance this fails on Mingw via environ $CC - import distutils.ccompiler - if distutils.ccompiler.get_default_compiler() == 'msvc': - lib_m = 'msvcrt' - -def test_simple(): - ffi = FFI(backend=FakeBackend()) - ffi.cdef("double sin(double x);") - m = ffi.dlopen(lib_m) - func = m.sin # should be a callable on real backends - assert func.name == 'sin' - assert func.BType == '<func (<double>), <double>, False>' - -def test_pipe(): - ffi = FFI(backend=FakeBackend()) - ffi.cdef("int pipe(int pipefd[2]);") - needs_dlopen_none() - C = ffi.dlopen(None) - func = C.pipe - assert func.name == 'pipe' - assert func.BType == '<func (<pointer to <int>>), <int>, False>' - -def test_vararg(): - ffi = FFI(backend=FakeBackend()) - ffi.cdef("short foo(int, ...);") - needs_dlopen_none() - C = ffi.dlopen(None) - func = C.foo - assert func.name == 'foo' - assert func.BType == '<func (<int>), <short>, True>' - -def test_no_args(): - ffi = FFI(backend=FakeBackend()) - ffi.cdef(""" - int foo(void); - """) - needs_dlopen_none() - C = ffi.dlopen(None) - assert C.foo.BType == '<func (), <int>, False>' - -def test_typedef(): - ffi = FFI(backend=FakeBackend()) - ffi.cdef(""" - typedef unsigned int UInt; - typedef UInt UIntReally; - UInt foo(void); - """) - needs_dlopen_none() - C = ffi.dlopen(None) - assert str(ffi.typeof("UIntReally")) == '<unsigned int>' - assert C.foo.BType == '<func (), <unsigned int>, False>' - -def test_typedef_more_complex(): - ffi = FFI(backend=FakeBackend()) - ffi.cdef(""" - typedef struct { int a, b; } foo_t, *foo_p; - int foo(foo_p[]); - """) - needs_dlopen_none() - C = ffi.dlopen(None) - assert str(ffi.typeof("foo_t")) == '<int>a, <int>b' - assert str(ffi.typeof("foo_p")) == '<pointer to <int>a, <int>b>' - assert C.foo.BType == ('<func (<pointer to <pointer to ' - '<int>a, <int>b>>), <int>, False>') - -def test_typedef_array_convert_array_to_pointer(): - ffi = FFI(backend=FakeBackend()) - ffi.cdef(""" - typedef int (*fn_t)(int[5]); - """) - with ffi._lock: - type = ffi._parser.parse_type("fn_t") - BType = ffi._get_cached_btype(type) - assert str(BType) == '<func (<pointer to <int>>), <int>, False>' - -def test_remove_comments(): - ffi = FFI(backend=FakeBackend()) - ffi.cdef(""" - double /*comment here*/ sin // blah blah - /* multi- - line- - //comment */ ( - // foo - double // bar /* <- ignored, because it's in a comment itself - x, double/*several*//*comment*/y) /*on the same line*/ - ; - """) - m = ffi.dlopen(lib_m) - func = m.sin - assert func.name == 'sin' - assert func.BType == '<func (<double>, <double>), <double>, False>' - -def test_remove_line_continuation_comments(): - ffi = FFI(backend=FakeBackend()) - ffi.cdef(""" - double // blah \\ - more comments - x(void); - double // blah // blah\\\\ - y(void); - double // blah\\ \ - etc - z(void); - """) - m = ffi.dlopen(lib_m) - m.x - m.y - m.z - -def test_dont_remove_comment_in_line_directives(): - ffi = FFI(backend=FakeBackend()) - e = py.test.raises(CDefError, ffi.cdef, """ - \t # \t line \t 8 \t "baz.c" \t - - some syntax error here - """) - assert str(e.value) == "parse error\nbaz.c:9:14: before: syntax" - # - e = py.test.raises(CDefError, ffi.cdef, """ - #line 7 "foo//bar.c" - - some syntax error here - """) - # - assert str(e.value) == "parse error\nfoo//bar.c:8:14: before: syntax" - ffi = FFI(backend=FakeBackend()) - e = py.test.raises(CDefError, ffi.cdef, """ - \t # \t 8 \t "baz.c" \t - - some syntax error here - """) - assert str(e.value) == "parse error\nbaz.c:9:14: before: syntax" - # - e = py.test.raises(CDefError, ffi.cdef, """ - # 7 "foo//bar.c" - - some syntax error here - """) - assert str(e.value) == "parse error\nfoo//bar.c:8:14: before: syntax" - -def test_multiple_line_directives(): - ffi = FFI(backend=FakeBackend()) - e = py.test.raises(CDefError, ffi.cdef, - """ #line 5 "foo.c" - extern int xx; - #line 6 "bar.c" - extern int yy; - #line 7 "baz.c" - some syntax error here - #line 8 "yadda.c" - extern int zz; - """) - assert str(e.value) == "parse error\nbaz.c:7:14: before: syntax" - # - e = py.test.raises(CDefError, ffi.cdef, - """ # 5 "foo.c" - extern int xx; - # 6 "bar.c" - extern int yy; - # 7 "baz.c" - some syntax error here - # 8 "yadda.c" - extern int zz; - """) - assert str(e.value) == "parse error\nbaz.c:7:14: before: syntax" - -def test_commented_line_directive(): - ffi = FFI(backend=FakeBackend()) - e = py.test.raises(CDefError, ffi.cdef, """ - /* - #line 5 "foo.c" - */ - void xx(void); - - #line 6 "bar.c" - /* - #line 35 "foo.c" - */ - some syntax error - """) - # - assert str(e.value) == "parse error\nbar.c:9:14: before: syntax" - e = py.test.raises(CDefError, ffi.cdef, """ - /* - # 5 "foo.c" - */ - void xx(void); - - # 6 "bar.c" - /* - # 35 "foo.c" - */ - some syntax error - """) - assert str(e.value) == "parse error\nbar.c:9:14: before: syntax" - -def test_line_continuation_in_defines(): - ffi = FFI(backend=FakeBackend()) - ffi.cdef(""" - #define ABC\\ - 42 - #define BCD \\ - 43 - """) - m = ffi.dlopen(lib_m) - assert m.ABC == 42 - assert m.BCD == 43 - -def test_define_not_supported_for_now(): - ffi = FFI(backend=FakeBackend()) - e = py.test.raises(CDefError, ffi.cdef, '#define FOO "blah"') - assert str(e.value) == ( - 'only supports one of the following syntax:\n' - ' #define FOO ... (literally dot-dot-dot)\n' - ' #define FOO NUMBER (with NUMBER an integer' - ' constant, decimal/hex/octal)\n' - 'got:\n' - ' #define FOO "blah"') - -def test_unnamed_struct(): - ffi = FFI(backend=FakeBackend()) - ffi.cdef("typedef struct { int x; } foo_t;\n" - "typedef struct { int y; } *bar_p;\n") - assert 'typedef foo_t' in ffi._parser._declarations - assert 'typedef bar_p' in ffi._parser._declarations - assert 'anonymous foo_t' in ffi._parser._declarations - type_foo = ffi._parser.parse_type("foo_t") - type_bar = ffi._parser.parse_type("bar_p").totype - assert repr(type_foo) == "<foo_t>" - assert repr(type_bar) == "<struct $1>" - py.test.raises(VerificationError, type_bar.get_c_name) - assert type_foo.get_c_name() == "foo_t" - -def test_override(): - ffi = FFI(backend=FakeBackend()) - needs_dlopen_none() - C = ffi.dlopen(None) - ffi.cdef("int foo(void);") - py.test.raises(FFIError, ffi.cdef, "long foo(void);") - assert C.foo.BType == '<func (), <int>, False>' - ffi.cdef("long foo(void);", override=True) - assert C.foo.BType == '<func (), <long>, False>' - -def test_cannot_have_only_variadic_part(): - # this checks that we get a sensible error if we try "int foo(...);" - ffi = FFI() - e = py.test.raises(CDefError, ffi.cdef, "int foo(...);") - assert str(e.value) == ( - "<cdef source string>:1: foo: a function with only '(...)' " - "as argument is not correct C") - -def test_parse_error(): - ffi = FFI() - e = py.test.raises(CDefError, ffi.cdef, " x y z ") - assert str(e.value).startswith( - 'cannot parse "x y z"\n<cdef source string>:1:') - e = py.test.raises(CDefError, ffi.cdef, "\n\n\n x y z ") - assert str(e.value).startswith( - 'cannot parse "x y z"\n<cdef source string>:4:') - -def test_error_custom_lineno(): - ffi = FFI() - e = py.test.raises(CDefError, ffi.cdef, """ -# 42 "foobar" - - a b c d - """) - assert str(e.value).startswith('parse error\nfoobar:43:') - -def test_cannot_declare_enum_later(): - ffi = FFI() - e = py.test.raises(NotImplementedError, ffi.cdef, - "typedef enum foo_e foo_t; enum foo_e { AA, BB };") - assert str(e.value) == ( - "enum foo_e: the '{}' declaration should appear on the " - "first time the enum is mentioned, not later") - -def test_unknown_name(): - ffi = FFI() - e = py.test.raises(CDefError, ffi.cast, "foobarbazunknown", 0) - assert str(e.value) == "unknown identifier 'foobarbazunknown'" - e = py.test.raises(CDefError, ffi.cast, "foobarbazunknown*", 0) - assert str(e.value).startswith('cannot parse "foobarbazunknown*"') - e = py.test.raises(CDefError, ffi.cast, "int(*)(foobarbazunknown)", 0) - assert str(e.value).startswith('cannot parse "int(*)(foobarbazunknown)"') - -def test_redefine_common_type(): - prefix = "" if sys.version_info < (3,) else "b" - ffi = FFI() - ffi.cdef("typedef char FILE;") - assert repr(ffi.cast("FILE", 123)) == "<cdata 'char' %s'{'>" % prefix - ffi.cdef("typedef char int32_t;") - assert repr(ffi.cast("int32_t", 123)) == "<cdata 'char' %s'{'>" % prefix - ffi = FFI() - ffi.cdef("typedef int bool, *FILE;") - assert repr(ffi.cast("bool", 123)) == "<cdata 'int' 123>" - assert re.match(r"<cdata 'int [*]' 0[xX]?0*7[bB]>", - repr(ffi.cast("FILE", 123))) - ffi = FFI() - ffi.cdef("typedef bool (*fn_t)(bool, bool);") # "bool," but within "( )" - -def test_bool(): - ffi = FFI() - ffi.cdef("void f(bool);") - # - ffi = FFI() - ffi.cdef("typedef _Bool bool; void f(bool);") - -def test_unknown_argument_type(): - ffi = FFI() - e = py.test.raises(CDefError, ffi.cdef, "void f(foobarbazzz);") - assert str(e.value) == ("<cdef source string>:1: f arg 1:" - " unknown type 'foobarbazzz' (if you meant" - " to use the old C syntax of giving untyped" - " arguments, it is not supported)") - -def test_void_renamed_as_only_arg(): - ffi = FFI() - ffi.cdef("typedef void void_t1;" - "typedef void_t1 void_t;" - "typedef int (*func_t)(void_t);") - assert ffi.typeof("func_t").args == () - -def test_WPARAM_on_windows(): - if sys.platform != 'win32': - py.test.skip("Only for Windows") - ffi = FFI() - ffi.cdef("void f(WPARAM);") - # - # WPARAM -> UINT_PTR -> unsigned 32/64-bit integer - ffi = FFI() - value = int(ffi.cast("WPARAM", -42)) - assert value == sys.maxsize * 2 - 40 - -def test__is_constant_globalvar(): - import warnings - for input, expected_output in [ - ("int a;", False), - ("const int a;", True), - ("int *a;", False), - ("const int *a;", False), - ("int const *a;", False), - ("int *const a;", True), - ("int a[5];", False), - ("const int a[5];", False), - ("int *a[5];", False), - ("const int *a[5];", False), - ("int const *a[5];", False), - ("int *const a[5];", False), - ("int a[5][6];", False), - ("const int a[5][6];", False), - ]: - ffi = FFI() - with warnings.catch_warnings(record=True) as log: - warnings.simplefilter("always") - ffi.cdef(input) - declarations = ffi._parser._declarations - assert ('constant a' in declarations) == expected_output - assert ('variable a' in declarations) == (not expected_output) - assert len(log) == (1 - expected_output) - -def test_restrict(): - from cffi import model - for input, expected_output in [ - ("int a;", False), - ("restrict int a;", True), - ("int *a;", False), - ]: - ffi = FFI() - ffi.cdef("extern " + input) - tp, quals = ffi._parser._declarations['variable a'] - assert bool(quals & model.Q_RESTRICT) == expected_output - -def test_different_const_funcptr_types(): - lst = [] - for input in [ - "int(*)(int *a)", - "int(*)(int const *a)", - "int(*)(int * const a)", - "int(*)(int const a[])"]: - ffi = FFI(backend=FakeBackend()) - lst.append(ffi._parser.parse_type(input)) - assert lst[0] != lst[1] - assert lst[0] == lst[2] - assert lst[1] == lst[3] - -def test_const_pointer_to_pointer(): - from cffi import model - ffi = FFI(backend=FakeBackend()) - # - tp, qual = ffi._parser.parse_type_and_quals("char * * (* const)") - assert (str(tp), qual) == ("<char * * *>", model.Q_CONST) - tp, qual = ffi._parser.parse_type_and_quals("char * (* const (*))") - assert (str(tp), qual) == ("<char * * const *>", 0) - tp, qual = ffi._parser.parse_type_and_quals("char (* const (* (*)))") - assert (str(tp), qual) == ("<char * const * *>", 0) - tp, qual = ffi._parser.parse_type_and_quals("char const * * *") - assert (str(tp), qual) == ("<char const * * *>", 0) - tp, qual = ffi._parser.parse_type_and_quals("const char * * *") - assert (str(tp), qual) == ("<char const * * *>", 0) - # - tp, qual = ffi._parser.parse_type_and_quals("char * * * const const") - assert (str(tp), qual) == ("<char * * *>", model.Q_CONST) - tp, qual = ffi._parser.parse_type_and_quals("char * * volatile *") - assert (str(tp), qual) == ("<char * * volatile *>", 0) - tp, qual = ffi._parser.parse_type_and_quals("char * volatile restrict * *") - assert (str(tp), qual) == ("<char * __restrict volatile * *>", 0) - tp, qual = ffi._parser.parse_type_and_quals("char const volatile * * *") - assert (str(tp), qual) == ("<char volatile const * * *>", 0) - tp, qual = ffi._parser.parse_type_and_quals("const char * * *") - assert (str(tp), qual) == ("<char const * * *>", 0) - # - tp, qual = ffi._parser.parse_type_and_quals( - "int(char*const*, short****const*)") - assert (str(tp), qual) == ( - "<int()(char * const *, short * * * * const *)>", 0) - tp, qual = ffi._parser.parse_type_and_quals( - "char*const*(short*const****)") - assert (str(tp), qual) == ( - "<char * const *()(short * const * * * *)>", 0) - -def test_enum(): - ffi = FFI() - ffi.cdef(""" - enum Enum { - POS = +1, - TWO = 2, - NIL = 0, - NEG = -1, - ADDSUB = (POS+TWO)-1, - DIVMULINT = (3 * 3) / 2, - SHIFT = (1 << 3) >> 1, - BINOPS = (0x7 & 0x1) | 0x8, - XOR = 0xf ^ 0xa - }; - """) - needs_dlopen_none() - C = ffi.dlopen(None) - assert C.POS == 1 - assert C.TWO == 2 - assert C.NIL == 0 - assert C.NEG == -1 - assert C.ADDSUB == 2 - assert C.DIVMULINT == 4 - assert C.SHIFT == 4 - assert C.BINOPS == 0b1001 - assert C.XOR == 0b0101 - -def test_stdcall(): - ffi = FFI() - tp = ffi.typeof("int(*)(int __stdcall x(int)," - " long (__cdecl*y)(void)," - " short(WINAPI *z)(short))") - if sys.platform == 'win32' and sys.maxsize < 2**32: - stdcall = '__stdcall ' - else: - stdcall = '' - assert str(tp) == ( - "<ctype 'int(*)(int(%s*)(int), " - "long(*)(), " - "short(%s*)(short))'>" % (stdcall, stdcall)) - -def test_extern_python(): - ffi = FFI() - ffi.cdef(""" - int bok(int, int); - extern "Python" int foobar(int, int); - int baz(int, int); - """) - assert sorted(ffi._parser._declarations) == [ - 'extern_python foobar', 'function baz', 'function bok'] - assert (ffi._parser._declarations['function bok'] == - ffi._parser._declarations['extern_python foobar'] == - ffi._parser._declarations['function baz']) - -def test_extern_python_group(): - ffi = FFI() - ffi.cdef(""" - int bok(int); - extern "Python" {int foobar(int, int);int bzrrr(int);} - int baz(int, int); - """) - assert sorted(ffi._parser._declarations) == [ - 'extern_python bzrrr', 'extern_python foobar', - 'function baz', 'function bok'] - assert (ffi._parser._declarations['function baz'] == - ffi._parser._declarations['extern_python foobar'] != - ffi._parser._declarations['function bok'] == - ffi._parser._declarations['extern_python bzrrr']) - -def test_error_invalid_syntax_for_cdef(): - ffi = FFI() - e = py.test.raises(CDefError, ffi.cdef, 'void foo(void) {}') - assert str(e.value) == ('<cdef source string>:1: unexpected <FuncDef>: ' - 'this construct is valid C but not valid in cdef()') - -def test_unsigned_int_suffix_for_constant(): - ffi = FFI() - ffi.cdef("""enum e { - bin_0=0b10, - bin_1=0b10u, - bin_2=0b10U, - bin_3=0b10l, - bin_4=0b10L, - bin_5=0b10ll, - bin_6=0b10LL, - oct_0=010, - oct_1=010u, - oct_2=010U, - oct_3=010l, - oct_4=010L, - oct_5=010ll, - oct_6=010LL, - dec_0=10, - dec_1=10u, - dec_2=10U, - dec_3=10l, - dec_4=10L, - dec_5=10ll, - dec_6=10LL, - hex_0=0x10, - hex_1=0x10u, - hex_2=0x10U, - hex_3=0x10l, - hex_4=0x10L, - hex_5=0x10ll, - hex_6=0x10LL,};""") - needs_dlopen_none() - C = ffi.dlopen(None) - for base, expected_result in (('bin', 2), ('oct', 8), ('dec', 10), ('hex', 16)): - for index in range(7): - assert getattr(C, '{base}_{index}'.format(base=base, index=index)) == expected_result diff --git a/testing/cffi0/test_platform.py b/testing/cffi0/test_platform.py deleted file mode 100644 index 55446ec..0000000 --- a/testing/cffi0/test_platform.py +++ /dev/null @@ -1,25 +0,0 @@ -import os -from cffi.ffiplatform import maybe_relative_path, flatten - - -def test_not_absolute(): - assert maybe_relative_path('foo/bar') == 'foo/bar' - assert maybe_relative_path('test_platform.py') == 'test_platform.py' - -def test_different_absolute(): - p = os.path.join('..', 'baz.py') - assert maybe_relative_path(p) == p - -def test_absolute_mapping(): - p = os.path.abspath('baz.py') - assert maybe_relative_path(p) == 'baz.py' - foobaz = os.path.join('foo', 'baz.py') - assert maybe_relative_path(os.path.abspath(foobaz)) == foobaz - -def test_flatten(): - assert flatten("foo") == "3sfoo" - assert flatten(-10000000000000000000000000000) == \ - "-10000000000000000000000000000i" - assert flatten([4, 5]) == "2l4i5i" - assert flatten({4: 5}) == "1d4i5i" - assert flatten({"foo": ("bar", "baaz")}) == "1d3sfoo2l3sbar4sbaaz" diff --git a/testing/cffi0/test_unicode_literals.py b/testing/cffi0/test_unicode_literals.py deleted file mode 100644 index 7b0a5cc..0000000 --- a/testing/cffi0/test_unicode_literals.py +++ /dev/null @@ -1,79 +0,0 @@ -# -# ---------------------------------------------- -# WARNING, ALL LITERALS IN THIS FILE ARE UNICODE -# ---------------------------------------------- -# -from __future__ import unicode_literals -# -# -# -import sys, math -from cffi import FFI - -lib_m = "m" -if sys.platform == 'win32': - #there is a small chance this fails on Mingw via environ $CC - import distutils.ccompiler - if distutils.ccompiler.get_default_compiler() == 'msvc': - lib_m = 'msvcrt' - - -def test_cast(): - ffi = FFI() - assert int(ffi.cast("int", 3.14)) == 3 # unicode literal - -def test_new(): - ffi = FFI() - assert ffi.new("int[]", [3, 4, 5])[2] == 5 # unicode literal - -def test_typeof(): - ffi = FFI() - tp = ffi.typeof("int[51]") # unicode literal - assert tp.length == 51 - -def test_sizeof(): - ffi = FFI() - assert ffi.sizeof("int[51]") == 51 * 4 # unicode literal - -def test_alignof(): - ffi = FFI() - assert ffi.alignof("int[51]") == 4 # unicode literal - -def test_getctype(): - ffi = FFI() - assert ffi.getctype("int**") == "int * *" # unicode literal - assert type(ffi.getctype("int**")) is str - -def test_cdef(): - ffi = FFI() - ffi.cdef("typedef int foo_t[50];") # unicode literal - -def test_offsetof(): - ffi = FFI() - ffi.cdef("typedef struct { int x, y; } foo_t;") - assert ffi.offsetof("foo_t", "y") == 4 # unicode literal - -def test_enum(): - ffi = FFI() - ffi.cdef("enum foo_e { AA, BB, CC };") # unicode literal - x = ffi.cast("enum foo_e", 1) - assert int(ffi.cast("int", x)) == 1 - -def test_dlopen(): - ffi = FFI() - ffi.cdef("double sin(double x);") - m = ffi.dlopen(lib_m) # unicode literal - x = m.sin(1.23) - assert x == math.sin(1.23) - -def test_verify(): - ffi = FFI() - ffi.cdef("double test_verify_1(double x);") # unicode literal - lib = ffi.verify("double test_verify_1(double x) { return x * 42.0; }") - assert lib.test_verify_1(-1.5) == -63.0 - -def test_callback(): - ffi = FFI() - cb = ffi.callback("int(int)", # unicode literal - lambda x: x + 42) - assert cb(5) == 47 diff --git a/testing/cffi0/test_verify.py b/testing/cffi0/test_verify.py deleted file mode 100644 index 3a1c0b9..0000000 --- a/testing/cffi0/test_verify.py +++ /dev/null @@ -1,2562 +0,0 @@ -import py, re -import pytest -import sys, os, math, weakref -from cffi import FFI, VerificationError, VerificationMissing, model, FFIError -from testing.support import * -from testing.support import extra_compile_args - - -lib_m = ['m'] -if sys.platform == 'win32': - #there is a small chance this fails on Mingw via environ $CC - import distutils.ccompiler - if distutils.ccompiler.get_default_compiler() == 'msvc': - lib_m = ['msvcrt'] - pass # no obvious -Werror equivalent on MSVC -else: - class FFI(FFI): - def verify(self, *args, **kwds): - return super(FFI, self).verify( - *args, extra_compile_args=extra_compile_args, **kwds) - -def setup_module(): - import cffi.verifier - cffi.verifier.cleanup_tmpdir() - # - # check that no $ sign is produced in the C file; it used to be the - # case that anonymous enums would produce '$enum_$1', which was - # used as part of a function name. GCC accepts such names, but it's - # apparently non-standard. - _r_comment = re.compile(r"/\*.*?\*/|//.*?$", re.DOTALL | re.MULTILINE) - _r_string = re.compile(r'\".*?\"') - def _write_source_and_check(self, file=None): - base_write_source(self, file) - if file is None: - f = open(self.sourcefilename) - data = f.read() - f.close() - data = _r_comment.sub(' ', data) - data = _r_string.sub('"skipped"', data) - assert '$' not in data - base_write_source = cffi.verifier.Verifier._write_source - cffi.verifier.Verifier._write_source = _write_source_and_check - - -def test_module_type(): - import cffi.verifier - ffi = FFI() - lib = ffi.verify() - if hasattr(lib, '_cffi_python_module'): - print('verify got a PYTHON module') - if hasattr(lib, '_cffi_generic_module'): - print('verify got a GENERIC module') - expected_generic = (cffi.verifier._FORCE_GENERIC_ENGINE or - '__pypy__' in sys.builtin_module_names) - assert hasattr(lib, '_cffi_python_module') == (not expected_generic) - assert hasattr(lib, '_cffi_generic_module') == expected_generic - -def test_missing_function(ffi=None): - # uses the FFI hacked above with '-Werror' - if ffi is None: - ffi = FFI() - ffi.cdef("void some_completely_unknown_function();") - try: - lib = ffi.verify() - except (VerificationError, OSError): - pass # expected case: we get a VerificationError - else: - # but depending on compiler and loader details, maybe - # 'lib' could actually be imported but will fail if we - # actually try to call the unknown function... Hard - # to test anything more. - pass - -def test_missing_function_import_error(): - # uses the original FFI that just gives a warning during compilation - import cffi - test_missing_function(ffi=cffi.FFI()) - -def test_simple_case(): - ffi = FFI() - ffi.cdef("double sin(double x);") - lib = ffi.verify('#include <math.h>', libraries=lib_m) - assert lib.sin(1.23) == math.sin(1.23) - -def _Wconversion(cdef, source, **kargs): - if sys.platform in ('win32', 'darwin'): - py.test.skip("needs GCC") - ffi = FFI() - ffi.cdef(cdef) - py.test.raises(VerificationError, ffi.verify, source, **kargs) - extra_compile_args_orig = extra_compile_args[:] - extra_compile_args.remove('-Wconversion') - try: - lib = ffi.verify(source, **kargs) - finally: - extra_compile_args[:] = extra_compile_args_orig - return lib - -def test_Wconversion_unsigned(): - _Wconversion("unsigned foo(void);", - "int foo(void) { return -1;}") - -def test_Wconversion_integer(): - _Wconversion("short foo(void);", - "long long foo(void) { return 1<<sizeof(short);}") - -def test_Wconversion_floating(): - lib = _Wconversion("float sin(double);", - "#include <math.h>", libraries=lib_m) - res = lib.sin(1.23) - assert res != math.sin(1.23) # not exact, because of double->float - assert abs(res - math.sin(1.23)) < 1E-5 - -def test_Wconversion_float2int(): - _Wconversion("int sinf(float);", - "#include <math.h>", libraries=lib_m) - -def test_Wconversion_double2int(): - _Wconversion("int sin(double);", - "#include <math.h>", libraries=lib_m) - -def test_rounding_1(): - ffi = FFI() - ffi.cdef("double sinf(float x);") - lib = ffi.verify('#include <math.h>', libraries=lib_m) - res = lib.sinf(1.23) - assert res != math.sin(1.23) # not exact, because of double->float - assert abs(res - math.sin(1.23)) < 1E-5 - -def test_rounding_2(): - ffi = FFI() - ffi.cdef("double sin(float x);") - lib = ffi.verify('#include <math.h>', libraries=lib_m) - res = lib.sin(1.23) - assert res != math.sin(1.23) # not exact, because of double->float - assert abs(res - math.sin(1.23)) < 1E-5 - -def test_strlen_exact(): - ffi = FFI() - ffi.cdef("size_t strlen(const char *s);") - lib = ffi.verify("#include <string.h>") - assert lib.strlen(b"hi there!") == 9 - -def test_strlen_approximate(): - lib = _Wconversion("int strlen(char *s);", - "#include <string.h>") - assert lib.strlen(b"hi there!") == 9 - -def test_return_approximate(): - for typename in ['short', 'int', 'long', 'long long']: - ffi = FFI() - ffi.cdef("%s foo(signed char x);" % typename) - lib = ffi.verify("signed char foo(signed char x) { return x;}") - assert lib.foo(-128) == -128 - assert lib.foo(+127) == +127 - -def test_strlen_array_of_char(): - ffi = FFI() - ffi.cdef("size_t strlen(char[]);") - lib = ffi.verify("#include <string.h>") - assert lib.strlen(b"hello") == 5 - -def test_longdouble(): - ffi = FFI() - ffi.cdef("long double sinl(long double x);") - lib = ffi.verify('#include <math.h>', libraries=lib_m) - for input in [1.23, - ffi.cast("double", 1.23), - ffi.cast("long double", 1.23)]: - x = lib.sinl(input) - assert repr(x).startswith("<cdata 'long double'") - assert (float(x) - math.sin(1.23)) < 1E-10 - -def test_longdouble_precision(): - # Test that we don't loose any precision of 'long double' when - # passing through Python and CFFI. - ffi = FFI() - ffi.cdef("long double step1(long double x);") - SAME_SIZE = ffi.sizeof("long double") == ffi.sizeof("double") - lib = ffi.verify(""" - long double step1(long double x) - { - return 4*x-x*x; - } - """) - def do(cast_to_double): - x = 0.9789 - for i in range(10000): - x = lib.step1(x) - if cast_to_double: - x = float(x) - return float(x) - - more_precise = do(False) - less_precise = do(True) - if SAME_SIZE: - assert more_precise == less_precise - else: - assert abs(more_precise - less_precise) > 0.1 - # Check the particular results on Intel - import platform - if (platform.machine().startswith('i386') or - platform.machine().startswith('i486') or - platform.machine().startswith('i586') or - platform.machine().startswith('i686') or - platform.machine().startswith('x86')): - assert abs(more_precise - 0.656769) < 0.001 - assert abs(less_precise - 3.99091) < 0.001 - else: - py.test.skip("don't know the very exact precision of 'long double'") - - -all_primitive_types = model.PrimitiveType.ALL_PRIMITIVE_TYPES -if sys.platform == 'win32': - all_primitive_types = all_primitive_types.copy() - del all_primitive_types['ssize_t'] -all_integer_types = sorted(tp for tp in all_primitive_types - if all_primitive_types[tp] == 'i') -all_float_types = sorted(tp for tp in all_primitive_types - if all_primitive_types[tp] == 'f') - -def all_signed_integer_types(ffi): - return [x for x in all_integer_types if int(ffi.cast(x, -1)) < 0] - -def all_unsigned_integer_types(ffi): - return [x for x in all_integer_types if int(ffi.cast(x, -1)) > 0] - - -def test_primitive_category(): - for typename in all_primitive_types: - tp = model.PrimitiveType(typename) - C = tp.is_char_type() - F = tp.is_float_type() - X = tp.is_complex_type() - I = tp.is_integer_type() - assert C == (typename in ('char', 'wchar_t', 'char16_t', 'char32_t')) - assert F == (typename in ('float', 'double', 'long double')) - assert X == (typename in ('float _Complex', 'double _Complex')) - assert I + F + C + X == 1 # one and only one of them is true - -def test_all_integer_and_float_types(): - typenames = [] - for typename in all_primitive_types: - if (all_primitive_types[typename] == 'c' or - all_primitive_types[typename] == 'j' or # complex - typename == '_Bool' or typename == 'long double'): - pass - else: - typenames.append(typename) - # - ffi = FFI() - ffi.cdef('\n'.join(["%s foo_%s(%s);" % (tp, tp.replace(' ', '_'), tp) - for tp in typenames])) - lib = ffi.verify('\n'.join(["%s foo_%s(%s x) { return (%s)(x+1); }" % - (tp, tp.replace(' ', '_'), tp, tp) - for tp in typenames])) - for typename in typenames: - foo = getattr(lib, 'foo_%s' % typename.replace(' ', '_')) - assert foo(42) == 43 - if sys.version < '3': - assert foo(long(44)) == 45 - assert foo(ffi.cast(typename, 46)) == 47 - py.test.raises(TypeError, foo, ffi.NULL) - # - # check for overflow cases - if all_primitive_types[typename] == 'f': - continue - for value in [-2**80, -2**40, -2**20, -2**10, -2**5, -1, - 2**5, 2**10, 2**20, 2**40, 2**80]: - overflows = int(ffi.cast(typename, value)) != value - if overflows: - py.test.raises(OverflowError, foo, value) - else: - assert foo(value) == value + 1 - -def test_var_signed_integer_types(): - ffi = FFI() - lst = all_signed_integer_types(ffi) - csource = "\n".join(["static %s somevar_%s;" % (tp, tp.replace(' ', '_')) - for tp in lst]) - ffi.cdef(csource) - lib = ffi.verify(csource) - for tp in lst: - varname = 'somevar_%s' % tp.replace(' ', '_') - sz = ffi.sizeof(tp) - max = (1 << (8*sz-1)) - 1 - min = -(1 << (8*sz-1)) - setattr(lib, varname, max) - assert getattr(lib, varname) == max - setattr(lib, varname, min) - assert getattr(lib, varname) == min - py.test.raises(OverflowError, setattr, lib, varname, max+1) - py.test.raises(OverflowError, setattr, lib, varname, min-1) - -def test_var_unsigned_integer_types(): - ffi = FFI() - lst = all_unsigned_integer_types(ffi) - csource = "\n".join(["static %s somevar_%s;" % (tp, tp.replace(' ', '_')) - for tp in lst]) - ffi.cdef(csource) - lib = ffi.verify(csource) - for tp in lst: - varname = 'somevar_%s' % tp.replace(' ', '_') - sz = ffi.sizeof(tp) - if tp != '_Bool': - max = (1 << (8*sz)) - 1 - else: - max = 1 - setattr(lib, varname, max) - assert getattr(lib, varname) == max - setattr(lib, varname, 0) - assert getattr(lib, varname) == 0 - py.test.raises(OverflowError, setattr, lib, varname, max+1) - py.test.raises(OverflowError, setattr, lib, varname, -1) - -def test_fn_signed_integer_types(): - ffi = FFI() - lst = all_signed_integer_types(ffi) - cdefsrc = "\n".join(["%s somefn_%s(%s);" % (tp, tp.replace(' ', '_'), tp) - for tp in lst]) - ffi.cdef(cdefsrc) - verifysrc = "\n".join(["%s somefn_%s(%s x) { return x; }" % - (tp, tp.replace(' ', '_'), tp) for tp in lst]) - lib = ffi.verify(verifysrc) - for tp in lst: - fnname = 'somefn_%s' % tp.replace(' ', '_') - sz = ffi.sizeof(tp) - max = (1 << (8*sz-1)) - 1 - min = -(1 << (8*sz-1)) - fn = getattr(lib, fnname) - assert fn(max) == max - assert fn(min) == min - py.test.raises(OverflowError, fn, max + 1) - py.test.raises(OverflowError, fn, min - 1) - -def test_fn_unsigned_integer_types(): - ffi = FFI() - lst = all_unsigned_integer_types(ffi) - cdefsrc = "\n".join(["%s somefn_%s(%s);" % (tp, tp.replace(' ', '_'), tp) - for tp in lst]) - ffi.cdef(cdefsrc) - verifysrc = "\n".join(["%s somefn_%s(%s x) { return x; }" % - (tp, tp.replace(' ', '_'), tp) for tp in lst]) - lib = ffi.verify(verifysrc) - for tp in lst: - fnname = 'somefn_%s' % tp.replace(' ', '_') - sz = ffi.sizeof(tp) - if tp != '_Bool': - max = (1 << (8*sz)) - 1 - else: - max = 1 - fn = getattr(lib, fnname) - assert fn(max) == max - assert fn(0) == 0 - py.test.raises(OverflowError, fn, max + 1) - py.test.raises(OverflowError, fn, -1) - -def test_char_type(): - ffi = FFI() - ffi.cdef("char foo(char);") - lib = ffi.verify("char foo(char x) { return ++x; }") - assert lib.foo(b"A") == b"B" - py.test.raises(TypeError, lib.foo, b"bar") - py.test.raises(TypeError, lib.foo, "bar") - -def test_wchar_type(): - ffi = FFI() - if ffi.sizeof('wchar_t') == 2: - uniexample1 = u+'\u1234' - uniexample2 = u+'\u1235' - else: - uniexample1 = u+'\U00012345' - uniexample2 = u+'\U00012346' - # - ffi.cdef("wchar_t foo(wchar_t);") - lib = ffi.verify("wchar_t foo(wchar_t x) { return x+1; }") - assert lib.foo(uniexample1) == uniexample2 - -def test_char16_char32_type(): - py.test.skip("XXX test or fully prevent char16_t and char32_t from " - "working in ffi.verify() mode") - -def test_no_argument(): - ffi = FFI() - ffi.cdef("int foo(void);") - lib = ffi.verify("int foo(void) { return 42; }") - assert lib.foo() == 42 - -def test_two_arguments(): - ffi = FFI() - ffi.cdef("int foo(int, int);") - lib = ffi.verify("int foo(int a, int b) { return a - b; }") - assert lib.foo(40, -2) == 42 - -def test_macro(): - ffi = FFI() - ffi.cdef("int foo(int, int);") - lib = ffi.verify("#define foo(a, b) ((a) * (b))") - assert lib.foo(-6, -7) == 42 - -def test_ptr(): - ffi = FFI() - ffi.cdef("int *foo(int *);") - lib = ffi.verify("int *foo(int *a) { return a; }") - assert lib.foo(ffi.NULL) == ffi.NULL - p = ffi.new("int *", 42) - q = ffi.new("int *", 42) - assert lib.foo(p) == p - assert lib.foo(q) != p - -def test_bogus_ptr(): - ffi = FFI() - ffi.cdef("int *foo(int *);") - lib = ffi.verify("int *foo(int *a) { return a; }") - py.test.raises(TypeError, lib.foo, ffi.new("short *", 42)) - - -def test_verify_typedefs(): - py.test.skip("ignored so far") - types = ['signed char', 'unsigned char', 'int', 'long'] - for cdefed in types: - for real in types: - ffi = FFI() - ffi.cdef("typedef %s foo_t;" % cdefed) - if cdefed == real: - ffi.verify("typedef %s foo_t;" % real) - else: - py.test.raises(VerificationError, ffi.verify, - "typedef %s foo_t;" % real) - -def test_nondecl_struct(): - ffi = FFI() - ffi.cdef("typedef struct foo_s foo_t; int bar(foo_t *);") - lib = ffi.verify("typedef struct foo_s foo_t;\n" - "int bar(foo_t *f) { (void)f; return 42; }\n") - assert lib.bar(ffi.NULL) == 42 - -def test_ffi_full_struct(): - ffi = FFI() - ffi.cdef("struct foo_s { char x; int y; long *z; };") - ffi.verify("struct foo_s { char x; int y; long *z; };") - # - if sys.platform != 'win32': # XXX fixme: only gives warnings - py.test.raises(VerificationError, ffi.verify, - "struct foo_s { char x; int y; int *z; };") - # - py.test.raises(VerificationError, ffi.verify, - "struct foo_s { int y; long *z; };") - # - e = py.test.raises(VerificationError, ffi.verify, - "struct foo_s { int y; char x; long *z; };") - assert str(e.value) == ( - "struct foo_s: wrong offset for field 'x'" - " (we have 0, but C compiler says 4)") - # - e = py.test.raises(VerificationError, ffi.verify, - "struct foo_s { char x; int y; long *z; char extra; };") - assert str(e.value) == ( - "struct foo_s: wrong total size" - " (we have %d, but C compiler says %d)" % ( - ffi.sizeof("struct foo_s"), - ffi.sizeof("struct foo_s") + ffi.sizeof("long*"))) - # - # a corner case that we cannot really detect, but where it has no - # bad consequences: the size is the same, but there is an extra field - # that replaces what is just padding in our declaration above - ffi.verify("struct foo_s { char x, extra; int y; long *z; };") - # - e = py.test.raises(VerificationError, ffi.verify, - "struct foo_s { char x; short pad; short y; long *z; };") - assert str(e.value) == ( - "struct foo_s: wrong size for field 'y'" - " (we have 4, but C compiler says 2)") - -def test_ffi_nonfull_struct(): - ffi = FFI() - ffi.cdef(""" - struct foo_s { - int x; - ...; - }; - """) - py.test.raises(VerificationMissing, ffi.sizeof, 'struct foo_s') - py.test.raises(VerificationMissing, ffi.offsetof, 'struct foo_s', 'x') - py.test.raises(VerificationMissing, ffi.new, 'struct foo_s *') - ffi.verify(""" - struct foo_s { - int a, b, x, c, d, e; - }; - """) - assert ffi.sizeof('struct foo_s') == 6 * ffi.sizeof('int') - assert ffi.offsetof('struct foo_s', 'x') == 2 * ffi.sizeof('int') - -def test_ffi_nonfull_alignment(): - ffi = FFI() - ffi.cdef("struct foo_s { char x; ...; };") - ffi.verify("struct foo_s { int a, b; char x; };") - assert ffi.sizeof('struct foo_s') == 3 * ffi.sizeof('int') - assert ffi.alignof('struct foo_s') == ffi.sizeof('int') - -def _check_field_match(typename, real, expect_mismatch): - ffi = FFI() - testing_by_size = (expect_mismatch == 'by_size') - if testing_by_size: - expect_mismatch = ffi.sizeof(typename) != ffi.sizeof(real) - ffi.cdef("struct foo_s { %s x; ...; };" % typename) - try: - ffi.verify("struct foo_s { %s x; };" % real) - except VerificationError: - if not expect_mismatch: - if testing_by_size and typename != real: - print("ignoring mismatch between %s* and %s* even though " - "they have the same size" % (typename, real)) - return - raise AssertionError("unexpected mismatch: %s should be accepted " - "as equal to %s" % (typename, real)) - else: - if expect_mismatch: - raise AssertionError("mismatch not detected: " - "%s != %s" % (typename, real)) - -def test_struct_bad_sized_integer(): - for typename in ['int8_t', 'int16_t', 'int32_t', 'int64_t']: - for real in ['int8_t', 'int16_t', 'int32_t', 'int64_t']: - _check_field_match(typename, real, "by_size") - -def test_struct_bad_sized_float(): - for typename in all_float_types: - for real in all_float_types: - _check_field_match(typename, real, "by_size") - -def test_struct_signedness_ignored(): - _check_field_match("int", "unsigned int", expect_mismatch=False) - _check_field_match("unsigned short", "signed short", expect_mismatch=False) - -def test_struct_float_vs_int(): - if sys.platform == 'win32': - py.test.skip("XXX fixme: only gives warnings") - ffi = FFI() - for typename in all_signed_integer_types(ffi): - for real in all_float_types: - _check_field_match(typename, real, expect_mismatch=True) - for typename in all_float_types: - for real in all_signed_integer_types(ffi): - _check_field_match(typename, real, expect_mismatch=True) - -def test_struct_array_field(): - ffi = FFI() - ffi.cdef("struct foo_s { int a[17]; ...; };") - ffi.verify("struct foo_s { int x; int a[17]; int y; };") - assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int') - s = ffi.new("struct foo_s *") - assert ffi.sizeof(s.a) == 17 * ffi.sizeof('int') - -def test_struct_array_no_length(): - ffi = FFI() - ffi.cdef("struct foo_s { int a[]; int y; ...; };\n" - "int bar(struct foo_s *);\n") - lib = ffi.verify("struct foo_s { int x; int a[17]; int y; };\n" - "int bar(struct foo_s *f) { return f->a[14]; }\n") - assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int') - s = ffi.new("struct foo_s *") - assert ffi.typeof(s.a) is ffi.typeof('int[]') # implicit max length - assert len(s.a) == 18 # max length, computed from the size and start offset - s.a[14] = 4242 - assert lib.bar(s) == 4242 - # with no declared length, out-of-bound accesses are not detected - s.a[17] = -521 - assert s.y == s.a[17] == -521 - # - s = ffi.new("struct foo_s *", {'a': list(range(17))}) - assert s.a[16] == 16 - # overflows at construction time not detected either - s = ffi.new("struct foo_s *", {'a': list(range(18))}) - assert s.y == s.a[17] == 17 - -def test_struct_array_guess_length(): - ffi = FFI() - ffi.cdef("struct foo_s { int a[...]; };") - ffi.verify("struct foo_s { int x; int a[17]; int y; };") - assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int') - s = ffi.new("struct foo_s *") - assert ffi.sizeof(s.a) == 17 * ffi.sizeof('int') - with pytest.raises(IndexError): - s.a[17] - -def test_struct_array_c99_1(): - if sys.platform == 'win32': - py.test.skip("requires C99") - ffi = FFI() - ffi.cdef("struct foo_s { int x; int a[]; };") - ffi.verify("struct foo_s { int x; int a[]; };") - assert ffi.sizeof('struct foo_s') == 1 * ffi.sizeof('int') - s = ffi.new("struct foo_s *", [424242, 4]) - assert ffi.sizeof(ffi.typeof(s[0])) == 1 * ffi.sizeof('int') - assert ffi.sizeof(s[0]) == 5 * ffi.sizeof('int') - # ^^^ explanation: if you write in C: "char x[5];", then - # "sizeof(x)" will evaluate to 5. The behavior above is - # a generalization of that to "struct foo_s[len(a)=5] x;" - # if you could do that in C. - assert s.a[3] == 0 - s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]]) - assert ffi.sizeof(s[0]) == 5 * ffi.sizeof('int') - assert s.a[3] == -10 - s = ffi.new("struct foo_s *") - assert ffi.sizeof(s[0]) == 1 * ffi.sizeof('int') - s = ffi.new("struct foo_s *", [424242]) - assert ffi.sizeof(s[0]) == 1 * ffi.sizeof('int') - -def test_struct_array_c99_2(): - if sys.platform == 'win32': - py.test.skip("requires C99") - ffi = FFI() - ffi.cdef("struct foo_s { int x; int a[]; ...; };") - ffi.verify("struct foo_s { int x, y; int a[]; };") - assert ffi.sizeof('struct foo_s') == 2 * ffi.sizeof('int') - s = ffi.new("struct foo_s *", [424242, 4]) - assert ffi.sizeof(s[0]) == 6 * ffi.sizeof('int') - assert s.a[3] == 0 - s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]]) - assert ffi.sizeof(s[0]) == 6 * ffi.sizeof('int') - assert s.a[3] == -10 - s = ffi.new("struct foo_s *") - assert ffi.sizeof(s[0]) == 2 * ffi.sizeof('int') - s = ffi.new("struct foo_s *", [424242]) - assert ffi.sizeof(s[0]) == 2 * ffi.sizeof('int') - -def test_struct_ptr_to_array_field(): - ffi = FFI() - ffi.cdef("struct foo_s { int (*a)[17]; ...; }; struct bar_s { ...; };") - ffi.verify("struct foo_s { int x; int (*a)[17]; int y; };\n" - "struct bar_s { int x; int *a; int y; };") - assert ffi.sizeof('struct foo_s') == ffi.sizeof("struct bar_s") - s = ffi.new("struct foo_s *") - assert ffi.sizeof(s.a) == ffi.sizeof('int(*)[17]') == ffi.sizeof("int *") - -def test_struct_with_bitfield_exact(): - ffi = FFI() - ffi.cdef("struct foo_s { int a:2, b:3; };") - ffi.verify("struct foo_s { int a:2, b:3; };") - s = ffi.new("struct foo_s *") - s.b = 3 - with pytest.raises(OverflowError): - s.b = 4 - assert s.b == 3 - -def test_struct_with_bitfield_enum(): - ffi = FFI() - code = """ - typedef enum { AA, BB, CC } foo_e; - typedef struct { foo_e f:2; } foo_s; - """ - ffi.cdef(code) - ffi.verify(code) - s = ffi.new("foo_s *") - s.f = 2 - assert s.f == 2 - -def test_unsupported_struct_with_bitfield_ellipsis(): - ffi = FFI() - py.test.raises(NotImplementedError, ffi.cdef, - "struct foo_s { int a:2, b:3; ...; };") - -def test_global_constants(): - ffi = FFI() - # use 'static const int', as generally documented, although in this - # case the 'static' is completely ignored. - ffi.cdef("static const int AA, BB, CC, DD;") - lib = ffi.verify("#define AA 42\n" - "#define BB (-43) // blah\n" - "#define CC (22*2) /* foobar */\n" - "#define DD ((unsigned int)142) /* foo\nbar */\n") - assert lib.AA == 42 - assert lib.BB == -43 - assert lib.CC == 44 - assert lib.DD == 142 - -def test_global_const_int_size(): - # integer constants: ignore the declared type, always just use the value - for value in [-2**63, -2**31, -2**15, - 2**15-1, 2**15, 2**31-1, 2**31, 2**32-1, 2**32, - 2**63-1, 2**63, 2**64-1]: - ffi = FFI() - if value == int(ffi.cast("long long", value)): - if value < 0: - vstr = '(-%dLL-1)' % (~value,) - else: - vstr = '%dLL' % value - elif value == int(ffi.cast("unsigned long long", value)): - vstr = '%dULL' % value - else: - raise AssertionError(value) - ffi.cdef("static const unsigned short AA;") - lib = ffi.verify("#define AA %s\n" % vstr) - assert lib.AA == value - assert type(lib.AA) is type(int(lib.AA)) - -def test_global_constants_non_int(): - ffi = FFI() - ffi.cdef("static char *const PP;") - lib = ffi.verify('static char *const PP = "testing!";\n') - assert ffi.typeof(lib.PP) == ffi.typeof("char *") - assert ffi.string(lib.PP) == b"testing!" - -def test_nonfull_enum(): - ffi = FFI() - ffi.cdef("enum ee { EE1, EE2, EE3, ... \n \t };") - py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE2') - ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };") - assert ffi.string(ffi.cast('enum ee', 11)) == "EE2" - assert ffi.string(ffi.cast('enum ee', -10)) == "EE3" - # - # try again - ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };") - assert ffi.string(ffi.cast('enum ee', 11)) == "EE2" - # - assert ffi.typeof("enum ee").relements == {'EE1': 10, 'EE2': 11, 'EE3': -10} - assert ffi.typeof("enum ee").elements == {10: 'EE1', 11: 'EE2', -10: 'EE3'} - -def test_full_enum(): - ffi = FFI() - ffi.cdef("enum ee { EE1, EE2, EE3 };") - ffi.verify("enum ee { EE1, EE2, EE3 };") - py.test.raises(VerificationError, ffi.verify, "enum ee { EE1, EE2 };") - e = py.test.raises(VerificationError, ffi.verify, - "enum ee { EE1, EE3, EE2 };") - assert str(e.value) == 'enum ee: EE2 has the real value 2, not 1' - # extra items cannot be seen and have no bad consequence anyway - lib = ffi.verify("enum ee { EE1, EE2, EE3, EE4 };") - assert lib.EE3 == 2 - -def test_enum_usage(): - ffi = FFI() - ffi.cdef("enum ee { EE1,EE2 }; typedef struct { enum ee x; } *sp;") - lib = ffi.verify("enum ee { EE1,EE2 }; typedef struct { enum ee x; } *sp;") - assert lib.EE2 == 1 - s = ffi.new("sp", [lib.EE2]) - assert s.x == 1 - s.x = 17 - assert s.x == 17 - -def test_anonymous_enum(): - ffi = FFI() - ffi.cdef("enum { EE1 }; enum { EE2, EE3 };") - lib = ffi.verify("enum { EE1 }; enum { EE2, EE3 };") - assert lib.EE1 == 0 - assert lib.EE2 == 0 - assert lib.EE3 == 1 - -def test_nonfull_anonymous_enum(): - ffi = FFI() - ffi.cdef("enum { EE1, ... }; enum { EE3, ... };") - lib = ffi.verify("enum { EE2, EE1 }; enum { EE3 };") - assert lib.EE1 == 1 - assert lib.EE3 == 0 - -def test_nonfull_enum_syntax2(): - ffi = FFI() - ffi.cdef("enum ee { EE1, EE2=\t..., EE3 };") - py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE1') - ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };") - assert ffi.string(ffi.cast('enum ee', 11)) == 'EE2' - assert ffi.string(ffi.cast('enum ee', -10)) == 'EE3' - # - ffi = FFI() - ffi.cdef("enum ee { EE1, EE2=\t... };") - py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE1') - ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };") - assert ffi.string(ffi.cast('enum ee', 11)) == 'EE2' - # - ffi = FFI() - ffi.cdef("enum ee2 { EE4=..., EE5=..., ... };") - ffi.verify("enum ee2 { EE4=-1234-5, EE5 }; ") - assert ffi.string(ffi.cast('enum ee2', -1239)) == 'EE4' - assert ffi.string(ffi.cast('enum ee2', -1238)) == 'EE5' - -def test_nonfull_enum_bug3(): - ffi = FFI() - ffi.cdef("enum ee2 { EE4=..., EE5=... };") - ffi.cdef("enum ee6 { EE7=10, EE8=..., EE9=... };") - -def test_get_set_errno(): - ffi = FFI() - ffi.cdef("int foo(int);") - lib = ffi.verify(""" - static int foo(int x) - { - errno += 1; - return x * 7; - } - """) - ffi.errno = 15 - assert lib.foo(6) == 42 - assert ffi.errno == 16 - -def test_define_int(): - ffi = FFI() - ffi.cdef("#define FOO ...\n" - "\t#\tdefine\tBAR\t...\t\n" - "#define BAZ ...\n") - lib = ffi.verify("#define FOO 42\n" - "#define BAR (-44)\n" - "#define BAZ 0xffffffffffffffffULL\n") - assert lib.FOO == 42 - assert lib.BAR == -44 - assert lib.BAZ == 0xffffffffffffffff - -def test_access_variable(): - ffi = FFI() - ffi.cdef("static int foo(void);\n" - "static int somenumber;") - lib = ffi.verify(""" - static int somenumber = 2; - static int foo(void) { - return somenumber * 7; - } - """) - assert lib.somenumber == 2 - assert lib.foo() == 14 - lib.somenumber = -6 - assert lib.foo() == -42 - assert lib.somenumber == -6 - lib.somenumber = 2 # reset for the next run, if any - -def test_access_address_of_variable(): - # access the address of 'somenumber': need a trick - ffi = FFI() - ffi.cdef("static int somenumber; static int *const somenumberptr;") - lib = ffi.verify(""" - static int somenumber = 2; - #define somenumberptr (&somenumber) - """) - assert lib.somenumber == 2 - lib.somenumberptr[0] = 42 - assert lib.somenumber == 42 - lib.somenumber = 2 # reset for the next run, if any - -def test_access_array_variable(length=5): - ffi = FFI() - ffi.cdef("int foo(int);\n" - "static int somenumber[%s];" % (length,)) - lib = ffi.verify(""" - static int somenumber[] = {2, 2, 3, 4, 5}; - static int foo(int i) { - return somenumber[i] * 7; - } - """) - if length == '': - # a global variable of an unknown array length is implicitly - # transformed into a global pointer variable, because we can only - # work with array instances whose length we know. using a pointer - # instead of an array gives the correct effects. - assert repr(lib.somenumber).startswith("<cdata 'int *' 0x") - py.test.raises(TypeError, len, lib.somenumber) - else: - assert repr(lib.somenumber).startswith("<cdata 'int[%s]' 0x" % length) - assert len(lib.somenumber) == 5 - assert lib.somenumber[3] == 4 - assert lib.foo(3) == 28 - lib.somenumber[3] = -6 - assert lib.foo(3) == -42 - assert lib.somenumber[3] == -6 - assert lib.somenumber[4] == 5 - lib.somenumber[3] = 4 # reset for the next run, if any - -def test_access_array_variable_length_hidden(): - test_access_array_variable(length='') - -def test_access_struct_variable(): - ffi = FFI() - ffi.cdef("struct foo { int x; ...; };\n" - "int foo(int);\n" - "static struct foo stuff;") - lib = ffi.verify(""" - struct foo { int x, y, z; }; - static struct foo stuff = {2, 5, 8}; - static int foo(int i) { - switch (i) { - case 0: return stuff.x * 7; - case 1: return stuff.y * 7; - case 2: return stuff.z * 7; - } - return -1; - } - """) - assert lib.stuff.x == 2 - assert lib.foo(0) == 14 - assert lib.foo(1) == 35 - assert lib.foo(2) == 56 - lib.stuff.x = -6 - assert lib.foo(0) == -42 - assert lib.foo(1) == 35 - lib.stuff.x = 2 # reset for the next run, if any - -def test_access_callback(): - ffi = FFI() - ffi.cdef("static int (*cb)(int);\n" - "static int foo(int);\n" - "static void reset_cb(void);") - lib = ffi.verify(""" - static int g(int x) { return x * 7; } - static int (*cb)(int); - static int foo(int i) { return cb(i) - 1; } - static void reset_cb(void) { cb = g; } - """) - lib.reset_cb() - assert lib.foo(6) == 41 - my_callback = ffi.callback("int(*)(int)", lambda n: n * 222) - lib.cb = my_callback - assert lib.foo(4) == 887 - -def test_access_callback_function_typedef(): - ffi = FFI() - ffi.cdef("typedef int mycallback_t(int);\n" - "static mycallback_t *cb;\n" - "static int foo(int);\n" - "static void reset_cb(void);") - lib = ffi.verify(""" - static int g(int x) { return x * 7; } - static int (*cb)(int); - static int foo(int i) { return cb(i) - 1; } - static void reset_cb(void) { cb = g; } - """) - lib.reset_cb() - assert lib.foo(6) == 41 - my_callback = ffi.callback("int(*)(int)", lambda n: n * 222) - lib.cb = my_callback - assert lib.foo(4) == 887 - -def test_ctypes_backend_forces_generic_engine(): - from cffi.backend_ctypes import CTypesBackend - ffi = FFI(backend=CTypesBackend()) - ffi.cdef("int func(int a);") - lib = ffi.verify("int func(int a) { return a * 42; }") - assert not hasattr(lib, '_cffi_python_module') - assert hasattr(lib, '_cffi_generic_module') - assert lib.func(100) == 4200 - -def test_call_with_struct_ptr(): - ffi = FFI() - ffi.cdef("typedef struct { int x; ...; } foo_t; int foo(foo_t *);") - lib = ffi.verify(""" - typedef struct { int y, x; } foo_t; - static int foo(foo_t *f) { return f->x * 7; } - """) - f = ffi.new("foo_t *") - f.x = 6 - assert lib.foo(f) == 42 - -def test_unknown_type(): - ffi = FFI() - ffi.cdef(""" - typedef ... token_t; - int foo(token_t *); - #define TOKEN_SIZE ... - """) - lib = ffi.verify(""" - typedef float token_t; - static int foo(token_t *tk) { - if (!tk) - return -42; - *tk += 1.601f; - return (int)*tk; - } - #define TOKEN_SIZE sizeof(token_t) - """) - # we cannot let ffi.new("token_t *") work, because we don't know ahead of - # time if it's ok to ask 'sizeof(token_t)' in the C code or not. - # See test_unknown_type_2. Workaround. - tkmem = ffi.new("char[]", lib.TOKEN_SIZE) # zero-initialized - tk = ffi.cast("token_t *", tkmem) - results = [lib.foo(tk) for i in range(6)] - assert results == [1, 3, 4, 6, 8, 9] - assert lib.foo(ffi.NULL) == -42 - -def test_unknown_type_2(): - ffi = FFI() - ffi.cdef("typedef ... token_t;") - lib = ffi.verify("typedef struct token_s token_t;") - # assert did not crash, even though 'sizeof(token_t)' is not valid in C. - -def test_unknown_type_3(): - ffi = FFI() - ffi.cdef(""" - typedef ... *token_p; - token_p foo(token_p); - """) - lib = ffi.verify(""" - typedef struct _token_s *token_p; - token_p foo(token_p arg) { - if (arg) - return (token_p)0x12347; - else - return (token_p)0x12345; - } - """) - p = lib.foo(ffi.NULL) - assert int(ffi.cast("intptr_t", p)) == 0x12345 - q = lib.foo(p) - assert int(ffi.cast("intptr_t", q)) == 0x12347 - -def test_varargs(): - ffi = FFI() - ffi.cdef("int foo(int x, ...);") - lib = ffi.verify(""" - int foo(int x, ...) { - va_list vargs; - va_start(vargs, x); - x -= va_arg(vargs, int); - x -= va_arg(vargs, int); - va_end(vargs); - return x; - } - """) - assert lib.foo(50, ffi.cast("int", 5), ffi.cast("int", 3)) == 42 - -def test_varargs_exact(): - if sys.platform == 'win32': - py.test.skip("XXX fixme: only gives warnings") - ffi = FFI() - ffi.cdef("int foo(int x, ...);") - py.test.raises(VerificationError, ffi.verify, """ - int foo(long long x, ...) { - return x; - } - """) - -def test_varargs_struct(): - ffi = FFI() - ffi.cdef("struct foo_s { char a; int b; }; int foo(int x, ...);") - lib = ffi.verify(""" - struct foo_s { - char a; int b; - }; - int foo(int x, ...) { - va_list vargs; - struct foo_s s; - va_start(vargs, x); - s = va_arg(vargs, struct foo_s); - va_end(vargs); - return s.a - s.b; - } - """) - s = ffi.new("struct foo_s *", [b'B', 1]) - assert lib.foo(50, s[0]) == ord('A') - -def test_autofilled_struct_as_argument(): - ffi = FFI() - ffi.cdef("struct foo_s { long a; double b; ...; };\n" - "int foo(struct foo_s);") - lib = ffi.verify(""" - struct foo_s { - double b; - long a; - }; - int foo(struct foo_s s) { - return (int)s.a - (int)s.b; - } - """) - s = ffi.new("struct foo_s *", [100, 1]) - assert lib.foo(s[0]) == 99 - assert lib.foo([100, 1]) == 99 - -def test_autofilled_struct_as_argument_dynamic(): - ffi = FFI() - ffi.cdef("struct foo_s { long a; ...; };\n" - "static int (*foo)(struct foo_s);") - lib = ffi.verify(""" - struct foo_s { - double b; - long a; - }; - int foo1(struct foo_s s) { - return (int)s.a - (int)s.b; - } - static int (*foo)(struct foo_s s) = &foo1; - """) - e = py.test.raises(NotImplementedError, lib.foo, "?") - msg = ("ctype 'struct foo_s' not supported as argument. It is a struct " - 'declared with "...;", but the C calling convention may depend on ' - "the missing fields; or, it contains anonymous struct/unions. " - "Such structs are only supported as argument " - "if the function is 'API mode' and non-variadic (i.e. declared " - "inside ffibuilder.cdef()+ffibuilder.set_source() and not taking " - "a final '...' argument)") - assert str(e.value) == msg - -def test_func_returns_struct(): - ffi = FFI() - ffi.cdef(""" - struct foo_s { int aa, bb; }; - struct foo_s foo(int a, int b); - """) - lib = ffi.verify(""" - struct foo_s { int aa, bb; }; - struct foo_s foo(int a, int b) { - struct foo_s r; - r.aa = a*a; - r.bb = b*b; - return r; - } - """) - s = lib.foo(6, 7) - assert repr(s) == "<cdata 'struct foo_s' owning 8 bytes>" - assert s.aa == 36 - assert s.bb == 49 - -def test_func_as_funcptr(): - ffi = FFI() - ffi.cdef("int *(*const fooptr)(void);") - lib = ffi.verify(""" - int *foo(void) { - return (int*)"foobar"; - } - int *(*fooptr)(void) = foo; - """) - foochar = ffi.cast("char *(*)(void)", lib.fooptr) - s = foochar() - assert ffi.string(s) == b"foobar" - -def test_funcptr_as_argument(): - ffi = FFI() - ffi.cdef(""" - void qsort(void *base, size_t nel, size_t width, - int (*compar)(const void *, const void *)); - """) - ffi.verify("#include <stdlib.h>") - -def test_func_as_argument(): - ffi = FFI() - ffi.cdef(""" - void qsort(void *base, size_t nel, size_t width, - int compar(const void *, const void *)); - """) - ffi.verify("#include <stdlib.h>") - -def test_array_as_argument(): - ffi = FFI() - ffi.cdef(""" - size_t strlen(char string[]); - """) - ffi.verify("#include <string.h>") - -def test_enum_as_argument(): - ffi = FFI() - ffi.cdef(""" - enum foo_e { AA, BB, ... }; - int foo_func(enum foo_e); - """) - lib = ffi.verify(""" - enum foo_e { AA, CC, BB }; - int foo_func(enum foo_e e) { return (int)e; } - """) - assert lib.foo_func(lib.BB) == 2 - py.test.raises(TypeError, lib.foo_func, "BB") - -def test_enum_as_function_result(): - ffi = FFI() - ffi.cdef(""" - enum foo_e { AA, BB, ... }; - enum foo_e foo_func(int x); - """) - lib = ffi.verify(""" - enum foo_e { AA, CC, BB }; - enum foo_e foo_func(int x) { return (enum foo_e)x; } - """) - assert lib.foo_func(lib.BB) == lib.BB == 2 - -def test_enum_values(): - ffi = FFI() - ffi.cdef("enum enum1_e { AA, BB };") - lib = ffi.verify("enum enum1_e { AA, BB };") - assert lib.AA == 0 - assert lib.BB == 1 - assert ffi.string(ffi.cast("enum enum1_e", 1)) == 'BB' - -def test_typedef_complete_enum(): - ffi = FFI() - ffi.cdef("typedef enum { AA, BB } enum1_t;") - lib = ffi.verify("typedef enum { AA, BB } enum1_t;") - assert ffi.string(ffi.cast("enum1_t", 1)) == 'BB' - assert lib.AA == 0 - assert lib.BB == 1 - -def test_typedef_broken_complete_enum(): - ffi = FFI() - ffi.cdef("typedef enum { AA, BB } enum1_t;") - py.test.raises(VerificationError, ffi.verify, - "typedef enum { AA, CC, BB } enum1_t;") - -def test_typedef_incomplete_enum(): - ffi = FFI() - ffi.cdef("typedef enum { AA, BB, ... } enum1_t;") - lib = ffi.verify("typedef enum { AA, CC, BB } enum1_t;") - assert ffi.string(ffi.cast("enum1_t", 1)) == '1' - assert ffi.string(ffi.cast("enum1_t", 2)) == 'BB' - assert lib.AA == 0 - assert lib.BB == 2 - -def test_typedef_enum_as_argument(): - ffi = FFI() - ffi.cdef(""" - typedef enum { AA, BB, ... } foo_t; - int foo_func(foo_t); - """) - lib = ffi.verify(""" - typedef enum { AA, CC, BB } foo_t; - int foo_func(foo_t e) { return (int)e; } - """) - assert lib.foo_func(lib.BB) == lib.BB == 2 - py.test.raises(TypeError, lib.foo_func, "BB") - -def test_typedef_enum_as_function_result(): - ffi = FFI() - ffi.cdef(""" - typedef enum { AA, BB, ... } foo_t; - foo_t foo_func(int x); - """) - lib = ffi.verify(""" - typedef enum { AA, CC, BB } foo_t; - foo_t foo_func(int x) { return (foo_t)x; } - """) - assert lib.foo_func(lib.BB) == lib.BB == 2 - -def test_function_typedef(): - ffi = FFI() - ffi.cdef(""" - typedef double func_t(double); - func_t sin; - """) - lib = ffi.verify('#include <math.h>', libraries=lib_m) - assert lib.sin(1.23) == math.sin(1.23) - -def test_opaque_integer_as_function_result(): - #import platform - #if platform.machine().startswith('sparc'): - # py.test.skip('Breaks horribly on sparc (SIGILL + corrupted stack)') - #elif platform.machine() == 'mips64' and sys.maxsize > 2**32: - # py.test.skip('Segfaults on mips64el') - # XXX bad abuse of "struct { ...; }". It only works a bit by chance - # anyway. XXX think about something better :-( - ffi = FFI() - ffi.cdef(""" - typedef struct { ...; } myhandle_t; - myhandle_t foo(void); - """) - lib = ffi.verify(""" - typedef short myhandle_t; - myhandle_t foo(void) { return 42; } - """) - h = lib.foo() - assert ffi.sizeof(h) == ffi.sizeof("short") - -def test_return_partial_struct(): - ffi = FFI() - ffi.cdef(""" - typedef struct { int x; ...; } foo_t; - foo_t foo(void); - """) - lib = ffi.verify(""" - typedef struct { int y, x; } foo_t; - foo_t foo(void) { foo_t r = { 45, 81 }; return r; } - """) - h = lib.foo() - assert ffi.sizeof(h) == 2 * ffi.sizeof("int") - assert h.x == 81 - -def test_take_and_return_partial_structs(): - ffi = FFI() - ffi.cdef(""" - typedef struct { int x; ...; } foo_t; - foo_t foo(foo_t, foo_t); - """) - lib = ffi.verify(""" - typedef struct { int y, x; } foo_t; - foo_t foo(foo_t a, foo_t b) { - foo_t r = { 100, a.x * 5 + b.x * 7 }; - return r; - } - """) - args = ffi.new("foo_t[3]") - args[0].x = 1000 - args[2].x = -498 - h = lib.foo(args[0], args[2]) - assert ffi.sizeof(h) == 2 * ffi.sizeof("int") - assert h.x == 1000 * 5 - 498 * 7 - -def test_cannot_name_struct_type(): - ffi = FFI() - ffi.cdef("typedef struct { int x; } **sp; void foo(sp);") - e = py.test.raises(VerificationError, ffi.verify, - "typedef struct { int x; } **sp; void foo(sp x) { }") - assert 'in argument of foo: unknown type name' in str(e.value) - -def test_dont_check_unnamable_fields(): - ffi = FFI() - ffi.cdef("struct foo_s { struct { int x; } someone; };") - ffi.verify("struct foo_s { struct { int x; } someone; };") - # assert did not crash - -def test_nested_anonymous_struct_exact(): - if sys.platform == 'win32': - py.test.skip("nested anonymous struct/union") - ffi = FFI() - ffi.cdef(""" - struct foo_s { struct { int a; char b; }; union { char c, d; }; }; - """) - ffi.verify(""" - struct foo_s { struct { int a; char b; }; union { char c, d; }; }; - """) - p = ffi.new("struct foo_s *") - assert ffi.sizeof(p[0]) == 3 * ffi.sizeof("int") # with alignment - p.a = 1234567 - p.b = b'X' - p.c = b'Y' - assert p.a == 1234567 - assert p.b == b'X' - assert p.c == b'Y' - assert p.d == b'Y' - -def test_nested_anonymous_struct_exact_error(): - if sys.platform == 'win32': - py.test.skip("nested anonymous struct/union") - ffi = FFI() - ffi.cdef(""" - struct foo_s { struct { int a; char b; }; union { char c, d; }; }; - """) - py.test.raises(VerificationError, ffi.verify, """ - struct foo_s { struct { int a; short b; }; union { char c, d; }; }; - """) - py.test.raises(VerificationError, ffi.verify, """ - struct foo_s { struct { int a; char e, b; }; union { char c, d; }; }; - """) - -def test_nested_anonymous_struct_inexact_1(): - ffi = FFI() - ffi.cdef(""" - struct foo_s { struct { char b; ...; }; union { char c, d; }; }; - """) - ffi.verify(""" - struct foo_s { int a, padding; char c, d, b; }; - """) - assert ffi.sizeof("struct foo_s") == 3 * ffi.sizeof("int") - -def test_nested_anonymous_struct_inexact_2(): - ffi = FFI() - ffi.cdef(""" - struct foo_s { union { char c, d; }; struct { int a; char b; }; ...; }; - """) - ffi.verify(""" - struct foo_s { int a, padding; char c, d, b; }; - """) - assert ffi.sizeof("struct foo_s") == 3 * ffi.sizeof("int") - -def test_ffi_union(): - ffi = FFI() - ffi.cdef("union foo_u { char x; long *z; };") - ffi.verify("union foo_u { char x; int y; long *z; };") - -def test_ffi_union_partial(): - ffi = FFI() - ffi.cdef("union foo_u { char x; ...; };") - ffi.verify("union foo_u { char x; int y; };") - assert ffi.sizeof("union foo_u") == 4 - -def test_ffi_union_with_partial_struct(): - ffi = FFI() - ffi.cdef("struct foo_s { int x; ...; }; union foo_u { struct foo_s s; };") - ffi.verify("struct foo_s { int a; int x; }; " - "union foo_u { char b[32]; struct foo_s s; };") - assert ffi.sizeof("struct foo_s") == 8 - assert ffi.sizeof("union foo_u") == 32 - -def test_ffi_union_partial_2(): - ffi = FFI() - ffi.cdef("typedef union { char x; ...; } u1;") - ffi.verify("typedef union { char x; int y; } u1;") - assert ffi.sizeof("u1") == 4 - -def test_ffi_union_with_partial_struct_2(): - ffi = FFI() - ffi.cdef("typedef struct { int x; ...; } s1;" - "typedef union { s1 s; } u1;") - ffi.verify("typedef struct { int a; int x; } s1; " - "typedef union { char b[32]; s1 s; } u1;") - assert ffi.sizeof("s1") == 8 - assert ffi.sizeof("u1") == 32 - assert ffi.offsetof("u1", "s") == 0 - -def test_ffi_struct_packed(): - if sys.platform == 'win32': - py.test.skip("needs a GCC extension") - ffi = FFI() - ffi.cdef("struct foo_s { int b; ...; };") - ffi.verify(""" - struct foo_s { - char a; - int b; - } __attribute__((packed)); - """) - -def test_tmpdir(): - import tempfile, os - from testing.udir import udir - tmpdir = tempfile.mkdtemp(dir=str(udir)) - ffi = FFI() - ffi.cdef("int foo(int);") - lib = ffi.verify("int foo(int a) { return a + 42; }", tmpdir=tmpdir) - assert os.listdir(tmpdir) - assert lib.foo(100) == 142 - -def test_relative_to(): - import tempfile, os - from testing.udir import udir - tmpdir = tempfile.mkdtemp(dir=str(udir)) - ffi = FFI() - ffi.cdef("int foo(int);") - f = open(os.path.join(tmpdir, 'foo.h'), 'w') - f.write("int foo(int a) { return a + 42; }\n") - f.close() - lib = ffi.verify('#include "foo.h"', - include_dirs=['.'], - relative_to=os.path.join(tmpdir, 'x')) - assert lib.foo(100) == 142 - -def test_bug1(): - ffi = FFI() - ffi.cdef(""" - typedef struct tdlhandle_s { ...; } *tdl_handle_t; - typedef struct my_error_code_ { - tdl_handle_t *rh; - } my_error_code_t; - """) - ffi.verify(""" - typedef struct tdlhandle_s { int foo; } *tdl_handle_t; - typedef struct my_error_code_ { - tdl_handle_t *rh; - } my_error_code_t; - """) - -def test_bool(): - if sys.platform == 'win32': - py.test.skip("_Bool not in MSVC") - ffi = FFI() - ffi.cdef("struct foo_s { _Bool x; };" - "_Bool foo(_Bool); static _Bool (*foop)(_Bool);") - lib = ffi.verify(""" - struct foo_s { _Bool x; }; - int foo(int arg) { - return !arg; - } - _Bool _foofunc(_Bool x) { - return !x; - } - static _Bool (*foop)(_Bool) = _foofunc; - """) - p = ffi.new("struct foo_s *") - p.x = 1 - assert p.x is True - with pytest.raises(OverflowError): - p.x = -1 - with pytest.raises(TypeError): - p.x = 0.0 - assert lib.foop(1) is False - assert lib.foop(True) is False - assert lib.foop(0) is True - py.test.raises(OverflowError, lib.foop, 42) - py.test.raises(TypeError, lib.foop, 0.0) - assert lib.foo(1) is False - assert lib.foo(True) is False - assert lib.foo(0) is True - py.test.raises(OverflowError, lib.foo, 42) - py.test.raises(TypeError, lib.foo, 0.0) - assert int(ffi.cast("_Bool", long(1))) == 1 - assert int(ffi.cast("_Bool", long(0))) == 0 - assert int(ffi.cast("_Bool", long(-1))) == 1 - assert int(ffi.cast("_Bool", 10**200)) == 1 - assert int(ffi.cast("_Bool", 10**40000)) == 1 - # - class Foo(object): - def __int__(self): - self.seen = 1 - return result - f = Foo() - f.seen = 0 - result = 42 - assert int(ffi.cast("_Bool", f)) == 1 - assert f.seen - f.seen = 0 - result = 0 - assert int(ffi.cast("_Bool", f)) == 0 - assert f.seen - # - py.test.raises(TypeError, ffi.cast, "_Bool", []) - -def test_bool_on_long_double(): - if sys.platform == 'win32': - py.test.skip("_Bool not in MSVC") - f = 1E-250 - if f == 0.0 or f*f != 0.0: - py.test.skip("unexpected precision") - ffi = FFI() - ffi.cdef("long double square(long double f); _Bool opposite(_Bool);") - lib = ffi.verify("long double square(long double f) { return f*f; }\n" - "_Bool opposite(_Bool x) { return !x; }") - f0 = lib.square(0.0) - f2 = lib.square(f) - f3 = lib.square(f * 2.0) - if repr(f2) == repr(f3): - py.test.skip("long double doesn't have enough precision") - assert float(f0) == float(f2) == float(f3) == 0.0 # too tiny for 'double' - assert int(ffi.cast("_Bool", f2)) == 1 - assert int(ffi.cast("_Bool", f3)) == 1 - assert int(ffi.cast("_Bool", f0)) == 0 - py.test.raises(TypeError, lib.opposite, f2) - -def test_cannot_pass_float(): - for basetype in ['char', 'short', 'int', 'long', 'long long']: - for sign in ['signed', 'unsigned']: - type = '%s %s' % (sign, basetype) - ffi = FFI() - ffi.cdef("struct foo_s { %s x; };\n" - "int foo(%s);" % (type, type)) - lib = ffi.verify(""" - struct foo_s { %s x; }; - int foo(%s arg) { - return !arg; - } - """ % (type, type)) - p = ffi.new("struct foo_s *") - with pytest.raises(TypeError): - p.x = 0.0 - assert lib.foo(42) == 0 - assert lib.foo(0) == 1 - py.test.raises(TypeError, lib.foo, 0.0) - -def test_cast_from_int_type_to_bool(): - ffi = FFI() - for basetype in ['char', 'short', 'int', 'long', 'long long']: - for sign in ['signed', 'unsigned']: - type = '%s %s' % (sign, basetype) - assert int(ffi.cast("_Bool", ffi.cast(type, 42))) == 1 - assert int(ffi.cast("bool", ffi.cast(type, 42))) == 1 - assert int(ffi.cast("_Bool", ffi.cast(type, 0))) == 0 - -def test_addressof(): - ffi = FFI() - ffi.cdef(""" - struct point_s { int x, y; }; - struct foo_s { int z; struct point_s point; }; - struct point_s sum_coord(struct point_s *); - """) - lib = ffi.verify(""" - struct point_s { int x, y; }; - struct foo_s { int z; struct point_s point; }; - struct point_s sum_coord(struct point_s *point) { - struct point_s r; - r.x = point->x + point->y; - r.y = point->x - point->y; - return r; - } - """) - p = ffi.new("struct foo_s *") - p.point.x = 16 - p.point.y = 9 - py.test.raises(TypeError, lib.sum_coord, p.point) - res = lib.sum_coord(ffi.addressof(p.point)) - assert res.x == 25 - assert res.y == 7 - res2 = lib.sum_coord(ffi.addressof(res)) - assert res2.x == 32 - assert res2.y == 18 - py.test.raises(TypeError, lib.sum_coord, res2) - -def test_callback_in_thread(): - if sys.platform == 'win32': - py.test.skip("pthread only") - import os, subprocess, imp - arg = os.path.join(os.path.dirname(__file__), 'callback_in_thread.py') - g = subprocess.Popen([sys.executable, arg, - os.path.dirname(imp.find_module('cffi')[1])]) - result = g.wait() - assert result == 0 - -def test_keepalive_lib(): - ffi = FFI() - ffi.cdef("int foobar(void);") - lib = ffi.verify("int foobar(void) { return 42; }") - func = lib.foobar - ffi_r = weakref.ref(ffi) - lib_r = weakref.ref(lib) - del ffi - import gc; gc.collect() # lib stays alive - assert lib_r() is not None - assert ffi_r() is not None - assert func() == 42 - -def test_keepalive_ffi(): - ffi = FFI() - ffi.cdef("int foobar(void);") - lib = ffi.verify("int foobar(void) { return 42; }") - func = lib.foobar - ffi_r = weakref.ref(ffi) - lib_r = weakref.ref(lib) - del lib - import gc; gc.collect() # ffi stays alive - assert ffi_r() is not None - assert lib_r() is not None - assert func() == 42 - -def test_FILE_stored_in_stdout(): - if not sys.platform.startswith('linux'): - py.test.skip("likely, we cannot assign to stdout") - ffi = FFI() - ffi.cdef("int printf(const char *, ...); FILE *setstdout(FILE *);") - lib = ffi.verify(""" - #include <stdio.h> - FILE *setstdout(FILE *f) { - FILE *result = stdout; - stdout = f; - return result; - } - """) - import os - fdr, fdw = os.pipe() - fw1 = os.fdopen(fdw, 'wb', 256) - old_stdout = lib.setstdout(fw1) - try: - # - fw1.write(b"X") - r = lib.printf(b"hello, %d!\n", ffi.cast("int", 42)) - fw1.close() - assert r == len("hello, 42!\n") - # - finally: - lib.setstdout(old_stdout) - # - result = os.read(fdr, 256) - os.close(fdr) - # the 'X' might remain in the user-level buffer of 'fw1' and - # end up showing up after the 'hello, 42!\n' - assert result == b"Xhello, 42!\n" or result == b"hello, 42!\nX" - -def test_FILE_stored_explicitly(): - ffi = FFI() - ffi.cdef("int myprintf11(const char *, int); extern FILE *myfile;") - lib = ffi.verify(""" - #include <stdio.h> - FILE *myfile; - int myprintf11(const char *out, int value) { - return fprintf(myfile, out, value); - } - """) - import os - fdr, fdw = os.pipe() - fw1 = os.fdopen(fdw, 'wb', 256) - lib.myfile = ffi.cast("FILE *", fw1) - # - fw1.write(b"X") - r = lib.myprintf11(b"hello, %d!\n", ffi.cast("int", 42)) - fw1.close() - assert r == len("hello, 42!\n") - # - result = os.read(fdr, 256) - os.close(fdr) - # the 'X' might remain in the user-level buffer of 'fw1' and - # end up showing up after the 'hello, 42!\n' - assert result == b"Xhello, 42!\n" or result == b"hello, 42!\nX" - -def test_global_array_with_missing_length(): - ffi = FFI() - ffi.cdef("extern int fooarray[];") - lib = ffi.verify("int fooarray[50];") - assert repr(lib.fooarray).startswith("<cdata 'int *'") - -def test_global_array_with_dotdotdot_length(): - ffi = FFI() - ffi.cdef("extern int fooarray[...];") - lib = ffi.verify("int fooarray[50];") - assert repr(lib.fooarray).startswith("<cdata 'int[50]'") - -def test_bad_global_array_with_dotdotdot_length(): - ffi = FFI() - ffi.cdef("extern int fooarray[...];") - py.test.raises(VerificationError, ffi.verify, "char fooarray[23];") - -def test_struct_containing_struct(): - ffi = FFI() - ffi.cdef("struct foo_s { ...; }; struct bar_s { struct foo_s f; ...; };") - ffi.verify("struct foo_s { int x; }; struct bar_s { struct foo_s f; };") - # - ffi = FFI() - ffi.cdef("struct foo_s { struct bar_s f; ...; }; struct bar_s { ...; };") - ffi.verify("struct bar_s { int x; }; struct foo_s { struct bar_s f; };") - -def test_struct_returned_by_func(): - ffi = FFI() - ffi.cdef("typedef ... foo_t; foo_t myfunc(void);") - e = py.test.raises(TypeError, ffi.verify, - "typedef struct { int x; } foo_t; " - "foo_t myfunc(void) { foo_t x = { 42 }; return x; }") - assert str(e.value) == ( - "function myfunc: 'foo_t' is used as result type, but is opaque") - -def test_include(): - ffi1 = FFI() - ffi1.cdef("typedef struct { int x; ...; } foo_t;") - ffi1.verify("typedef struct { int y, x; } foo_t;") - ffi2 = FFI() - ffi2.include(ffi1) - ffi2.cdef("int myfunc(foo_t *);") - lib = ffi2.verify("typedef struct { int y, x; } foo_t;" - "int myfunc(foo_t *p) { return 42 * p->x; }") - res = lib.myfunc(ffi2.new("foo_t *", {'x': 10})) - assert res == 420 - res = lib.myfunc(ffi1.new("foo_t *", {'x': -10})) - assert res == -420 - -def test_include_enum(): - ffi1 = FFI() - ffi1.cdef("enum foo_e { AA, ... };") - lib1 = ffi1.verify("enum foo_e { CC, BB, AA };") - ffi2 = FFI() - ffi2.include(ffi1) - ffi2.cdef("int myfunc(enum foo_e);") - lib2 = ffi2.verify("enum foo_e { CC, BB, AA };" - "int myfunc(enum foo_e x) { return (int)x; }") - res = lib2.myfunc(lib2.AA) - assert res == 2 - -def test_named_pointer_as_argument(): - ffi = FFI() - ffi.cdef("typedef struct { int x; } *mystruct_p;\n" - "mystruct_p ff5a(mystruct_p);") - lib = ffi.verify("typedef struct { int x; } *mystruct_p;\n" - "mystruct_p ff5a(mystruct_p p) { p->x += 40; return p; }") - p = ffi.new("mystruct_p", [-2]) - q = lib.ff5a(p) - assert q == p - assert p.x == 38 - -def test_enum_size(): - cases = [('123', 4, 4294967295), - ('4294967295U', 4, 4294967295), - ('-123', 4, -1), - ('-2147483647-1', 4, -1), - ] - if FFI().sizeof("long") == 8: - cases += [('4294967296L', 8, 2**64-1), - ('%dUL' % (2**64-1), 8, 2**64-1), - ('-2147483649L', 8, -1), - ('%dL-1L' % (1-2**63), 8, -1)] - for hidden_value, expected_size, expected_minus1 in cases: - if sys.platform == 'win32' and 'U' in hidden_value: - continue # skipped on Windows - ffi = FFI() - ffi.cdef("enum foo_e { AA, BB, ... };") - lib = ffi.verify("enum foo_e { AA, BB=%s };" % hidden_value) - assert lib.AA == 0 - assert lib.BB == eval(hidden_value.replace('U', '').replace('L', '')) - assert ffi.sizeof("enum foo_e") == expected_size - assert int(ffi.cast("enum foo_e", -1)) == expected_minus1 - # test with the large value hidden: - # disabled so far, doesn't work -## for hidden_value, expected_size, expected_minus1 in cases: -## ffi = FFI() -## ffi.cdef("enum foo_e { AA, BB, ... };") -## lib = ffi.verify("enum foo_e { AA, BB=%s };" % hidden_value) -## assert lib.AA == 0 -## assert ffi.sizeof("enum foo_e") == expected_size -## assert int(ffi.cast("enum foo_e", -1)) == expected_minus1 - -def test_enum_bug118(): - maxulong = 256 ** FFI().sizeof("unsigned long") - 1 - for c1, c2, c2c in [(0xffffffff, -1, ''), - (maxulong, -1, ''), - (-1, 0xffffffff, 'U'), - (-1, maxulong, 'UL')]: - if c2c and sys.platform == 'win32': - continue # enums may always be signed with MSVC - ffi = FFI() - ffi.cdef("enum foo_e { AA=%s };" % c1) - e = py.test.raises(VerificationError, ffi.verify, - "enum foo_e { AA=%s%s };" % (c2, c2c)) - assert str(e.value) == ('enum foo_e: AA has the real value %d, not %d' - % (c2, c1)) - -def test_string_to_voidp_arg(): - ffi = FFI() - ffi.cdef("int myfunc(void *);") - lib = ffi.verify("int myfunc(void *p) { return ((signed char *)p)[0]; }") - res = lib.myfunc(b"hi!") - assert res == ord(b"h") - p = ffi.new("char[]", b"gah") - res = lib.myfunc(p) - assert res == ord(b"g") - res = lib.myfunc(ffi.cast("void *", p)) - assert res == ord(b"g") - res = lib.myfunc(ffi.cast("int *", p)) - assert res == ord(b"g") - -def test_callback_indirection(): - ffi = FFI() - ffi.cdef(""" - static int (*python_callback)(int how_many, int *values); - int (*const c_callback)(int,...); /* pass this ptr to C routines */ - int some_c_function(int(*cb)(int,...)); - """) - lib = ffi.verify(""" - #include <stdarg.h> - #ifdef _WIN32 - #include <malloc.h> - #define alloca _alloca - #else - # ifdef __FreeBSD__ - # include <stdlib.h> - # else - # include <alloca.h> - # endif - #endif - static int (*python_callback)(int how_many, int *values); - static int c_callback(int how_many, ...) { - va_list ap; - /* collect the "..." arguments into the values[] array */ - int i, *values = alloca((size_t)how_many * sizeof(int)); - va_start(ap, how_many); - for (i=0; i<how_many; i++) - values[i] = va_arg(ap, int); - va_end(ap); - return python_callback(how_many, values); - } - int some_c_function(int(*cb)(int,...)) { - int result = cb(2, 10, 20); - result += cb(3, 30, 40, 50); - return result; - } - """) - seen = [] - @ffi.callback("int(int, int*)") - def python_callback(how_many, values): - seen.append([values[i] for i in range(how_many)]) - return 42 - lib.python_callback = python_callback - - res = lib.some_c_function(lib.c_callback) - assert res == 84 - assert seen == [[10, 20], [30, 40, 50]] - -def test_floatstar_argument(): - ffi = FFI() - ffi.cdef("float sum3floats(float *);") - lib = ffi.verify(""" - float sum3floats(float *f) { - return f[0] + f[1] + f[2]; - } - """) - assert lib.sum3floats((1.5, 2.5, 3.5)) == 7.5 - p = ffi.new("float[]", (1.5, 2.5, 3.5)) - assert lib.sum3floats(p) == 7.5 - -def test_charstar_argument(): - ffi = FFI() - ffi.cdef("char sum3chars(char *);") - lib = ffi.verify(""" - char sum3chars(char *f) { - return (char)(f[0] + f[1] + f[2]); - } - """) - assert lib.sum3chars((b'\x10', b'\x20', b'\x30')) == b'\x60' - p = ffi.new("char[]", b'\x10\x20\x30') - assert lib.sum3chars(p) == b'\x60' - -def test_passing_string_or_NULL(): - ffi = FFI() - ffi.cdef("int seeme1(char *); int seeme2(int *);") - lib = ffi.verify(""" - int seeme1(char *x) { - return (x == NULL); - } - int seeme2(int *x) { - return (x == NULL); - } - """) - assert lib.seeme1(b"foo") == 0 - assert lib.seeme1(ffi.NULL) == 1 - assert lib.seeme2([42, 43]) == 0 - assert lib.seeme2(ffi.NULL) == 1 - py.test.raises(TypeError, lib.seeme1, None) - py.test.raises(TypeError, lib.seeme2, None) - py.test.raises(TypeError, lib.seeme1, 0.0) - py.test.raises(TypeError, lib.seeme2, 0.0) - py.test.raises(TypeError, lib.seeme1, 0) - py.test.raises(TypeError, lib.seeme2, 0) - zeroL = 99999999999999999999 - zeroL -= 99999999999999999999 - py.test.raises(TypeError, lib.seeme2, zeroL) - -def test_typeof_function(): - ffi = FFI() - ffi.cdef("int foo(int, char);") - lib = ffi.verify("int foo(int x, char y) { (void)x; (void)y; return 42; }") - ctype = ffi.typeof(lib.foo) - assert len(ctype.args) == 2 - assert ctype.result == ffi.typeof("int") - -def test_call_with_voidstar_arg(): - ffi = FFI() - ffi.cdef("int f(void *);") - lib = ffi.verify("int f(void *x) { return ((char*)x)[0]; }") - assert lib.f(b"foobar") == ord(b"f") - -def test_dir(): - ffi = FFI() - ffi.cdef("""void somefunc(void); - extern int somevar, somearray[2]; - static char *const sv2; - enum my_e { AA, BB, ... }; - #define FOO ...""") - lib = ffi.verify("""void somefunc(void) { } - int somevar, somearray[2]; - #define sv2 "text" - enum my_e { AA, BB }; - #define FOO 42""") - assert dir(lib) == ['AA', 'BB', 'FOO', 'somearray', - 'somefunc', 'somevar', 'sv2'] - -def test_typeof_func_with_struct_argument(): - ffi = FFI() - ffi.cdef("""struct s { int a; }; int foo(struct s);""") - lib = ffi.verify("""struct s { int a; }; - int foo(struct s x) { return x.a; }""") - s = ffi.new("struct s *", [-1234]) - m = lib.foo(s[0]) - assert m == -1234 - assert repr(ffi.typeof(lib.foo)) == "<ctype 'int(*)(struct s)'>" - -def test_bug_const_char_ptr_array_1(): - ffi = FFI() - ffi.cdef("""extern const char *a[...];""") - lib = ffi.verify("""const char *a[5];""") - assert repr(ffi.typeof(lib.a)) == "<ctype 'char *[5]'>" - -def test_bug_const_char_ptr_array_2(): - from cffi import FFI # ignore warnings - ffi = FFI() - ffi.cdef("""extern const int a[];""") - lib = ffi.verify("""const int a[5];""") - assert repr(ffi.typeof(lib.a)) == "<ctype 'int *'>" - -def _test_various_calls(force_libffi): - cdef_source = """ - extern int xvalue; - extern long long ivalue, rvalue; - extern float fvalue; - extern double dvalue; - extern long double Dvalue; - signed char tf_bb(signed char x, signed char c); - unsigned char tf_bB(signed char x, unsigned char c); - short tf_bh(signed char x, short c); - unsigned short tf_bH(signed char x, unsigned short c); - int tf_bi(signed char x, int c); - unsigned int tf_bI(signed char x, unsigned int c); - long tf_bl(signed char x, long c); - unsigned long tf_bL(signed char x, unsigned long c); - long long tf_bq(signed char x, long long c); - unsigned long long tf_bQ(signed char x, unsigned long long c); - float tf_bf(signed char x, float c); - double tf_bd(signed char x, double c); - long double tf_bD(signed char x, long double c); - """ - if force_libffi: - cdef_source = (cdef_source - .replace('tf_', '(*const tf_') - .replace('(signed char x', ')(signed char x')) - ffi = FFI() - ffi.cdef(cdef_source) - lib = ffi.verify(""" - int xvalue; - long long ivalue, rvalue; - float fvalue; - double dvalue; - long double Dvalue; - - typedef signed char b_t; - typedef unsigned char B_t; - typedef short h_t; - typedef unsigned short H_t; - typedef int i_t; - typedef unsigned int I_t; - typedef long l_t; - typedef unsigned long L_t; - typedef long long q_t; - typedef unsigned long long Q_t; - typedef float f_t; - typedef double d_t; - typedef long double D_t; - #define S(letter) xvalue = (int)x; letter##value = (letter##_t)c; - #define R(letter) return (letter##_t)rvalue; - - signed char tf_bb(signed char x, signed char c) { S(i) R(b) } - unsigned char tf_bB(signed char x, unsigned char c) { S(i) R(B) } - short tf_bh(signed char x, short c) { S(i) R(h) } - unsigned short tf_bH(signed char x, unsigned short c) { S(i) R(H) } - int tf_bi(signed char x, int c) { S(i) R(i) } - unsigned int tf_bI(signed char x, unsigned int c) { S(i) R(I) } - long tf_bl(signed char x, long c) { S(i) R(l) } - unsigned long tf_bL(signed char x, unsigned long c) { S(i) R(L) } - long long tf_bq(signed char x, long long c) { S(i) R(q) } - unsigned long long tf_bQ(signed char x, unsigned long long c) { S(i) R(Q) } - float tf_bf(signed char x, float c) { S(f) R(f) } - double tf_bd(signed char x, double c) { S(d) R(d) } - long double tf_bD(signed char x, long double c) { S(D) R(D) } - """) - lib.rvalue = 0x7182838485868788 - for kind, cname in [('b', 'signed char'), - ('B', 'unsigned char'), - ('h', 'short'), - ('H', 'unsigned short'), - ('i', 'int'), - ('I', 'unsigned int'), - ('l', 'long'), - ('L', 'unsigned long'), - ('q', 'long long'), - ('Q', 'unsigned long long'), - ('f', 'float'), - ('d', 'double'), - ('D', 'long double')]: - sign = +1 if 'unsigned' in cname else -1 - lib.xvalue = 0 - lib.ivalue = 0 - lib.fvalue = 0 - lib.dvalue = 0 - lib.Dvalue = 0 - fun = getattr(lib, 'tf_b' + kind) - res = fun(-42, sign * 99) - if kind == 'D': - res = float(res) - assert res == int(ffi.cast(cname, 0x7182838485868788)) - assert lib.xvalue == -42 - if kind in 'fdD': - assert float(getattr(lib, kind + 'value')) == -99.0 - else: - assert lib.ivalue == sign * 99 - -def test_various_calls_direct(): - _test_various_calls(force_libffi=False) - -def test_various_calls_libffi(): - _test_various_calls(force_libffi=True) - -def test_ptr_to_opaque(): - ffi = FFI() - ffi.cdef("typedef ... foo_t; int f1(foo_t*); foo_t *f2(int);") - lib = ffi.verify(""" - #include <stdlib.h> - typedef struct { int x; } foo_t; - int f1(foo_t* p) { - int x = p->x; - free(p); - return x; - } - foo_t *f2(int x) { - foo_t *p = malloc(sizeof(foo_t)); - p->x = x; - return p; - } - """) - p = lib.f2(42) - x = lib.f1(p) - assert x == 42 - -def _run_in_multiple_threads(test1): - test1() - import sys - try: - import thread - except ImportError: - import _thread as thread - errors = [] - def wrapper(lock): - try: - test1() - except: - errors.append(sys.exc_info()) - lock.release() - locks = [] - for i in range(10): - _lock = thread.allocate_lock() - _lock.acquire() - thread.start_new_thread(wrapper, (_lock,)) - locks.append(_lock) - for _lock in locks: - _lock.acquire() - if errors: - raise errors[0][1] - -def test_errno_working_even_with_pypys_jit(): - # NOTE: on some platforms, to work correctly, this test needs to be - # compiled with -pthread. Otherwise, the accesses to errno done from f() - # are compiled by assuming this small library won't be used from multiple - # threads, which is wrong. If you see failures _and_ if you pass your - # own CFLAGS environment variable, please make sure "-pthread" is in it. - ffi = FFI() - ffi.cdef("int f(int);") - lib = ffi.verify(""" - #include <errno.h> - int f(int x) { return (errno = errno + x); } - """) - @_run_in_multiple_threads - def test1(): - ffi.errno = 0 - for i in range(10000): - e = lib.f(1) - assert e == i + 1 - assert ffi.errno == e - for i in range(10000): - ffi.errno = i - e = lib.f(42) - assert e == i + 42 - -def test_getlasterror_working_even_with_pypys_jit(): - if sys.platform != 'win32': - py.test.skip("win32-only test") - ffi = FFI() - ffi.cdef("void SetLastError(DWORD);") - lib = ffi.dlopen("Kernel32.dll") - @_run_in_multiple_threads - def test1(): - for i in range(10000): - n = (1 << 29) + i - lib.SetLastError(n) - assert ffi.getwinerror()[0] == n - -def test_verify_dlopen_flags(): - # Careful with RTLD_GLOBAL. If by chance the FFI is not deleted - # promptly, like on PyPy, then other tests may see the same - # exported symbols as well. So we must not export a simple name - # like 'foo'! - ffi1 = FFI() - ffi1.cdef("extern int foo_verify_dlopen_flags;") - - lib1 = ffi1.verify("int foo_verify_dlopen_flags;", - flags=ffi1.RTLD_GLOBAL | ffi1.RTLD_LAZY) - lib2 = get_second_lib() - - lib1.foo_verify_dlopen_flags = 42 - assert lib2.foo_verify_dlopen_flags == 42 - lib2.foo_verify_dlopen_flags += 1 - assert lib1.foo_verify_dlopen_flags == 43 - -def get_second_lib(): - # Hack, using modulename makes the test fail - ffi2 = FFI() - ffi2.cdef("extern int foo_verify_dlopen_flags;") - lib2 = ffi2.verify("int foo_verify_dlopen_flags;", - flags=ffi2.RTLD_GLOBAL | ffi2.RTLD_LAZY) - return lib2 - -def test_consider_not_implemented_function_type(): - ffi = FFI() - ffi.cdef("typedef union { int a; float b; } Data;" - "typedef struct { int a:2; } MyStr;" - "typedef void (*foofunc_t)(Data);" - "typedef Data (*bazfunc_t)(void);" - "typedef MyStr (*barfunc_t)(void);") - fooptr = ffi.cast("foofunc_t", 123) - bazptr = ffi.cast("bazfunc_t", 123) - barptr = ffi.cast("barfunc_t", 123) - # assert did not crash so far - e = py.test.raises(NotImplementedError, fooptr, ffi.new("Data *")) - assert str(e.value) == ( - "ctype 'Data' not supported as argument by libffi. Unions are only " - "supported as argument if the function is 'API mode' and " - "non-variadic (i.e. declared inside ffibuilder.cdef()+" - "ffibuilder.set_source() and not taking a final '...' argument)") - e = py.test.raises(NotImplementedError, bazptr) - assert str(e.value) == ( - "ctype 'Data' not supported as return value by libffi. Unions are " - "only supported as return value if the function is 'API mode' and " - "non-variadic (i.e. declared inside ffibuilder.cdef()+" - "ffibuilder.set_source() and not taking a final '...' argument)") - e = py.test.raises(NotImplementedError, barptr) - assert str(e.value) == ( - "ctype 'MyStr' not supported as return value. It is a struct with " - "bit fields, which libffi does not support. Such structs are only " - "supported as return value if the function is 'API mode' and non-" - "variadic (i.e. declared inside ffibuilder.cdef()+ffibuilder." - "set_source() and not taking a final '...' argument)") - -def test_verify_extra_arguments(): - ffi = FFI() - ffi.cdef("#define ABA ...") - lib = ffi.verify("", define_macros=[('ABA', '42')]) - assert lib.ABA == 42 - -def test_implicit_unicode_on_windows(): - if sys.platform != 'win32': - py.test.skip("win32-only test") - ffi = FFI() - e = py.test.raises(FFIError, ffi.cdef, "int foo(LPTSTR);") - assert str(e.value) == ("The Windows type 'LPTSTR' is only available after" - " you call ffi.set_unicode()") - for with_unicode in [True, False]: - ffi = FFI() - ffi.set_unicode(with_unicode) - ffi.cdef(""" - DWORD GetModuleFileName(HMODULE hModule, LPTSTR lpFilename, - DWORD nSize); - """) - lib = ffi.verify(""" - #include <windows.h> - """, libraries=['Kernel32']) - outbuf = ffi.new("TCHAR[]", 200) - n = lib.GetModuleFileName(ffi.NULL, outbuf, 500) - assert 0 < n < 500 - for i in range(n): - #print repr(outbuf[i]) - assert ord(outbuf[i]) != 0 - assert ord(outbuf[n]) == 0 - assert ord(outbuf[0]) < 128 # should be a letter, or '\' - -def test_use_local_dir(): - ffi = FFI() - lib = ffi.verify("", modulename="test_use_local_dir") - this_dir = os.path.dirname(__file__) - pycache_files = os.listdir(os.path.join(this_dir, '__pycache__')) - assert any('test_use_local_dir' in s for s in pycache_files) - -def test_define_known_value(): - ffi = FFI() - ffi.cdef("#define FOO 0x123") - lib = ffi.verify("#define FOO 0x123") - assert lib.FOO == 0x123 - -def test_define_wrong_value(): - ffi = FFI() - ffi.cdef("#define FOO 123") - e = py.test.raises(VerificationError, ffi.verify, "#define FOO 124") - assert str(e.value).endswith("FOO has the real value 124, not 123") - -def test_static_const_int_known_value(): - ffi = FFI() - ffi.cdef("static const int FOO = 0x123;") - lib = ffi.verify("#define FOO 0x123") - assert lib.FOO == 0x123 - -def test_static_const_int_wrong_value(): - ffi = FFI() - ffi.cdef("static const int FOO = 123;") - e = py.test.raises(VerificationError, ffi.verify, "#define FOO 124") - assert str(e.value).endswith("FOO has the real value 124, not 123") - -def test_const_struct_global(): - ffi = FFI() - ffi.cdef("typedef struct { int x; ...; } T; const T myglob;") - lib = ffi.verify("typedef struct { double y; int x; } T;" - "const T myglob = { 0.1, 42 };") - assert ffi.typeof(lib.myglob) == ffi.typeof("T") - assert lib.myglob.x == 42 - -def test_dont_support_int_dotdotdot(): - ffi = FFI() - ffi.cdef("typedef int... t1;") - e = py.test.raises(VerificationError, ffi.verify, "") - assert str(e.value) == ("feature not supported with ffi.verify(), but only " - "with ffi.set_source(): 'typedef int... t1'") - ffi = FFI() - ffi.cdef("typedef double ... t1;") - e = py.test.raises(VerificationError, ffi.verify, "") - assert str(e.value) == ("feature not supported with ffi.verify(), but only " - "with ffi.set_source(): 'typedef float... t1'") - -def test_const_fields(): - ffi = FFI() - ffi.cdef("""struct foo_s { const int a; void *const b; };""") - ffi.verify("""struct foo_s { const int a; void *const b; };""") - foo_s = ffi.typeof("struct foo_s") - assert foo_s.fields[0][0] == 'a' - assert foo_s.fields[0][1].type is ffi.typeof("int") - assert foo_s.fields[1][0] == 'b' - assert foo_s.fields[1][1].type is ffi.typeof("void *") - -def test_win32_calling_convention_0(): - ffi = FFI() - ffi.cdef(""" - int call1(int(__cdecl *cb)(int)); - int (*const call2)(int(__stdcall *cb)(int)); - """) - lib = ffi.verify(r""" - #ifndef _MSC_VER - # define __stdcall /* nothing */ - #endif - int call1(int(*cb)(int)) { - int i, result = 0; - //printf("call1: cb = %p\n", cb); - for (i = 0; i < 1000; i++) - result += cb(i); - //printf("result = %d\n", result); - return result; - } - int call2(int(__stdcall *cb)(int)) { - int i, result = 0; - //printf("call2: cb = %p\n", cb); - for (i = 0; i < 1000; i++) - result += cb(-i); - //printf("result = %d\n", result); - return result; - } - """) - @ffi.callback("int(int)") - def cb1(x): - return x * 2 - @ffi.callback("int __stdcall(int)") - def cb2(x): - return x * 3 - #print 'cb1 =', cb1 - res = lib.call1(cb1) - assert res == 500*999*2 - #print 'cb2 =', cb2 - #print ffi.typeof(lib.call2) - #print 'call2 =', lib.call2 - res = lib.call2(cb2) - #print '...' - assert res == -500*999*3 - #print 'done' - if sys.platform == 'win32' and sys.maxsize < 2**32: - assert '__stdcall' in str(ffi.typeof(cb2)) - assert '__stdcall' not in str(ffi.typeof(cb1)) - py.test.raises(TypeError, lib.call1, cb2) - py.test.raises(TypeError, lib.call2, cb1) - else: - assert '__stdcall' not in str(ffi.typeof(cb2)) - assert ffi.typeof(cb2) is ffi.typeof(cb1) - -def test_win32_calling_convention_1(): - ffi = FFI() - ffi.cdef(""" - int __cdecl call1(int(__cdecl *cb)(int)); - int __stdcall call2(int(__stdcall *cb)(int)); - int (__cdecl *const cb1)(int); - int (__stdcall *const cb2)(int); - """) - lib = ffi.verify(r""" - #ifndef _MSC_VER - # define __cdecl - # define __stdcall - #endif - int __cdecl cb1(int x) { return x * 2; } - int __stdcall cb2(int x) { return x * 3; } - - int __cdecl call1(int(__cdecl *cb)(int)) { - int i, result = 0; - //printf("here1\n"); - //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1); - for (i = 0; i < 1000; i++) - result += cb(i); - //printf("result = %d\n", result); - return result; - } - int __stdcall call2(int(__stdcall *cb)(int)) { - int i, result = 0; - //printf("here1\n"); - //printf("cb = %p, cb2 = %p\n", cb, (void *)cb2); - for (i = 0; i < 1000; i++) - result += cb(-i); - //printf("result = %d\n", result); - return result; - } - """) - assert lib.call1(lib.cb1) == 500*999*2 - assert lib.call2(lib.cb2) == -500*999*3 - -def test_win32_calling_convention_2(): - # any mistake in the declaration of plain function (including the - # precise argument types and, here, the calling convention) are - # automatically corrected. But this does not apply to the 'cb' - # function pointer argument. - ffi = FFI() - ffi.cdef(""" - int __stdcall call1(int(__cdecl *cb)(int)); - int __cdecl call2(int(__stdcall *cb)(int)); - int (__cdecl *const cb1)(int); - int (__stdcall *const cb2)(int); - """) - lib = ffi.verify(r""" - #ifndef _MSC_VER - # define __cdecl - # define __stdcall - #endif - int __cdecl call1(int(__cdecl *cb)(int)) { - int i, result = 0; - for (i = 0; i < 1000; i++) - result += cb(i); - return result; - } - int __stdcall call2(int(__stdcall *cb)(int)) { - int i, result = 0; - for (i = 0; i < 1000; i++) - result += cb(-i); - return result; - } - int __cdecl cb1(int x) { return x * 2; } - int __stdcall cb2(int x) { return x * 3; } - """) - assert lib.call1(lib.cb1) == 500*999*2 - assert lib.call2(lib.cb2) == -500*999*3 - -def test_win32_calling_convention_3(): - ffi = FFI() - ffi.cdef(""" - struct point { int x, y; }; - - int (*const cb1)(struct point); - int (__stdcall *const cb2)(struct point); - - struct point __stdcall call1(int(*cb)(struct point)); - struct point call2(int(__stdcall *cb)(struct point)); - """) - lib = ffi.verify(r""" - #ifndef _MSC_VER - # define __cdecl - # define __stdcall - #endif - struct point { int x, y; }; - int cb1(struct point pt) { return pt.x + 10 * pt.y; } - int __stdcall cb2(struct point pt) { return pt.x + 100 * pt.y; } - struct point __stdcall call1(int(__cdecl *cb)(struct point)) { - int i; - struct point result = { 0, 0 }; - //printf("here1\n"); - //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1); - for (i = 0; i < 1000; i++) { - struct point p = { i, -i }; - int r = cb(p); - result.x += r; - result.y -= r; - } - return result; - } - struct point __cdecl call2(int(__stdcall *cb)(struct point)) { - int i; - struct point result = { 0, 0 }; - for (i = 0; i < 1000; i++) { - struct point p = { -i, i }; - int r = cb(p); - result.x += r; - result.y -= r; - } - return result; - } - """) - if sys.platform == 'win32' and sys.maxsize < 2**32: - py.test.raises(TypeError, lib.call1, lib.cb2) - py.test.raises(TypeError, lib.call2, lib.cb1) - pt = lib.call1(lib.cb1) - assert (pt.x, pt.y) == (-9*500*999, 9*500*999) - pt = lib.call2(lib.cb2) - assert (pt.x, pt.y) == (99*500*999, -99*500*999) - -def _only_test_on_linux_intel(): - if not sys.platform.startswith('linux'): - py.test.skip('only running the memory-intensive test on Linux') - import platform - machine = platform.machine() - if 'x86' not in machine and 'x64' not in machine: - py.test.skip('only running the memory-intensive test on x86/x64') - -def test_ffi_gc_size_arg(): - # with PyPy's GC, these calls to ffi.gc() would rapidly consume - # 40 GB of RAM without the third argument - _only_test_on_linux_intel() - ffi = FFI() - ffi.cdef("void *malloc(size_t); void free(void *);") - lib = ffi.verify(r""" - #include <stdlib.h> - """) - for i in range(2000): - p = lib.malloc(20*1024*1024) # 20 MB - p1 = ffi.cast("char *", p) - for j in range(0, 20*1024*1024, 4096): - p1[j] = b'!' - p = ffi.gc(p, lib.free, 20*1024*1024) - del p - -def test_ffi_gc_size_arg_2(): - # a variant of the above: this "attack" works on cpython's cyclic gc too - # and I found no obvious way to prevent that. So for now, this test - # is skipped on CPython, where it eats all the memory. - if '__pypy__' not in sys.builtin_module_names: - py.test.skip("find a way to tweak the cyclic GC of CPython") - _only_test_on_linux_intel() - ffi = FFI() - ffi.cdef("void *malloc(size_t); void free(void *);") - lib = ffi.verify(r""" - #include <stdlib.h> - """) - class X(object): - pass - for i in range(2000): - p = lib.malloc(50*1024*1024) # 50 MB - p1 = ffi.cast("char *", p) - for j in range(0, 50*1024*1024, 4096): - p1[j] = b'!' - p = ffi.gc(p, lib.free, 50*1024*1024) - x = X() - x.p = p - x.cyclic = x - del p, x - -def test_ffi_new_with_cycles(): - # still another variant, with ffi.new() - if '__pypy__' not in sys.builtin_module_names: - py.test.skip("find a way to tweak the cyclic GC of CPython") - ffi = FFI() - ffi.cdef("") - lib = ffi.verify("") - class X(object): - pass - for i in range(2000): - p = ffi.new("char[]", 50*1024*1024) # 50 MB - for j in range(0, 50*1024*1024, 4096): - p[j] = b'!' - x = X() - x.p = p - x.cyclic = x - del p, x - -def test_arithmetic_in_cdef(): - for a in [0, 11, 15]: - ffi = FFI() - ffi.cdef(""" - enum FOO { - DIVNN = ((-?) / (-3)), - DIVNP = ((-?) / (+3)), - DIVPN = ((+?) / (-3)), - MODNN = ((-?) % (-3)), - MODNP = ((-?) % (+3)), - MODPN = ((+?) % (-3)), - }; - """.replace('?', str(a))) - lib = ffi.verify(""" - enum FOO { - DIVNN = ((-?) / (-3)), - DIVNP = ((-?) / (+3)), - DIVPN = ((+?) / (-3)), - MODNN = ((-?) % (-3)), - MODNP = ((-?) % (+3)), - MODPN = ((+?) % (-3)), - }; - """.replace('?', str(a))) - # the verify() crashes if the values in the enum are different from - # the values we computed ourselves from the cdef() - -def test_passing_large_list(): - ffi = FFI() - ffi.cdef("""void passing_large_list(long[]);""") - lib = ffi.verify(""" - static void passing_large_list(long a[]) { } - """) - arg = list(range(20000000)) - lib.passing_large_list(arg) - # assert did not segfault diff --git a/testing/cffi0/test_verify2.py b/testing/cffi0/test_verify2.py deleted file mode 100644 index a4af6d6..0000000 --- a/testing/cffi0/test_verify2.py +++ /dev/null @@ -1,9 +0,0 @@ -from .test_verify import * - -# This test file runs normally after test_verify. We only clean up the .c -# sources, to check that it also works when we have only the .so. The -# tests should run much faster than test_verify. - -def setup_module(): - import cffi.verifier - cffi.verifier.cleanup_tmpdir(keep_so=True) diff --git a/testing/cffi0/test_version.py b/testing/cffi0/test_version.py deleted file mode 100644 index facb84c..0000000 --- a/testing/cffi0/test_version.py +++ /dev/null @@ -1,68 +0,0 @@ -import py, os, sys -import cffi, _cffi_backend - -def setup_module(mod): - if '_cffi_backend' in sys.builtin_module_names: - py.test.skip("this is embedded version") - -#BACKEND_VERSIONS = { -# '0.4.2': '0.4', # did not change -# '0.7.1': '0.7', # did not change -# '0.7.2': '0.7', # did not change -# '0.8.1': '0.8', # did not change (essentially) -# '0.8.4': '0.8.3', # did not change -# } - -def test_version(): - v = cffi.__version__ - version_info = '.'.join(str(i) for i in cffi.__version_info__) - version_info = version_info.replace('.beta.', 'b') - version_info = version_info.replace('.plus', '+') - version_info = version_info.replace('.rc', 'rc') - assert v == version_info - #v = BACKEND_VERSIONS.get(v, v) - assert v == _cffi_backend.__version__ - -def test_doc_version(): - parent = os.path.dirname(os.path.dirname(cffi.__file__)) - p = os.path.join(parent, 'doc', 'source', 'conf.py') - content = open(p).read() - # - v = cffi.__version__ - assert ("version = '%s'\n" % v[:4]) in content - assert ("release = '%s'\n" % v) in content - -def test_doc_version_file(): - parent = os.path.dirname(os.path.dirname(cffi.__file__)) - v = cffi.__version__.replace('+', '') - p = os.path.join(parent, 'doc', 'source', 'installation.rst') - content = open(p).read() - if " package version %s:" % v not in content: - for i in range(5): - if " package version %s-%d:" % (v, i) in content: - break - else: - assert 0, "doc/source/installation.rst needs updating" - -def test_setup_version(): - parent = os.path.dirname(os.path.dirname(cffi.__file__)) - p = os.path.join(parent, 'setup.py') - content = open(p).read() - # - v = cffi.__version__.replace('+', '') - assert ("version='%s'" % v) in content - -def test_c_version(): - parent = os.path.dirname(os.path.dirname(cffi.__file__)) - v = cffi.__version__ - p = os.path.join(parent, 'c', 'test_c.py') - content = open(p).read() - #v = BACKEND_VERSIONS.get(v, v) - assert (('assert __version__ == "%s"' % v) in content) - -def test_embedding_h(): - parent = os.path.dirname(os.path.dirname(cffi.__file__)) - v = cffi.__version__ - p = os.path.join(parent, 'cffi', '_embedding.h') - content = open(p).read() - assert ('cffi version: %s"' % (v,)) in content diff --git a/testing/cffi0/test_vgen.py b/testing/cffi0/test_vgen.py deleted file mode 100644 index 1a7e05d..0000000 --- a/testing/cffi0/test_vgen.py +++ /dev/null @@ -1,12 +0,0 @@ -import cffi.verifier -from .test_verify import * - - -def setup_module(): - cffi.verifier.cleanup_tmpdir() - cffi.verifier._FORCE_GENERIC_ENGINE = True - # Runs all tests with _FORCE_GENERIC_ENGINE = True, to make sure we - # also test vengine_gen.py. - -def teardown_module(): - cffi.verifier._FORCE_GENERIC_ENGINE = False diff --git a/testing/cffi0/test_vgen2.py b/testing/cffi0/test_vgen2.py deleted file mode 100644 index 34147c8..0000000 --- a/testing/cffi0/test_vgen2.py +++ /dev/null @@ -1,13 +0,0 @@ -import cffi.verifier -from .test_vgen import * - -# This test file runs normally after test_vgen. We only clean up the .c -# sources, to check that it also works when we have only the .so. The -# tests should run much faster than test_vgen. - -def setup_module(): - cffi.verifier.cleanup_tmpdir(keep_so=True) - cffi.verifier._FORCE_GENERIC_ENGINE = True - -def teardown_module(): - cffi.verifier._FORCE_GENERIC_ENGINE = False diff --git a/testing/cffi0/test_zdistutils.py b/testing/cffi0/test_zdistutils.py deleted file mode 100644 index 35b3d0c..0000000 --- a/testing/cffi0/test_zdistutils.py +++ /dev/null @@ -1,288 +0,0 @@ -import sys, os, imp, math, shutil -import py -from cffi import FFI, FFIError -from cffi.verifier import Verifier, _locate_engine_class, _get_so_suffixes -from cffi.ffiplatform import maybe_relative_path -from testing.udir import udir - - -class DistUtilsTest(object): - def setup_class(self): - self.lib_m = "m" - if sys.platform == 'win32': - #there is a small chance this fails on Mingw via environ $CC - import distutils.ccompiler - if distutils.ccompiler.get_default_compiler() == 'msvc': - self.lib_m = 'msvcrt' - - def teardown_class(self): - if udir.isdir(): - udir.remove(ignore_errors=True) - udir.ensure(dir=1) - - def test_locate_engine_class(self): - cls = _locate_engine_class(FFI(), self.generic) - if self.generic: - # asked for the generic engine, which must not generate a - # CPython extension module - assert not cls._gen_python_module - else: - # asked for the CPython engine: check that we got it, unless - # we are running on top of PyPy, where the generic engine is - # always better - if '__pypy__' not in sys.builtin_module_names: - assert cls._gen_python_module - - def test_write_source(self): - ffi = FFI() - ffi.cdef("double sin(double x);") - csrc = '/*hi there %s!*/\n#include <math.h>\n' % self - v = Verifier(ffi, csrc, force_generic_engine=self.generic, - libraries=[self.lib_m]) - v.write_source() - with open(v.sourcefilename, 'r') as f: - data = f.read() - assert csrc in data - - def test_write_source_explicit_filename(self): - ffi = FFI() - ffi.cdef("double sin(double x);") - csrc = '/*hi there %s!*/\n#include <math.h>\n' % self - v = Verifier(ffi, csrc, force_generic_engine=self.generic, - libraries=[self.lib_m]) - v.sourcefilename = filename = str(udir.join('write_source.c')) - v.write_source() - assert filename == v.sourcefilename - with open(filename, 'r') as f: - data = f.read() - assert csrc in data - - def test_write_source_to_file_obj(self): - ffi = FFI() - ffi.cdef("double sin(double x);") - csrc = '/*hi there %s!*/\n#include <math.h>\n' % self - v = Verifier(ffi, csrc, force_generic_engine=self.generic, - libraries=[self.lib_m]) - try: - from StringIO import StringIO - except ImportError: - from io import StringIO - f = StringIO() - v.write_source(file=f) - assert csrc in f.getvalue() - - def test_compile_module(self): - ffi = FFI() - ffi.cdef("double sin(double x);") - csrc = '/*hi there %s!*/\n#include <math.h>\n' % self - v = Verifier(ffi, csrc, force_generic_engine=self.generic, - libraries=[self.lib_m]) - v.compile_module() - assert v.get_module_name().startswith('_cffi_') - if v.generates_python_module(): - mod = imp.load_dynamic(v.get_module_name(), v.modulefilename) - assert hasattr(mod, '_cffi_setup') - - def test_compile_module_explicit_filename(self): - ffi = FFI() - ffi.cdef("double sin(double x);") - csrc = '/*hi there %s!2*/\n#include <math.h>\n' % self - v = Verifier(ffi, csrc, force_generic_engine=self.generic, - libraries=[self.lib_m]) - basename = self.__class__.__name__[:10] + '_test_compile_module' - v.modulefilename = filename = str(udir.join(basename + '.so')) - v.compile_module() - assert filename == v.modulefilename - assert v.get_module_name() == basename - if v.generates_python_module(): - mod = imp.load_dynamic(v.get_module_name(), v.modulefilename) - assert hasattr(mod, '_cffi_setup') - - def test_name_from_checksum_of_cdef(self): - names = [] - for csrc in ['double', 'double', 'float']: - ffi = FFI() - ffi.cdef("%s sin(double x);" % csrc) - v = Verifier(ffi, "#include <math.h>", - force_generic_engine=self.generic, - libraries=[self.lib_m]) - names.append(v.get_module_name()) - assert names[0] == names[1] != names[2] - - def test_name_from_checksum_of_csrc(self): - names = [] - for csrc in ['123', '123', '1234']: - ffi = FFI() - ffi.cdef("double sin(double x);") - v = Verifier(ffi, csrc, force_generic_engine=self.generic) - names.append(v.get_module_name()) - assert names[0] == names[1] != names[2] - - def test_load_library(self): - ffi = FFI() - ffi.cdef("double sin(double x);") - csrc = '/*hi there %s!3*/\n#include <math.h>\n' % self - v = Verifier(ffi, csrc, force_generic_engine=self.generic, - libraries=[self.lib_m]) - library = v.load_library() - assert library.sin(12.3) == math.sin(12.3) - - def test_verifier_args(self): - ffi = FFI() - ffi.cdef("double sin(double x);") - csrc = '/*hi there %s!4*/#include "test_verifier_args.h"\n' % self - udir.join('test_verifier_args.h').write('#include <math.h>\n') - v = Verifier(ffi, csrc, include_dirs=[str(udir)], - force_generic_engine=self.generic, - libraries=[self.lib_m]) - library = v.load_library() - assert library.sin(12.3) == math.sin(12.3) - - def test_verifier_object_from_ffi(self): - ffi = FFI() - ffi.cdef("double sin(double x);") - csrc = "/*6%s*/\n#include <math.h>" % self - lib = ffi.verify(csrc, force_generic_engine=self.generic, - libraries=[self.lib_m]) - assert lib.sin(12.3) == math.sin(12.3) - assert isinstance(ffi.verifier, Verifier) - with open(ffi.verifier.sourcefilename, 'r') as f: - data = f.read() - assert csrc in data - - def test_extension_object(self): - ffi = FFI() - ffi.cdef("double sin(double x);") - csrc = '/*7%s*/' % self + ''' - #include <math.h> - #ifndef TEST_EXTENSION_OBJECT - # error "define_macros missing" - #endif - ''' - lib = ffi.verify(csrc, define_macros=[('TEST_EXTENSION_OBJECT', '1')], - force_generic_engine=self.generic, - libraries=[self.lib_m]) - assert lib.sin(12.3) == math.sin(12.3) - v = ffi.verifier - ext = v.get_extension() - assert 'distutils.extension.Extension' in str(ext.__class__) or \ - 'setuptools.extension.Extension' in str(ext.__class__) - assert ext.sources == [maybe_relative_path(v.sourcefilename)] - assert ext.name == v.get_module_name() - assert ext.define_macros == [('TEST_EXTENSION_OBJECT', '1')] - - def test_extension_forces_write_source(self): - ffi = FFI() - ffi.cdef("double sin(double x);") - csrc = '/*hi there9!%s*/\n#include <math.h>\n' % self - v = Verifier(ffi, csrc, force_generic_engine=self.generic, - libraries=[self.lib_m]) - assert not os.path.exists(v.sourcefilename) - v.get_extension() - assert os.path.exists(v.sourcefilename) - - def test_extension_object_extra_sources(self): - ffi = FFI() - ffi.cdef("double test1eoes(double x);") - extra_source = str(udir.join('extension_extra_sources.c')) - with open(extra_source, 'w') as f: - f.write('double test1eoes(double x) { return x * 6.0; }\n') - csrc = '/*9%s*/' % self + ''' - double test1eoes(double x); /* or #include "extra_sources.h" */ - ''' - lib = ffi.verify(csrc, sources=[extra_source], - force_generic_engine=self.generic) - assert lib.test1eoes(7.0) == 42.0 - v = ffi.verifier - ext = v.get_extension() - assert 'distutils.extension.Extension' in str(ext.__class__) or \ - 'setuptools.extension.Extension' in str(ext.__class__) - assert ext.sources == [maybe_relative_path(v.sourcefilename), - extra_source] - assert ext.name == v.get_module_name() - - def test_install_and_reload_module(self, targetpackage='', ext_package=''): - KEY = repr(self) - if not hasattr(os, 'fork'): - py.test.skip("test requires os.fork()") - - if targetpackage: - udir.ensure(targetpackage, dir=1).ensure('__init__.py') - sys.path.insert(0, str(udir)) - - def make_ffi(**verifier_args): - ffi = FFI() - ffi.cdef("/* %s, %s, %s */" % (KEY, targetpackage, ext_package)) - ffi.cdef("double test1iarm(double x);") - csrc = "double test1iarm(double x) { return x * 42.0; }" - lib = ffi.verify(csrc, force_generic_engine=self.generic, - ext_package=ext_package, - **verifier_args) - return ffi, lib - - childpid = os.fork() - if childpid == 0: - # in the child - ffi, lib = make_ffi() - assert lib.test1iarm(1.5) == 63.0 - # "install" the module by moving it into udir (/targetpackage) - if targetpackage: - target = udir.join(targetpackage) - else: - target = udir - shutil.move(ffi.verifier.modulefilename, str(target)) - os._exit(0) - # in the parent - _, status = os.waitpid(childpid, 0) - if not (os.WIFEXITED(status) and os.WEXITSTATUS(status) == 0): - raise AssertionError # see error above in subprocess - - from cffi import ffiplatform - prev_compile = ffiplatform.compile - try: - if targetpackage == ext_package: - ffiplatform.compile = lambda *args: dont_call_me_any_more - # won't find it in tmpdir, but should find it correctly - # installed in udir - ffi, lib = make_ffi() - assert lib.test1iarm(0.5) == 21.0 - finally: - ffiplatform.compile = prev_compile - - def test_install_and_reload_module_package(self): - self.test_install_and_reload_module(targetpackage='foo_iarmp', - ext_package='foo_iarmp') - - def test_install_and_reload_module_ext_package_not_found(self): - self.test_install_and_reload_module(targetpackage='foo_epnf', - ext_package='not_found') - - def test_tag(self): - ffi = FFI() - ffi.cdef("/* %s test_tag */ double test1tag(double x);" % self) - csrc = "double test1tag(double x) { return x - 42.0; }" - lib = ffi.verify(csrc, force_generic_engine=self.generic, - tag='xxtest_tagxx') - assert lib.test1tag(143) == 101.0 - assert '_cffi_xxtest_tagxx_' in ffi.verifier.modulefilename - - def test_modulename(self): - ffi = FFI() - ffi.cdef("/* %s test_modulename */ double test1foo(double x);" % self) - csrc = "double test1foo(double x) { return x - 63.0; }" - modname = 'xxtest_modulenamexx%d' % (self.generic,) - lib = ffi.verify(csrc, force_generic_engine=self.generic, - modulename=modname) - assert lib.test1foo(143) == 80.0 - suffix = _get_so_suffixes()[0] - fn1 = os.path.join(ffi.verifier.tmpdir, modname + '.c') - fn2 = os.path.join(ffi.verifier.tmpdir, modname + suffix) - assert ffi.verifier.sourcefilename == fn1 - assert ffi.verifier.modulefilename == fn2 - - -class TestDistUtilsCPython(DistUtilsTest): - generic = False - -class TestDistUtilsGeneric(DistUtilsTest): - generic = True diff --git a/testing/cffi0/test_zintegration.py b/testing/cffi0/test_zintegration.py deleted file mode 100644 index ce925b8..0000000 --- a/testing/cffi0/test_zintegration.py +++ /dev/null @@ -1,181 +0,0 @@ -import py, os, sys, shutil -import subprocess -from testing.udir import udir -import pytest - -if sys.platform == 'win32': - pytestmark = pytest.mark.skip('snippets do not run on win32') -if sys.version_info < (2, 7): - pytestmark = pytest.mark.skip( - 'fails e.g. on a Debian/Ubuntu which patches virtualenv' - ' in a non-2.6-friendly way') - -def create_venv(name): - tmpdir = udir.join(name) - try: - subprocess.check_call(['virtualenv', - #'--never-download', <= could be added, but causes failures - # in random cases on random machines - '-p', os.path.abspath(sys.executable), - str(tmpdir)]) - except OSError as e: - py.test.skip("Cannot execute virtualenv: %s" % (e,)) - - site_packages = None - for dirpath, dirnames, filenames in os.walk(str(tmpdir)): - if os.path.basename(dirpath) == 'site-packages': - site_packages = dirpath - break - paths = "" - if site_packages: - try: - from cffi import _pycparser - modules = ('cffi', '_cffi_backend') - except ImportError: - modules = ('cffi', '_cffi_backend', 'pycparser') - try: - import ply - except ImportError: - pass - else: - modules += ('ply',) # needed for older versions of pycparser - paths = [] - for module in modules: - target = __import__(module, None, None, []) - if not hasattr(target, '__file__'): # for _cffi_backend on pypy - continue - src = os.path.abspath(target.__file__) - for end in ['__init__.pyc', '__init__.pyo', '__init__.py']: - if src.lower().endswith(end): - src = src[:-len(end)-1] - break - paths.append(os.path.dirname(src)) - paths = os.pathsep.join(paths) - return tmpdir, paths - -SNIPPET_DIR = py.path.local(__file__).join('..', 'snippets') - -def really_run_setup_and_program(dirname, venv_dir_and_paths, python_snippet): - venv_dir, paths = venv_dir_and_paths - def remove(dir): - dir = str(SNIPPET_DIR.join(dirname, dir)) - shutil.rmtree(dir, ignore_errors=True) - remove('build') - remove('__pycache__') - for basedir in os.listdir(str(SNIPPET_DIR.join(dirname))): - remove(os.path.join(basedir, '__pycache__')) - olddir = os.getcwd() - python_f = udir.join('x.py') - python_f.write(py.code.Source(python_snippet)) - try: - os.chdir(str(SNIPPET_DIR.join(dirname))) - if os.name == 'nt': - bindir = 'Scripts' - else: - bindir = 'bin' - vp = str(venv_dir.join(bindir).join('python')) - env = os.environ.copy() - env['PYTHONPATH'] = paths - subprocess.check_call((vp, 'setup.py', 'clean'), env=env) - # there's a setuptools/easy_install bug that causes this to fail when the build/install occur together and - # we're in the same directory with the build (it tries to look up dependencies for itself on PyPI); - # subsequent runs will succeed because this test doesn't properly clean up the build- use pip for now. - subprocess.check_call((vp, '-m', 'pip', 'install', '.'), env=env) - subprocess.check_call((vp, str(python_f)), env=env) - finally: - os.chdir(olddir) - -def run_setup_and_program(dirname, python_snippet): - venv_dir = create_venv(dirname + '-cpy') - really_run_setup_and_program(dirname, venv_dir, python_snippet) - # - sys._force_generic_engine_ = True - try: - venv_dir = create_venv(dirname + '-gen') - really_run_setup_and_program(dirname, venv_dir, python_snippet) - finally: - del sys._force_generic_engine_ - # the two files lextab.py and yacctab.py are created by not-correctly- - # installed versions of pycparser. - assert not os.path.exists(str(SNIPPET_DIR.join(dirname, 'lextab.py'))) - assert not os.path.exists(str(SNIPPET_DIR.join(dirname, 'yacctab.py'))) - -class TestZIntegration(object): - def teardown_class(self): - if udir.isdir(): - udir.remove(ignore_errors=True) - udir.ensure(dir=1) - - def test_infrastructure(self): - run_setup_and_program('infrastructure', ''' - import snip_infrastructure - assert snip_infrastructure.func() == 42 - ''') - - def test_distutils_module(self): - run_setup_and_program("distutils_module", ''' - import snip_basic_verify - p = snip_basic_verify.C.getpwuid(0) - assert snip_basic_verify.ffi.string(p.pw_name) == b"root" - ''') - - def test_distutils_package_1(self): - run_setup_and_program("distutils_package_1", ''' - import snip_basic_verify1 - p = snip_basic_verify1.C.getpwuid(0) - assert snip_basic_verify1.ffi.string(p.pw_name) == b"root" - ''') - - def test_distutils_package_2(self): - run_setup_and_program("distutils_package_2", ''' - import snip_basic_verify2 - p = snip_basic_verify2.C.getpwuid(0) - assert snip_basic_verify2.ffi.string(p.pw_name) == b"root" - ''') - - def test_setuptools_module(self): - run_setup_and_program("setuptools_module", ''' - import snip_setuptools_verify - p = snip_setuptools_verify.C.getpwuid(0) - assert snip_setuptools_verify.ffi.string(p.pw_name) == b"root" - ''') - - def test_setuptools_package_1(self): - run_setup_and_program("setuptools_package_1", ''' - import snip_setuptools_verify1 - p = snip_setuptools_verify1.C.getpwuid(0) - assert snip_setuptools_verify1.ffi.string(p.pw_name) == b"root" - ''') - - def test_setuptools_package_2(self): - run_setup_and_program("setuptools_package_2", ''' - import snip_setuptools_verify2 - p = snip_setuptools_verify2.C.getpwuid(0) - assert snip_setuptools_verify2.ffi.string(p.pw_name) == b"root" - ''') - - def test_set_py_limited_api(self): - from cffi.setuptools_ext import _set_py_limited_api - try: - import setuptools - except ImportError as e: - py.test.skip(str(e)) - orig_version = setuptools.__version__ - expecting_limited_api = not hasattr(sys, 'gettotalrefcount') - try: - setuptools.__version__ = '26.0.0' - from setuptools import Extension - - kwds = _set_py_limited_api(Extension, {}) - assert kwds.get('py_limited_api', False) == expecting_limited_api - - setuptools.__version__ = '25.0' - kwds = _set_py_limited_api(Extension, {}) - assert kwds.get('py_limited_api', False) == False - - setuptools.__version__ = 'development' - kwds = _set_py_limited_api(Extension, {}) - assert kwds.get('py_limited_api', False) == expecting_limited_api - - finally: - setuptools.__version__ = orig_version diff --git a/testing/cffi1/__init__.py b/testing/cffi1/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/testing/cffi1/__init__.py +++ /dev/null diff --git a/testing/cffi1/test_cffi_binary.py b/testing/cffi1/test_cffi_binary.py deleted file mode 100644 index 7cfbace..0000000 --- a/testing/cffi1/test_cffi_binary.py +++ /dev/null @@ -1,22 +0,0 @@ -import py, sys, os -import _cffi_backend - -def test_no_unknown_exported_symbols(): - if not hasattr(_cffi_backend, '__file__'): - py.test.skip("_cffi_backend module is built-in") - if not sys.platform.startswith('linux'): - py.test.skip("linux-only") - g = os.popen("objdump -T '%s'" % _cffi_backend.__file__, 'r') - for line in g: - if not line.startswith('0'): - continue - if line[line.find(' ') + 1] == 'l': - continue - if '*UND*' in line: - continue - name = line.split()[-1] - if name.startswith('_') or name.startswith('.'): - continue - if name not in ('init_cffi_backend', 'PyInit__cffi_backend'): - raise Exception("Unexpected exported name %r" % (name,)) - g.close() diff --git a/testing/cffi1/test_commontypes.py b/testing/cffi1/test_commontypes.py deleted file mode 100644 index ea7ffde..0000000 --- a/testing/cffi1/test_commontypes.py +++ /dev/null @@ -1,34 +0,0 @@ -import py, os, cffi, re -import _cffi_backend - - -def getlines(): - try: - f = open(os.path.join(os.path.dirname(cffi.__file__), - '..', 'c', 'commontypes.c')) - except IOError: - py.test.skip("cannot find ../c/commontypes.c") - lines = [line for line in f.readlines() if line.strip().startswith('EQ(')] - f.close() - return lines - -def test_alphabetical_order(): - lines = getlines() - assert lines == sorted(lines) - -def test_dependencies(): - r = re.compile(r'EQ[(]"([^"]+)",(?:\s*"([A-Z0-9_]+)\s*[*]*"[)])?') - lines = getlines() - d = {} - for line in lines: - match = r.search(line) - if match is not None: - d[match.group(1)] = match.group(2) - for value in d.values(): - if value: - assert value in d - -def test_get_common_types(): - d = {} - _cffi_backend._get_common_types(d) - assert d["bool"] == "_Bool" diff --git a/testing/cffi1/test_dlopen.py b/testing/cffi1/test_dlopen.py deleted file mode 100644 index 26a2717..0000000 --- a/testing/cffi1/test_dlopen.py +++ /dev/null @@ -1,225 +0,0 @@ -import py -from cffi import FFI, VerificationError, CDefError -from cffi.recompiler import make_py_source -from testing.udir import udir - - -def test_simple(): - ffi = FFI() - ffi.cdef("int close(int); static const int BB = 42; extern int somevar;") - target = udir.join('test_simple.py') - make_py_source(ffi, 'test_simple', str(target)) - assert target.read() == r"""# auto-generated file -import _cffi_backend - -ffi = _cffi_backend.FFI('test_simple', - _version = 0x2601, - _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F', - _globals = (b'\xFF\xFF\xFF\x1FBB',42,b'\x00\x00\x00\x23close',0,b'\x00\x00\x01\x21somevar',0), -) -""" - -def test_global_constant(): - ffi = FFI() - ffi.cdef("static const long BB; static const float BF = 12;") - target = udir.join('test_valid_global_constant.py') - make_py_source(ffi, 'test_valid_global_constant', str(target)) - assert target.read() == r"""# auto-generated file -import _cffi_backend - -ffi = _cffi_backend.FFI('test_valid_global_constant', - _version = 0x2601, - _types = b'\x00\x00\x0D\x01\x00\x00\x09\x01', - _globals = (b'\x00\x00\x01\x25BB',0,b'\x00\x00\x00\x25BF',0), -) -""" - -def test_invalid_global_constant_3(): - ffi = FFI() - e = py.test.raises(CDefError, ffi.cdef, "#define BB 12.34") - assert str(e.value).startswith( - "only supports one of the following syntax:") - -def test_invalid_dotdotdot_in_macro(): - ffi = FFI() - ffi.cdef("#define FOO ...") - target = udir.join('test_invalid_dotdotdot_in_macro.py') - e = py.test.raises(VerificationError, make_py_source, ffi, - 'test_invalid_dotdotdot_in_macro', str(target)) - assert str(e.value) == ("macro FOO: cannot use the syntax '...' in " - "'#define FOO ...' when using the ABI mode") - -def test_typename(): - ffi = FFI() - ffi.cdef("typedef int foobar_t;") - target = udir.join('test_typename.py') - make_py_source(ffi, 'test_typename', str(target)) - assert target.read() == r"""# auto-generated file -import _cffi_backend - -ffi = _cffi_backend.FFI('test_typename', - _version = 0x2601, - _types = b'\x00\x00\x07\x01', - _typenames = (b'\x00\x00\x00\x00foobar_t',), -) -""" - -def test_enum(): - ffi = FFI() - ffi.cdef("enum myenum_e { AA, BB, CC=-42 };") - target = udir.join('test_enum.py') - make_py_source(ffi, 'test_enum', str(target)) - assert target.read() == r"""# auto-generated file -import _cffi_backend - -ffi = _cffi_backend.FFI('test_enum', - _version = 0x2601, - _types = b'\x00\x00\x00\x0B', - _globals = (b'\xFF\xFF\xFF\x0BAA',0,b'\xFF\xFF\xFF\x0BBB',1,b'\xFF\xFF\xFF\x0BCC',-42), - _enums = (b'\x00\x00\x00\x00\x00\x00\x00\x15myenum_e\x00AA,BB,CC',), -) -""" - -def test_struct(): - ffi = FFI() - ffi.cdef("struct foo_s { int a; signed char b[]; }; struct bar_s;") - target = udir.join('test_struct.py') - make_py_source(ffi, 'test_struct', str(target)) - assert target.read() == r"""# auto-generated file -import _cffi_backend - -ffi = _cffi_backend.FFI('test_struct', - _version = 0x2601, - _types = b'\x00\x00\x07\x01\x00\x00\x03\x01\x00\x00\x01\x07\x00\x00\x00\x09\x00\x00\x01\x09', - _struct_unions = ((b'\x00\x00\x00\x03\x00\x00\x00\x10bar_s',),(b'\x00\x00\x00\x04\x00\x00\x00\x02foo_s',b'\x00\x00\x00\x11a',b'\x00\x00\x02\x11b')), -) -""" - -def test_include(): - ffi = FFI() - ffi.cdef("#define ABC 123") - ffi.set_source('test_include', None) - target = udir.join('test_include.py') - make_py_source(ffi, 'test_include', str(target)) - assert target.read() == r"""# auto-generated file -import _cffi_backend - -ffi = _cffi_backend.FFI('test_include', - _version = 0x2601, - _types = b'', - _globals = (b'\xFF\xFF\xFF\x1FABC',123,), -) -""" - # - ffi2 = FFI() - ffi2.include(ffi) - target2 = udir.join('test2_include.py') - make_py_source(ffi2, 'test2_include', str(target2)) - assert target2.read() == r"""# auto-generated file -import _cffi_backend -from test_include import ffi as _ffi0 - -ffi = _cffi_backend.FFI('test2_include', - _version = 0x2601, - _types = b'', - _includes = (_ffi0,), -) -""" - -def test_negative_constant(): - ffi = FFI() - ffi.cdef("static const int BB = -42;") - target = udir.join('test_negative_constant.py') - make_py_source(ffi, 'test_negative_constant', str(target)) - assert target.read() == r"""# auto-generated file -import _cffi_backend - -ffi = _cffi_backend.FFI('test_negative_constant', - _version = 0x2601, - _types = b'', - _globals = (b'\xFF\xFF\xFF\x1FBB',-42,), -) -""" - -def test_struct_included(): - baseffi = FFI() - baseffi.cdef("struct foo_s { int x; };") - baseffi.set_source('test_struct_included_base', None) - # - ffi = FFI() - ffi.include(baseffi) - target = udir.join('test_struct_included.py') - make_py_source(ffi, 'test_struct_included', str(target)) - assert target.read() == r"""# auto-generated file -import _cffi_backend -from test_struct_included_base import ffi as _ffi0 - -ffi = _cffi_backend.FFI('test_struct_included', - _version = 0x2601, - _types = b'\x00\x00\x00\x09', - _struct_unions = ((b'\x00\x00\x00\x00\x00\x00\x00\x08foo_s',),), - _includes = (_ffi0,), -) -""" - -def test_no_cross_include(): - baseffi = FFI() - baseffi.set_source('test_no_cross_include_base', "..source..") - # - ffi = FFI() - ffi.include(baseffi) - target = udir.join('test_no_cross_include.py') - py.test.raises(VerificationError, make_py_source, - ffi, 'test_no_cross_include', str(target)) - -def test_array(): - ffi = FFI() - ffi.cdef("typedef int32_t my_array_t[42];") - target = udir.join('test_array.py') - make_py_source(ffi, 'test_array', str(target)) - assert target.read() == r"""# auto-generated file -import _cffi_backend - -ffi = _cffi_backend.FFI('test_array', - _version = 0x2601, - _types = b'\x00\x00\x15\x01\x00\x00\x00\x05\x00\x00\x00\x2A', - _typenames = (b'\x00\x00\x00\x01my_array_t',), -) -""" - -def test_array_overflow(): - ffi = FFI() - ffi.cdef("typedef int32_t my_array_t[3000000000];") - target = udir.join('test_array_overflow.py') - py.test.raises(OverflowError, make_py_source, - ffi, 'test_array_overflow', str(target)) - -def test_global_var(): - ffi = FFI() - ffi.cdef("extern int myglob;") - target = udir.join('test_global_var.py') - make_py_source(ffi, 'test_global_var', str(target)) - assert target.read() == r"""# auto-generated file -import _cffi_backend - -ffi = _cffi_backend.FFI('test_global_var', - _version = 0x2601, - _types = b'\x00\x00\x07\x01', - _globals = (b'\x00\x00\x00\x21myglob',0,), -) -""" - -def test_bitfield(): - ffi = FFI() - ffi.cdef("struct foo_s { int y:10; short x:5; };") - target = udir.join('test_bitfield.py') - make_py_source(ffi, 'test_bitfield', str(target)) - assert target.read() == r"""# auto-generated file -import _cffi_backend - -ffi = _cffi_backend.FFI('test_bitfield', - _version = 0x2601, - _types = b'\x00\x00\x07\x01\x00\x00\x05\x01\x00\x00\x00\x09', - _struct_unions = ((b'\x00\x00\x00\x02\x00\x00\x00\x02foo_s',b'\x00\x00\x00\x13\x00\x00\x00\x0Ay',b'\x00\x00\x01\x13\x00\x00\x00\x05x'),), -) -""" diff --git a/testing/cffi1/test_dlopen_unicode_literals.py b/testing/cffi1/test_dlopen_unicode_literals.py deleted file mode 100644 index e792866..0000000 --- a/testing/cffi1/test_dlopen_unicode_literals.py +++ /dev/null @@ -1,9 +0,0 @@ -import py, os - -s = """from __future__ import unicode_literals -""" - -with open(os.path.join(os.path.dirname(__file__), 'test_dlopen.py')) as f: - s += f.read() - -exec(py.code.compile(s)) diff --git a/testing/cffi1/test_ffi_obj.py b/testing/cffi1/test_ffi_obj.py deleted file mode 100644 index 0d29290..0000000 --- a/testing/cffi1/test_ffi_obj.py +++ /dev/null @@ -1,536 +0,0 @@ -import py, sys -import pytest -import _cffi_backend as _cffi1_backend - - -def test_ffi_new(): - ffi = _cffi1_backend.FFI() - p = ffi.new("int *") - p[0] = -42 - assert p[0] == -42 - assert type(ffi) is ffi.__class__ is _cffi1_backend.FFI - -def test_ffi_subclass(): - class FOO(_cffi1_backend.FFI): - def __init__(self, x): - self.x = x - foo = FOO(42) - assert foo.x == 42 - p = foo.new("int *") - assert p[0] == 0 - assert type(foo) is foo.__class__ is FOO - -def test_ffi_no_argument(): - py.test.raises(TypeError, _cffi1_backend.FFI, 42) - -def test_ffi_cache_type(): - ffi = _cffi1_backend.FFI() - t1 = ffi.typeof("int **") - t2 = ffi.typeof("int *") - assert t2.item is t1.item.item - assert t2 is t1.item - assert ffi.typeof("int[][10]") is ffi.typeof("int[][10]") - assert ffi.typeof("int(*)()") is ffi.typeof("int(*)()") - -def test_ffi_type_not_immortal(): - import weakref, gc - ffi = _cffi1_backend.FFI() - t1 = ffi.typeof("int **") - t2 = ffi.typeof("int *") - w1 = weakref.ref(t1) - w2 = weakref.ref(t2) - del t1, ffi - gc.collect() - assert w1() is None - assert w2() is t2 - ffi = _cffi1_backend.FFI() - assert ffi.typeof(ffi.new("int **")[0]) is t2 - # - ffi = _cffi1_backend.FFI() - t1 = ffi.typeof("int ***") - t2 = ffi.typeof("int **") - w1 = weakref.ref(t1) - w2 = weakref.ref(t2) - del t2, ffi - gc.collect() - assert w1() is t1 - assert w2() is not None # kept alive by t1 - ffi = _cffi1_backend.FFI() - assert ffi.typeof("int * *") is t1.item - -def test_ffi_cache_type_globally(): - ffi1 = _cffi1_backend.FFI() - ffi2 = _cffi1_backend.FFI() - t1 = ffi1.typeof("int *") - t2 = ffi2.typeof("int *") - assert t1 is t2 - -def test_ffi_invalid(): - ffi = _cffi1_backend.FFI() - # array of 10 times an "int[]" is invalid - py.test.raises(ValueError, ffi.typeof, "int[10][]") - -def test_ffi_docstrings(): - # check that all methods of the FFI class have a docstring. - check_type = type(_cffi1_backend.FFI.new) - for methname in dir(_cffi1_backend.FFI): - if not methname.startswith('_'): - method = getattr(_cffi1_backend.FFI, methname) - if isinstance(method, check_type): - assert method.__doc__, "method FFI.%s() has no docstring" % ( - methname,) - -def test_ffi_NULL(): - NULL = _cffi1_backend.FFI.NULL - assert _cffi1_backend.FFI().typeof(NULL).cname == "void *" - -def test_ffi_no_attr(): - ffi = _cffi1_backend.FFI() - with pytest.raises(AttributeError): - ffi.no_such_name - with pytest.raises(AttributeError): - ffi.no_such_name = 42 - with pytest.raises(AttributeError): - del ffi.no_such_name - -def test_ffi_string(): - ffi = _cffi1_backend.FFI() - p = ffi.new("char[]", init=b"foobar\x00baz") - assert ffi.string(p) == b"foobar" - assert ffi.string(cdata=p, maxlen=3) == b"foo" - -def test_ffi_errno(): - # xxx not really checking errno, just checking that we can read/write it - ffi = _cffi1_backend.FFI() - ffi.errno = 42 - assert ffi.errno == 42 - -def test_ffi_alignof(): - ffi = _cffi1_backend.FFI() - assert ffi.alignof("int") == 4 - assert ffi.alignof("int[]") == 4 - assert ffi.alignof("int[41]") == 4 - assert ffi.alignof("short[41]") == 2 - assert ffi.alignof(ffi.new("int[41]")) == 4 - assert ffi.alignof(ffi.new("int[]", 41)) == 4 - -def test_ffi_sizeof(): - ffi = _cffi1_backend.FFI() - assert ffi.sizeof("int") == 4 - py.test.raises(ffi.error, ffi.sizeof, "int[]") - assert ffi.sizeof("int[41]") == 41 * 4 - assert ffi.sizeof(ffi.new("int[41]")) == 41 * 4 - assert ffi.sizeof(ffi.new("int[]", 41)) == 41 * 4 - -def test_ffi_callback(): - ffi = _cffi1_backend.FFI() - assert ffi.callback("int(int)", lambda x: x + 42)(10) == 52 - assert ffi.callback("int(*)(int)", lambda x: x + 42)(10) == 52 - assert ffi.callback("int(int)", lambda x: x + "", -66)(10) == -66 - assert ffi.callback("int(int)", lambda x: x + "", error=-66)(10) == -66 - -def test_ffi_callback_decorator(): - ffi = _cffi1_backend.FFI() - assert ffi.callback(ffi.typeof("int(*)(int)"))(lambda x: x + 42)(10) == 52 - deco = ffi.callback("int(int)", error=-66) - assert deco(lambda x: x + "")(10) == -66 - assert deco(lambda x: x + 42)(10) == 52 - -def test_ffi_callback_onerror(): - ffi = _cffi1_backend.FFI() - seen = [] - def oops(*args): - seen.append(args) - - @ffi.callback("int(int)", onerror=oops) - def fn1(x): - return x + "" - assert fn1(10) == 0 - - @ffi.callback("int(int)", onerror=oops, error=-66) - def fn2(x): - return x + "" - assert fn2(10) == -66 - - assert len(seen) == 2 - exc, val, tb = seen[0] - assert exc is TypeError - assert isinstance(val, TypeError) - assert tb.tb_frame.f_code.co_name == "fn1" - exc, val, tb = seen[1] - assert exc is TypeError - assert isinstance(val, TypeError) - assert tb.tb_frame.f_code.co_name == "fn2" - # - py.test.raises(TypeError, ffi.callback, "int(int)", - lambda x: x, onerror=42) # <- not callable - -def test_ffi_getctype(): - ffi = _cffi1_backend.FFI() - assert ffi.getctype("int") == "int" - assert ffi.getctype("int", 'x') == "int x" - assert ffi.getctype("int*") == "int *" - assert ffi.getctype("int*", '') == "int *" - assert ffi.getctype("int*", 'x') == "int * x" - assert ffi.getctype("int", '*') == "int *" - assert ffi.getctype("int", replace_with=' * x ') == "int * x" - assert ffi.getctype(ffi.typeof("int*"), '*') == "int * *" - assert ffi.getctype("int", '[5]') == "int[5]" - assert ffi.getctype("int[5]", '[6]') == "int[6][5]" - assert ffi.getctype("int[5]", '(*)') == "int(*)[5]" - # special-case for convenience: automatically put '()' around '*' - assert ffi.getctype("int[5]", '*') == "int(*)[5]" - assert ffi.getctype("int[5]", '*foo') == "int(*foo)[5]" - assert ffi.getctype("int[5]", ' ** foo ') == "int(** foo)[5]" - -def test_addressof(): - ffi = _cffi1_backend.FFI() - a = ffi.new("int[10]") - b = ffi.addressof(a, 5) - b[2] = -123 - assert a[7] == -123 - -def test_handle(): - ffi = _cffi1_backend.FFI() - x = [2, 4, 6] - xp = ffi.new_handle(x) - assert ffi.typeof(xp) == ffi.typeof("void *") - assert ffi.from_handle(xp) is x - yp = ffi.new_handle([6, 4, 2]) - assert ffi.from_handle(yp) == [6, 4, 2] - -def test_handle_unique(): - ffi = _cffi1_backend.FFI() - assert ffi.new_handle(None) is not ffi.new_handle(None) - assert ffi.new_handle(None) != ffi.new_handle(None) - -def test_ffi_cast(): - ffi = _cffi1_backend.FFI() - assert ffi.cast("int(*)(int)", 0) == ffi.NULL - ffi.callback("int(int)") # side-effect of registering this string - py.test.raises(ffi.error, ffi.cast, "int(int)", 0) - -def test_ffi_invalid_type(): - ffi = _cffi1_backend.FFI() - e = py.test.raises(ffi.error, ffi.cast, "", 0) - assert str(e.value) == ("identifier expected\n" - "\n" - "^") - e = py.test.raises(ffi.error, ffi.cast, "struct struct", 0) - assert str(e.value) == ("struct or union name expected\n" - "struct struct\n" - " ^") - e = py.test.raises(ffi.error, ffi.cast, "struct never_heard_of_s", 0) - assert str(e.value) == ("undefined struct/union name\n" - "struct never_heard_of_s\n" - " ^") - e = py.test.raises(ffi.error, ffi.cast, "\t\n\x01\x1f~\x7f\x80\xff", 0) - marks = "?" if sys.version_info < (3,) else "??" - assert str(e.value) == ("identifier expected\n" - " ??~?%s%s\n" - " ^" % (marks, marks)) - e = py.test.raises(ffi.error, ffi.cast, "X" * 600, 0) - assert str(e.value) == ("undefined type name") - -def test_ffi_buffer(): - ffi = _cffi1_backend.FFI() - a = ffi.new("signed char[]", [5, 6, 7]) - assert ffi.buffer(a)[:] == b'\x05\x06\x07' - assert ffi.buffer(cdata=a, size=2)[:] == b'\x05\x06' - assert type(ffi.buffer(a)) is ffi.buffer - -def test_ffi_from_buffer(): - import array - ffi = _cffi1_backend.FFI() - a = array.array('H', [10000, 20000, 30000, 40000]) - c = ffi.from_buffer(a) - assert ffi.typeof(c) is ffi.typeof("char[]") - assert len(c) == 8 - ffi.cast("unsigned short *", c)[1] += 500 - assert list(a) == [10000, 20500, 30000, 40000] - py.test.raises(TypeError, ffi.from_buffer, a, True) - assert c == ffi.from_buffer("char[]", a, True) - assert c == ffi.from_buffer(a, require_writable=True) - # - c = ffi.from_buffer("unsigned short[]", a) - assert len(c) == 4 - assert c[1] == 20500 - # - c = ffi.from_buffer("unsigned short[2][2]", a) - assert len(c) == 2 - assert len(c[0]) == 2 - assert c[0][1] == 20500 - # - p = ffi.from_buffer(b"abcd") - assert p[2] == b"c" - # - assert p == ffi.from_buffer(b"abcd", require_writable=False) - py.test.raises((TypeError, BufferError), ffi.from_buffer, - "char[]", b"abcd", True) - py.test.raises((TypeError, BufferError), ffi.from_buffer, b"abcd", - require_writable=True) - -def test_memmove(): - ffi = _cffi1_backend.FFI() - p = ffi.new("short[]", [-1234, -2345, -3456, -4567, -5678]) - ffi.memmove(p, p + 1, 4) - assert list(p) == [-2345, -3456, -3456, -4567, -5678] - p[2] = 999 - ffi.memmove(p + 2, p, 6) - assert list(p) == [-2345, -3456, -2345, -3456, 999] - ffi.memmove(p + 4, ffi.new("char[]", b"\x71\x72"), 2) - if sys.byteorder == 'little': - assert list(p) == [-2345, -3456, -2345, -3456, 0x7271] - else: - assert list(p) == [-2345, -3456, -2345, -3456, 0x7172] - -def test_memmove_buffer(): - import array - ffi = _cffi1_backend.FFI() - a = array.array('H', [10000, 20000, 30000]) - p = ffi.new("short[]", 5) - ffi.memmove(p, a, 6) - assert list(p) == [10000, 20000, 30000, 0, 0] - ffi.memmove(p + 1, a, 6) - assert list(p) == [10000, 10000, 20000, 30000, 0] - b = array.array('h', [-1000, -2000, -3000]) - ffi.memmove(b, a, 4) - assert b.tolist() == [10000, 20000, -3000] - assert a.tolist() == [10000, 20000, 30000] - p[0] = 999 - p[1] = 998 - p[2] = 997 - p[3] = 996 - p[4] = 995 - ffi.memmove(b, p, 2) - assert b.tolist() == [999, 20000, -3000] - ffi.memmove(b, p + 2, 4) - assert b.tolist() == [997, 996, -3000] - p[2] = -p[2] - p[3] = -p[3] - ffi.memmove(b, p + 2, 6) - assert b.tolist() == [-997, -996, 995] - -def test_memmove_readonly_readwrite(): - ffi = _cffi1_backend.FFI() - p = ffi.new("signed char[]", 5) - ffi.memmove(p, b"abcde", 3) - assert list(p) == [ord("a"), ord("b"), ord("c"), 0, 0] - ffi.memmove(p, bytearray(b"ABCDE"), 2) - assert list(p) == [ord("A"), ord("B"), ord("c"), 0, 0] - py.test.raises((TypeError, BufferError), ffi.memmove, b"abcde", p, 3) - ba = bytearray(b"xxxxx") - ffi.memmove(dest=ba, src=p, n=3) - assert ba == bytearray(b"ABcxx") - -def test_ffi_types(): - CData = _cffi1_backend.FFI.CData - CType = _cffi1_backend.FFI.CType - ffi = _cffi1_backend.FFI() - assert isinstance(ffi.cast("int", 42), CData) - assert isinstance(ffi.typeof("int"), CType) - -def test_ffi_getwinerror(): - if sys.platform != "win32": - py.test.skip("for windows") - ffi = _cffi1_backend.FFI() - n = (1 << 29) + 42 - code, message = ffi.getwinerror(code=n) - assert code == n - -def test_ffi_new_allocator_1(): - ffi = _cffi1_backend.FFI() - alloc1 = ffi.new_allocator() - alloc2 = ffi.new_allocator(should_clear_after_alloc=False) - for retry in range(100): - p1 = alloc1("int[10]") - p2 = alloc2("int[10]") - combination = 0 - for i in range(10): - assert p1[i] == 0 - combination |= p2[i] - p1[i] = -42 - p2[i] = -43 - if combination != 0: - break - del p1, p2 - import gc; gc.collect() - else: - raise AssertionError("cannot seem to get an int[10] not " - "completely cleared") - -def test_ffi_new_allocator_2(): - ffi = _cffi1_backend.FFI() - seen = [] - def myalloc(size): - seen.append(size) - return ffi.new("char[]", b"X" * size) - def myfree(raw): - seen.append(raw) - alloc1 = ffi.new_allocator(myalloc, myfree) - alloc2 = ffi.new_allocator(alloc=myalloc, free=myfree, - should_clear_after_alloc=False) - p1 = alloc1("int[10]") - p2 = alloc2("int[]", 10) - assert seen == [40, 40] - assert ffi.typeof(p1) == ffi.typeof("int[10]") - assert ffi.sizeof(p1) == 40 - assert ffi.typeof(p2) == ffi.typeof("int[]") - assert ffi.sizeof(p2) == 40 - assert p1[5] == 0 - assert p2[6] == ord('X') * 0x01010101 - raw1 = ffi.cast("char *", p1) - raw2 = ffi.cast("char *", p2) - del p1, p2 - retries = 0 - while len(seen) != 4: - retries += 1 - assert retries <= 5 - import gc; gc.collect() - assert (seen == [40, 40, raw1, raw2] or - seen == [40, 40, raw2, raw1]) - assert repr(seen[2]) == "<cdata 'char[]' owning 41 bytes>" - assert repr(seen[3]) == "<cdata 'char[]' owning 41 bytes>" - -def test_ffi_new_allocator_3(): - ffi = _cffi1_backend.FFI() - seen = [] - def myalloc(size): - seen.append(size) - return ffi.new("char[]", b"X" * size) - alloc1 = ffi.new_allocator(myalloc) # no 'free' - p1 = alloc1("int[10]") - assert seen == [40] - assert ffi.typeof(p1) == ffi.typeof("int[10]") - assert ffi.sizeof(p1) == 40 - assert p1[5] == 0 - -def test_ffi_new_allocator_4(): - ffi = _cffi1_backend.FFI() - py.test.raises(TypeError, ffi.new_allocator, free=lambda x: None) - # - def myalloc2(size): - raise LookupError - alloc2 = ffi.new_allocator(myalloc2) - py.test.raises(LookupError, alloc2, "int[5]") - # - def myalloc3(size): - return 42 - alloc3 = ffi.new_allocator(myalloc3) - e = py.test.raises(TypeError, alloc3, "int[5]") - assert str(e.value) == "alloc() must return a cdata object (got int)" - # - def myalloc4(size): - return ffi.cast("int", 42) - alloc4 = ffi.new_allocator(myalloc4) - e = py.test.raises(TypeError, alloc4, "int[5]") - assert str(e.value) == "alloc() must return a cdata pointer, not 'int'" - # - def myalloc5(size): - return ffi.NULL - alloc5 = ffi.new_allocator(myalloc5) - py.test.raises(MemoryError, alloc5, "int[5]") - -def test_bool_issue228(): - ffi = _cffi1_backend.FFI() - fntype = ffi.typeof("int(*callback)(bool is_valid)") - assert repr(fntype.args[0]) == "<ctype '_Bool'>" - -def test_FILE_issue228(): - fntype1 = _cffi1_backend.FFI().typeof("FILE *") - fntype2 = _cffi1_backend.FFI().typeof("FILE *") - assert repr(fntype1) == "<ctype 'FILE *'>" - assert fntype1 is fntype2 - -def test_cast_from_int_type_to_bool(): - ffi = _cffi1_backend.FFI() - for basetype in ['char', 'short', 'int', 'long', 'long long']: - for sign in ['signed', 'unsigned']: - type = '%s %s' % (sign, basetype) - assert int(ffi.cast("_Bool", ffi.cast(type, 42))) == 1 - assert int(ffi.cast("bool", ffi.cast(type, 42))) == 1 - assert int(ffi.cast("_Bool", ffi.cast(type, 0))) == 0 - -def test_init_once(): - def do_init(): - seen.append(1) - return 42 - ffi = _cffi1_backend.FFI() - seen = [] - for i in range(3): - res = ffi.init_once(do_init, "tag1") - assert res == 42 - assert seen == [1] - for i in range(3): - res = ffi.init_once(do_init, "tag2") - assert res == 42 - assert seen == [1, 1] - -def test_init_once_multithread(): - if sys.version_info < (3,): - import thread - else: - import _thread as thread - import time - # - def do_init(): - print('init!') - seen.append('init!') - time.sleep(1) - seen.append('init done') - print('init done') - return 7 - ffi = _cffi1_backend.FFI() - seen = [] - for i in range(6): - def f(): - res = ffi.init_once(do_init, "tag") - seen.append(res) - thread.start_new_thread(f, ()) - time.sleep(1.5) - assert seen == ['init!', 'init done'] + 6 * [7] - -def test_init_once_failure(): - def do_init(): - seen.append(1) - raise ValueError - ffi = _cffi1_backend.FFI() - seen = [] - for i in range(5): - py.test.raises(ValueError, ffi.init_once, do_init, "tag") - assert seen == [1] * (i + 1) - -def test_init_once_multithread_failure(): - if sys.version_info < (3,): - import thread - else: - import _thread as thread - import time - def do_init(): - seen.append('init!') - time.sleep(1) - seen.append('oops') - raise ValueError - ffi = _cffi1_backend.FFI() - seen = [] - for i in range(3): - def f(): - py.test.raises(ValueError, ffi.init_once, do_init, "tag") - thread.start_new_thread(f, ()) - i = 0 - while len(seen) < 6: - i += 1 - assert i < 20 - time.sleep(0.51) - assert seen == ['init!', 'oops'] * 3 - -def test_unpack(): - ffi = _cffi1_backend.FFI() - p = ffi.new("char[]", b"abc\x00def") - assert ffi.unpack(p+1, 7) == b"bc\x00def\x00" - p = ffi.new("int[]", [-123456789]) - assert ffi.unpack(p, 1) == [-123456789] - -def test_negative_array_size(): - ffi = _cffi1_backend.FFI() - py.test.raises(ffi.error, ffi.cast, "int[-5]", 0) diff --git a/testing/cffi1/test_function_args.py b/testing/cffi1/test_function_args.py deleted file mode 100644 index 30c6fed..0000000 --- a/testing/cffi1/test_function_args.py +++ /dev/null @@ -1,208 +0,0 @@ -import pytest, sys -try: - # comment out the following line to run this test. - # the latest on x86-64 linux: https://github.com/libffi/libffi/issues/574 - if sys.platform != 'win32': - raise ImportError("this test is skipped because it keeps finding " - "failures in libffi, instead of cffi") - - from hypothesis import given, settings, example - from hypothesis import strategies as st -except ImportError as e: - e1 = e - def test_types(): - pytest.skip(str(e1)) -else: - - from cffi import FFI - import sys, random - from .test_recompiler import verify - - ALL_PRIMITIVES = [ - 'unsigned char', - 'short', - 'int', - 'long', - 'long long', - 'float', - 'double', - #'long double', --- on x86 it can give libffi crashes - ] - def _make_struct(s): - return st.lists(s, min_size=1) - types = st.one_of(st.sampled_from(ALL_PRIMITIVES), - st.lists(st.sampled_from(ALL_PRIMITIVES), min_size=1)) - # NB. 'types' could be st.recursive instead, but it doesn't - # really seem useful - - def draw_primitive(ffi, typename): - value = random.random() * 2**40 - if typename != 'long double': - return ffi.cast(typename, value) - else: - return value - - TEST_RUN_COUNTER = 0 - - - @given(st.lists(types), types) - @settings(max_examples=100, deadline=5000) # 5000ms - def test_types(tp_args, tp_result): - global TEST_RUN_COUNTER - print(tp_args, tp_result) - cdefs = [] - structs = {} - - def build_type(tp): - if type(tp) is list: - field_types = [build_type(tp1) for tp1 in tp] - fields = ['%s f%d;' % (ftp, j) - for (j, ftp) in enumerate(field_types)] - fields = '\n '.join(fields) - name = 's%d' % len(cdefs) - cdefs.append("typedef struct {\n %s\n} %s;" % (fields, name)) - structs[name] = field_types - return name - else: - return tp - - args = [build_type(tp) for tp in tp_args] - result = build_type(tp_result) - - TEST_RUN_COUNTER += 1 - signature = "%s testfargs(%s)" % (result, - ', '.join(['%s a%d' % (arg, i) for (i, arg) in enumerate(args)]) - or 'void') - - source = list(cdefs) - - cdefs.append("%s;" % signature) - cdefs.append("extern %s testfargs_result;" % result) - for i, arg in enumerate(args): - cdefs.append("extern %s testfargs_arg%d;" % (arg, i)) - source.append("%s testfargs_result;" % result) - for i, arg in enumerate(args): - source.append("%s testfargs_arg%d;" % (arg, i)) - source.append(signature) - source.append("{") - for i, arg in enumerate(args): - source.append(" testfargs_arg%d = a%d;" % (i, i)) - source.append(" return testfargs_result;") - source.append("}") - - typedef_line = "typedef %s;" % (signature.replace('testfargs', - '(*mycallback_t)'),) - assert signature.endswith(')') - sig_callback = "%s testfcallback(mycallback_t callback)" % result - cdefs.append(typedef_line) - cdefs.append("%s;" % sig_callback) - source.append(typedef_line) - source.append(sig_callback) - source.append("{") - source.append(" return callback(%s);" % - ', '.join(["testfargs_arg%d" % i for i in range(len(args))])) - source.append("}") - - ffi = FFI() - ffi.cdef("\n".join(cdefs)) - lib = verify(ffi, 'test_function_args_%d' % TEST_RUN_COUNTER, - "\n".join(source), no_cpp=True) - - # when getting segfaults, enable this: - if False: - from testing.udir import udir - import subprocess - f = open(str(udir.join('run1.py')), 'w') - f.write('import sys; sys.path = %r\n' % (sys.path,)) - f.write('from _CFFI_test_function_args_%d import ffi, lib\n' % - TEST_RUN_COUNTER) - for i in range(len(args)): - f.write('a%d = ffi.new("%s *")\n' % (i, args[i])) - aliststr = ', '.join(['a%d[0]' % i for i in range(len(args))]) - f.write('lib.testfargs(%s)\n' % aliststr) - f.write('ffi.addressof(lib, "testfargs")(%s)\n' % aliststr) - f.close() - print("checking for segfault for direct call...") - rc = subprocess.call([sys.executable, 'run1.py'], cwd=str(udir)) - assert rc == 0, rc - - def make_arg(tp): - if tp in structs: - return [make_arg(tp1) for tp1 in structs[tp]] - else: - return draw_primitive(ffi, tp) - - passed_args = [make_arg(arg) for arg in args] - returned_value = make_arg(result) - - def write(p, v): - if type(v) is list: - for i, v1 in enumerate(v): - write(ffi.addressof(p, 'f%d' % i), v1) - else: - p[0] = v - - write(ffi.addressof(lib, 'testfargs_result'), returned_value) - - ## CALL forcing libffi - print("CALL forcing libffi") - received_return = ffi.addressof(lib, 'testfargs')(*passed_args) - ## - - _tp_long_double = ffi.typeof("long double") - def check(p, v): - if type(v) is list: - for i, v1 in enumerate(v): - check(ffi.addressof(p, 'f%d' % i), v1) - else: - if ffi.typeof(p).item is _tp_long_double: - assert ffi.cast("double", p[0]) == v - else: - assert p[0] == v - - for i, arg in enumerate(passed_args): - check(ffi.addressof(lib, 'testfargs_arg%d' % i), arg) - ret = ffi.new(result + "*", received_return) - check(ret, returned_value) - - ## CALLBACK - def expand(value): - if isinstance(value, ffi.CData): - t = ffi.typeof(value) - if t is _tp_long_double: - return float(ffi.cast("double", value)) - return [expand(getattr(value, 'f%d' % i)) - for i in range(len(t.fields))] - else: - return value - - # when getting segfaults, enable this: - if False: - from testing.udir import udir - import subprocess - f = open(str(udir.join('run1.py')), 'w') - f.write('import sys; sys.path = %r\n' % (sys.path,)) - f.write('from _CFFI_test_function_args_%d import ffi, lib\n' % - TEST_RUN_COUNTER) - f.write('def callback(*args): return ffi.new("%s *")[0]\n' % result) - f.write('fptr = ffi.callback("%s(%s)", callback)\n' % (result, - ','.join(args))) - f.write('print(lib.testfcallback(fptr))\n') - f.close() - print("checking for segfault for callback...") - rc = subprocess.call([sys.executable, 'run1.py'], cwd=str(udir)) - assert rc == 0, rc - - seen_args = [] - def callback(*args): - seen_args.append([expand(arg) for arg in args]) - return returned_value - - fptr = ffi.callback("%s(%s)" % (result, ','.join(args)), callback) - print("CALL with callback") - received_return = lib.testfcallback(fptr) - - assert len(seen_args) == 1 - assert passed_args == seen_args[0] - ret = ffi.new(result + "*", received_return) - check(ret, returned_value) diff --git a/testing/cffi1/test_new_ffi_1.py b/testing/cffi1/test_new_ffi_1.py deleted file mode 100644 index 640830b..0000000 --- a/testing/cffi1/test_new_ffi_1.py +++ /dev/null @@ -1,1831 +0,0 @@ -import py -import pytest -import platform, imp -import sys, os, ctypes -import cffi -from testing.udir import udir -from testing.support import * -from cffi.recompiler import recompile -from cffi.cffi_opcode import PRIMITIVE_TO_INDEX - -SIZE_OF_INT = ctypes.sizeof(ctypes.c_int) -SIZE_OF_LONG = ctypes.sizeof(ctypes.c_long) -SIZE_OF_SHORT = ctypes.sizeof(ctypes.c_short) -SIZE_OF_PTR = ctypes.sizeof(ctypes.c_void_p) -SIZE_OF_WCHAR = ctypes.sizeof(ctypes.c_wchar) - - -def setup_module(): - global ffi, construction_params - ffi1 = cffi.FFI() - DEFS = r""" - struct repr { short a, b, c; }; - struct simple { int a; short b, c; }; - struct array { int a[2]; char b[3]; }; - struct recursive { int value; struct recursive *next; }; - union simple_u { int a; short b, c; }; - union init_u { char a; int b; }; - struct four_s { int a; short b, c, d; }; - union four_u { int a; short b, c, d; }; - struct string { const char *name; }; - struct ustring { const wchar_t *name; }; - struct voidp { void *p; int *q; short *r; }; - struct ab { int a, b; }; - struct abc { int a, b, c; }; - - /* don't use A0, B0, CC0, D0 because termios.h might be included - and it has its own #defines for these names */ - enum foq { cffiA0, cffiB0, cffiCC0, cffiD0 }; - enum bar { A1, B1=-2, CC1, D1, E1 }; - enum baz { A2=0x1000, B2=0x2000 }; - enum foo2 { A3, B3, C3, D3 }; - struct bar_with_e { enum foo2 e; }; - enum noncont { A4, B4=42, C4 }; - enum etypes {A5='!', B5='\'', C5=0x10, D5=010, E5=- 0x10, F5=-010}; - typedef enum { Value0 = 0 } e_t, *pe_t; - enum e_noninj { AA3=0, BB3=0, CC3=0, DD3=0 }; - enum e_prev { AA4, BB4=2, CC4=4, DD4=BB4, EE4, FF4=CC4, GG4=FF4 }; - - struct nesting { struct abc d, e; }; - struct array2 { int a, b; int c[99]; }; - struct align { char a; short b; char c; }; - struct bitfield { int a:10, b:20, c:3; }; - typedef enum { AA2, BB2, CC2 } foo_e_t; - typedef struct { foo_e_t f:2; } bfenum_t; - typedef struct { int a; } anon_foo_t; - typedef struct { char b, c; } anon_bar_t; - typedef struct named_foo_s { int a; } named_foo_t, *named_foo_p; - typedef struct { int a; } unnamed_foo_t, *unnamed_foo_p; - struct nonpacked { char a; int b; }; - struct array0 { int len; short data[0]; }; - struct array_no_length { int x; int a[]; }; - - struct nested_anon { - struct { int a, b; }; - union { int c, d; }; - }; - struct nested_field_ofs_s { - struct { int a; char b; }; - union { char c; }; - }; - union nested_anon_u { - struct { int a, b; }; - union { int c, d; }; - }; - struct abc50 { int a, b; int c[50]; }; - struct ints_and_bitfield { int a,b,c,d,e; int x:1; }; - """ - DEFS_PACKED = """ - struct is_packed { char a; int b; } /*here*/; - """ - if sys.platform == "win32": - DEFS = DEFS.replace('data[0]', 'data[1]') # not supported - CCODE = (DEFS + "\n#pragma pack(push,1)\n" + DEFS_PACKED + - "\n#pragma pack(pop)\n") - else: - CCODE = (DEFS + - DEFS_PACKED.replace('/*here*/', '__attribute__((packed))')) - - ffi1.cdef(DEFS) - ffi1.cdef(DEFS_PACKED, packed=True) - ffi1.set_source("test_new_ffi_1", CCODE) - - outputfilename = recompile(ffi1, "test_new_ffi_1", CCODE, - tmpdir=str(udir)) - module = imp.load_dynamic("test_new_ffi_1", outputfilename) - ffi = module.ffi - construction_params = (ffi1, CCODE) - - -class TestNewFFI1: - - def test_integer_ranges(self): - for (c_type, size) in [('char', 1), - ('short', 2), - ('short int', 2), - ('', 4), - ('int', 4), - ('long', SIZE_OF_LONG), - ('long int', SIZE_OF_LONG), - ('long long', 8), - ('long long int', 8), - ]: - for unsigned in [None, False, True]: - c_decl = {None: '', - False: 'signed ', - True: 'unsigned '}[unsigned] + c_type - if c_decl == 'char' or c_decl == '': - continue - self._test_int_type(ffi, c_decl, size, unsigned) - - def test_fixedsize_int(self): - for size in [1, 2, 4, 8]: - self._test_int_type(ffi, 'int%d_t' % (8*size), size, False) - self._test_int_type(ffi, 'uint%d_t' % (8*size), size, True) - self._test_int_type(ffi, 'intptr_t', SIZE_OF_PTR, False) - self._test_int_type(ffi, 'uintptr_t', SIZE_OF_PTR, True) - self._test_int_type(ffi, 'ptrdiff_t', SIZE_OF_PTR, False) - self._test_int_type(ffi, 'size_t', SIZE_OF_PTR, True) - self._test_int_type(ffi, 'ssize_t', SIZE_OF_PTR, False) - - def _test_int_type(self, ffi, c_decl, size, unsigned): - if unsigned: - min = 0 - max = (1 << (8*size)) - 1 - else: - min = -(1 << (8*size-1)) - max = (1 << (8*size-1)) - 1 - min = int(min) - max = int(max) - p = ffi.cast(c_decl, min) - assert p == min - assert bool(p) is bool(min) - assert int(p) == min - p = ffi.cast(c_decl, max) - assert int(p) == max - p = ffi.cast(c_decl, long(max)) - assert int(p) == max - q = ffi.cast(c_decl, min - 1) - assert ffi.typeof(q) is ffi.typeof(p) and int(q) == max - q = ffi.cast(c_decl, long(min - 1)) - assert ffi.typeof(q) is ffi.typeof(p) and int(q) == max - assert q == p - assert int(q) == int(p) - assert hash(q) == hash(p) - c_decl_ptr = '%s *' % c_decl - py.test.raises(OverflowError, ffi.new, c_decl_ptr, min - 1) - py.test.raises(OverflowError, ffi.new, c_decl_ptr, max + 1) - py.test.raises(OverflowError, ffi.new, c_decl_ptr, long(min - 1)) - py.test.raises(OverflowError, ffi.new, c_decl_ptr, long(max + 1)) - assert ffi.new(c_decl_ptr, min)[0] == min - assert ffi.new(c_decl_ptr, max)[0] == max - assert ffi.new(c_decl_ptr, long(min))[0] == min - assert ffi.new(c_decl_ptr, long(max))[0] == max - - def test_new_unsupported_type(self): - e = py.test.raises(TypeError, ffi.new, "int") - assert str(e.value) == "expected a pointer or array ctype, got 'int'" - - def test_new_single_integer(self): - p = ffi.new("int *") # similar to ffi.new("int[1]") - assert p[0] == 0 - p[0] = -123 - assert p[0] == -123 - p = ffi.new("int *", -42) - assert p[0] == -42 - assert repr(p) == "<cdata 'int *' owning %d bytes>" % SIZE_OF_INT - - def test_new_array_no_arg(self): - p = ffi.new("int[10]") - # the object was zero-initialized: - for i in range(10): - assert p[i] == 0 - - def test_array_indexing(self): - p = ffi.new("int[10]") - p[0] = 42 - p[9] = 43 - assert p[0] == 42 - assert p[9] == 43 - with pytest.raises(IndexError): - p[10] - with pytest.raises(IndexError): - p[10] = 44 - with pytest.raises(IndexError): - p[-1] - with pytest.raises(IndexError): - p[-1] = 44 - - def test_new_array_args(self): - # this tries to be closer to C: where we say "int x[5] = {10, 20, ..}" - # then here we must enclose the items in a list - p = ffi.new("int[5]", [10, 20, 30, 40, 50]) - assert p[0] == 10 - assert p[1] == 20 - assert p[2] == 30 - assert p[3] == 40 - assert p[4] == 50 - p = ffi.new("int[4]", [25]) - assert p[0] == 25 - assert p[1] == 0 # follow C convention rather than LuaJIT's - assert p[2] == 0 - assert p[3] == 0 - p = ffi.new("int[4]", [ffi.cast("int", -5)]) - assert p[0] == -5 - assert repr(p) == "<cdata 'int[4]' owning %d bytes>" % (4*SIZE_OF_INT) - - def test_new_array_varsize(self): - p = ffi.new("int[]", 10) # a single integer is the length - assert p[9] == 0 - with pytest.raises(IndexError): - p[10] - # - py.test.raises(TypeError, ffi.new, "int[]") - # - p = ffi.new("int[]", [-6, -7]) # a list is all the items, like C - assert p[0] == -6 - assert p[1] == -7 - with pytest.raises(IndexError): - p[2] - assert repr(p) == "<cdata 'int[]' owning %d bytes>" % (2*SIZE_OF_INT) - # - p = ffi.new("int[]", 0) - with pytest.raises(IndexError): - p[0] - py.test.raises(ValueError, ffi.new, "int[]", -1) - assert repr(p) == "<cdata 'int[]' owning 0 bytes>" - - def test_pointer_init(self): - n = ffi.new("int *", 24) - a = ffi.new("int *[10]", [ffi.NULL, ffi.NULL, n, n, ffi.NULL]) - for i in range(10): - if i not in (2, 3): - assert a[i] == ffi.NULL - assert a[2] == a[3] == n - - def test_cannot_cast(self): - a = ffi.new("short int[10]") - e = py.test.raises(TypeError, ffi.new, "long int **", a) - msg = str(e.value) - assert "'short[10]'" in msg and "'long *'" in msg - - def test_new_pointer_to_array(self): - a = ffi.new("int[4]", [100, 102, 104, 106]) - p = ffi.new("int **", a) - assert p[0] == ffi.cast("int *", a) - assert p[0][2] == 104 - p = ffi.cast("int *", a) - assert p[0] == 100 - assert p[1] == 102 - assert p[2] == 104 - assert p[3] == 106 - # keepalive: a - - def test_pointer_direct(self): - p = ffi.cast("int*", 0) - assert p is not None - assert bool(p) is False - assert p == ffi.cast("int*", 0) - assert p != None - assert repr(p) == "<cdata 'int *' NULL>" - a = ffi.new("int[]", [123, 456]) - p = ffi.cast("int*", a) - assert bool(p) is True - assert p == ffi.cast("int*", a) - assert p != ffi.cast("int*", 0) - assert p[0] == 123 - assert p[1] == 456 - - def test_repr(self): - typerepr = "<ctype '%s'>" - p = ffi.cast("short unsigned int", 0) - assert repr(p) == "<cdata 'unsigned short' 0>" - assert repr(ffi.typeof(p)) == typerepr % "unsigned short" - p = ffi.cast("unsigned short int", 0) - assert repr(p) == "<cdata 'unsigned short' 0>" - assert repr(ffi.typeof(p)) == typerepr % "unsigned short" - p = ffi.cast("int*", 0) - assert repr(p) == "<cdata 'int *' NULL>" - assert repr(ffi.typeof(p)) == typerepr % "int *" - # - p = ffi.new("int*") - assert repr(p) == "<cdata 'int *' owning %d bytes>" % SIZE_OF_INT - assert repr(ffi.typeof(p)) == typerepr % "int *" - p = ffi.new("int**") - assert repr(p) == "<cdata 'int * *' owning %d bytes>" % SIZE_OF_PTR - assert repr(ffi.typeof(p)) == typerepr % "int * *" - p = ffi.new("int [2]") - assert repr(p) == "<cdata 'int[2]' owning %d bytes>" % (2*SIZE_OF_INT) - assert repr(ffi.typeof(p)) == typerepr % "int[2]" - p = ffi.new("int*[2][3]") - assert repr(p) == "<cdata 'int *[2][3]' owning %d bytes>" % ( - 6*SIZE_OF_PTR) - assert repr(ffi.typeof(p)) == typerepr % "int *[2][3]" - p = ffi.new("struct repr *") - assert repr(p) == "<cdata 'struct repr *' owning %d bytes>" % ( - 3*SIZE_OF_SHORT) - assert repr(ffi.typeof(p)) == typerepr % "struct repr *" - # - q = ffi.cast("short", -123) - assert repr(q) == "<cdata 'short' -123>" - assert repr(ffi.typeof(q)) == typerepr % "short" - p = ffi.new("int*") - q = ffi.cast("short*", p) - assert repr(q).startswith("<cdata 'short *' 0x") - assert repr(ffi.typeof(q)) == typerepr % "short *" - p = ffi.new("int [2]") - q = ffi.cast("int*", p) - assert repr(q).startswith("<cdata 'int *' 0x") - assert repr(ffi.typeof(q)) == typerepr % "int *" - p = ffi.new("struct repr*") - q = ffi.cast("struct repr *", p) - assert repr(q).startswith("<cdata 'struct repr *' 0x") - assert repr(ffi.typeof(q)) == typerepr % "struct repr *" - prevrepr = repr(q) - q = q[0] - assert repr(q) == prevrepr.replace(' *', ' &') - assert repr(ffi.typeof(q)) == typerepr % "struct repr" - - def test_new_array_of_array(self): - p = ffi.new("int[3][4]") - p[0][0] = 10 - p[2][3] = 33 - assert p[0][0] == 10 - assert p[2][3] == 33 - with pytest.raises(IndexError): - p[1][-1] - - def test_constructor_array_of_array(self): - p = ffi.new("int[3][2]", [[10, 11], [12, 13], [14, 15]]) - assert p[2][1] == 15 - - def test_new_array_of_pointer_1(self): - n = ffi.new("int*", 99) - p = ffi.new("int*[4]") - p[3] = n - a = p[3] - assert repr(a).startswith("<cdata 'int *' 0x") - assert a[0] == 99 - - def test_new_array_of_pointer_2(self): - n = ffi.new("int[1]", [99]) - p = ffi.new("int*[4]") - p[3] = n - a = p[3] - assert repr(a).startswith("<cdata 'int *' 0x") - assert a[0] == 99 - - def test_char(self): - assert ffi.new("char*", b"\xff")[0] == b'\xff' - assert ffi.new("char*")[0] == b'\x00' - assert int(ffi.cast("char", 300)) == 300 - 256 - assert not bool(ffi.cast("char", 0)) - assert bool(ffi.cast("char", 1)) - assert bool(ffi.cast("char", 255)) - py.test.raises(TypeError, ffi.new, "char*", 32) - py.test.raises(TypeError, ffi.new, "char*", u+"x") - py.test.raises(TypeError, ffi.new, "char*", b"foo") - # - p = ffi.new("char[]", [b'a', b'b', b'\x9c']) - assert len(p) == 3 - assert p[0] == b'a' - assert p[1] == b'b' - assert p[2] == b'\x9c' - p[0] = b'\xff' - assert p[0] == b'\xff' - p = ffi.new("char[]", b"abcd") - assert len(p) == 5 - assert p[4] == b'\x00' # like in C, with: char[] p = "abcd"; - # - p = ffi.new("char[4]", b"ab") - assert len(p) == 4 - assert [p[i] for i in range(4)] == [b'a', b'b', b'\x00', b'\x00'] - p = ffi.new("char[2]", b"ab") - assert len(p) == 2 - assert [p[i] for i in range(2)] == [b'a', b'b'] - py.test.raises(IndexError, ffi.new, "char[2]", b"abc") - - def check_wchar_t(self, ffi): - try: - ffi.cast("wchar_t", 0) - except NotImplementedError: - py.test.skip("NotImplementedError: wchar_t") - - def test_wchar_t(self): - self.check_wchar_t(ffi) - assert ffi.new("wchar_t*", u+'x')[0] == u+'x' - assert ffi.new("wchar_t*", u+'\u1234')[0] == u+'\u1234' - if SIZE_OF_WCHAR > 2: - assert ffi.new("wchar_t*", u+'\U00012345')[0] == u+'\U00012345' - else: - py.test.raises(TypeError, ffi.new, "wchar_t*", u+'\U00012345') - assert ffi.new("wchar_t*")[0] == u+'\x00' - assert int(ffi.cast("wchar_t", 300)) == 300 - assert not bool(ffi.cast("wchar_t", 0)) - assert bool(ffi.cast("wchar_t", 1)) - assert bool(ffi.cast("wchar_t", 65535)) - if SIZE_OF_WCHAR > 2: - assert bool(ffi.cast("wchar_t", 65536)) - py.test.raises(TypeError, ffi.new, "wchar_t*", 32) - py.test.raises(TypeError, ffi.new, "wchar_t*", "foo") - # - p = ffi.new("wchar_t[]", [u+'a', u+'b', u+'\u1234']) - assert len(p) == 3 - assert p[0] == u+'a' - assert p[1] == u+'b' and type(p[1]) is unicode - assert p[2] == u+'\u1234' - p[0] = u+'x' - assert p[0] == u+'x' and type(p[0]) is unicode - p[1] = u+'\u1357' - assert p[1] == u+'\u1357' - p = ffi.new("wchar_t[]", u+"abcd") - assert len(p) == 5 - assert p[4] == u+'\x00' - p = ffi.new("wchar_t[]", u+"a\u1234b") - assert len(p) == 4 - assert p[1] == u+'\u1234' - # - p = ffi.new("wchar_t[]", u+'\U00023456') - if SIZE_OF_WCHAR == 2: - assert len(p) == 3 - assert p[0] == u+'\ud84d' - assert p[1] == u+'\udc56' - assert p[2] == u+'\x00' - else: - assert len(p) == 2 - assert p[0] == u+'\U00023456' - assert p[1] == u+'\x00' - # - p = ffi.new("wchar_t[4]", u+"ab") - assert len(p) == 4 - assert [p[i] for i in range(4)] == [u+'a', u+'b', u+'\x00', u+'\x00'] - p = ffi.new("wchar_t[2]", u+"ab") - assert len(p) == 2 - assert [p[i] for i in range(2)] == [u+'a', u+'b'] - py.test.raises(IndexError, ffi.new, "wchar_t[2]", u+"abc") - - def test_none_as_null_doesnt_work(self): - p = ffi.new("int*[1]") - assert p[0] is not None - assert p[0] != None - assert p[0] == ffi.NULL - assert repr(p[0]) == "<cdata 'int *' NULL>" - # - n = ffi.new("int*", 99) - p = ffi.new("int*[]", [n]) - assert p[0][0] == 99 - with pytest.raises(TypeError): - p[0] = None - p[0] = ffi.NULL - assert p[0] == ffi.NULL - - def test_float(self): - p = ffi.new("float[]", [-2, -2.5]) - assert p[0] == -2.0 - assert p[1] == -2.5 - p[1] += 17.75 - assert p[1] == 15.25 - # - p = ffi.new("float*", 15.75) - assert p[0] == 15.75 - py.test.raises(TypeError, int, p) - py.test.raises(TypeError, float, p) - p[0] = 0.0 - assert bool(p) is True - # - p = ffi.new("float*", 1.1) - f = p[0] - assert f != 1.1 # because of rounding effect - assert abs(f - 1.1) < 1E-7 - # - INF = 1E200 * 1E200 - assert 1E200 != INF - p[0] = 1E200 - assert p[0] == INF # infinite, not enough precision - - def test_struct_simple(self): - s = ffi.new("struct simple*") - assert s.a == s.b == s.c == 0 - s.b = -23 - assert s.b == -23 - with pytest.raises(OverflowError): - s.b = 32768 - # - s = ffi.new("struct simple*", [-2, -3]) - assert s.a == -2 - assert s.b == -3 - assert s.c == 0 - with pytest.raises((AttributeError, TypeError)): - del s.a - assert repr(s) == "<cdata 'struct simple *' owning %d bytes>" % ( - SIZE_OF_INT + 2 * SIZE_OF_SHORT) - # - py.test.raises(ValueError, ffi.new, "struct simple*", [1, 2, 3, 4]) - - def test_constructor_struct_from_dict(self): - s = ffi.new("struct simple*", {'b': 123, 'c': 456}) - assert s.a == 0 - assert s.b == 123 - assert s.c == 456 - py.test.raises(KeyError, ffi.new, "struct simple*", {'d': 456}) - - def test_struct_pointer(self): - s = ffi.new("struct simple*") - assert s[0].a == s[0].b == s[0].c == 0 - s[0].b = -23 - assert s[0].b == s.b == -23 - with pytest.raises(OverflowError): - s[0].b = -32769 - with pytest.raises(IndexError): - s[1] - - def test_struct_opaque(self): - py.test.raises(ffi.error, ffi.new, "struct baz*") - # should 'ffi.new("struct baz **") work? it used to, but it was - # not particularly useful... - py.test.raises(ffi.error, ffi.new, "struct baz**") - - def test_pointer_to_struct(self): - s = ffi.new("struct simple *") - s.a = -42 - assert s[0].a == -42 - p = ffi.new("struct simple **", s) - assert p[0].a == -42 - assert p[0][0].a == -42 - p[0].a = -43 - assert s.a == -43 - assert s[0].a == -43 - p[0][0].a = -44 - assert s.a == -44 - assert s[0].a == -44 - s.a = -45 - assert p[0].a == -45 - assert p[0][0].a == -45 - s[0].a = -46 - assert p[0].a == -46 - assert p[0][0].a == -46 - - def test_constructor_struct_of_array(self): - s = ffi.new("struct array *", [[10, 11], [b'a', b'b', b'c']]) - assert s.a[1] == 11 - assert s.b[2] == b'c' - s.b[1] = b'X' - assert s.b[0] == b'a' - assert s.b[1] == b'X' - assert s.b[2] == b'c' - - def test_recursive_struct(self): - s = ffi.new("struct recursive*") - t = ffi.new("struct recursive*") - s.value = 123 - s.next = t - t.value = 456 - assert s.value == 123 - assert s.next.value == 456 - - def test_union_simple(self): - u = ffi.new("union simple_u*") - assert u.a == u.b == u.c == 0 - u.b = -23 - assert u.b == -23 - assert u.a != 0 - with pytest.raises(OverflowError): - u.b = 32768 - # - u = ffi.new("union simple_u*", [-2]) - assert u.a == -2 - with pytest.raises((AttributeError, TypeError)): - del u.a - assert repr(u) == "<cdata 'union simple_u *' owning %d bytes>" % ( - SIZE_OF_INT,) - - def test_union_opaque(self): - py.test.raises(ffi.error, ffi.new, "union baz*") - # should 'ffi.new("union baz **") work? it used to, but it was - # not particularly useful... - py.test.raises(ffi.error, ffi.new, "union baz**") - - def test_union_initializer(self): - py.test.raises(TypeError, ffi.new, "union init_u*", b'A') - py.test.raises(TypeError, ffi.new, "union init_u*", 5) - py.test.raises(ValueError, ffi.new, "union init_u*", [b'A', 5]) - u = ffi.new("union init_u*", [b'A']) - assert u.a == b'A' - py.test.raises(TypeError, ffi.new, "union init_u*", [1005]) - u = ffi.new("union init_u*", {'b': 12345}) - assert u.b == 12345 - u = ffi.new("union init_u*", []) - assert u.a == b'\x00' - assert u.b == 0 - - def test_sizeof_type(self): - for c_type, expected_size in [ - ('char', 1), - ('unsigned int', 4), - ('char *', SIZE_OF_PTR), - ('int[5]', 20), - ('struct four_s', 12), - ('union four_u', 4), - ]: - size = ffi.sizeof(c_type) - assert size == expected_size, (size, expected_size, ctype) - - def test_sizeof_cdata(self): - assert ffi.sizeof(ffi.new("short*")) == SIZE_OF_PTR - assert ffi.sizeof(ffi.cast("short", 123)) == SIZE_OF_SHORT - # - a = ffi.new("int[]", [10, 11, 12, 13, 14]) - assert len(a) == 5 - assert ffi.sizeof(a) == 5 * SIZE_OF_INT - - def test_string_from_char_pointer(self): - x = ffi.new("char*", b"x") - assert str(x) == repr(x) - assert ffi.string(x) == b"x" - assert ffi.string(ffi.new("char*", b"\x00")) == b"" - py.test.raises(TypeError, ffi.new, "char*", unicode("foo")) - - def test_unicode_from_wchar_pointer(self): - self.check_wchar_t(ffi) - x = ffi.new("wchar_t*", u+"x") - assert unicode(x) == unicode(repr(x)) - assert ffi.string(x) == u+"x" - assert ffi.string(ffi.new("wchar_t*", u+"\x00")) == u+"" - - def test_string_from_char_array(self): - p = ffi.new("char[]", b"hello.") - p[5] = b'!' - assert ffi.string(p) == b"hello!" - p[6] = b'?' - assert ffi.string(p) == b"hello!?" - p[3] = b'\x00' - assert ffi.string(p) == b"hel" - assert ffi.string(p, 2) == b"he" - with pytest.raises(IndexError): - p[7] = b'X' - # - a = ffi.new("char[]", b"hello\x00world") - assert len(a) == 12 - p = ffi.cast("char *", a) - assert ffi.string(p) == b'hello' - - def test_string_from_wchar_array(self): - self.check_wchar_t(ffi) - assert ffi.string(ffi.cast("wchar_t", "x")) == u+"x" - assert ffi.string(ffi.cast("wchar_t", u+"x")) == u+"x" - x = ffi.cast("wchar_t", "x") - assert str(x) == repr(x) - assert ffi.string(x) == u+"x" - # - p = ffi.new("wchar_t[]", u+"hello.") - p[5] = u+'!' - assert ffi.string(p) == u+"hello!" - p[6] = u+'\u04d2' - assert ffi.string(p) == u+"hello!\u04d2" - p[3] = u+'\x00' - assert ffi.string(p) == u+"hel" - assert ffi.string(p, 123) == u+"hel" - with pytest.raises(IndexError): - p[7] = u+'X' - # - a = ffi.new("wchar_t[]", u+"hello\x00world") - assert len(a) == 12 - p = ffi.cast("wchar_t *", a) - assert ffi.string(p) == u+'hello' - assert ffi.string(p, 123) == u+'hello' - assert ffi.string(p, 5) == u+'hello' - assert ffi.string(p, 2) == u+'he' - - def test_fetch_const_char_p_field(self): - # 'const' is ignored so far, in the declaration of 'struct string' - t = ffi.new("const char[]", b"testing") - s = ffi.new("struct string*", [t]) - assert type(s.name) not in (bytes, str, unicode) - assert ffi.string(s.name) == b"testing" - with pytest.raises(TypeError): - s.name = None - s.name = ffi.NULL - assert s.name == ffi.NULL - - def test_fetch_const_wchar_p_field(self): - # 'const' is ignored so far - self.check_wchar_t(ffi) - t = ffi.new("const wchar_t[]", u+"testing") - s = ffi.new("struct ustring*", [t]) - assert type(s.name) not in (bytes, str, unicode) - assert ffi.string(s.name) == u+"testing" - s.name = ffi.NULL - assert s.name == ffi.NULL - - def test_voidp(self): - py.test.raises(TypeError, ffi.new, "void*") - p = ffi.new("void **") - assert p[0] == ffi.NULL - a = ffi.new("int[]", [10, 11, 12]) - p = ffi.new("void **", a) - vp = p[0] - with pytest.raises(TypeError): - vp[0] - py.test.raises(TypeError, ffi.new, "short **", a) - # - s = ffi.new("struct voidp *") - s.p = a # works - s.q = a # works - with pytest.raises(TypeError): - s.r = a # fails - b = ffi.cast("int *", a) - s.p = b # works - s.q = b # works - with pytest.raises(TypeError): - s.r = b # fails - - def test_functionptr_simple(self): - py.test.raises(TypeError, ffi.callback, "int(*)(int)", 0) - def cb(n): - return n + 1 - cb.__qualname__ = 'cb' - p = ffi.callback("int(*)(int)", cb) - res = p(41) # calling an 'int(*)(int)', i.e. a function pointer - assert res == 42 and type(res) is int - res = p(ffi.cast("int", -41)) - assert res == -40 and type(res) is int - assert repr(p).startswith( - "<cdata 'int(*)(int)' calling <function cb at 0x") - assert ffi.typeof(p) is ffi.typeof("int(*)(int)") - q = ffi.new("int(**)(int)", p) - assert repr(q) == "<cdata 'int(* *)(int)' owning %d bytes>" % ( - SIZE_OF_PTR) - with pytest.raises(TypeError): - q(43) - res = q[0](43) - assert res == 44 - q = ffi.cast("int(*)(int)", p) - assert repr(q).startswith("<cdata 'int(*)(int)' 0x") - res = q(45) - assert res == 46 - - def test_functionptr_advanced(self): - t = ffi.typeof("int(*(*)(int))(int)") - assert repr(t) == "<ctype '%s'>" % "int(*(*)(int))(int)" - - def test_functionptr_voidptr_return(self): - def cb(): - return ffi.NULL - p = ffi.callback("void*(*)()", cb) - res = p() - assert res is not None - assert res == ffi.NULL - int_ptr = ffi.new('int*') - void_ptr = ffi.cast('void*', int_ptr) - def cb(): - return void_ptr - p = ffi.callback("void*(*)()", cb) - res = p() - assert res == void_ptr - - def test_functionptr_intptr_return(self): - def cb(): - return ffi.NULL - p = ffi.callback("int*(*)()", cb) - res = p() - assert res == ffi.NULL - int_ptr = ffi.new('int*') - def cb(): - return int_ptr - p = ffi.callback("int*(*)()", cb) - res = p() - assert repr(res).startswith("<cdata 'int *' 0x") - assert res == int_ptr - int_array_ptr = ffi.new('int[1]') - def cb(): - return int_array_ptr - p = ffi.callback("int*(*)()", cb) - res = p() - assert repr(res).startswith("<cdata 'int *' 0x") - assert res == int_array_ptr - - def test_functionptr_void_return(self): - def foo(): - pass - foo_cb = ffi.callback("void foo()", foo) - result = foo_cb() - assert result is None - - def test_char_cast(self): - p = ffi.cast("int", b'\x01') - assert ffi.typeof(p) is ffi.typeof("int") - assert int(p) == 1 - p = ffi.cast("int", ffi.cast("char", b"a")) - assert int(p) == ord("a") - p = ffi.cast("int", ffi.cast("char", b"\x80")) - assert int(p) == 0x80 # "char" is considered unsigned in this case - p = ffi.cast("int", b"\x81") - assert int(p) == 0x81 - - def test_wchar_cast(self): - self.check_wchar_t(ffi) - p = ffi.cast("int", ffi.cast("wchar_t", u+'\u1234')) - assert int(p) == 0x1234 - p = ffi.cast("long long", ffi.cast("wchar_t", -1)) - if SIZE_OF_WCHAR == 2: # 2 bytes, unsigned - assert int(p) == 0xffff - elif (sys.platform.startswith('linux') and - platform.machine().startswith('x86')): # known to be signed - assert int(p) == -1 - else: # in general, it can be either signed or not - assert int(p) in [-1, 0xffffffff] # e.g. on arm, both cases occur - p = ffi.cast("int", u+'\u1234') - assert int(p) == 0x1234 - - def test_cast_array_to_charp(self): - a = ffi.new("short int[]", [0x1234, 0x5678]) - p = ffi.cast("char*", a) - data = b''.join([p[i] for i in range(4)]) - if sys.byteorder == 'little': - assert data == b'\x34\x12\x78\x56' - else: - assert data == b'\x12\x34\x56\x78' - - def test_cast_between_pointers(self): - a = ffi.new("short int[]", [0x1234, 0x5678]) - p = ffi.cast("short*", a) - p2 = ffi.cast("int*", p) - q = ffi.cast("char*", p2) - data = b''.join([q[i] for i in range(4)]) - if sys.byteorder == 'little': - assert data == b'\x34\x12\x78\x56' - else: - assert data == b'\x12\x34\x56\x78' - - def test_cast_pointer_and_int(self): - a = ffi.new("short int[]", [0x1234, 0x5678]) - l1 = ffi.cast("intptr_t", a) - p = ffi.cast("short*", a) - l2 = ffi.cast("intptr_t", p) - assert int(l1) == int(l2) != 0 - q = ffi.cast("short*", l1) - assert q == ffi.cast("short*", int(l1)) - assert q[0] == 0x1234 - assert int(ffi.cast("intptr_t", ffi.NULL)) == 0 - - def test_cast_functionptr_and_int(self): - def cb(n): - return n + 1 - a = ffi.callback("int(*)(int)", cb) - p = ffi.cast("void *", a) - assert p - b = ffi.cast("int(*)(int)", p) - assert b(41) == 42 - assert a == b - assert hash(a) == hash(b) - - def test_callback_crash(self): - def cb(n): - raise Exception - a = ffi.callback("int(*)(int)", cb, error=42) - res = a(1) # and the error reported to stderr - assert res == 42 - - def test_structptr_argument(self): - def cb(p): - return p[0].a * 1000 + p[0].b * 100 + p[1].a * 10 + p[1].b - a = ffi.callback("int(*)(struct ab[])", cb) - res = a([[5, 6], {'a': 7, 'b': 8}]) - assert res == 5678 - res = a([[5], {'b': 8}]) - assert res == 5008 - - def test_array_argument_as_list(self): - seen = [] - def cb(argv): - seen.append(ffi.string(argv[0])) - seen.append(ffi.string(argv[1])) - a = ffi.callback("void(*)(char *[])", cb) - a([ffi.new("char[]", b"foobar"), ffi.new("char[]", b"baz")]) - assert seen == [b"foobar", b"baz"] - - def test_cast_float(self): - a = ffi.cast("float", 12) - assert float(a) == 12.0 - a = ffi.cast("float", 12.5) - assert float(a) == 12.5 - a = ffi.cast("float", b"A") - assert float(a) == ord("A") - a = ffi.cast("int", 12.9) - assert int(a) == 12 - a = ffi.cast("char", 66.9 + 256) - assert ffi.string(a) == b"B" - # - a = ffi.cast("float", ffi.cast("int", 12)) - assert float(a) == 12.0 - a = ffi.cast("float", ffi.cast("double", 12.5)) - assert float(a) == 12.5 - a = ffi.cast("float", ffi.cast("char", b"A")) - assert float(a) == ord("A") - a = ffi.cast("int", ffi.cast("double", 12.9)) - assert int(a) == 12 - a = ffi.cast("char", ffi.cast("double", 66.9 + 256)) - assert ffi.string(a) == b"B" - - def test_enum(self): - # enum foq { A0, B0, CC0, D0 }; - assert ffi.string(ffi.cast("enum foq", 0)) == "cffiA0" - assert ffi.string(ffi.cast("enum foq", 2)) == "cffiCC0" - assert ffi.string(ffi.cast("enum foq", 3)) == "cffiD0" - assert ffi.string(ffi.cast("enum foq", 4)) == "4" - # enum bar { A1, B1=-2, CC1, D1, E1 }; - assert ffi.string(ffi.cast("enum bar", 0)) == "A1" - assert ffi.string(ffi.cast("enum bar", -2)) == "B1" - assert ffi.string(ffi.cast("enum bar", -1)) == "CC1" - assert ffi.string(ffi.cast("enum bar", 1)) == "E1" - assert ffi.cast("enum bar", -2) == ffi.cast("enum bar", -2) - assert ffi.cast("enum foq", 0) == ffi.cast("enum bar", 0) - assert ffi.cast("enum bar", 0) == ffi.cast("int", 0) - assert repr(ffi.cast("enum bar", -1)) == "<cdata 'enum bar' -1: CC1>" - assert repr(ffi.cast("enum foq", -1)) == ( # enums are unsigned, if - "<cdata 'enum foq' 4294967295>") or ( # they contain no neg value - sys.platform == "win32") # (but not on msvc) - # enum baz { A2=0x1000, B2=0x2000 }; - assert ffi.string(ffi.cast("enum baz", 0x1000)) == "A2" - assert ffi.string(ffi.cast("enum baz", 0x2000)) == "B2" - - def test_enum_in_struct(self): - # enum foo2 { A3, B3, C3, D3 }; - # struct bar_with_e { enum foo2 e; }; - s = ffi.new("struct bar_with_e *") - s.e = 0 - assert s.e == 0 - s.e = 3 - assert s.e == 3 - assert s[0].e == 3 - s[0].e = 2 - assert s.e == 2 - assert s[0].e == 2 - s.e = ffi.cast("enum foo2", -1) - assert s.e in (4294967295, -1) # two choices - assert s[0].e in (4294967295, -1) - s.e = s.e - with pytest.raises(TypeError): - s.e = 'B3' - with pytest.raises(TypeError): - s.e = '2' - with pytest.raises(TypeError): - s.e = '#2' - with pytest.raises(TypeError): - s.e = '#7' - - def test_enum_non_contiguous(self): - # enum noncont { A4, B4=42, C4 }; - assert ffi.string(ffi.cast("enum noncont", 0)) == "A4" - assert ffi.string(ffi.cast("enum noncont", 42)) == "B4" - assert ffi.string(ffi.cast("enum noncont", 43)) == "C4" - invalid_value = ffi.cast("enum noncont", 2) - assert int(invalid_value) == 2 - assert ffi.string(invalid_value) == "2" - - def test_enum_char_hex_oct(self): - # enum etypes {A5='!', B5='\'', C5=0x10, D5=010, E5=- 0x10, F5=-010}; - assert ffi.string(ffi.cast("enum etypes", ord('!'))) == "A5" - assert ffi.string(ffi.cast("enum etypes", ord("'"))) == "B5" - assert ffi.string(ffi.cast("enum etypes", 16)) == "C5" - assert ffi.string(ffi.cast("enum etypes", 8)) == "D5" - assert ffi.string(ffi.cast("enum etypes", -16)) == "E5" - assert ffi.string(ffi.cast("enum etypes", -8)) == "F5" - - def test_array_of_struct(self): - s = ffi.new("struct ab[1]") - with pytest.raises(AttributeError): - s.b - with pytest.raises(AttributeError): - s.b = 412 - s[0].b = 412 - assert s[0].b == 412 - with pytest.raises(IndexError): - s[1] - - def test_pointer_to_array(self): - p = ffi.new("int(**)[5]") - assert repr(p) == "<cdata 'int(* *)[5]' owning %d bytes>" % SIZE_OF_PTR - - def test_iterate_array(self): - a = ffi.new("char[]", b"hello") - assert list(a) == [b"h", b"e", b"l", b"l", b"o", b"\0"] - assert list(iter(a)) == [b"h", b"e", b"l", b"l", b"o", b"\0"] - # - py.test.raises(TypeError, iter, ffi.cast("char *", a)) - py.test.raises(TypeError, list, ffi.cast("char *", a)) - py.test.raises(TypeError, iter, ffi.new("int *")) - py.test.raises(TypeError, list, ffi.new("int *")) - - def test_offsetof(self): - # struct abc { int a, b, c; }; - assert ffi.offsetof("struct abc", "a") == 0 - assert ffi.offsetof("struct abc", "b") == 4 - assert ffi.offsetof("struct abc", "c") == 8 - - def test_offsetof_nested(self): - # struct nesting { struct abc d, e; }; - assert ffi.offsetof("struct nesting", "e") == 12 - py.test.raises(KeyError, ffi.offsetof, "struct nesting", "e.a") - assert ffi.offsetof("struct nesting", "e", "a") == 12 - assert ffi.offsetof("struct nesting", "e", "b") == 16 - assert ffi.offsetof("struct nesting", "e", "c") == 20 - - def test_offsetof_array(self): - assert ffi.offsetof("int[]", 51) == 51 * ffi.sizeof("int") - assert ffi.offsetof("int *", 51) == 51 * ffi.sizeof("int") - # struct array2 { int a, b; int c[99]; }; - assert ffi.offsetof("struct array2", "c") == 2 * ffi.sizeof("int") - assert ffi.offsetof("struct array2", "c", 0) == 2 * ffi.sizeof("int") - assert ffi.offsetof("struct array2", "c", 51) == 53 * ffi.sizeof("int") - - def test_alignof(self): - # struct align { char a; short b; char c; }; - assert ffi.alignof("int") == 4 - assert ffi.alignof("double") in (4, 8) - assert ffi.alignof("struct align") == 2 - - def test_bitfield(self): - # struct bitfield { int a:10, b:20, c:3; }; - assert ffi.sizeof("struct bitfield") == 8 - s = ffi.new("struct bitfield *") - s.a = 511 - with pytest.raises(OverflowError): - s.a = 512 - with pytest.raises(OverflowError): - s[0].a = 512 - assert s.a == 511 - s.a = -512 - with pytest.raises(OverflowError): - s.a = -513 - with pytest.raises(OverflowError): - s[0].a = -513 - assert s.a == -512 - s.c = 3 - assert s.c == 3 - with pytest.raises(OverflowError): - s.c = 4 - with pytest.raises(OverflowError): - s[0].c = 4 - s.c = -4 - assert s.c == -4 - - def test_bitfield_enum(self): - # typedef enum { AA1, BB1, CC1 } foo_e_t; - # typedef struct { foo_e_t f:2; } bfenum_t; - if sys.platform == "win32": - py.test.skip("enums are not unsigned") - s = ffi.new("bfenum_t *") - s.f = 2 - assert s.f == 2 - - def test_anonymous_struct(self): - # typedef struct { int a; } anon_foo_t; - # typedef struct { char b, c; } anon_bar_t; - f = ffi.new("anon_foo_t *", [12345]) - b = ffi.new("anon_bar_t *", [b"B", b"C"]) - assert f.a == 12345 - assert b.b == b"B" - assert b.c == b"C" - assert repr(b).startswith("<cdata 'anon_bar_t *'") - - def test_struct_with_two_usages(self): - # typedef struct named_foo_s { int a; } named_foo_t, *named_foo_p; - # typedef struct { int a; } unnamed_foo_t, *unnamed_foo_p; - f = ffi.new("named_foo_t *", [12345]) - ps = ffi.new("named_foo_p[]", [f]) - f = ffi.new("unnamed_foo_t *", [12345]) - ps = ffi.new("unnamed_foo_p[]", [f]) - - def test_pointer_arithmetic(self): - s = ffi.new("short[]", list(range(100, 110))) - p = ffi.cast("short *", s) - assert p[2] == 102 - assert p+1 == p+1 - assert p+1 != p+0 - assert p == p+0 == p-0 - assert (p+1)[0] == 101 - assert (p+19)[-10] == 109 - assert (p+5) - (p+1) == 4 - assert p == s+0 - assert p+1 == s+1 - - def test_pointer_comparison(self): - s = ffi.new("short[]", list(range(100))) - p = ffi.cast("short *", s) - assert (p < s) is False - assert (p <= s) is True - assert (p == s) is True - assert (p != s) is False - assert (p > s) is False - assert (p >= s) is True - assert (s < p) is False - assert (s <= p) is True - assert (s == p) is True - assert (s != p) is False - assert (s > p) is False - assert (s >= p) is True - q = p + 1 - assert (q < s) is False - assert (q <= s) is False - assert (q == s) is False - assert (q != s) is True - assert (q > s) is True - assert (q >= s) is True - assert (s < q) is True - assert (s <= q) is True - assert (s == q) is False - assert (s != q) is True - assert (s > q) is False - assert (s >= q) is False - assert (q < p) is False - assert (q <= p) is False - assert (q == p) is False - assert (q != p) is True - assert (q > p) is True - assert (q >= p) is True - assert (p < q) is True - assert (p <= q) is True - assert (p == q) is False - assert (p != q) is True - assert (p > q) is False - assert (p >= q) is False - # - assert (None == s) is False - assert (None != s) is True - assert (s == None) is False - assert (s != None) is True - assert (None == q) is False - assert (None != q) is True - assert (q == None) is False - assert (q != None) is True - - def test_integer_comparison(self): - x = ffi.cast("int", 123) - y = ffi.cast("int", 456) - assert x < y - # - z = ffi.cast("double", 78.9) - assert x > z - assert y > z - - def test_ffi_buffer_ptr(self): - a = ffi.new("short *", 100) - try: - b = ffi.buffer(a) - except NotImplementedError as e: - py.test.skip(str(e)) - content = b[:] - assert len(content) == len(b) == 2 - if sys.byteorder == 'little': - assert content == b'\x64\x00' - assert b[0] == b'\x64' - b[0] = b'\x65' - else: - assert content == b'\x00\x64' - assert b[1] == b'\x64' - b[1] = b'\x65' - assert a[0] == 101 - - def test_ffi_buffer_array(self): - a = ffi.new("int[]", list(range(100, 110))) - try: - b = ffi.buffer(a) - except NotImplementedError as e: - py.test.skip(str(e)) - content = b[:] - if sys.byteorder == 'little': - assert content.startswith(b'\x64\x00\x00\x00\x65\x00\x00\x00') - b[4] = b'\x45' - else: - assert content.startswith(b'\x00\x00\x00\x64\x00\x00\x00\x65') - b[7] = b'\x45' - assert len(content) == 4 * 10 - assert a[1] == 0x45 - - def test_ffi_buffer_ptr_size(self): - a = ffi.new("short *", 0x4243) - try: - b = ffi.buffer(a, 1) - except NotImplementedError as e: - py.test.skip(str(e)) - content = b[:] - assert len(content) == 1 - if sys.byteorder == 'little': - assert content == b'\x43' - b[0] = b'\x62' - assert a[0] == 0x4262 - else: - assert content == b'\x42' - b[0] = b'\x63' - assert a[0] == 0x6343 - - def test_ffi_buffer_array_size(self): - a1 = ffi.new("int[]", list(range(100, 110))) - a2 = ffi.new("int[]", list(range(100, 115))) - try: - ffi.buffer(a1) - except NotImplementedError as e: - py.test.skip(str(e)) - assert ffi.buffer(a1)[:] == ffi.buffer(a2, 4*10)[:] - - def test_ffi_buffer_with_file(self): - import tempfile, os, array - fd, filename = tempfile.mkstemp() - f = os.fdopen(fd, 'r+b') - a = ffi.new("int[]", list(range(1005))) - try: - ffi.buffer(a, 512) - except NotImplementedError as e: - py.test.skip(str(e)) - f.write(ffi.buffer(a, 1000 * ffi.sizeof("int"))) - f.seek(0) - assert f.read() == arraytostring(array.array('i', range(1000))) - f.seek(0) - b = ffi.new("int[]", 1005) - f.readinto(ffi.buffer(b, 1000 * ffi.sizeof("int"))) - assert list(a)[:1000] + [0] * (len(a)-1000) == list(b) - f.close() - os.unlink(filename) - - def test_ffi_buffer_with_io(self): - import io, array - f = io.BytesIO() - a = ffi.new("int[]", list(range(1005))) - try: - ffi.buffer(a, 512) - except NotImplementedError as e: - py.test.skip(str(e)) - f.write(ffi.buffer(a, 1000 * ffi.sizeof("int"))) - f.seek(0) - assert f.read() == arraytostring(array.array('i', range(1000))) - f.seek(0) - b = ffi.new("int[]", 1005) - f.readinto(ffi.buffer(b, 1000 * ffi.sizeof("int"))) - assert list(a)[:1000] + [0] * (len(a)-1000) == list(b) - f.close() - - def test_array_in_struct(self): - # struct array { int a[2]; char b[3]; }; - p = ffi.new("struct array *") - p.a[1] = 5 - assert p.a[1] == 5 - assert repr(p.a).startswith("<cdata 'int[2]' 0x") - - def test_struct_containing_array_varsize_workaround(self): - if sys.platform == "win32": - py.test.skip("array of length 0 not supported") - # struct array0 { int len; short data[0]; }; - p = ffi.new("char[]", ffi.sizeof("struct array0") + 7 * SIZE_OF_SHORT) - q = ffi.cast("struct array0 *", p) - assert q.len == 0 - # 'q.data' gets not a 'short[0]', but just a 'short *' instead - assert repr(q.data).startswith("<cdata 'short *' 0x") - assert q.data[6] == 0 - q.data[6] = 15 - assert q.data[6] == 15 - - def test_new_struct_containing_array_varsize(self): - py.test.skip("later?") - ffi.cdef("struct foo_s { int len; short data[]; };") - p = ffi.new("struct foo_s *", 10) # a single integer is the length - assert p.len == 0 - assert p.data[9] == 0 - with pytest.raises(IndexError): - p.data[10] - - def test_ffi_typeof_getcname(self): - assert ffi.getctype("int") == "int" - assert ffi.getctype("int", 'x') == "int x" - assert ffi.getctype("int*") == "int *" - assert ffi.getctype("int*", '') == "int *" - assert ffi.getctype("int*", 'x') == "int * x" - assert ffi.getctype("int", '*') == "int *" - assert ffi.getctype("int", ' * x ') == "int * x" - assert ffi.getctype(ffi.typeof("int*"), '*') == "int * *" - assert ffi.getctype("int", '[5]') == "int[5]" - assert ffi.getctype("int[5]", '[6]') == "int[6][5]" - assert ffi.getctype("int[5]", '(*)') == "int(*)[5]" - # special-case for convenience: automatically put '()' around '*' - assert ffi.getctype("int[5]", '*') == "int(*)[5]" - assert ffi.getctype("int[5]", '*foo') == "int(*foo)[5]" - assert ffi.getctype("int[5]", ' ** foo ') == "int(** foo)[5]" - - def test_array_of_func_ptr(self): - f = ffi.cast("int(*)(int)", 42) - assert f != ffi.NULL - py.test.raises(ffi.error, ffi.cast, "int(int)", 42) - py.test.raises(ffi.error, ffi.new, "int([5])(int)") - a = ffi.new("int(*[5])(int)", [f]) - assert ffi.getctype(ffi.typeof(a)) == "int(*[5])(int)" - assert len(a) == 5 - assert a[0] == f - assert a[1] == ffi.NULL - py.test.raises(TypeError, ffi.cast, "int(*)(int)[5]", 0) - # - def cb(n): - return n + 1 - f = ffi.callback("int(*)(int)", cb) - a = ffi.new("int(*[5])(int)", [f, f]) - assert a[1](42) == 43 - - def test_callback_as_function_argument(self): - # In C, function arguments can be declared with a function type, - # which is automatically replaced with the ptr-to-function type. - def cb(a, b): - return chr(ord(a) + ord(b)).encode() - f = ffi.callback("char cb(char, char)", cb) - assert f(b'A', b'\x01') == b'B' - def g(callback): - return callback(b'A', b'\x01') - g = ffi.callback("char g(char cb(char, char))", g) - assert g(f) == b'B' - - def test_vararg_callback(self): - py.test.skip("callback with '...'") - def cb(i, va_list): - j = ffi.va_arg(va_list, "int") - k = ffi.va_arg(va_list, "long long") - return i * 2 + j * 3 + k * 5 - f = ffi.callback("long long cb(long i, ...)", cb) - res = f(10, ffi.cast("int", 100), ffi.cast("long long", 1000)) - assert res == 20 + 300 + 5000 - - def test_callback_decorator(self): - # - @ffi.callback("long(long, long)", error=42) - def cb(a, b): - return a - b - # - assert cb(-100, -10) == -90 - sz = ffi.sizeof("long") - assert cb((1 << (sz*8-1)) - 1, -10) == 42 - - def test_anonymous_enum(self): - # typedef enum { Value0 = 0 } e_t, *pe_t; - assert ffi.getctype("e_t*") == 'e_t *' - assert ffi.getctype("pe_t") == 'e_t *' - assert ffi.getctype("foo_e_t*") == 'foo_e_t *' - - def test_new_ctype(self): - p = ffi.new("int *") - py.test.raises(TypeError, ffi.new, p) - p = ffi.new(ffi.typeof("int *"), 42) - assert p[0] == 42 - - def test_enum_with_non_injective_mapping(self): - # enum e_noninj { AA3=0, BB3=0, CC3=0, DD3=0 }; - e = ffi.cast("enum e_noninj", 0) - assert ffi.string(e) == "AA3" # pick the first one arbitrarily - - def test_enum_refer_previous_enum_value(self): - # enum e_prev { AA4, BB4=2, CC4=4, DD4=BB4, EE4, FF4=CC4, GG4=FF4 }; - assert ffi.string(ffi.cast("enum e_prev", 2)) == "BB4" - assert ffi.string(ffi.cast("enum e_prev", 3)) == "EE4" - assert ffi.sizeof("char[DD4]") == 2 - assert ffi.sizeof("char[EE4]") == 3 - assert ffi.sizeof("char[FF4]") == 4 - assert ffi.sizeof("char[GG4]") == 4 - - def test_nested_anonymous_struct(self): - # struct nested_anon { - # struct { int a, b; }; - # union { int c, d; }; - # }; - assert ffi.sizeof("struct nested_anon") == 3 * SIZE_OF_INT - p = ffi.new("struct nested_anon *", [1, 2, 3]) - assert p.a == 1 - assert p.b == 2 - assert p.c == 3 - assert p.d == 3 - p.d = 17 - assert p.c == 17 - p.b = 19 - assert p.a == 1 - assert p.b == 19 - assert p.c == 17 - assert p.d == 17 - p = ffi.new("struct nested_anon *", {'b': 12, 'd': 14}) - assert p.a == 0 - assert p.b == 12 - assert p.c == 14 - assert p.d == 14 - - def test_nested_field_offset_align(self): - # struct nested_field_ofs_s { - # struct { int a; char b; }; - # union { char c; }; - # }; - assert ffi.offsetof("struct nested_field_ofs_s", "c") == 2 * SIZE_OF_INT - assert ffi.sizeof("struct nested_field_ofs_s") == 3 * SIZE_OF_INT - - def test_nested_anonymous_union(self): - # union nested_anon_u { - # struct { int a, b; }; - # union { int c, d; }; - # }; - assert ffi.sizeof("union nested_anon_u") == 2 * SIZE_OF_INT - p = ffi.new("union nested_anon_u *", [5]) - assert p.a == 5 - assert p.b == 0 - assert p.c == 5 - assert p.d == 5 - p.d = 17 - assert p.c == 17 - assert p.a == 17 - p.b = 19 - assert p.a == 17 - assert p.b == 19 - assert p.c == 17 - assert p.d == 17 - p = ffi.new("union nested_anon_u *", {'d': 14}) - assert p.a == 14 - assert p.b == 0 - assert p.c == 14 - assert p.d == 14 - p = ffi.new("union nested_anon_u *", {'b': 12}) - assert p.a == 0 - assert p.b == 12 - assert p.c == 0 - assert p.d == 0 - # we cannot specify several items in the dict, even though - # in theory in this particular case it would make sense - # to give both 'a' and 'b' - - def test_cast_to_array_type(self): - p = ffi.new("int[4]", [-5]) - q = ffi.cast("int[3]", p) - assert q[0] == -5 - assert repr(q).startswith("<cdata 'int[3]' 0x") - - def test_gc(self): - p = ffi.new("int *", 123) - seen = [] - def destructor(p1): - assert p1 is p - assert p1[0] == 123 - seen.append(1) - q = ffi.gc(p, destructor=destructor) - import gc; gc.collect() - assert seen == [] - del q - import gc; gc.collect(); gc.collect(); gc.collect() - assert seen == [1] - - def test_gc_2(self): - p = ffi.new("int *", 123) - seen = [] - q1 = ffi.gc(p, lambda p: seen.append(1)) - q2 = ffi.gc(q1, lambda p: seen.append(2)) - import gc; gc.collect() - assert seen == [] - del q1, q2 - import gc; gc.collect(); gc.collect(); gc.collect(); gc.collect() - assert seen == [2, 1] - - def test_gc_3(self): - p = ffi.new("int *", 123) - r = ffi.new("int *", 123) - seen = [] - seen_r = [] - q1 = ffi.gc(p, lambda p: seen.append(1)) - s1 = ffi.gc(r, lambda r: seen_r.append(4)) - q2 = ffi.gc(q1, lambda p: seen.append(2)) - s2 = ffi.gc(s1, lambda r: seen_r.append(5)) - q3 = ffi.gc(q2, lambda p: seen.append(3)) - import gc; gc.collect() - assert seen == [] - assert seen_r == [] - del q1, q2, q3, s2, s1 - import gc; gc.collect(); gc.collect(); gc.collect(); gc.collect() - assert seen == [3, 2, 1] - assert seen_r == [5, 4] - - def test_gc_4(self): - p = ffi.new("int *", 123) - seen = [] - q1 = ffi.gc(p, lambda p: seen.append(1)) - q2 = ffi.gc(q1, lambda p: seen.append(2)) - q3 = ffi.gc(q2, lambda p: seen.append(3)) - import gc; gc.collect() - assert seen == [] - del q1, q3 # q2 remains, and has a hard ref to q1 - import gc; gc.collect(); gc.collect(); gc.collect() - assert seen == [3] - - def test_release(self): - p = ffi.new("int[]", 123) - ffi.release(p) - # here, reading p[0] might give garbage or segfault... - ffi.release(p) # no effect - - def test_release_new_allocator(self): - seen = [] - def myalloc(size): - seen.append(size) - return ffi.new("char[]", b"X" * size) - def myfree(raw): - seen.append(raw) - alloc2 = ffi.new_allocator(alloc=myalloc, free=myfree) - p = alloc2("int[]", 15) - assert seen == [15 * 4] - ffi.release(p) - assert seen == [15 * 4, p] - ffi.release(p) # no effect - assert seen == [15 * 4, p] - # - del seen[:] - p = alloc2("struct ab *") - assert seen == [2 * 4] - ffi.release(p) - assert seen == [2 * 4, p] - ffi.release(p) # no effect - assert seen == [2 * 4, p] - - def test_CData_CType(self): - assert isinstance(ffi.cast("int", 0), ffi.CData) - assert isinstance(ffi.new("int *"), ffi.CData) - assert not isinstance(ffi.typeof("int"), ffi.CData) - assert not isinstance(ffi.cast("int", 0), ffi.CType) - assert not isinstance(ffi.new("int *"), ffi.CType) - - def test_CData_CType_2(self): - assert isinstance(ffi.typeof("int"), ffi.CType) - - def test_bool(self): - assert int(ffi.cast("_Bool", 0.1)) == 1 - assert int(ffi.cast("_Bool", -0.0)) == 0 - assert int(ffi.cast("_Bool", b'\x02')) == 1 - assert int(ffi.cast("_Bool", b'\x00')) == 0 - assert int(ffi.cast("_Bool", b'\x80')) == 1 - assert ffi.new("_Bool *", False)[0] == 0 - assert ffi.new("_Bool *", 1)[0] == 1 - py.test.raises(OverflowError, ffi.new, "_Bool *", 2) - py.test.raises(TypeError, ffi.string, ffi.cast("_Bool", 2)) - - def test_addressof(self): - p = ffi.new("struct ab *") - a = ffi.addressof(p[0]) - assert repr(a).startswith("<cdata 'struct ab *' 0x") - assert a == p - py.test.raises(TypeError, ffi.addressof, p) - py.test.raises((AttributeError, TypeError), ffi.addressof, 5) - py.test.raises(TypeError, ffi.addressof, ffi.cast("int", 5)) - - def test_addressof_field(self): - p = ffi.new("struct ab *") - b = ffi.addressof(p[0], 'b') - assert repr(b).startswith("<cdata 'int *' 0x") - assert int(ffi.cast("uintptr_t", b)) == ( - int(ffi.cast("uintptr_t", p)) + ffi.sizeof("int")) - assert b == ffi.addressof(p, 'b') - assert b != ffi.addressof(p, 'a') - - def test_addressof_field_nested(self): - # struct nesting { struct abc d, e; }; - p = ffi.new("struct nesting *") - py.test.raises(KeyError, ffi.addressof, p[0], 'e.b') - a = ffi.addressof(p[0], 'e', 'b') - assert int(ffi.cast("uintptr_t", a)) == ( - int(ffi.cast("uintptr_t", p)) + - ffi.sizeof("struct abc") + ffi.sizeof("int")) - - def test_addressof_anonymous_struct(self): - # typedef struct { int a; } anon_foo_t; - p = ffi.new("anon_foo_t *") - a = ffi.addressof(p[0]) - assert a == p - - def test_addressof_array(self): - p = ffi.new("int[52]") - p0 = ffi.addressof(p) - assert p0 == p - assert ffi.typeof(p0) is ffi.typeof("int(*)[52]") - py.test.raises(TypeError, ffi.addressof, p0) - # - p1 = ffi.addressof(p, 25) - assert ffi.typeof(p1) is ffi.typeof("int *") - assert (p1 - p) == 25 - assert ffi.addressof(p, 0) == p - - def test_addressof_pointer(self): - array = ffi.new("int[50]") - p = ffi.cast("int *", array) - py.test.raises(TypeError, ffi.addressof, p) - assert ffi.addressof(p, 0) == p - assert ffi.addressof(p, 25) == p + 25 - assert ffi.typeof(ffi.addressof(p, 25)) == ffi.typeof(p) - # - array = ffi.new("struct ab[50]") - p = ffi.cast("int *", array) - py.test.raises(TypeError, ffi.addressof, p) - assert ffi.addressof(p, 0) == p - assert ffi.addressof(p, 25) == p + 25 - assert ffi.typeof(ffi.addressof(p, 25)) == ffi.typeof(p) - - def test_addressof_array_in_struct(self): - # struct abc50 { int a, b; int c[50]; }; - p = ffi.new("struct abc50 *") - p1 = ffi.addressof(p, "c", 25) - assert ffi.typeof(p1) is ffi.typeof("int *") - assert p1 == ffi.cast("int *", p) + 27 - assert ffi.addressof(p, "c") == ffi.cast("int *", p) + 2 - assert ffi.addressof(p, "c", 0) == ffi.cast("int *", p) + 2 - p2 = ffi.addressof(p, 1) - assert ffi.typeof(p2) is ffi.typeof("struct abc50 *") - assert p2 == p + 1 - - def test_multiple_independent_structs(self): - CDEF2 = "struct ab { int x; };" - ffi2 = cffi.FFI(); ffi2.cdef(CDEF2) - outputfilename = recompile(ffi2, "test_multiple_independent_structs", - CDEF2, tmpdir=str(udir)) - module = imp.load_dynamic("test_multiple_independent_structs", - outputfilename) - ffi1 = module.ffi - foo1 = ffi1.new("struct ab *", [10]) - foo2 = ffi .new("struct ab *", [20, 30]) - assert foo1.x == 10 - assert foo2.a == 20 - assert foo2.b == 30 - - def test_include_struct_union_enum_typedef(self): - ffi1, CCODE = construction_params - ffi2 = cffi.FFI() - ffi2.include(ffi1) - outputfilename = recompile(ffi2, - "test_include_struct_union_enum_typedef", - CCODE, tmpdir=str(udir)) - module = imp.load_dynamic("test_include_struct_union_enum_typedef", - outputfilename) - ffi2 = module.ffi - # - p = ffi2.new("struct nonpacked *", [b'A', -43141]) - assert p.a == b'A' - assert p.b == -43141 - # - p = ffi.new("union simple_u *", [-52525]) - assert p.a == -52525 - # - p = ffi.cast("enum foq", 2) - assert ffi.string(p) == "cffiCC0" - assert ffi2.sizeof("char[cffiCC0]") == 2 - # - p = ffi.new("anon_foo_t *", [-52526]) - assert p.a == -52526 - p = ffi.new("named_foo_p", [-52527]) - assert p.a == -52527 - - def test_struct_packed(self): - # struct nonpacked { char a; int b; }; - # struct is_packed { char a; int b; } __attribute__((packed)); - assert ffi.sizeof("struct nonpacked") == 8 - assert ffi.sizeof("struct is_packed") == 5 - assert ffi.alignof("struct nonpacked") == 4 - assert ffi.alignof("struct is_packed") == 1 - s = ffi.new("struct is_packed[2]") - s[0].b = 42623381 - s[0].a = b'X' - s[1].b = -4892220 - s[1].a = b'Y' - assert s[0].b == 42623381 - assert s[0].a == b'X' - assert s[1].b == -4892220 - assert s[1].a == b'Y' - - def test_not_supported_bitfield_in_result(self): - # struct ints_and_bitfield { int a,b,c,d,e; int x:1; }; - e = py.test.raises(NotImplementedError, ffi.callback, - "struct ints_and_bitfield foo(void)", lambda: 42) - assert str(e.value) == ("struct ints_and_bitfield(*)(): " - "callback with unsupported argument or return type or with '...'") - - def test_inspecttype(self): - assert ffi.typeof("long").kind == "primitive" - assert ffi.typeof("long(*)(long, long**, ...)").cname == ( - "long(*)(long, long * *, ...)") - assert ffi.typeof("long(*)(long, long**, ...)").ellipsis is True - - def test_new_handle(self): - o = [2, 3, 4] - p = ffi.new_handle(o) - assert ffi.typeof(p) == ffi.typeof("void *") - assert ffi.from_handle(p) is o - assert ffi.from_handle(ffi.cast("char *", p)) is o - py.test.raises(RuntimeError, ffi.from_handle, ffi.NULL) - - def test_struct_array_no_length(self): - # struct array_no_length { int x; int a[]; }; - p = ffi.new("struct array_no_length *", [100, [200, 300, 400]]) - assert p.x == 100 - assert ffi.typeof(p.a) is ffi.typeof("int[]") # length available - assert p.a[0] == 200 - assert p.a[1] == 300 - assert p.a[2] == 400 - assert len(p.a) == 3 - assert list(p.a) == [200, 300, 400] - q = ffi.cast("struct array_no_length *", p) - assert ffi.typeof(q.a) is ffi.typeof("int *") # no length available - assert q.a[0] == 200 - assert q.a[1] == 300 - assert q.a[2] == 400 - py.test.raises(TypeError, len, q.a) - py.test.raises(TypeError, list, q.a) - - def test_all_primitives(self): - assert set(PRIMITIVE_TO_INDEX) == set([ - "char", - "short", - "int", - "long", - "long long", - "signed char", - "unsigned char", - "unsigned short", - "unsigned int", - "unsigned long", - "unsigned long long", - "float", - "double", - "long double", - "wchar_t", - "char16_t", - "char32_t", - "_Bool", - "int8_t", - "uint8_t", - "int16_t", - "uint16_t", - "int32_t", - "uint32_t", - "int64_t", - "uint64_t", - "int_least8_t", - "uint_least8_t", - "int_least16_t", - "uint_least16_t", - "int_least32_t", - "uint_least32_t", - "int_least64_t", - "uint_least64_t", - "int_fast8_t", - "uint_fast8_t", - "int_fast16_t", - "uint_fast16_t", - "int_fast32_t", - "uint_fast32_t", - "int_fast64_t", - "uint_fast64_t", - "intptr_t", - "uintptr_t", - "intmax_t", - "uintmax_t", - "ptrdiff_t", - "size_t", - "ssize_t", - 'float _Complex', - 'double _Complex', - ]) - for name in PRIMITIVE_TO_INDEX: - x = ffi.sizeof(name) - assert 1 <= x <= 16 - - def test_emit_c_code(self): - ffi = cffi.FFI() - ffi.set_source("foobar", "??") - c_file = str(udir.join('test_emit_c_code')) - ffi.emit_c_code(c_file) - assert os.path.isfile(c_file) - - def test_import_from_lib(self): - ffi2 = cffi.FFI() - ffi2.cdef("int myfunc(int); extern int myvar;\n#define MYFOO ...\n") - outputfilename = recompile(ffi2, "_test_import_from_lib", - "int myfunc(int x) { return x + 1; }\n" - "int myvar = -5;\n" - "#define MYFOO 42", tmpdir=str(udir)) - imp.load_dynamic("_test_import_from_lib", outputfilename) - from _test_import_from_lib.lib import myfunc, myvar, MYFOO - assert MYFOO == 42 - assert myfunc(43) == 44 - assert myvar == -5 # but can't be changed, so not very useful - with pytest.raises(ImportError): - from _test_import_from_lib.lib import bar - d = {} - exec("from _test_import_from_lib.lib import *", d) - assert (set(key for key in d if not key.startswith('_')) == - set(['myfunc', 'MYFOO'])) - # - # also test "import *" on the module itself, which should be - # equivalent to "import ffi, lib" - d = {} - exec("from _test_import_from_lib import *", d) - assert (sorted([x for x in d.keys() if not x.startswith('__')]) == - ['ffi', 'lib']) - - def test_char16_t(self): - x = ffi.new("char16_t[]", 5) - assert len(x) == 5 and ffi.sizeof(x) == 10 - x[2] = u+'\u1324' - assert x[2] == u+'\u1324' - y = ffi.new("char16_t[]", u+'\u1234\u5678') - assert len(y) == 3 - assert list(y) == [u+'\u1234', u+'\u5678', u+'\x00'] - assert ffi.string(y) == u+'\u1234\u5678' - z = ffi.new("char16_t[]", u+'\U00012345') - assert len(z) == 3 - assert list(z) == [u+'\ud808', u+'\udf45', u+'\x00'] - assert ffi.string(z) == u+'\U00012345' - - def test_char32_t(self): - x = ffi.new("char32_t[]", 5) - assert len(x) == 5 and ffi.sizeof(x) == 20 - x[3] = u+'\U00013245' - assert x[3] == u+'\U00013245' - y = ffi.new("char32_t[]", u+'\u1234\u5678') - assert len(y) == 3 - assert list(y) == [u+'\u1234', u+'\u5678', u+'\x00'] - z = ffi.new("char32_t[]", u+'\U00012345') - assert len(z) == 2 - assert list(z) == [u+'\U00012345', u+'\x00'] # maybe a 2-unichars strin - assert ffi.string(z) == u+'\U00012345' diff --git a/testing/cffi1/test_parse_c_type.py b/testing/cffi1/test_parse_c_type.py deleted file mode 100644 index a9f5fcb..0000000 --- a/testing/cffi1/test_parse_c_type.py +++ /dev/null @@ -1,372 +0,0 @@ -import sys, re, os, py -import cffi -from cffi import cffi_opcode - -if '__pypy__' in sys.builtin_module_names: - try: - # pytest >= 4.0 - py.test.skip("not available on pypy", allow_module_level=True) - except TypeError: - # older pytest - py.test.skip("not available on pypy") - -cffi_dir = os.path.dirname(cffi_opcode.__file__) - -r_macro = re.compile(r"#define \w+[(][^\n]*|#include [^\n]*") -r_define = re.compile(r"(#define \w+) [^\n]*") -r_ifdefs = re.compile(r"(#ifdef |#endif)[^\n]*") -header = open(os.path.join(cffi_dir, 'parse_c_type.h')).read() -header = r_macro.sub(r"", header) -header = r_define.sub(r"\1 ...", header) -header = r_ifdefs.sub(r"", header) - -ffi = cffi.FFI() -ffi.cdef(header) - -lib = ffi.verify( - open(os.path.join(cffi_dir, '..', 'c', 'parse_c_type.c')).read() + """ -static const char *get_common_type(const char *search, size_t search_len) { - return NULL; -} -""", include_dirs=[cffi_dir]) - -class ParseError(Exception): - pass - -struct_names = ["bar_s", "foo", "foo_", "foo_s", "foo_s1", "foo_s12"] -assert struct_names == sorted(struct_names) - -enum_names = ["ebar_s", "efoo", "efoo_", "efoo_s", "efoo_s1", "efoo_s12"] -assert enum_names == sorted(enum_names) - -identifier_names = ["id", "id0", "id05", "id05b", "tail"] -assert identifier_names == sorted(identifier_names) - -global_names = ["FIVE", "NEG", "ZERO"] -assert global_names == sorted(global_names) - -ctx = ffi.new("struct _cffi_type_context_s *") -c_struct_names = [ffi.new("char[]", _n.encode('ascii')) for _n in struct_names] -ctx_structs = ffi.new("struct _cffi_struct_union_s[]", len(struct_names)) -for _i in range(len(struct_names)): - ctx_structs[_i].name = c_struct_names[_i] -ctx_structs[3].flags = lib._CFFI_F_UNION -ctx.struct_unions = ctx_structs -ctx.num_struct_unions = len(struct_names) - -c_enum_names = [ffi.new("char[]", _n.encode('ascii')) for _n in enum_names] -ctx_enums = ffi.new("struct _cffi_enum_s[]", len(enum_names)) -for _i in range(len(enum_names)): - ctx_enums[_i].name = c_enum_names[_i] -ctx.enums = ctx_enums -ctx.num_enums = len(enum_names) - -c_identifier_names = [ffi.new("char[]", _n.encode('ascii')) - for _n in identifier_names] -ctx_identifiers = ffi.new("struct _cffi_typename_s[]", len(identifier_names)) -for _i in range(len(identifier_names)): - ctx_identifiers[_i].name = c_identifier_names[_i] - ctx_identifiers[_i].type_index = 100 + _i -ctx.typenames = ctx_identifiers -ctx.num_typenames = len(identifier_names) - -@ffi.callback("int(unsigned long long *)") -def fetch_constant_five(p): - p[0] = 5 - return 0 -@ffi.callback("int(unsigned long long *)") -def fetch_constant_zero(p): - p[0] = 0 - return 1 -@ffi.callback("int(unsigned long long *)") -def fetch_constant_neg(p): - p[0] = 123321 - return 1 - -ctx_globals = ffi.new("struct _cffi_global_s[]", len(global_names)) -c_glob_names = [ffi.new("char[]", _n.encode('ascii')) for _n in global_names] -for _i, _fn in enumerate([fetch_constant_five, - fetch_constant_neg, - fetch_constant_zero]): - ctx_globals[_i].name = c_glob_names[_i] - ctx_globals[_i].address = _fn - ctx_globals[_i].type_op = ffi.cast("_cffi_opcode_t", - cffi_opcode.OP_CONSTANT_INT if _i != 1 - else cffi_opcode.OP_ENUM) -ctx.globals = ctx_globals -ctx.num_globals = len(global_names) - - -def parse(input): - out = ffi.new("_cffi_opcode_t[]", 100) - info = ffi.new("struct _cffi_parse_info_s *") - info.ctx = ctx - info.output = out - info.output_size = len(out) - for j in range(len(out)): - out[j] = ffi.cast("void *", -424242) - res = lib.parse_c_type(info, input.encode('ascii')) - if res < 0: - raise ParseError(ffi.string(info.error_message).decode('ascii'), - info.error_location) - assert 0 <= res < len(out) - result = [] - for j in range(len(out)): - if out[j] == ffi.cast("void *", -424242): - assert res < j - break - i = int(ffi.cast("intptr_t", out[j])) - if j == res: - result.append('->') - result.append(i) - return result - -def parsex(input): - result = parse(input) - def str_if_int(x): - if isinstance(x, str): - return x - return '%d,%d' % (x & 255, x >> 8) - return ' '.join(map(str_if_int, result)) - -def parse_error(input, expected_msg, expected_location): - e = py.test.raises(ParseError, parse, input) - assert e.value.args[0] == expected_msg - assert e.value.args[1] == expected_location - -def make_getter(name): - opcode = getattr(lib, '_CFFI_OP_' + name) - def getter(value): - return opcode | (value << 8) - return getter - -Prim = make_getter('PRIMITIVE') -Pointer = make_getter('POINTER') -Array = make_getter('ARRAY') -OpenArray = make_getter('OPEN_ARRAY') -NoOp = make_getter('NOOP') -Func = make_getter('FUNCTION') -FuncEnd = make_getter('FUNCTION_END') -Struct = make_getter('STRUCT_UNION') -Enum = make_getter('ENUM') -Typename = make_getter('TYPENAME') - - -def test_simple(): - for simple_type, expected in [ - ("int", lib._CFFI_PRIM_INT), - ("signed int", lib._CFFI_PRIM_INT), - (" long ", lib._CFFI_PRIM_LONG), - ("long int", lib._CFFI_PRIM_LONG), - ("unsigned short", lib._CFFI_PRIM_USHORT), - ("long double", lib._CFFI_PRIM_LONGDOUBLE), - (" float _Complex", lib._CFFI_PRIM_FLOATCOMPLEX), - ("double _Complex ", lib._CFFI_PRIM_DOUBLECOMPLEX), - ]: - assert parse(simple_type) == ['->', Prim(expected)] - -def test_array(): - assert parse("int[5]") == [Prim(lib._CFFI_PRIM_INT), '->', Array(0), 5] - assert parse("int[]") == [Prim(lib._CFFI_PRIM_INT), '->', OpenArray(0)] - assert parse("int[5][8]") == [Prim(lib._CFFI_PRIM_INT), - '->', Array(3), - 5, - Array(0), - 8] - assert parse("int[][8]") == [Prim(lib._CFFI_PRIM_INT), - '->', OpenArray(2), - Array(0), - 8] - -def test_pointer(): - assert parse("int*") == [Prim(lib._CFFI_PRIM_INT), '->', Pointer(0)] - assert parse("int***") == [Prim(lib._CFFI_PRIM_INT), - Pointer(0), Pointer(1), '->', Pointer(2)] - -def test_grouping(): - assert parse("int*[]") == [Prim(lib._CFFI_PRIM_INT), - Pointer(0), '->', OpenArray(1)] - assert parse("int**[][8]") == [Prim(lib._CFFI_PRIM_INT), - Pointer(0), Pointer(1), - '->', OpenArray(4), Array(2), 8] - assert parse("int(*)[]") == [Prim(lib._CFFI_PRIM_INT), - NoOp(3), '->', Pointer(1), OpenArray(0)] - assert parse("int(*)[][8]") == [Prim(lib._CFFI_PRIM_INT), - NoOp(3), '->', Pointer(1), - OpenArray(4), Array(0), 8] - assert parse("int**(**)") == [Prim(lib._CFFI_PRIM_INT), - Pointer(0), Pointer(1), - NoOp(2), Pointer(3), '->', Pointer(4)] - assert parse("int**(**)[]") == [Prim(lib._CFFI_PRIM_INT), - Pointer(0), Pointer(1), - NoOp(6), Pointer(3), '->', Pointer(4), - OpenArray(2)] - -def test_simple_function(): - assert parse("int()") == [Prim(lib._CFFI_PRIM_INT), - '->', Func(0), FuncEnd(0), 0] - assert parse("int(int)") == [Prim(lib._CFFI_PRIM_INT), - '->', Func(0), NoOp(4), FuncEnd(0), - Prim(lib._CFFI_PRIM_INT)] - assert parse("int(long, char)") == [ - Prim(lib._CFFI_PRIM_INT), - '->', Func(0), NoOp(5), NoOp(6), FuncEnd(0), - Prim(lib._CFFI_PRIM_LONG), - Prim(lib._CFFI_PRIM_CHAR)] - assert parse("int(int*)") == [Prim(lib._CFFI_PRIM_INT), - '->', Func(0), NoOp(5), FuncEnd(0), - Prim(lib._CFFI_PRIM_INT), - Pointer(4)] - assert parse("int*(void)") == [Prim(lib._CFFI_PRIM_INT), - Pointer(0), - '->', Func(1), FuncEnd(0), 0] - assert parse("int(int, ...)") == [Prim(lib._CFFI_PRIM_INT), - '->', Func(0), NoOp(5), FuncEnd(1), 0, - Prim(lib._CFFI_PRIM_INT)] - -def test_internal_function(): - assert parse("int(*)()") == [Prim(lib._CFFI_PRIM_INT), - NoOp(3), '->', Pointer(1), - Func(0), FuncEnd(0), 0] - assert parse("int(*())[]") == [Prim(lib._CFFI_PRIM_INT), - NoOp(6), Pointer(1), - '->', Func(2), FuncEnd(0), 0, - OpenArray(0)] - assert parse("int(char(*)(long, short))") == [ - Prim(lib._CFFI_PRIM_INT), - '->', Func(0), NoOp(6), FuncEnd(0), - Prim(lib._CFFI_PRIM_CHAR), - NoOp(7), Pointer(5), - Func(4), NoOp(11), NoOp(12), FuncEnd(0), - Prim(lib._CFFI_PRIM_LONG), - Prim(lib._CFFI_PRIM_SHORT)] - -def test_fix_arg_types(): - assert parse("int(char(long, short))") == [ - Prim(lib._CFFI_PRIM_INT), - '->', Func(0), Pointer(5), FuncEnd(0), - Prim(lib._CFFI_PRIM_CHAR), - Func(4), NoOp(9), NoOp(10), FuncEnd(0), - Prim(lib._CFFI_PRIM_LONG), - Prim(lib._CFFI_PRIM_SHORT)] - assert parse("int(char[])") == [ - Prim(lib._CFFI_PRIM_INT), - '->', Func(0), Pointer(4), FuncEnd(0), - Prim(lib._CFFI_PRIM_CHAR), - OpenArray(4)] - -def test_enum(): - for i in range(len(enum_names)): - assert parse("enum %s" % (enum_names[i],)) == ['->', Enum(i)] - assert parse("enum %s*" % (enum_names[i],)) == [Enum(i), - '->', Pointer(0)] - -def test_error(): - parse_error("short short int", "'short' after another 'short' or 'long'", 6) - parse_error("long long long", "'long long long' is too long", 10) - parse_error("short long", "'long' after 'short'", 6) - parse_error("signed unsigned int", "multiple 'signed' or 'unsigned'", 7) - parse_error("unsigned signed int", "multiple 'signed' or 'unsigned'", 9) - parse_error("long char", "invalid combination of types", 5) - parse_error("short char", "invalid combination of types", 6) - parse_error("signed void", "invalid combination of types", 7) - parse_error("unsigned struct", "invalid combination of types", 9) - # - parse_error("", "identifier expected", 0) - parse_error("]", "identifier expected", 0) - parse_error("*", "identifier expected", 0) - parse_error("int ]**", "unexpected symbol", 4) - parse_error("char char", "unexpected symbol", 5) - parse_error("int(int]", "expected ')'", 7) - parse_error("int(*]", "expected ')'", 5) - parse_error("int(]", "identifier expected", 4) - parse_error("int[?]", "expected a positive integer constant", 4) - parse_error("int[24)", "expected ']'", 6) - parse_error("struct", "struct or union name expected", 6) - parse_error("struct 24", "struct or union name expected", 7) - parse_error("int[5](*)", "unexpected symbol", 6) - parse_error("int a(*)", "identifier expected", 6) - parse_error("int[123456789012345678901234567890]", "number too large", 4) - # - parse_error("_Complex", "identifier expected", 0) - parse_error("int _Complex", "_Complex type combination unsupported", 4) - parse_error("long double _Complex", "_Complex type combination unsupported", - 12) - -def test_number_too_large(): - num_max = sys.maxsize - assert parse("char[%d]" % num_max) == [Prim(lib._CFFI_PRIM_CHAR), - '->', Array(0), num_max] - parse_error("char[%d]" % (num_max + 1), "number too large", 5) - -def test_complexity_limit(): - parse_error("int" + "[]" * 2500, "internal type complexity limit reached", - 202) - -def test_struct(): - for i in range(len(struct_names)): - if i == 3: - tag = "union" - else: - tag = "struct" - assert parse("%s %s" % (tag, struct_names[i])) == ['->', Struct(i)] - assert parse("%s %s*" % (tag, struct_names[i])) == [Struct(i), - '->', Pointer(0)] - -def test_exchanging_struct_union(): - parse_error("union %s" % (struct_names[0],), - "wrong kind of tag: struct vs union", 6) - parse_error("struct %s" % (struct_names[3],), - "wrong kind of tag: struct vs union", 7) - -def test_identifier(): - for i in range(len(identifier_names)): - assert parse("%s" % (identifier_names[i])) == ['->', Typename(i)] - assert parse("%s*" % (identifier_names[i])) == [Typename(i), - '->', Pointer(0)] - -def test_cffi_opcode_sync(): - import cffi.model - for name in dir(lib): - if name.startswith('_CFFI_'): - assert getattr(cffi_opcode, name[6:]) == getattr(lib, name) - assert sorted(cffi_opcode.PRIMITIVE_TO_INDEX.keys()) == ( - sorted(cffi.model.PrimitiveType.ALL_PRIMITIVE_TYPES.keys())) - -def test_array_length_from_constant(): - parse_error("int[UNKNOWN]", "expected a positive integer constant", 4) - assert parse("int[FIVE]") == [Prim(lib._CFFI_PRIM_INT), '->', Array(0), 5] - assert parse("int[ZERO]") == [Prim(lib._CFFI_PRIM_INT), '->', Array(0), 0] - parse_error("int[NEG]", "expected a positive integer constant", 4) - -def test_various_constant_exprs(): - def array(n): - return [Prim(lib._CFFI_PRIM_CHAR), '->', Array(0), n] - assert parse("char[21]") == array(21) - assert parse("char[0x10]") == array(16) - assert parse("char[0X21]") == array(33) - assert parse("char[0Xb]") == array(11) - assert parse("char[0x1C]") == array(0x1C) - assert parse("char[0xc6]") == array(0xC6) - assert parse("char[010]") == array(8) - assert parse("char[021]") == array(17) - parse_error("char[08]", "invalid number", 5) - parse_error("char[1C]", "invalid number", 5) - parse_error("char[0C]", "invalid number", 5) - # not supported (really obscure): - # "char[+5]" - # "char['A']" - -def test_stdcall_cdecl(): - assert parse("int __stdcall(int)") == [Prim(lib._CFFI_PRIM_INT), - '->', Func(0), NoOp(4), FuncEnd(2), - Prim(lib._CFFI_PRIM_INT)] - assert parse("int __stdcall func(int)") == parse("int __stdcall(int)") - assert parse("int (__stdcall *)()") == [Prim(lib._CFFI_PRIM_INT), - NoOp(3), '->', Pointer(1), - Func(0), FuncEnd(2), 0] - assert parse("int (__stdcall *p)()") == parse("int (__stdcall*)()") - parse_error("__stdcall int", "identifier expected", 0) - parse_error("__cdecl int", "identifier expected", 0) - parse_error("int __stdcall", "expected '('", 13) - parse_error("int __cdecl", "expected '('", 11) diff --git a/testing/cffi1/test_pkgconfig.py b/testing/cffi1/test_pkgconfig.py deleted file mode 100644 index c725cca..0000000 --- a/testing/cffi1/test_pkgconfig.py +++ /dev/null @@ -1,94 +0,0 @@ -import sys -import subprocess -import py -import cffi.pkgconfig as pkgconfig -from cffi import PkgConfigError - - -def mock_call(libname, flag): - assert libname=="foobarbaz" - flags = { - "--cflags": "-I/usr/include/python3.6m -DABCD -DCFFI_TEST=1 -O42\n", - "--libs": "-L/usr/lib64 -lpython3.6 -shared\n", - } - return flags[flag] - - -def test_merge_flags(): - d1 = {"ham": [1, 2, 3], "spam" : ["a", "b", "c"], "foo" : []} - d2 = {"spam" : ["spam", "spam", "spam"], "bar" : ["b", "a", "z"]} - - pkgconfig.merge_flags(d1, d2) - assert d1 == { - "ham": [1, 2, 3], - "spam" : ["a", "b", "c", "spam", "spam", "spam"], - "bar" : ["b", "a", "z"], - "foo" : []} - - -def test_pkgconfig(): - assert pkgconfig.flags_from_pkgconfig([]) == {} - - saved = pkgconfig.call - try: - pkgconfig.call = mock_call - flags = pkgconfig.flags_from_pkgconfig(["foobarbaz"]) - finally: - pkgconfig.call = saved - assert flags == { - 'include_dirs': ['/usr/include/python3.6m'], - 'library_dirs': ['/usr/lib64'], - 'libraries': ['python3.6'], - 'define_macros': [('ABCD', None), ('CFFI_TEST', '1')], - 'extra_compile_args': ['-O42'], - 'extra_link_args': ['-shared'] - } - -class mock_subprocess: - PIPE = Ellipsis - class Popen: - def __init__(self, cmd, stdout, stderr): - if mock_subprocess.RESULT is None: - raise OSError("oops can't run") - assert cmd == ['pkg-config', '--print-errors', '--cflags', 'libfoo'] - def communicate(self): - bout, berr, rc = mock_subprocess.RESULT - self.returncode = rc - return bout, berr - -def test_call(): - saved = pkgconfig.subprocess - try: - pkgconfig.subprocess = mock_subprocess - - mock_subprocess.RESULT = None - e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") - assert str(e.value) == "cannot run pkg-config: oops can't run" - - mock_subprocess.RESULT = b"", "Foo error!\n", 1 - e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") - assert str(e.value) == "Foo error!" - - mock_subprocess.RESULT = b"abc\\def\n", "", 0 - e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") - assert str(e.value).startswith("pkg-config --cflags libfoo returned an " - "unsupported backslash-escaped output:") - - mock_subprocess.RESULT = b"abc def\n", "", 0 - result = pkgconfig.call("libfoo", "--cflags") - assert result == "abc def\n" - - mock_subprocess.RESULT = b"abc def\n", "", 0 - result = pkgconfig.call("libfoo", "--cflags") - assert result == "abc def\n" - - if sys.version_info >= (3,): - mock_subprocess.RESULT = b"\xff\n", "", 0 - e = py.test.raises(PkgConfigError, pkgconfig.call, - "libfoo", "--cflags", encoding="utf-8") - assert str(e.value) == ( - "pkg-config --cflags libfoo returned bytes that cannot be " - "decoded with encoding 'utf-8':\nb'\\xff\\n'") - - finally: - pkgconfig.subprocess = saved diff --git a/testing/cffi1/test_re_python.py b/testing/cffi1/test_re_python.py deleted file mode 100644 index 2ae0dd1..0000000 --- a/testing/cffi1/test_re_python.py +++ /dev/null @@ -1,288 +0,0 @@ -import sys, os -import py -from cffi import FFI -from cffi import recompiler, ffiplatform, VerificationMissing -from testing.udir import udir -from testing.support import u - - -def setup_module(mod): - SRC = """ - #include <string.h> - #define FOOBAR (-42) - static const int FOOBAZ = -43; - #define BIGPOS 420000000000L - #define BIGNEG -420000000000L - int add42(int x) { return x + 42; } - int add43(int x, ...) { return x; } - int globalvar42 = 1234; - const int globalconst42 = 4321; - const char *const globalconsthello = "hello"; - struct foo_s; - typedef struct bar_s { int x; signed char a[]; } bar_t; - enum foo_e { AA, BB, CC }; - - void init_test_re_python(void) { } /* windows hack */ - void PyInit__test_re_python(void) { } /* windows hack */ - """ - tmpdir = udir.join('test_re_python') - tmpdir.ensure(dir=1) - c_file = tmpdir.join('_test_re_python.c') - c_file.write(SRC) - ext = ffiplatform.get_extension( - str(c_file), - '_test_re_python', - export_symbols=['add42', 'add43', 'globalvar42', - 'globalconst42', 'globalconsthello'] - ) - outputfilename = ffiplatform.compile(str(tmpdir), ext) - - # test with a non-ascii char - ofn, oext = os.path.splitext(outputfilename) - if sys.platform == "win32": - unicode_name = ofn + (u+'\u03be') + oext - else: - unicode_name = ofn + (u+'\xe9') + oext - try: - unicode_name.encode(sys.getfilesystemencoding()) - except UnicodeEncodeError: - unicode_name = None - if unicode_name is not None: - print(repr(outputfilename) + ' ==> ' + repr(unicode_name)) - os.rename(outputfilename, unicode_name) - outputfilename = unicode_name - - mod.extmod = outputfilename - mod.tmpdir = tmpdir - # - ffi = FFI() - ffi.cdef(""" - #define FOOBAR -42 - static const int FOOBAZ = -43; - #define BIGPOS 420000000000L - #define BIGNEG -420000000000L - int add42(int); - int add43(int, ...); - extern int globalvar42; - const int globalconst42; - const char *const globalconsthello; - int no_such_function(int); - extern int no_such_globalvar; - struct foo_s; - typedef struct bar_s { int x; signed char a[]; } bar_t; - enum foo_e { AA, BB, CC }; - int strlen(const char *); - struct with_union { union { int a; char b; }; }; - union with_struct { struct { int a; char b; }; }; - struct with_struct_with_union { struct { union { int x; }; } cp; }; - struct NVGcolor { union { float rgba[4]; struct { float r,g,b,a; }; }; }; - typedef struct selfref { struct selfref *next; } *selfref_ptr_t; - """) - ffi.set_source('re_python_pysrc', None) - ffi.emit_python_code(str(tmpdir.join('re_python_pysrc.py'))) - mod.original_ffi = ffi - # - sys.path.insert(0, str(tmpdir)) - - -def test_constant(): - from re_python_pysrc import ffi - assert ffi.integer_const('FOOBAR') == -42 - assert ffi.integer_const('FOOBAZ') == -43 - -def test_large_constant(): - from re_python_pysrc import ffi - assert ffi.integer_const('BIGPOS') == 420000000000 - assert ffi.integer_const('BIGNEG') == -420000000000 - -def test_function(): - import _cffi_backend - from re_python_pysrc import ffi - lib = ffi.dlopen(extmod) - assert lib.add42(-10) == 32 - assert type(lib.add42) is _cffi_backend.FFI.CData - -def test_function_with_varargs(): - import _cffi_backend - from re_python_pysrc import ffi - lib = ffi.dlopen(extmod, 0) - assert lib.add43(45, ffi.cast("int", -5)) == 45 - assert type(lib.add43) is _cffi_backend.FFI.CData - -def test_dlopen_none(): - import _cffi_backend - from re_python_pysrc import ffi - name = None - if sys.platform == 'win32': - import ctypes.util - name = ctypes.util.find_msvcrt() - if name is None: - py.test.skip("dlopen(None) cannot work on Windows with Python 3") - lib = ffi.dlopen(name) - assert lib.strlen(b"hello") == 5 - -def test_dlclose(): - import _cffi_backend - from re_python_pysrc import ffi - lib = ffi.dlopen(extmod) - ffi.dlclose(lib) - if type(extmod) is not str: # unicode, on python 2 - str_extmod = extmod.encode('utf-8') - else: - str_extmod = extmod - e = py.test.raises(ffi.error, getattr, lib, 'add42') - assert str(e.value) == ( - "library '%s' has been closed" % (str_extmod,)) - ffi.dlclose(lib) # does not raise - -def test_constant_via_lib(): - from re_python_pysrc import ffi - lib = ffi.dlopen(extmod) - assert lib.FOOBAR == -42 - assert lib.FOOBAZ == -43 - -def test_opaque_struct(): - from re_python_pysrc import ffi - ffi.cast("struct foo_s *", 0) - py.test.raises(TypeError, ffi.new, "struct foo_s *") - -def test_nonopaque_struct(): - from re_python_pysrc import ffi - for p in [ffi.new("struct bar_s *", [5, b"foobar"]), - ffi.new("bar_t *", [5, b"foobar"])]: - assert p.x == 5 - assert p.a[0] == ord('f') - assert p.a[5] == ord('r') - -def test_enum(): - from re_python_pysrc import ffi - assert ffi.integer_const("BB") == 1 - e = ffi.cast("enum foo_e", 2) - assert ffi.string(e) == "CC" - -def test_include_1(): - sub_ffi = FFI() - sub_ffi.cdef("static const int k2 = 121212;") - sub_ffi.include(original_ffi) - assert 'macro FOOBAR' in original_ffi._parser._declarations - assert 'macro FOOBAZ' in original_ffi._parser._declarations - sub_ffi.set_source('re_python_pysrc', None) - sub_ffi.emit_python_code(str(tmpdir.join('_re_include_1.py'))) - # - if sys.version_info[:2] >= (3, 3): - import importlib - importlib.invalidate_caches() # issue 197 (but can't reproduce myself) - # - from _re_include_1 import ffi - assert ffi.integer_const('FOOBAR') == -42 - assert ffi.integer_const('FOOBAZ') == -43 - assert ffi.integer_const('k2') == 121212 - lib = ffi.dlopen(extmod) # <- a random unrelated library would be fine - assert lib.FOOBAR == -42 - assert lib.FOOBAZ == -43 - assert lib.k2 == 121212 - # - p = ffi.new("bar_t *", [5, b"foobar"]) - assert p.a[4] == ord('a') - -def test_global_var(): - from re_python_pysrc import ffi - lib = ffi.dlopen(extmod) - assert lib.globalvar42 == 1234 - p = ffi.addressof(lib, 'globalvar42') - lib.globalvar42 += 5 - assert p[0] == 1239 - p[0] -= 1 - assert lib.globalvar42 == 1238 - -def test_global_const_int(): - from re_python_pysrc import ffi - lib = ffi.dlopen(extmod) - assert lib.globalconst42 == 4321 - py.test.raises(AttributeError, ffi.addressof, lib, 'globalconst42') - -def test_global_const_nonint(): - from re_python_pysrc import ffi - lib = ffi.dlopen(extmod) - assert ffi.string(lib.globalconsthello, 8) == b"hello" - py.test.raises(AttributeError, ffi.addressof, lib, 'globalconsthello') - -def test_rtld_constants(): - from re_python_pysrc import ffi - ffi.RTLD_NOW # check that we have the attributes - ffi.RTLD_LAZY - ffi.RTLD_GLOBAL - -def test_no_such_function_or_global_var(): - from re_python_pysrc import ffi - lib = ffi.dlopen(extmod) - e = py.test.raises(ffi.error, getattr, lib, 'no_such_function') - assert str(e.value).startswith( - "symbol 'no_such_function' not found in library '") - e = py.test.raises(ffi.error, getattr, lib, 'no_such_globalvar') - assert str(e.value).startswith( - "symbol 'no_such_globalvar' not found in library '") - -def test_check_version(): - import _cffi_backend - e = py.test.raises(ImportError, _cffi_backend.FFI, - "foobar", _version=0x2594) - assert str(e.value).startswith( - "cffi out-of-line Python module 'foobar' has unknown version") - -def test_partial_enum(): - ffi = FFI() - ffi.cdef("enum foo { A, B, ... };") - ffi.set_source('test_partial_enum', None) - py.test.raises(VerificationMissing, ffi.emit_python_code, - str(tmpdir.join('test_partial_enum.py'))) - -def test_anonymous_union_inside_struct(): - # based on issue #357 - from re_python_pysrc import ffi - INT = ffi.sizeof("int") - assert ffi.offsetof("struct with_union", "a") == 0 - assert ffi.offsetof("struct with_union", "b") == 0 - assert ffi.sizeof("struct with_union") == INT - # - assert ffi.offsetof("union with_struct", "a") == 0 - assert ffi.offsetof("union with_struct", "b") == INT - assert ffi.sizeof("union with_struct") >= INT + 1 - # - assert ffi.sizeof("struct with_struct_with_union") == INT - p = ffi.new("struct with_struct_with_union *") - assert p.cp.x == 0 - # - FLOAT = ffi.sizeof("float") - assert ffi.sizeof("struct NVGcolor") == FLOAT * 4 - assert ffi.offsetof("struct NVGcolor", "rgba") == 0 - assert ffi.offsetof("struct NVGcolor", "r") == 0 - assert ffi.offsetof("struct NVGcolor", "g") == FLOAT - assert ffi.offsetof("struct NVGcolor", "b") == FLOAT * 2 - assert ffi.offsetof("struct NVGcolor", "a") == FLOAT * 3 - -def test_selfref(): - # based on issue #429 - from re_python_pysrc import ffi - ffi.new("selfref_ptr_t") - -def test_dlopen_handle(): - import _cffi_backend - from re_python_pysrc import ffi - if sys.platform == 'win32': - py.test.skip("uses 'dl' explicitly") - ffi1 = FFI() - ffi1.cdef("""void *dlopen(const char *filename, int flags); - int dlclose(void *handle);""") - lib1 = ffi1.dlopen('dl') - handle = lib1.dlopen(extmod.encode(sys.getfilesystemencoding()), - _cffi_backend.RTLD_LAZY) - assert ffi1.typeof(handle) == ffi1.typeof("void *") - assert handle - - lib = ffi.dlopen(handle) - assert lib.add42(-10) == 32 - assert type(lib.add42) is _cffi_backend.FFI.CData - - err = lib1.dlclose(handle) - assert err == 0 diff --git a/testing/cffi1/test_realize_c_type.py b/testing/cffi1/test_realize_c_type.py deleted file mode 100644 index a1f31e6..0000000 --- a/testing/cffi1/test_realize_c_type.py +++ /dev/null @@ -1,73 +0,0 @@ -import py, sys -from cffi import cffi_opcode - - -def check(input, expected_output=None, expected_ffi_error=False): - import _cffi_backend - ffi = _cffi_backend.FFI() - if not expected_ffi_error: - ct = ffi.typeof(input) - assert isinstance(ct, ffi.CType) - assert ct.cname == (expected_output or input) - else: - e = py.test.raises(ffi.error, ffi.typeof, input) - if isinstance(expected_ffi_error, str): - assert str(e.value) == expected_ffi_error - -def test_void(): - check("void", "void") - check(" void ", "void") - -def test_int_star(): - check("int") - check("int *") - check("int*", "int *") - check("long int", "long") - check("long") - -def test_noop(): - check("int(*)", "int *") - -def test_array(): - check("int[6]") - -def test_funcptr(): - check("int(*)(long)") - check("int(long)", expected_ffi_error="the type 'int(long)' is a" - " function type, not a pointer-to-function type") - check("int(void)", expected_ffi_error="the type 'int()' is a" - " function type, not a pointer-to-function type") - -def test_funcptr_rewrite_args(): - check("int(*)(int(int))", "int(*)(int(*)(int))") - check("int(*)(long[])", "int(*)(long *)") - check("int(*)(long[5])", "int(*)(long *)") - -def test_all_primitives(): - for name in cffi_opcode.PRIMITIVE_TO_INDEX: - check(name, name) - -def check_func(input, expected_output=None): - import _cffi_backend - ffi = _cffi_backend.FFI() - ct = ffi.typeof(ffi.callback(input, lambda: None)) - assert isinstance(ct, ffi.CType) - if sys.platform != 'win32' or sys.maxsize > 2**32: - expected_output = expected_output.replace('__stdcall *', '*') - assert ct.cname == expected_output - -def test_funcptr_stdcall(): - check_func("int(int)", "int(*)(int)") - check_func("int foobar(int)", "int(*)(int)") - check_func("int __stdcall(int)", "int(__stdcall *)(int)") - check_func("int __stdcall foobar(int)", "int(__stdcall *)(int)") - check_func("void __cdecl(void)", "void(*)()") - check_func("void __cdecl foobar(void)", "void(*)()") - check_func("void __stdcall(void)", "void(__stdcall *)()") - check_func("void __stdcall foobar(long, short)", - "void(__stdcall *)(long, short)") - check_func("void(void __cdecl(void), void __stdcall(void))", - "void(*)(void(*)(), void(__stdcall *)())") - -def test_variadic_overrides_stdcall(): - check("void (__stdcall*)(int, ...)", "void(*)(int, ...)") diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py deleted file mode 100644 index fdb4d5a..0000000 --- a/testing/cffi1/test_recompiler.py +++ /dev/null @@ -1,2495 +0,0 @@ - -import sys, os, py -import pytest -from cffi import FFI, VerificationError, FFIError, CDefError -from cffi import recompiler -from testing.udir import udir -from testing.support import u, long -from testing.support import FdWriteCapture, StdErrCapture, _verify - -try: - import importlib -except ImportError: - importlib = None - - -def check_type_table(input, expected_output, included=None): - ffi = FFI() - if included: - ffi1 = FFI() - ffi1.cdef(included) - ffi.include(ffi1) - ffi.cdef(input) - recomp = recompiler.Recompiler(ffi, 'testmod') - recomp.collect_type_table() - assert ''.join(map(str, recomp.cffi_types)) == expected_output - -def verify(ffi, module_name, source, *args, **kwds): - no_cpp = kwds.pop('no_cpp', False) - ignore_warnings = kwds.pop('ignore_warnings', False) - kwds.setdefault('undef_macros', ['NDEBUG']) - module_name = '_CFFI_' + module_name - ffi.set_source(module_name, source) - if not os.environ.get('NO_CPP') and not no_cpp: # test the .cpp mode too - kwds.setdefault('source_extension', '.cpp') - source = 'extern "C" {\n%s\n}' % (source,) - elif sys.platform != 'win32' and not ignore_warnings: - # add '-Werror' to the existing 'extra_compile_args' flags - from testing.support import extra_compile_args - kwds['extra_compile_args'] = (kwds.get('extra_compile_args', []) + - extra_compile_args) - if sys.platform == 'darwin': - kwds['extra_link_args'] = (kwds.get('extra_link_args', []) + - ['-stdlib=libc++']) - return _verify(ffi, module_name, source, *args, **kwds) - -def test_set_source_no_slashes(): - ffi = FFI() - py.test.raises(ValueError, ffi.set_source, "abc/def", None) - py.test.raises(ValueError, ffi.set_source, "abc/def", "C code") - - -def test_type_table_func(): - check_type_table("double sin(double);", - "(FUNCTION 1)(PRIMITIVE 14)(FUNCTION_END 0)") - check_type_table("float sin(double);", - "(FUNCTION 3)(PRIMITIVE 14)(FUNCTION_END 0)(PRIMITIVE 13)") - check_type_table("float sin(void);", - "(FUNCTION 2)(FUNCTION_END 0)(PRIMITIVE 13)") - check_type_table("double sin(float); double cos(float);", - "(FUNCTION 3)(PRIMITIVE 13)(FUNCTION_END 0)(PRIMITIVE 14)") - check_type_table("double sin(float); double cos(double);", - "(FUNCTION 1)(PRIMITIVE 14)(FUNCTION_END 0)" # cos - "(FUNCTION 1)(PRIMITIVE 13)(FUNCTION_END 0)") # sin - check_type_table("float sin(double); float cos(float);", - "(FUNCTION 4)(PRIMITIVE 14)(FUNCTION_END 0)" # sin - "(FUNCTION 4)(PRIMITIVE 13)(FUNCTION_END 0)") # cos - -def test_type_table_use_noop_for_repeated_args(): - check_type_table("double sin(double *, double *);", - "(FUNCTION 4)(POINTER 4)(NOOP 1)(FUNCTION_END 0)" - "(PRIMITIVE 14)") - check_type_table("double sin(double *, double *, double);", - "(FUNCTION 3)(POINTER 3)(NOOP 1)(PRIMITIVE 14)" - "(FUNCTION_END 0)") - -def test_type_table_dont_use_noop_for_primitives(): - check_type_table("double sin(double, double);", - "(FUNCTION 1)(PRIMITIVE 14)(PRIMITIVE 14)(FUNCTION_END 0)") - -def test_type_table_funcptr_as_argument(): - check_type_table("int sin(double(float));", - "(FUNCTION 6)(PRIMITIVE 13)(FUNCTION_END 0)" - "(FUNCTION 7)(POINTER 0)(FUNCTION_END 0)" - "(PRIMITIVE 14)(PRIMITIVE 7)") - -def test_type_table_variadic_function(): - check_type_table("int sin(int, ...);", - "(FUNCTION 1)(PRIMITIVE 7)(FUNCTION_END 1)(POINTER 0)") - -def test_type_table_array(): - check_type_table("extern int a[100];", - "(PRIMITIVE 7)(ARRAY 0)(None 100)") - -def test_type_table_typedef(): - check_type_table("typedef int foo_t;", - "(PRIMITIVE 7)") - -def test_type_table_prebuilt_type(): - check_type_table("int32_t f(void);", - "(FUNCTION 2)(FUNCTION_END 0)(PRIMITIVE 21)") - -def test_type_table_struct_opaque(): - check_type_table("struct foo_s;", - "(STRUCT_UNION 0)") - -def test_type_table_struct(): - check_type_table("struct foo_s { int a; long b; };", - "(PRIMITIVE 7)(PRIMITIVE 9)(STRUCT_UNION 0)") - -def test_type_table_union(): - check_type_table("union foo_u { int a; long b; };", - "(PRIMITIVE 7)(PRIMITIVE 9)(STRUCT_UNION 0)") - -def test_type_table_struct_used(): - check_type_table("struct foo_s { int a; long b; }; int f(struct foo_s*);", - "(FUNCTION 3)(POINTER 5)(FUNCTION_END 0)" - "(PRIMITIVE 7)(PRIMITIVE 9)" - "(STRUCT_UNION 0)") - -def test_type_table_anonymous_struct_with_typedef(): - check_type_table("typedef struct { int a; long b; } foo_t;", - "(STRUCT_UNION 0)(PRIMITIVE 7)(PRIMITIVE 9)") - -def test_type_table_enum(): - check_type_table("enum foo_e { AA, BB, ... };", - "(ENUM 0)") - -def test_type_table_include_1(): - check_type_table("foo_t sin(foo_t);", - "(FUNCTION 1)(PRIMITIVE 14)(FUNCTION_END 0)", - included="typedef double foo_t;") - -def test_type_table_include_2(): - check_type_table("struct foo_s *sin(struct foo_s *);", - "(FUNCTION 1)(POINTER 3)(FUNCTION_END 0)(STRUCT_UNION 0)", - included="struct foo_s { int x, y; };") - - -def test_math_sin(): - import math - ffi = FFI() - ffi.cdef("float sin(double); double cos(double);") - lib = verify(ffi, 'test_math_sin', '#include <math.h>', - ignore_warnings=True) - assert lib.cos(1.43) == math.cos(1.43) - -def test_repr_lib(): - ffi = FFI() - lib = verify(ffi, 'test_repr_lib', '') - assert repr(lib) == "<Lib object for '_CFFI_test_repr_lib'>" - -def test_funcarg_ptr(): - ffi = FFI() - ffi.cdef("int foo(int *);") - lib = verify(ffi, 'test_funcarg_ptr', 'int foo(int *p) { return *p; }') - assert lib.foo([-12345]) == -12345 - -def test_funcres_ptr(): - ffi = FFI() - ffi.cdef("int *foo(void);") - lib = verify(ffi, 'test_funcres_ptr', - 'int *foo(void) { static int x=-12345; return &x; }') - assert lib.foo()[0] == -12345 - -def test_global_var_array(): - ffi = FFI() - ffi.cdef("extern int a[100];") - lib = verify(ffi, 'test_global_var_array', 'int a[100] = { 9999 };') - lib.a[42] = 123456 - assert lib.a[42] == 123456 - assert lib.a[0] == 9999 - -def test_verify_typedef(): - ffi = FFI() - ffi.cdef("typedef int **foo_t;") - lib = verify(ffi, 'test_verify_typedef', 'typedef int **foo_t;') - assert ffi.sizeof("foo_t") == ffi.sizeof("void *") - -def test_verify_typedef_dotdotdot(): - ffi = FFI() - ffi.cdef("typedef ... foo_t;") - verify(ffi, 'test_verify_typedef_dotdotdot', 'typedef int **foo_t;') - -def test_verify_typedef_star_dotdotdot(): - ffi = FFI() - ffi.cdef("typedef ... *foo_t;") - verify(ffi, 'test_verify_typedef_star_dotdotdot', 'typedef int **foo_t;') - -def test_global_var_int(): - ffi = FFI() - ffi.cdef("extern int a, b, c;") - lib = verify(ffi, 'test_global_var_int', 'int a = 999, b, c;') - assert lib.a == 999 - lib.a -= 1001 - assert lib.a == -2 - lib.a = -2147483648 - assert lib.a == -2147483648 - with pytest.raises(OverflowError): - lib.a = 2147483648 - with pytest.raises(OverflowError): - lib.a = -2147483649 - lib.b = 525 # try with the first access being in setattr, too - assert lib.b == 525 - with pytest.raises(AttributeError): - del lib.a - with pytest.raises(AttributeError): - del lib.c - with pytest.raises(AttributeError): - del lib.foobarbaz - -def test_macro(): - ffi = FFI() - ffi.cdef("#define FOOBAR ...") - lib = verify(ffi, 'test_macro', "#define FOOBAR (-6912)") - assert lib.FOOBAR == -6912 - with pytest.raises(AttributeError): - lib.FOOBAR = 2 - -def test_macro_check_value(): - # the value '-0x80000000' in C sources does not have a clear meaning - # to me; it appears to have a different effect than '-2147483648'... - # Moreover, on 32-bits, -2147483648 is actually equal to - # -2147483648U, which in turn is equal to 2147483648U and so positive. - vals = ['42', '-42', '0x80000000', '-2147483648', - '0', '9223372036854775809ULL', - '-9223372036854775807LL'] - if sys.maxsize <= 2**32 or sys.platform == 'win32': - vals.remove('-2147483648') - ffi = FFI() - cdef_lines = ['#define FOO_%d_%d %s' % (i, j, vals[i]) - for i in range(len(vals)) - for j in range(len(vals))] - ffi.cdef('\n'.join(cdef_lines)) - - verify_lines = ['#define FOO_%d_%d %s' % (i, j, vals[j]) # [j], not [i] - for i in range(len(vals)) - for j in range(len(vals))] - lib = verify(ffi, 'test_macro_check_value_ok', - '\n'.join(verify_lines)) - # - for j in range(len(vals)): - c_got = int(vals[j].replace('U', '').replace('L', ''), 0) - c_compiler_msg = str(c_got) - if c_got > 0: - c_compiler_msg += ' (0x%x)' % (c_got,) - # - for i in range(len(vals)): - attrname = 'FOO_%d_%d' % (i, j) - if i == j: - x = getattr(lib, attrname) - assert x == c_got - else: - e = py.test.raises(ffi.error, getattr, lib, attrname) - assert str(e.value) == ( - "the C compiler says '%s' is equal to " - "%s, but the cdef disagrees" % (attrname, c_compiler_msg)) - -def test_constant(): - ffi = FFI() - ffi.cdef("static const int FOOBAR;") - lib = verify(ffi, 'test_constant', "#define FOOBAR (-6912)") - assert lib.FOOBAR == -6912 - with pytest.raises(AttributeError): - lib.FOOBAR = 2 - -def test_check_value_of_static_const(): - ffi = FFI() - ffi.cdef("static const int FOOBAR = 042;") - lib = verify(ffi, 'test_check_value_of_static_const', - "#define FOOBAR (-6912)") - e = py.test.raises(ffi.error, getattr, lib, 'FOOBAR') - assert str(e.value) == ( - "the C compiler says 'FOOBAR' is equal to -6912, but the cdef disagrees") - -def test_constant_nonint(): - ffi = FFI() - ffi.cdef("static const double FOOBAR;") - lib = verify(ffi, 'test_constant_nonint', "#define FOOBAR (-6912.5)") - assert lib.FOOBAR == -6912.5 - with pytest.raises(AttributeError): - lib.FOOBAR = 2 - -def test_constant_ptr(): - ffi = FFI() - ffi.cdef("static double *const FOOBAR;") - lib = verify(ffi, 'test_constant_ptr', "#define FOOBAR NULL") - assert lib.FOOBAR == ffi.NULL - assert ffi.typeof(lib.FOOBAR) == ffi.typeof("double *") - -def test_dir(): - ffi = FFI() - ffi.cdef("int ff(int); extern int aa; static const int my_constant;") - lib = verify(ffi, 'test_dir', """ - #define my_constant (-45) - int aa; - int ff(int x) { return x+aa; } - """) - lib.aa = 5 - assert dir(lib) == ['aa', 'ff', 'my_constant'] - # - aaobj = lib.__dict__['aa'] - assert not isinstance(aaobj, int) # some internal object instead - assert lib.__dict__ == { - 'ff': lib.ff, - 'aa': aaobj, - 'my_constant': -45} - lib.__dict__['ff'] = "??" - assert lib.ff(10) == 15 - -def test_verify_opaque_struct(): - ffi = FFI() - ffi.cdef("struct foo_s;") - lib = verify(ffi, 'test_verify_opaque_struct', "struct foo_s;") - assert ffi.typeof("struct foo_s").cname == "struct foo_s" - -def test_verify_opaque_union(): - ffi = FFI() - ffi.cdef("union foo_s;") - lib = verify(ffi, 'test_verify_opaque_union', "union foo_s;") - assert ffi.typeof("union foo_s").cname == "union foo_s" - -def test_verify_struct(): - ffi = FFI() - ffi.cdef("""struct foo_s { int b; short a; ...; }; - struct bar_s { struct foo_s *f; };""") - lib = verify(ffi, 'test_verify_struct', - """struct foo_s { short a; int b; }; - struct bar_s { struct foo_s *f; };""") - ffi.typeof("struct bar_s *") - p = ffi.new("struct foo_s *", {'a': -32768, 'b': -2147483648}) - assert p.a == -32768 - assert p.b == -2147483648 - with pytest.raises(OverflowError): - p.a -= 1 - with pytest.raises(OverflowError): - p.b -= 1 - q = ffi.new("struct bar_s *", {'f': p}) - assert q.f == p - # - assert ffi.offsetof("struct foo_s", "a") == 0 - assert ffi.offsetof("struct foo_s", "b") == 4 - assert ffi.offsetof(u+"struct foo_s", u+"b") == 4 - # - py.test.raises(TypeError, ffi.addressof, p) - assert ffi.addressof(p[0]) == p - assert ffi.typeof(ffi.addressof(p[0])) is ffi.typeof("struct foo_s *") - assert ffi.typeof(ffi.addressof(p, "b")) is ffi.typeof("int *") - assert ffi.addressof(p, "b")[0] == p.b - -def test_verify_exact_field_offset(): - ffi = FFI() - ffi.cdef("""struct foo_s { int b; short a; };""") - lib = verify(ffi, 'test_verify_exact_field_offset', - """struct foo_s { short a; int b; };""") - e = py.test.raises(ffi.error, ffi.new, "struct foo_s *", []) # lazily - assert str(e.value).startswith( - "struct foo_s: wrong offset for field 'b' (cdef " - 'says 0, but C compiler says 4). fix it or use "...;" ') - -def test_type_caching(): - ffi1 = FFI(); ffi1.cdef("struct foo_s;") - ffi2 = FFI(); ffi2.cdef("struct foo_s;") # different one! - lib1 = verify(ffi1, 'test_type_caching_1', 'struct foo_s;') - lib2 = verify(ffi2, 'test_type_caching_2', 'struct foo_s;') - # shared types - assert ffi1.typeof("long") is ffi2.typeof("long") - assert ffi1.typeof("long**") is ffi2.typeof("long * *") - assert ffi1.typeof("long(*)(int, ...)") is ffi2.typeof("long(*)(int, ...)") - # non-shared types - assert ffi1.typeof("struct foo_s") is not ffi2.typeof("struct foo_s") - assert ffi1.typeof("struct foo_s *") is not ffi2.typeof("struct foo_s *") - assert ffi1.typeof("struct foo_s*(*)()") is not ( - ffi2.typeof("struct foo_s*(*)()")) - assert ffi1.typeof("void(*)(struct foo_s*)") is not ( - ffi2.typeof("void(*)(struct foo_s*)")) - -def test_verify_enum(): - ffi = FFI() - ffi.cdef("""enum e1 { B1, A1, ... }; enum e2 { B2, A2, ... };""") - lib = verify(ffi, 'test_verify_enum', - "enum e1 { A1, B1, C1=%d };" % sys.maxsize + - "enum e2 { A2, B2, C2 };") - ffi.typeof("enum e1") - ffi.typeof("enum e2") - assert lib.A1 == 0 - assert lib.B1 == 1 - assert lib.A2 == 0 - assert lib.B2 == 1 - assert ffi.sizeof("enum e1") == ffi.sizeof("long") - assert ffi.sizeof("enum e2") == ffi.sizeof("int") - assert repr(ffi.cast("enum e1", 0)) == "<cdata 'enum e1' 0: A1>" - -def test_duplicate_enum(): - ffi = FFI() - ffi.cdef("enum e1 { A1, ... }; enum e2 { A1, ... };") - py.test.raises(VerificationError, verify, ffi, 'test_duplicate_enum', - "enum e1 { A1 }; enum e2 { B1 };") - -def test_dotdotdot_length_of_array_field(): - ffi = FFI() - ffi.cdef("struct foo_s { int a[...]; int b[...]; };") - verify(ffi, 'test_dotdotdot_length_of_array_field', - "struct foo_s { int a[42]; int b[11]; };") - assert ffi.sizeof("struct foo_s") == (42 + 11) * 4 - p = ffi.new("struct foo_s *") - assert p.a[41] == p.b[10] == 0 - with pytest.raises(IndexError): - p.a[42] - with pytest.raises(IndexError): - p.b[11] - -def test_dotdotdot_global_array(): - ffi = FFI() - ffi.cdef("extern int aa[...]; extern int bb[...];") - lib = verify(ffi, 'test_dotdotdot_global_array', - "int aa[41]; int bb[12];") - assert ffi.sizeof(lib.aa) == 41 * 4 - assert ffi.sizeof(lib.bb) == 12 * 4 - assert lib.aa[40] == lib.bb[11] == 0 - with pytest.raises(IndexError): - lib.aa[41] - with pytest.raises(IndexError): - lib.bb[12] - -def test_misdeclared_field_1(): - ffi = FFI() - ffi.cdef("struct foo_s { int a[5]; };") - try: - verify(ffi, 'test_misdeclared_field_1', - "struct foo_s { int a[6]; };") - except VerificationError: - pass # ok, fail during compilation already (e.g. C++) - else: - assert ffi.sizeof("struct foo_s") == 24 # found by the actual C code - try: - # lazily build the fields and boom: - p = ffi.new("struct foo_s *") - p.a - assert False, "should have raised" - except ffi.error as e: - assert str(e).startswith("struct foo_s: wrong size for field 'a' " - "(cdef says 20, but C compiler says 24)") - -def test_open_array_in_struct(): - ffi = FFI() - ffi.cdef("struct foo_s { int b; int a[]; };") - verify(ffi, 'test_open_array_in_struct', - "struct foo_s { int b; int a[]; };") - assert ffi.sizeof("struct foo_s") == 4 - p = ffi.new("struct foo_s *", [5, [10, 20, 30, 40]]) - assert p.a[2] == 30 - assert ffi.sizeof(p) == ffi.sizeof("void *") - assert ffi.sizeof(p[0]) == 5 * ffi.sizeof("int") - -def test_math_sin_type(): - ffi = FFI() - ffi.cdef("double sin(double); void *xxtestfunc();") - lib = verify(ffi, 'test_math_sin_type', """ - #include <math.h> - void *xxtestfunc(void) { return 0; } - """) - # 'lib.sin' is typed as a <built-in method> object on lib - assert ffi.typeof(lib.sin).cname == "double(*)(double)" - # 'x' is another <built-in method> object on lib, made very indirectly - x = type(lib).__dir__.__get__(lib) - py.test.raises(TypeError, ffi.typeof, x) - # - # present on built-in functions on CPython; must be emulated on PyPy: - assert lib.sin.__name__ == 'sin' - assert lib.sin.__module__ == '_CFFI_test_math_sin_type' - assert lib.sin.__doc__ == ( - "double sin(double);\n" - "\n" - "CFFI C function from _CFFI_test_math_sin_type.lib") - - assert ffi.typeof(lib.xxtestfunc).cname == "void *(*)()" - assert lib.xxtestfunc.__doc__ == ( - "void *xxtestfunc();\n" - "\n" - "CFFI C function from _CFFI_test_math_sin_type.lib") - -def test_verify_anonymous_struct_with_typedef(): - ffi = FFI() - ffi.cdef("typedef struct { int a; long b; ...; } foo_t;") - verify(ffi, 'test_verify_anonymous_struct_with_typedef', - "typedef struct { long b; int hidden, a; } foo_t;") - p = ffi.new("foo_t *", {'b': 42}) - assert p.b == 42 - assert repr(p).startswith("<cdata 'foo_t *' ") - -def test_verify_anonymous_struct_with_star_typedef(): - ffi = FFI() - ffi.cdef("typedef struct { int a; long b; } *foo_t;") - verify(ffi, 'test_verify_anonymous_struct_with_star_typedef', - "typedef struct { int a; long b; } *foo_t;") - p = ffi.new("foo_t", {'b': 42}) - assert p.b == 42 - -def test_verify_anonymous_enum_with_typedef(): - ffi = FFI() - ffi.cdef("typedef enum { AA, ... } e1;") - lib = verify(ffi, 'test_verify_anonymous_enum_with_typedef1', - "typedef enum { BB, CC, AA } e1;") - assert lib.AA == 2 - assert ffi.sizeof("e1") == ffi.sizeof("int") - assert repr(ffi.cast("e1", 2)) == "<cdata 'e1' 2: AA>" - # - ffi = FFI() - ffi.cdef("typedef enum { AA=%d } e1;" % sys.maxsize) - lib = verify(ffi, 'test_verify_anonymous_enum_with_typedef2', - "typedef enum { AA=%d } e1;" % sys.maxsize) - assert lib.AA == int(ffi.cast("long", sys.maxsize)) - assert ffi.sizeof("e1") == ffi.sizeof("long") - -def test_unique_types(): - CDEF = "struct foo_s; union foo_u; enum foo_e { AA };" - ffi1 = FFI(); ffi1.cdef(CDEF); verify(ffi1, "test_unique_types_1", CDEF) - ffi2 = FFI(); ffi2.cdef(CDEF); verify(ffi2, "test_unique_types_2", CDEF) - # - assert ffi1.typeof("char") is ffi2.typeof("char ") - assert ffi1.typeof("long") is ffi2.typeof("signed long int") - assert ffi1.typeof("double *") is ffi2.typeof("double*") - assert ffi1.typeof("int ***") is ffi2.typeof(" int * * *") - assert ffi1.typeof("int[]") is ffi2.typeof("signed int[]") - assert ffi1.typeof("signed int*[17]") is ffi2.typeof("int *[17]") - assert ffi1.typeof("void") is ffi2.typeof("void") - assert ffi1.typeof("int(*)(int,int)") is ffi2.typeof("int(*)(int,int)") - # - # these depend on user-defined data, so should not be shared - for name in ["struct foo_s", - "union foo_u *", - "enum foo_e", - "struct foo_s *(*)()", - "void(*)(struct foo_s *)", - "struct foo_s *(*[5])[8]", - ]: - assert ffi1.typeof(name) is not ffi2.typeof(name) - # sanity check: twice 'ffi1' - assert ffi1.typeof("struct foo_s*") is ffi1.typeof("struct foo_s *") - -def test_module_name_in_package(): - ffi = FFI() - ffi.cdef("int foo(int);") - recompiler.recompile(ffi, "test_module_name_in_package.mymod", - "int foo(int x) { return x + 32; }", - tmpdir=str(udir)) - old_sys_path = sys.path[:] - try: - package_dir = udir.join('test_module_name_in_package') - for name in os.listdir(str(udir)): - assert not name.startswith('test_module_name_in_package.') - assert os.path.isdir(str(package_dir)) - assert len(os.listdir(str(package_dir))) > 0 - assert os.path.exists(str(package_dir.join('mymod.c'))) - package_dir.join('__init__.py').write('') - # - getattr(importlib, 'invalidate_caches', object)() - # - sys.path.insert(0, str(udir)) - import test_module_name_in_package.mymod - assert test_module_name_in_package.mymod.lib.foo(10) == 42 - assert test_module_name_in_package.mymod.__name__ == ( - 'test_module_name_in_package.mymod') - finally: - sys.path[:] = old_sys_path - -def test_bad_size_of_global_1(): - ffi = FFI() - ffi.cdef("extern short glob;") - py.test.raises(VerificationError, verify, ffi, - "test_bad_size_of_global_1", "long glob;") - -def test_bad_size_of_global_2(): - ffi = FFI() - ffi.cdef("extern int glob[10];") - py.test.raises(VerificationError, verify, ffi, - "test_bad_size_of_global_2", "int glob[9];") - -def test_unspecified_size_of_global_1(): - ffi = FFI() - ffi.cdef("extern int glob[];") - lib = verify(ffi, "test_unspecified_size_of_global_1", "int glob[10];") - assert ffi.typeof(lib.glob) == ffi.typeof("int *") - -def test_unspecified_size_of_global_2(): - ffi = FFI() - ffi.cdef("extern int glob[][5];") - lib = verify(ffi, "test_unspecified_size_of_global_2", "int glob[10][5];") - assert ffi.typeof(lib.glob) == ffi.typeof("int(*)[5]") - -def test_unspecified_size_of_global_3(): - ffi = FFI() - ffi.cdef("extern int glob[][...];") - lib = verify(ffi, "test_unspecified_size_of_global_3", "int glob[10][5];") - assert ffi.typeof(lib.glob) == ffi.typeof("int(*)[5]") - -def test_unspecified_size_of_global_4(): - ffi = FFI() - ffi.cdef("extern int glob[...][...];") - lib = verify(ffi, "test_unspecified_size_of_global_4", "int glob[10][5];") - assert ffi.typeof(lib.glob) == ffi.typeof("int[10][5]") - -def test_include_1(): - ffi1 = FFI() - ffi1.cdef("typedef double foo_t;") - verify(ffi1, "test_include_1_parent", "typedef double foo_t;") - ffi = FFI() - ffi.include(ffi1) - ffi.cdef("foo_t ff1(foo_t);") - lib = verify(ffi, "test_include_1", "double ff1(double x) { return 42.5; }") - assert lib.ff1(0) == 42.5 - assert ffi1.typeof("foo_t") is ffi.typeof("foo_t") is ffi.typeof("double") - -def test_include_1b(): - ffi1 = FFI() - ffi1.cdef("int foo1(int);") - lib1 = verify(ffi1, "test_include_1b_parent", - "int foo1(int x) { return x + 10; }") - ffi = FFI() - ffi.include(ffi1) - ffi.cdef("int foo2(int);") - lib = verify(ffi, "test_include_1b", "int foo2(int x) { return x - 5; }") - assert lib.foo2(42) == 37 - assert lib.foo1(42) == 52 - assert lib.foo1 is lib1.foo1 - -def test_include_2(): - ffi1 = FFI() - ffi1.cdef("struct foo_s { int x, y; };") - verify(ffi1, "test_include_2_parent", "struct foo_s { int x, y; };") - ffi = FFI() - ffi.include(ffi1) - ffi.cdef("struct foo_s *ff2(struct foo_s *);") - lib = verify(ffi, "test_include_2", - "struct foo_s { int x, y; }; //usually from a #include\n" - "struct foo_s *ff2(struct foo_s *p) { p->y++; return p; }") - p = ffi.new("struct foo_s *") - p.y = 41 - q = lib.ff2(p) - assert q == p - assert p.y == 42 - assert ffi1.typeof("struct foo_s") is ffi.typeof("struct foo_s") - -def test_include_3(): - ffi1 = FFI() - ffi1.cdef("typedef short sshort_t;") - verify(ffi1, "test_include_3_parent", "typedef short sshort_t;") - ffi = FFI() - ffi.include(ffi1) - ffi.cdef("sshort_t ff3(sshort_t);") - lib = verify(ffi, "test_include_3", - "typedef short sshort_t; //usually from a #include\n" - "sshort_t ff3(sshort_t x) { return (sshort_t)(x + 42); }") - assert lib.ff3(10) == 52 - assert ffi.typeof(ffi.cast("sshort_t", 42)) is ffi.typeof("short") - assert ffi1.typeof("sshort_t") is ffi.typeof("sshort_t") - -def test_include_4(): - ffi1 = FFI() - ffi1.cdef("typedef struct { int x; } mystruct_t;") - verify(ffi1, "test_include_4_parent", - "typedef struct { int x; } mystruct_t;") - ffi = FFI() - ffi.include(ffi1) - ffi.cdef("mystruct_t *ff4(mystruct_t *);") - lib = verify(ffi, "test_include_4", - "typedef struct {int x; } mystruct_t; //usually from a #include\n" - "mystruct_t *ff4(mystruct_t *p) { p->x += 42; return p; }") - p = ffi.new("mystruct_t *", [10]) - q = lib.ff4(p) - assert q == p - assert p.x == 52 - assert ffi1.typeof("mystruct_t") is ffi.typeof("mystruct_t") - -def test_include_5(): - ffi1 = FFI() - ffi1.cdef("typedef struct { int x[2]; int y; } *mystruct_p;") - verify(ffi1, "test_include_5_parent", - "typedef struct { int x[2]; int y; } *mystruct_p;") - ffi = FFI() - ffi.include(ffi1) - ffi.cdef("mystruct_p ff5(mystruct_p);") - lib = verify(ffi, "test_include_5", - "typedef struct {int x[2]; int y; } *mystruct_p; //usually #include\n" - "mystruct_p ff5(mystruct_p p) { p->x[1] += 42; return p; }") - assert ffi.alignof(ffi.typeof("mystruct_p").item) == 4 - assert ffi1.typeof("mystruct_p") is ffi.typeof("mystruct_p") - p = ffi.new("mystruct_p", [[5, 10], -17]) - q = lib.ff5(p) - assert q == p - assert p.x[0] == 5 - assert p.x[1] == 52 - assert p.y == -17 - assert ffi.alignof(ffi.typeof(p[0])) == 4 - -def test_include_6(): - ffi1 = FFI() - ffi1.cdef("typedef ... mystruct_t;") - verify(ffi1, "test_include_6_parent", - "typedef struct _mystruct_s mystruct_t;") - ffi = FFI() - ffi.include(ffi1) - ffi.cdef("mystruct_t *ff6(void); int ff6b(mystruct_t *);") - lib = verify(ffi, "test_include_6", - "typedef struct _mystruct_s mystruct_t; //usually from a #include\n" - "struct _mystruct_s { int x; };\n" - "static mystruct_t result_struct = { 42 };\n" - "mystruct_t *ff6(void) { return &result_struct; }\n" - "int ff6b(mystruct_t *p) { return p->x; }") - p = lib.ff6() - assert ffi.cast("int *", p)[0] == 42 - assert lib.ff6b(p) == 42 - -def test_include_7(): - ffi1 = FFI() - ffi1.cdef("typedef ... mystruct_t;\n" - "int ff7b(mystruct_t *);") - verify(ffi1, "test_include_7_parent", - "typedef struct { int x; } mystruct_t;\n" - "int ff7b(mystruct_t *p) { return p->x; }") - ffi = FFI() - ffi.include(ffi1) - ffi.cdef("mystruct_t *ff7(void);") - lib = verify(ffi, "test_include_7", - "typedef struct { int x; } mystruct_t; //usually from a #include\n" - "static mystruct_t result_struct = { 42 };" - "mystruct_t *ff7(void) { return &result_struct; }") - p = lib.ff7() - assert ffi.cast("int *", p)[0] == 42 - assert lib.ff7b(p) == 42 - -def test_include_8(): - ffi1 = FFI() - ffi1.cdef("struct foo_s;") - verify(ffi1, "test_include_8_parent", "struct foo_s;") - ffi = FFI() - ffi.include(ffi1) - ffi.cdef("struct foo_s { int x, y; };") - verify(ffi, "test_include_8", "struct foo_s { int x, y; };") - e = py.test.raises(NotImplementedError, ffi.new, "struct foo_s *") - assert str(e.value) == ( - "'struct foo_s' is opaque in the ffi.include(), but no longer in " - "the ffi doing the include (workaround: don't use ffi.include() but" - " duplicate the declarations of everything using struct foo_s)") - -def test_unicode_libraries(): - try: - unicode - except NameError: - py.test.skip("for python 2.x") - # - import math - lib_m = "m" - if sys.platform == 'win32': - #there is a small chance this fails on Mingw via environ $CC - import distutils.ccompiler - if distutils.ccompiler.get_default_compiler() == 'msvc': - lib_m = 'msvcrt' - ffi = FFI() - ffi.cdef(unicode("float sin(double); double cos(double);")) - lib = verify(ffi, 'test_math_sin_unicode', unicode('#include <math.h>'), - libraries=[unicode(lib_m)], ignore_warnings=True) - assert lib.cos(1.43) == math.cos(1.43) - -def test_incomplete_struct_as_arg(): - ffi = FFI() - ffi.cdef("struct foo_s { int x; ...; }; int f(int, struct foo_s);") - lib = verify(ffi, "test_incomplete_struct_as_arg", - "struct foo_s { int a, x, z; };\n" - "int f(int b, struct foo_s s) { return s.x * b; }") - s = ffi.new("struct foo_s *", [21]) - assert s.x == 21 - assert ffi.sizeof(s[0]) == 12 - assert ffi.offsetof(ffi.typeof(s), 'x') == 4 - assert lib.f(2, s[0]) == 42 - assert ffi.typeof(lib.f) == ffi.typeof("int(*)(int, struct foo_s)") - -def test_incomplete_struct_as_result(): - ffi = FFI() - ffi.cdef("struct foo_s { int x; ...; }; struct foo_s f(int);") - lib = verify(ffi, "test_incomplete_struct_as_result", - "struct foo_s { int a, x, z; };\n" - "struct foo_s f(int x) { struct foo_s r; r.x = x * 2; return r; }") - s = lib.f(21) - assert s.x == 42 - assert ffi.typeof(lib.f) == ffi.typeof("struct foo_s(*)(int)") - -def test_incomplete_struct_as_both(): - ffi = FFI() - ffi.cdef("struct foo_s { int x; ...; }; struct bar_s { int y; ...; };\n" - "struct foo_s f(int, struct bar_s);") - lib = verify(ffi, "test_incomplete_struct_as_both", - "struct foo_s { int a, x, z; };\n" - "struct bar_s { int b, c, y, d; };\n" - "struct foo_s f(int x, struct bar_s b) {\n" - " struct foo_s r; r.x = x * b.y; return r;\n" - "}") - b = ffi.new("struct bar_s *", [7]) - s = lib.f(6, b[0]) - assert s.x == 42 - assert ffi.typeof(lib.f) == ffi.typeof( - "struct foo_s(*)(int, struct bar_s)") - s = lib.f(14, {'y': -3}) - assert s.x == -42 - -def test_name_of_unnamed_struct(): - ffi = FFI() - ffi.cdef("typedef struct { int x; } foo_t;\n" - "typedef struct { int y; } *bar_p;\n" - "typedef struct { int y; } **baz_pp;\n") - verify(ffi, "test_name_of_unnamed_struct", - "typedef struct { int x; } foo_t;\n" - "typedef struct { int y; } *bar_p;\n" - "typedef struct { int y; } **baz_pp;\n") - assert repr(ffi.typeof("foo_t")) == "<ctype 'foo_t'>" - assert repr(ffi.typeof("bar_p")) == "<ctype 'struct $1 *'>" - assert repr(ffi.typeof("baz_pp")) == "<ctype 'struct $2 * *'>" - -def test_address_of_global_var(): - ffi = FFI() - ffi.cdef(""" - extern long bottom, bottoms[2]; - long FetchRectBottom(void); - long FetchRectBottoms1(void); - #define FOOBAR 42 - """) - lib = verify(ffi, "test_address_of_global_var", """ - long bottom, bottoms[2]; - long FetchRectBottom(void) { return bottom; } - long FetchRectBottoms1(void) { return bottoms[1]; } - #define FOOBAR 42 - """) - lib.bottom = 300 - assert lib.FetchRectBottom() == 300 - lib.bottom += 1 - assert lib.FetchRectBottom() == 301 - lib.bottoms[1] = 500 - assert lib.FetchRectBottoms1() == 500 - lib.bottoms[1] += 2 - assert lib.FetchRectBottoms1() == 502 - # - p = ffi.addressof(lib, 'bottom') - assert ffi.typeof(p) == ffi.typeof("long *") - assert p[0] == 301 - p[0] += 1 - assert lib.FetchRectBottom() == 302 - p = ffi.addressof(lib, 'bottoms') - assert ffi.typeof(p) == ffi.typeof("long(*)[2]") - assert p[0] == lib.bottoms - # - py.test.raises(AttributeError, ffi.addressof, lib, 'unknown_var') - py.test.raises(AttributeError, ffi.addressof, lib, "FOOBAR") - -def test_defines__CFFI_(): - # Check that we define the macro _CFFI_ automatically. - # It should be done before including Python.h, so that PyPy's Python.h - # can check for it. - ffi = FFI() - ffi.cdef(""" - #define CORRECT 1 - """) - lib = verify(ffi, "test_defines__CFFI_", """ - #ifdef _CFFI_ - # define CORRECT 1 - #endif - """) - assert lib.CORRECT == 1 - -def test_unpack_args(): - ffi = FFI() - ffi.cdef("void foo0(void); void foo1(int); void foo2(int, int);") - lib = verify(ffi, "test_unpack_args", """ - void foo0(void) { } - void foo1(int x) { } - void foo2(int x, int y) { } - """) - assert 'foo0' in repr(lib.foo0) - assert 'foo1' in repr(lib.foo1) - assert 'foo2' in repr(lib.foo2) - lib.foo0() - lib.foo1(42) - lib.foo2(43, 44) - e1 = py.test.raises(TypeError, lib.foo0, 42) - e2 = py.test.raises(TypeError, lib.foo0, 43, 44) - e3 = py.test.raises(TypeError, lib.foo1) - e4 = py.test.raises(TypeError, lib.foo1, 43, 44) - e5 = py.test.raises(TypeError, lib.foo2) - e6 = py.test.raises(TypeError, lib.foo2, 42) - e7 = py.test.raises(TypeError, lib.foo2, 45, 46, 47) - def st1(s): - s = str(s) - if s.startswith("_CFFI_test_unpack_args.Lib."): - s = s[len("_CFFI_test_unpack_args.Lib."):] - return s - assert st1(e1.value) == "foo0() takes no arguments (1 given)" - assert st1(e2.value) == "foo0() takes no arguments (2 given)" - assert st1(e3.value) == "foo1() takes exactly one argument (0 given)" - assert st1(e4.value) == "foo1() takes exactly one argument (2 given)" - assert st1(e5.value) in ["foo2 expected 2 arguments, got 0", - "foo2() takes exactly 2 arguments (0 given)"] - assert st1(e6.value) in ["foo2 expected 2 arguments, got 1", - "foo2() takes exactly 2 arguments (1 given)"] - assert st1(e7.value) in ["foo2 expected 2 arguments, got 3", - "foo2() takes exactly 2 arguments (3 given)"] - -def test_address_of_function(): - ffi = FFI() - ffi.cdef("long myfunc(long x);") - lib = verify(ffi, "test_addressof_function", """ - char myfunc(char x) { return (char)(x + 42); } - """, ignore_warnings=True) - assert lib.myfunc(5) == 47 - assert lib.myfunc(0xABC05) == 47 - assert not isinstance(lib.myfunc, ffi.CData) - assert ffi.typeof(lib.myfunc) == ffi.typeof("long(*)(long)") - addr = ffi.addressof(lib, 'myfunc') - assert addr(5) == 47 - assert addr(0xABC05) == 47 - assert isinstance(addr, ffi.CData) - assert ffi.typeof(addr) == ffi.typeof("long(*)(long)") - -def test_address_of_function_with_struct(): - ffi = FFI() - ffi.cdef("struct foo_s { int x; }; long myfunc(struct foo_s);") - lib = verify(ffi, "test_addressof_function_with_struct", """ - struct foo_s { int x; }; - char myfunc(struct foo_s input) { return (char)(input.x + 42); } - """) - s = ffi.new("struct foo_s *", [5])[0] - assert lib.myfunc(s) == 47 - assert not isinstance(lib.myfunc, ffi.CData) - assert ffi.typeof(lib.myfunc) == ffi.typeof("long(*)(struct foo_s)") - addr = ffi.addressof(lib, 'myfunc') - assert addr(s) == 47 - assert isinstance(addr, ffi.CData) - assert ffi.typeof(addr) == ffi.typeof("long(*)(struct foo_s)") - -def test_issue198(): - ffi = FFI() - ffi.cdef(""" - typedef struct{...;} opaque_t; - const opaque_t CONSTANT; - int toint(opaque_t); - """) - lib = verify(ffi, 'test_issue198', """ - typedef int opaque_t; - #define CONSTANT ((opaque_t)42) - static int toint(opaque_t o) { return o; } - """) - def random_stuff(): - pass - assert lib.toint(lib.CONSTANT) == 42 - random_stuff() - assert lib.toint(lib.CONSTANT) == 42 - -def test_constant_is_not_a_compiler_constant(): - ffi = FFI() - ffi.cdef("static const float almost_forty_two;") - lib = verify(ffi, 'test_constant_is_not_a_compiler_constant', """ - static float f(void) { return 42.25; } - #define almost_forty_two (f()) - """) - assert lib.almost_forty_two == 42.25 - -def test_constant_of_unknown_size(): - ffi = FFI() - ffi.cdef(""" - typedef ... opaque_t; - const opaque_t CONSTANT; - """) - lib = verify(ffi, 'test_constant_of_unknown_size', - "typedef int opaque_t;" - "const int CONSTANT = 42;") - e = py.test.raises(ffi.error, getattr, lib, 'CONSTANT') - assert str(e.value) == ("constant 'CONSTANT' is of " - "type 'opaque_t', whose size is not known") - -def test_variable_of_unknown_size(): - ffi = FFI() - ffi.cdef(""" - typedef ... opaque_t; - extern opaque_t globvar; - """) - lib = verify(ffi, 'test_variable_of_unknown_size', """ - typedef char opaque_t[6]; - opaque_t globvar = "hello"; - """) - # can't read or write it at all - e = py.test.raises(TypeError, getattr, lib, 'globvar') - assert str(e.value) in ["cdata 'opaque_t' is opaque", - "'opaque_t' is opaque or not completed yet"] #pypy - e = py.test.raises(TypeError, setattr, lib, 'globvar', []) - assert str(e.value) in ["'opaque_t' is opaque", - "'opaque_t' is opaque or not completed yet"] #pypy - # but we can get its address - p = ffi.addressof(lib, 'globvar') - assert ffi.typeof(p) == ffi.typeof('opaque_t *') - assert ffi.string(ffi.cast("char *", p), 8) == b"hello" - -def test_constant_of_value_unknown_to_the_compiler(): - extra_c_source = udir.join( - 'extra_test_constant_of_value_unknown_to_the_compiler.c') - extra_c_source.write('const int external_foo = 42;\n') - ffi = FFI() - ffi.cdef("const int external_foo;") - lib = verify(ffi, 'test_constant_of_value_unknown_to_the_compiler', """ - extern const int external_foo; - """, sources=[str(extra_c_source)]) - assert lib.external_foo == 42 - -def test_dotdot_in_source_file_names(): - extra_c_source = udir.join( - 'extra_test_dotdot_in_source_file_names.c') - extra_c_source.write('const int external_foo = 42;\n') - ffi = FFI() - ffi.cdef("const int external_foo;") - lib = verify(ffi, 'test_dotdot_in_source_file_names', """ - extern const int external_foo; - """, sources=[os.path.join(os.path.dirname(str(extra_c_source)), - 'foobar', '..', - os.path.basename(str(extra_c_source)))]) - assert lib.external_foo == 42 - -def test_call_with_incomplete_structs(): - ffi = FFI() - ffi.cdef("typedef struct {...;} foo_t; " - "extern foo_t myglob; " - "foo_t increment(foo_t s); " - "double getx(foo_t s);") - lib = verify(ffi, 'test_call_with_incomplete_structs', """ - typedef double foo_t; - double myglob = 42.5; - double getx(double x) { return x; } - double increment(double x) { return x + 1; } - """) - assert lib.getx(lib.myglob) == 42.5 - assert lib.getx(lib.increment(lib.myglob)) == 43.5 - -def test_struct_array_guess_length_2(): - ffi = FFI() - ffi.cdef("struct foo_s { int a[...][...]; };") - lib = verify(ffi, 'test_struct_array_guess_length_2', - "struct foo_s { int x; int a[5][8]; int y; };") - assert ffi.sizeof('struct foo_s') == 42 * ffi.sizeof('int') - s = ffi.new("struct foo_s *") - assert ffi.typeof(s.a) == ffi.typeof("int[5][8]") - assert ffi.sizeof(s.a) == 40 * ffi.sizeof('int') - assert s.a[4][7] == 0 - with pytest.raises(IndexError): - s.a[4][8] - with pytest.raises(IndexError): - s.a[5][0] - assert ffi.typeof(s.a) == ffi.typeof("int[5][8]") - assert ffi.typeof(s.a[0]) == ffi.typeof("int[8]") - -def test_struct_array_guess_length_3(): - ffi = FFI() - ffi.cdef("struct foo_s { int a[][...]; };") - lib = verify(ffi, 'test_struct_array_guess_length_3', - "struct foo_s { int x; int a[5][7]; int y; };") - assert ffi.sizeof('struct foo_s') == 37 * ffi.sizeof('int') - s = ffi.new("struct foo_s *") - assert ffi.typeof(s.a) == ffi.typeof("int[][7]") - assert s.a[4][6] == 0 - with pytest.raises(IndexError): - s.a[4][7] - assert ffi.typeof(s.a[0]) == ffi.typeof("int[7]") - -def test_global_var_array_2(): - ffi = FFI() - ffi.cdef("extern int a[...][...];") - lib = verify(ffi, 'test_global_var_array_2', 'int a[10][8];') - lib.a[9][7] = 123456 - assert lib.a[9][7] == 123456 - with pytest.raises(IndexError): - lib.a[0][8] - with pytest.raises(IndexError): - lib.a[10][0] - assert ffi.typeof(lib.a) == ffi.typeof("int[10][8]") - assert ffi.typeof(lib.a[0]) == ffi.typeof("int[8]") - -def test_global_var_array_3(): - ffi = FFI() - ffi.cdef("extern int a[][...];") - lib = verify(ffi, 'test_global_var_array_3', 'int a[10][8];') - lib.a[9][7] = 123456 - assert lib.a[9][7] == 123456 - with pytest.raises(IndexError): - lib.a[0][8] - assert ffi.typeof(lib.a) == ffi.typeof("int(*)[8]") - assert ffi.typeof(lib.a[0]) == ffi.typeof("int[8]") - -def test_global_var_array_4(): - ffi = FFI() - ffi.cdef("extern int a[10][...];") - lib = verify(ffi, 'test_global_var_array_4', 'int a[10][8];') - lib.a[9][7] = 123456 - assert lib.a[9][7] == 123456 - with pytest.raises(IndexError): - lib.a[0][8] - with pytest.raises(IndexError): - lib.a[10][8] - assert ffi.typeof(lib.a) == ffi.typeof("int[10][8]") - assert ffi.typeof(lib.a[0]) == ffi.typeof("int[8]") - -def test_some_integer_type(): - ffi = FFI() - ffi.cdef(""" - typedef int... foo_t; - typedef unsigned long... bar_t; - typedef struct { foo_t a, b; } mystruct_t; - foo_t foobar(bar_t, mystruct_t); - static const bar_t mu = -20; - static const foo_t nu = 20; - """) - lib = verify(ffi, 'test_some_integer_type', """ - typedef unsigned long long foo_t; - typedef short bar_t; - typedef struct { foo_t a, b; } mystruct_t; - static foo_t foobar(bar_t x, mystruct_t s) { - return (foo_t)x + s.a + s.b; - } - static const bar_t mu = -20; - static const foo_t nu = 20; - """) - assert ffi.sizeof("foo_t") == ffi.sizeof("unsigned long long") - assert ffi.sizeof("bar_t") == ffi.sizeof("short") - maxulonglong = 2 ** 64 - 1 - assert int(ffi.cast("foo_t", -1)) == maxulonglong - assert int(ffi.cast("bar_t", -1)) == -1 - assert lib.foobar(-1, [0, 0]) == maxulonglong - assert lib.foobar(2 ** 15 - 1, [0, 0]) == 2 ** 15 - 1 - assert lib.foobar(10, [20, 31]) == 61 - assert lib.foobar(0, [0, maxulonglong]) == maxulonglong - py.test.raises(OverflowError, lib.foobar, 2 ** 15, [0, 0]) - py.test.raises(OverflowError, lib.foobar, -(2 ** 15) - 1, [0, 0]) - py.test.raises(OverflowError, ffi.new, "mystruct_t *", [0, -1]) - assert lib.mu == -20 - assert lib.nu == 20 - -def test_some_float_type(): - ffi = FFI() - ffi.cdef(""" - typedef double... foo_t; - typedef float... bar_t; - foo_t sum(foo_t[]); - bar_t neg(bar_t); - """) - lib = verify(ffi, 'test_some_float_type', """ - typedef float foo_t; - static foo_t sum(foo_t x[]) { return x[0] + x[1]; } - typedef double bar_t; - static double neg(double x) { return -x; } - """) - assert lib.sum([40.0, 2.25]) == 42.25 - assert lib.sum([12.3, 45.6]) != 12.3 + 45.6 # precision loss - assert lib.neg(12.3) == -12.3 # no precision loss - assert ffi.sizeof("foo_t") == ffi.sizeof("float") - assert ffi.sizeof("bar_t") == ffi.sizeof("double") - -def test_some_float_invalid_1(): - ffi = FFI() - py.test.raises((FFIError, # with pycparser <= 2.17 - CDefError), # with pycparser >= 2.18 - ffi.cdef, "typedef long double... foo_t;") - -def test_some_float_invalid_2(): - ffi = FFI() - ffi.cdef("typedef double... foo_t; foo_t neg(foo_t);") - lib = verify(ffi, 'test_some_float_invalid_2', """ - typedef unsigned long foo_t; - foo_t neg(foo_t x) { return -x; } - """) - e = py.test.raises(ffi.error, getattr, lib, 'neg') - assert str(e.value) == ("primitive floating-point type with an unexpected " - "size (or not a float type at all)") - -def test_some_float_invalid_3(): - ffi = FFI() - ffi.cdef("typedef double... foo_t; foo_t neg(foo_t);") - lib = verify(ffi, 'test_some_float_invalid_3', """ - typedef long double foo_t; - foo_t neg(foo_t x) { return -x; } - """, ignore_warnings=True) - if ffi.sizeof("long double") == ffi.sizeof("double"): - assert lib.neg(12.3) == -12.3 - else: - e = py.test.raises(ffi.error, getattr, lib, 'neg') - assert str(e.value) == ("primitive floating-point type is " - "'long double', not supported for now with " - "the syntax 'typedef double... xxx;'") - -def test_issue200(): - ffi = FFI() - ffi.cdef(""" - typedef void (function_t)(void*); - void function(void *); - """) - lib = verify(ffi, 'test_issue200', """ - static void function(void *p) { (void)p; } - """) - ffi.typeof('function_t*') - lib.function(ffi.NULL) - # assert did not crash - -def test_alignment_of_longlong(): - ffi = FFI() - x1 = ffi.alignof('unsigned long long') - assert x1 in [4, 8] - ffi.cdef("struct foo_s { unsigned long long x; };") - lib = verify(ffi, 'test_alignment_of_longlong', - "struct foo_s { unsigned long long x; };") - assert ffi.alignof('unsigned long long') == x1 - assert ffi.alignof('struct foo_s') == x1 - -def test_import_from_lib(): - ffi = FFI() - ffi.cdef("int mybar(int); static int myvar;\n#define MYFOO ...") - lib = verify(ffi, 'test_import_from_lib', - "#define MYFOO 42\n" - "static int mybar(int x) { return x + 1; }\n" - "static int myvar = -5;") - assert sys.modules['_CFFI_test_import_from_lib'].lib is lib - assert sys.modules['_CFFI_test_import_from_lib.lib'] is lib - from _CFFI_test_import_from_lib.lib import MYFOO - assert MYFOO == 42 - assert hasattr(lib, '__dict__') - assert lib.__all__ == ['MYFOO', 'mybar'] # but not 'myvar' - assert lib.__name__ == '_CFFI_test_import_from_lib.lib' - assert lib.__class__ is type(sys) # !! hack for help() - -def test_macro_var_callback(): - ffi = FFI() - ffi.cdef("extern int my_value; extern int *(*get_my_value)(void);") - lib = verify(ffi, 'test_macro_var_callback', - "int *(*get_my_value)(void);\n" - "#define my_value (*get_my_value())") - # - values = ffi.new("int[50]") - def it(): - for i in range(50): - yield i - it = it() - # - @ffi.callback("int *(*)(void)") - def get_my_value(): - for nextvalue in it: - return values + nextvalue - lib.get_my_value = get_my_value - # - values[0] = 41 - assert lib.my_value == 41 # [0] - p = ffi.addressof(lib, 'my_value') # [1] - assert p == values + 1 - assert p[-1] == 41 - assert p[+1] == 0 - lib.my_value = 42 # [2] - assert values[2] == 42 - assert p[-1] == 41 - assert p[+1] == 42 - # - # if get_my_value raises or returns nonsense, the exception is printed - # to stderr like with any callback, but then the C expression 'my_value' - # expand to '*NULL'. We assume here that '&my_value' will return NULL - # without segfaulting, and check for NULL when accessing the variable. - @ffi.callback("int *(*)(void)") - def get_my_value(): - raise LookupError - lib.get_my_value = get_my_value - py.test.raises(ffi.error, getattr, lib, 'my_value') - py.test.raises(ffi.error, setattr, lib, 'my_value', 50) - py.test.raises(ffi.error, ffi.addressof, lib, 'my_value') - @ffi.callback("int *(*)(void)") - def get_my_value(): - return "hello" - lib.get_my_value = get_my_value - py.test.raises(ffi.error, getattr, lib, 'my_value') - e = py.test.raises(ffi.error, setattr, lib, 'my_value', 50) - assert str(e.value) == "global variable 'my_value' is at address NULL" - -def test_const_fields(): - ffi = FFI() - ffi.cdef("""struct foo_s { const int a; void *const b; };""") - lib = verify(ffi, 'test_const_fields', """ - struct foo_s { const int a; void *const b; };""") - foo_s = ffi.typeof("struct foo_s") - assert foo_s.fields[0][0] == 'a' - assert foo_s.fields[0][1].type is ffi.typeof("int") - assert foo_s.fields[1][0] == 'b' - assert foo_s.fields[1][1].type is ffi.typeof("void *") - -def test_restrict_fields(): - ffi = FFI() - ffi.cdef("""struct foo_s { void * restrict b; };""") - lib = verify(ffi, 'test_restrict_fields', """ - struct foo_s { void * __restrict b; };""") - foo_s = ffi.typeof("struct foo_s") - assert foo_s.fields[0][0] == 'b' - assert foo_s.fields[0][1].type is ffi.typeof("void *") - -def test_volatile_fields(): - ffi = FFI() - ffi.cdef("""struct foo_s { void * volatile b; };""") - lib = verify(ffi, 'test_volatile_fields', """ - struct foo_s { void * volatile b; };""") - foo_s = ffi.typeof("struct foo_s") - assert foo_s.fields[0][0] == 'b' - assert foo_s.fields[0][1].type is ffi.typeof("void *") - -def test_const_array_fields(): - ffi = FFI() - ffi.cdef("""struct foo_s { const int a[4]; };""") - lib = verify(ffi, 'test_const_array_fields', """ - struct foo_s { const int a[4]; };""") - foo_s = ffi.typeof("struct foo_s") - assert foo_s.fields[0][0] == 'a' - assert foo_s.fields[0][1].type is ffi.typeof("int[4]") - -def test_const_array_fields_varlength(): - ffi = FFI() - ffi.cdef("""struct foo_s { const int a[]; ...; };""") - lib = verify(ffi, 'test_const_array_fields_varlength', """ - struct foo_s { const int a[4]; };""") - foo_s = ffi.typeof("struct foo_s") - assert foo_s.fields[0][0] == 'a' - assert foo_s.fields[0][1].type is ffi.typeof("int[]") - -def test_const_array_fields_unknownlength(): - ffi = FFI() - ffi.cdef("""struct foo_s { const int a[...]; ...; };""") - lib = verify(ffi, 'test_const_array_fields_unknownlength', """ - struct foo_s { const int a[4]; };""") - foo_s = ffi.typeof("struct foo_s") - assert foo_s.fields[0][0] == 'a' - assert foo_s.fields[0][1].type is ffi.typeof("int[4]") - -def test_const_function_args(): - ffi = FFI() - ffi.cdef("""int foobar(const int a, const int *b, const int c[]);""") - lib = verify(ffi, 'test_const_function_args', """ - int foobar(const int a, const int *b, const int c[]) { - return a + *b + *c; - } - """) - assert lib.foobar(100, ffi.new("int *", 40), ffi.new("int *", 2)) == 142 - -def test_const_function_type_args(): - ffi = FFI() - ffi.cdef("""extern int(*foobar)(const int a,const int*b,const int c[]);""") - lib = verify(ffi, 'test_const_function_type_args', """ - int (*foobar)(const int a, const int *b, const int c[]); - """) - t = ffi.typeof(lib.foobar) - assert t.args[0] is ffi.typeof("int") - assert t.args[1] is ffi.typeof("int *") - assert t.args[2] is ffi.typeof("int *") - -def test_const_constant(): - ffi = FFI() - ffi.cdef("""struct foo_s { int x,y; }; const struct foo_s myfoo;""") - lib = verify(ffi, 'test_const_constant', """ - struct foo_s { int x,y; }; const struct foo_s myfoo = { 40, 2 }; - """) - assert lib.myfoo.x == 40 - assert lib.myfoo.y == 2 - -def test_const_via_typedef(): - ffi = FFI() - ffi.cdef("""typedef const int const_t; const_t aaa;""") - lib = verify(ffi, 'test_const_via_typedef', """ - typedef const int const_t; - #define aaa 42 - """) - assert lib.aaa == 42 - with pytest.raises(AttributeError): - lib.aaa = 43 - -def test_win32_calling_convention_0(): - ffi = FFI() - ffi.cdef(""" - int call1(int(__cdecl *cb)(int)); - int (*const call2)(int(__stdcall *cb)(int)); - """) - lib = verify(ffi, 'test_win32_calling_convention_0', r""" - #ifndef _MSC_VER - # define __stdcall /* nothing */ - #endif - int call1(int(*cb)(int)) { - int i, result = 0; - //printf("call1: cb = %p\n", cb); - for (i = 0; i < 1000; i++) - result += cb(i); - //printf("result = %d\n", result); - return result; - } - int call2(int(__stdcall *cb)(int)) { - int i, result = 0; - //printf("call2: cb = %p\n", cb); - for (i = 0; i < 1000; i++) - result += cb(-i); - //printf("result = %d\n", result); - return result; - } - """) - @ffi.callback("int(int)") - def cb1(x): - return x * 2 - @ffi.callback("int __stdcall(int)") - def cb2(x): - return x * 3 - res = lib.call1(cb1) - assert res == 500*999*2 - assert res == ffi.addressof(lib, 'call1')(cb1) - res = lib.call2(cb2) - assert res == -500*999*3 - assert res == ffi.addressof(lib, 'call2')(cb2) - if sys.platform == 'win32' and not sys.maxsize > 2**32: - assert '__stdcall' in str(ffi.typeof(cb2)) - assert '__stdcall' not in str(ffi.typeof(cb1)) - py.test.raises(TypeError, lib.call1, cb2) - py.test.raises(TypeError, lib.call2, cb1) - else: - assert '__stdcall' not in str(ffi.typeof(cb2)) - assert ffi.typeof(cb2) is ffi.typeof(cb1) - -def test_win32_calling_convention_1(): - ffi = FFI() - ffi.cdef(""" - int __cdecl call1(int(__cdecl *cb)(int)); - int __stdcall call2(int(__stdcall *cb)(int)); - int (__cdecl *const cb1)(int); - int (__stdcall *const cb2)(int); - """) - lib = verify(ffi, 'test_win32_calling_convention_1', r""" - #ifndef _MSC_VER - # define __cdecl - # define __stdcall - #endif - int __cdecl cb1(int x) { return x * 2; } - int __stdcall cb2(int x) { return x * 3; } - - int __cdecl call1(int(__cdecl *cb)(int)) { - int i, result = 0; - //printf("here1\n"); - //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1); - for (i = 0; i < 1000; i++) - result += cb(i); - //printf("result = %d\n", result); - return result; - } - int __stdcall call2(int(__stdcall *cb)(int)) { - int i, result = 0; - //printf("here1\n"); - //printf("cb = %p, cb2 = %p\n", cb, (void *)cb2); - for (i = 0; i < 1000; i++) - result += cb(-i); - //printf("result = %d\n", result); - return result; - } - """) - #print '<<< cb1 =', ffi.addressof(lib, 'cb1') - ptr_call1 = ffi.addressof(lib, 'call1') - assert lib.call1(ffi.addressof(lib, 'cb1')) == 500*999*2 - assert ptr_call1(ffi.addressof(lib, 'cb1')) == 500*999*2 - #print '<<< cb2 =', ffi.addressof(lib, 'cb2') - ptr_call2 = ffi.addressof(lib, 'call2') - assert lib.call2(ffi.addressof(lib, 'cb2')) == -500*999*3 - assert ptr_call2(ffi.addressof(lib, 'cb2')) == -500*999*3 - #print '<<< done' - -def test_win32_calling_convention_2(): - # any mistake in the declaration of plain function (including the - # precise argument types and, here, the calling convention) are - # automatically corrected. But this does not apply to the 'cb' - # function pointer argument. - ffi = FFI() - ffi.cdef(""" - int __stdcall call1(int(__cdecl *cb)(int)); - int __cdecl call2(int(__stdcall *cb)(int)); - int (__cdecl *const cb1)(int); - int (__stdcall *const cb2)(int); - """) - lib = verify(ffi, 'test_win32_calling_convention_2', """ - #ifndef _MSC_VER - # define __cdecl - # define __stdcall - #endif - int __cdecl call1(int(__cdecl *cb)(int)) { - int i, result = 0; - for (i = 0; i < 1000; i++) - result += cb(i); - return result; - } - int __stdcall call2(int(__stdcall *cb)(int)) { - int i, result = 0; - for (i = 0; i < 1000; i++) - result += cb(-i); - return result; - } - int __cdecl cb1(int x) { return x * 2; } - int __stdcall cb2(int x) { return x * 3; } - """) - ptr_call1 = ffi.addressof(lib, 'call1') - ptr_call2 = ffi.addressof(lib, 'call2') - if sys.platform == 'win32' and not sys.maxsize > 2**32: - py.test.raises(TypeError, lib.call1, ffi.addressof(lib, 'cb2')) - py.test.raises(TypeError, ptr_call1, ffi.addressof(lib, 'cb2')) - py.test.raises(TypeError, lib.call2, ffi.addressof(lib, 'cb1')) - py.test.raises(TypeError, ptr_call2, ffi.addressof(lib, 'cb1')) - assert lib.call1(ffi.addressof(lib, 'cb1')) == 500*999*2 - assert ptr_call1(ffi.addressof(lib, 'cb1')) == 500*999*2 - assert lib.call2(ffi.addressof(lib, 'cb2')) == -500*999*3 - assert ptr_call2(ffi.addressof(lib, 'cb2')) == -500*999*3 - -def test_win32_calling_convention_3(): - ffi = FFI() - ffi.cdef(""" - struct point { int x, y; }; - - int (*const cb1)(struct point); - int (__stdcall *const cb2)(struct point); - - struct point __stdcall call1(int(*cb)(struct point)); - struct point call2(int(__stdcall *cb)(struct point)); - """) - lib = verify(ffi, 'test_win32_calling_convention_3', r""" - #ifndef _MSC_VER - # define __cdecl - # define __stdcall - #endif - struct point { int x, y; }; - int cb1(struct point pt) { return pt.x + 10 * pt.y; } - int __stdcall cb2(struct point pt) { return pt.x + 100 * pt.y; } - struct point __stdcall call1(int(__cdecl *cb)(struct point)) { - int i; - struct point result = { 0, 0 }; - //printf("here1\n"); - //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1); - for (i = 0; i < 1000; i++) { - struct point p = { i, -i }; - int r = cb(p); - result.x += r; - result.y -= r; - } - return result; - } - struct point __cdecl call2(int(__stdcall *cb)(struct point)) { - int i; - struct point result = { 0, 0 }; - for (i = 0; i < 1000; i++) { - struct point p = { -i, i }; - int r = cb(p); - result.x += r; - result.y -= r; - } - return result; - } - """) - ptr_call1 = ffi.addressof(lib, 'call1') - ptr_call2 = ffi.addressof(lib, 'call2') - if sys.platform == 'win32' and not sys.maxsize > 2**32: - py.test.raises(TypeError, lib.call1, ffi.addressof(lib, 'cb2')) - py.test.raises(TypeError, ptr_call1, ffi.addressof(lib, 'cb2')) - py.test.raises(TypeError, lib.call2, ffi.addressof(lib, 'cb1')) - py.test.raises(TypeError, ptr_call2, ffi.addressof(lib, 'cb1')) - pt = lib.call1(ffi.addressof(lib, 'cb1')) - assert (pt.x, pt.y) == (-9*500*999, 9*500*999) - pt = ptr_call1(ffi.addressof(lib, 'cb1')) - assert (pt.x, pt.y) == (-9*500*999, 9*500*999) - pt = lib.call2(ffi.addressof(lib, 'cb2')) - assert (pt.x, pt.y) == (99*500*999, -99*500*999) - pt = ptr_call2(ffi.addressof(lib, 'cb2')) - assert (pt.x, pt.y) == (99*500*999, -99*500*999) - -def test_extern_python_1(): - import warnings - ffi = FFI() - with warnings.catch_warnings(record=True) as log: - ffi.cdef(""" - extern "Python" { - int bar(int, int); - void baz(int, int); - int bok(void); - void boz(void); - } - """) - assert len(log) == 0, "got a warning: %r" % (log,) - lib = verify(ffi, 'test_extern_python_1', """ - static void baz(int, int); /* forward */ - """) - assert ffi.typeof(lib.bar) == ffi.typeof("int(*)(int, int)") - with FdWriteCapture() as f: - res = lib.bar(4, 5) - assert res == 0 - assert f.getvalue() == ( - b"extern \"Python\": function _CFFI_test_extern_python_1.bar() called, " - b"but no code was attached " - b"to it yet with @ffi.def_extern(). Returning 0.\n") - - @ffi.def_extern("bar") - def my_bar(x, y): - seen.append(("Bar", x, y)) - return x * y - assert my_bar != lib.bar - seen = [] - res = lib.bar(6, 7) - assert seen == [("Bar", 6, 7)] - assert res == 42 - - def baz(x, y): - seen.append(("Baz", x, y)) - baz1 = ffi.def_extern()(baz) - assert baz1 is baz - seen = [] - baz(long(40), long(4)) - res = lib.baz(long(50), long(8)) - assert res is None - assert seen == [("Baz", 40, 4), ("Baz", 50, 8)] - assert type(seen[0][1]) is type(seen[0][2]) is long - assert type(seen[1][1]) is type(seen[1][2]) is int - - @ffi.def_extern(name="bok") - def bokk(): - seen.append("Bok") - return 42 - seen = [] - assert lib.bok() == 42 - assert seen == ["Bok"] - - @ffi.def_extern() - def boz(): - seen.append("Boz") - seen = [] - assert lib.boz() is None - assert seen == ["Boz"] - -def test_extern_python_bogus_name(): - ffi = FFI() - ffi.cdef("extern int abc;") - lib = verify(ffi, 'test_extern_python_bogus_name', "int abc;") - def fn(): - pass - py.test.raises(ffi.error, ffi.def_extern("unknown_name"), fn) - py.test.raises(ffi.error, ffi.def_extern("abc"), fn) - assert lib.abc == 0 - e = py.test.raises(ffi.error, ffi.def_extern("abc"), fn) - assert str(e.value) == ("ffi.def_extern('abc'): no 'extern \"Python\"' " - "function with this name") - e = py.test.raises(ffi.error, ffi.def_extern(), fn) - assert str(e.value) == ("ffi.def_extern('fn'): no 'extern \"Python\"' " - "function with this name") - # - py.test.raises(TypeError, ffi.def_extern(42), fn) - py.test.raises((TypeError, AttributeError), ffi.def_extern(), "foo") - class X: - pass - x = X() - x.__name__ = x - py.test.raises(TypeError, ffi.def_extern(), x) - -def test_extern_python_bogus_result_type(): - ffi = FFI() - ffi.cdef("""extern "Python" void bar(int);""") - lib = verify(ffi, 'test_extern_python_bogus_result_type', "") - # - @ffi.def_extern() - def bar(n): - return n * 10 - with StdErrCapture() as f: - res = lib.bar(321) - assert res is None - msg = f.getvalue() - assert "rom cffi callback %r" % (bar,) in msg - assert "rying to convert the result back to C:\n" in msg - assert msg.endswith( - "TypeError: callback with the return type 'void' must return None\n") - -def test_extern_python_redefine(): - ffi = FFI() - ffi.cdef("""extern "Python" int bar(int);""") - lib = verify(ffi, 'test_extern_python_redefine', "") - # - @ffi.def_extern() - def bar(n): - return n * 10 - assert lib.bar(42) == 420 - # - @ffi.def_extern() - def bar(n): - return -n - assert lib.bar(42) == -42 - -def test_extern_python_struct(): - ffi = FFI() - ffi.cdef(""" - struct foo_s { int a, b, c; }; - extern "Python" int bar(int, struct foo_s, int); - extern "Python" { struct foo_s baz(int, int); - struct foo_s bok(void); } - """) - lib = verify(ffi, 'test_extern_python_struct', - "struct foo_s { int a, b, c; };") - # - @ffi.def_extern() - def bar(x, s, z): - return x + s.a + s.b + s.c + z - res = lib.bar(1000, [1001, 1002, 1004], 1008) - assert res == 5015 - # - @ffi.def_extern() - def baz(x, y): - return [x + y, x - y, x * y] - res = lib.baz(1000, 42) - assert res.a == 1042 - assert res.b == 958 - assert res.c == 42000 - # - @ffi.def_extern() - def bok(): - return [10, 20, 30] - res = lib.bok() - assert [res.a, res.b, res.c] == [10, 20, 30] - -def test_extern_python_long_double(): - ffi = FFI() - ffi.cdef(""" - extern "Python" int bar(int, long double, int); - extern "Python" long double baz(int, int); - extern "Python" long double bok(void); - """) - lib = verify(ffi, 'test_extern_python_long_double', "") - # - @ffi.def_extern() - def bar(x, l, z): - seen.append((x, l, z)) - return 6 - seen = [] - lib.bar(10, 3.5, 20) - expected = ffi.cast("long double", 3.5) - assert repr(seen) == repr([(10, expected, 20)]) - # - @ffi.def_extern() - def baz(x, z): - assert x == 10 and z == 20 - return expected - res = lib.baz(10, 20) - assert repr(res) == repr(expected) - # - @ffi.def_extern() - def bok(): - return expected - res = lib.bok() - assert repr(res) == repr(expected) - -def test_extern_python_signature(): - ffi = FFI() - lib = verify(ffi, 'test_extern_python_signature', "") - py.test.raises(TypeError, ffi.def_extern(425), None) - py.test.raises(TypeError, ffi.def_extern, 'a', 'b', 'c', 'd') - -def test_extern_python_errors(): - ffi = FFI() - ffi.cdef(""" - extern "Python" int bar(int); - """) - lib = verify(ffi, 'test_extern_python_errors', "") - - seen = [] - def oops(*args): - seen.append(args) - - @ffi.def_extern(onerror=oops) - def bar(x): - return x + "" - assert lib.bar(10) == 0 - - @ffi.def_extern(name="bar", onerror=oops, error=-66) - def bar2(x): - return x + "" - assert lib.bar(10) == -66 - - assert len(seen) == 2 - exc, val, tb = seen[0] - assert exc is TypeError - assert isinstance(val, TypeError) - assert tb.tb_frame.f_code.co_name == "bar" - exc, val, tb = seen[1] - assert exc is TypeError - assert isinstance(val, TypeError) - assert tb.tb_frame.f_code.co_name == "bar2" - # - # a case where 'onerror' is not callable - py.test.raises(TypeError, ffi.def_extern(name='bar', onerror=42), - lambda x: x) - -def test_extern_python_stdcall(): - ffi = FFI() - ffi.cdef(""" - extern "Python" int __stdcall foo(int); - extern "Python" int WINAPI bar(int); - static int (__stdcall * mycb1)(int); - static int indirect_call(int); - """) - lib = verify(ffi, 'test_extern_python_stdcall', """ - #ifndef _MSC_VER - # define __stdcall - #endif - static int (__stdcall * mycb1)(int); - static int indirect_call(int x) { - return mycb1(x); - } - """) - # - @ffi.def_extern() - def foo(x): - return x + 42 - @ffi.def_extern() - def bar(x): - return x + 43 - assert lib.foo(100) == 142 - assert lib.bar(100) == 143 - lib.mycb1 = lib.foo - assert lib.mycb1(200) == 242 - assert lib.indirect_call(300) == 342 - -def test_extern_python_plus_c(): - ffi = FFI() - ffi.cdef(""" - extern "Python+C" int foo(int); - extern "C +\tPython" int bar(int); - int call_me(int); - """) - lib = verify(ffi, 'test_extern_python_plus_c', """ - int foo(int); - #ifdef __GNUC__ - __attribute__((visibility("hidden"))) - #endif - int bar(int); - - static int call_me(int x) { - return foo(x) - bar(x); - } - """) - # - @ffi.def_extern() - def foo(x): - return x * 42 - @ffi.def_extern() - def bar(x): - return x * 63 - assert lib.foo(100) == 4200 - assert lib.bar(100) == 6300 - assert lib.call_me(100) == -2100 - -def test_introspect_function(): - ffi = FFI() - ffi.cdef("float f1(double);") - lib = verify(ffi, 'test_introspect_function', """ - float f1(double x) { return (float)x; } - """) - assert dir(lib) == ['f1'] - FUNC = ffi.typeof(lib.f1) - assert FUNC.kind == 'function' - assert FUNC.args[0].cname == 'double' - assert FUNC.result.cname == 'float' - assert ffi.typeof(ffi.addressof(lib, 'f1')) is FUNC - -def test_introspect_global_var(): - ffi = FFI() - ffi.cdef("extern float g1;") - lib = verify(ffi, 'test_introspect_global_var', """ - float g1; - """) - assert dir(lib) == ['g1'] - FLOATPTR = ffi.typeof(ffi.addressof(lib, 'g1')) - assert FLOATPTR.kind == 'pointer' - assert FLOATPTR.item.cname == 'float' - -def test_introspect_global_var_array(): - ffi = FFI() - ffi.cdef("extern float g1[100];") - lib = verify(ffi, 'test_introspect_global_var_array', """ - float g1[100]; - """) - assert dir(lib) == ['g1'] - FLOATARRAYPTR = ffi.typeof(ffi.addressof(lib, 'g1')) - assert FLOATARRAYPTR.kind == 'pointer' - assert FLOATARRAYPTR.item.kind == 'array' - assert FLOATARRAYPTR.item.length == 100 - assert ffi.typeof(lib.g1) is FLOATARRAYPTR.item - -def test_introspect_integer_const(): - ffi = FFI() - ffi.cdef("#define FOO 42") - lib = verify(ffi, 'test_introspect_integer_const', """ - #define FOO 42 - """) - assert dir(lib) == ['FOO'] - assert lib.FOO == ffi.integer_const('FOO') == 42 - -def test_introspect_typedef(): - ffi = FFI() - ffi.cdef("typedef int foo_t;") - lib = verify(ffi, 'test_introspect_typedef', """ - typedef int foo_t; - """) - assert ffi.list_types() == (['foo_t'], [], []) - assert ffi.typeof('foo_t').kind == 'primitive' - assert ffi.typeof('foo_t').cname == 'int' - -def test_introspect_typedef_multiple(): - ffi = FFI() - ffi.cdef("typedef signed char a_t, c_t, g_t, b_t;") - lib = verify(ffi, 'test_introspect_typedef_multiple', """ - typedef signed char a_t, c_t, g_t, b_t; - """) - assert ffi.list_types() == (['a_t', 'b_t', 'c_t', 'g_t'], [], []) - -def test_introspect_struct(): - ffi = FFI() - ffi.cdef("struct foo_s { int a; };") - lib = verify(ffi, 'test_introspect_struct', """ - struct foo_s { int a; }; - """) - assert ffi.list_types() == ([], ['foo_s'], []) - assert ffi.typeof('struct foo_s').kind == 'struct' - assert ffi.typeof('struct foo_s').cname == 'struct foo_s' - -def test_introspect_union(): - ffi = FFI() - ffi.cdef("union foo_s { int a; };") - lib = verify(ffi, 'test_introspect_union', """ - union foo_s { int a; }; - """) - assert ffi.list_types() == ([], [], ['foo_s']) - assert ffi.typeof('union foo_s').kind == 'union' - assert ffi.typeof('union foo_s').cname == 'union foo_s' - -def test_introspect_struct_and_typedef(): - ffi = FFI() - ffi.cdef("typedef struct { int a; } foo_t;") - lib = verify(ffi, 'test_introspect_struct_and_typedef', """ - typedef struct { int a; } foo_t; - """) - assert ffi.list_types() == (['foo_t'], [], []) - assert ffi.typeof('foo_t').kind == 'struct' - assert ffi.typeof('foo_t').cname == 'foo_t' - -def test_introspect_included_type(): - SOURCE = """ - typedef signed char schar_t; - struct sint_t { int x; }; - """ - ffi1 = FFI() - ffi1.cdef(SOURCE) - ffi2 = FFI() - ffi2.include(ffi1) - verify(ffi1, "test_introspect_included_type_parent", SOURCE) - verify(ffi2, "test_introspect_included_type", SOURCE) - assert ffi1.list_types() == ffi2.list_types() == ( - ['schar_t'], ['sint_t'], []) - -def test_introspect_order(): - ffi = FFI() - ffi.cdef("union CFFIaaa { int a; }; typedef struct CFFIccc { int a; } CFFIb;") - ffi.cdef("union CFFIg { int a; }; typedef struct CFFIcc { int a; } CFFIbbb;") - ffi.cdef("union CFFIaa { int a; }; typedef struct CFFIa { int a; } CFFIbb;") - verify(ffi, "test_introspect_order", """ - union CFFIaaa { int a; }; typedef struct CFFIccc { int a; } CFFIb; - union CFFIg { int a; }; typedef struct CFFIcc { int a; } CFFIbbb; - union CFFIaa { int a; }; typedef struct CFFIa { int a; } CFFIbb; - """) - assert ffi.list_types() == (['CFFIb', 'CFFIbb', 'CFFIbbb'], - ['CFFIa', 'CFFIcc', 'CFFIccc'], - ['CFFIaa', 'CFFIaaa', 'CFFIg']) - -def test_bool_in_cpp(): - # this works when compiled as C, but in cffi < 1.7 it fails as C++ - ffi = FFI() - ffi.cdef("bool f(void);") - lib = verify(ffi, "test_bool_in_cpp", "char f(void) { return 2; }") - assert lib.f() is True - -def test_bool_in_cpp_2(): - ffi = FFI() - ffi.cdef('int add(int a, int b);') - lib = verify(ffi, "test_bool_bug_cpp", ''' - typedef bool _Bool; /* there is a Windows header with this line */ - int add(int a, int b) - { - return a + b; - }''', source_extension='.cpp') - c = lib.add(2, 3) - assert c == 5 - -def test_struct_field_opaque(): - ffi = FFI() - ffi.cdef("struct a { struct b b; };") - e = py.test.raises(TypeError, verify, - ffi, "test_struct_field_opaque", "?") - assert str(e.value) == ("struct a: field 'a.b' is of an opaque" - " type (not declared in cdef())") - ffi = FFI() - ffi.cdef("struct a { struct b b[2]; };") - e = py.test.raises(TypeError, verify, - ffi, "test_struct_field_opaque", "?") - assert str(e.value) == ("struct a: field 'a.b' is of an opaque" - " type (not declared in cdef())") - ffi = FFI() - ffi.cdef("struct a { struct b b[]; };") - e = py.test.raises(TypeError, verify, - ffi, "test_struct_field_opaque", "?") - assert str(e.value) == ("struct a: field 'a.b' is of an opaque" - " type (not declared in cdef())") - -def test_function_arg_opaque(): - py.test.skip("can currently declare a function with an opaque struct " - "as argument, but AFAICT it's impossible to call it later") - -def test_function_returns_opaque(): - ffi = FFI() - ffi.cdef("struct a foo(int);") - e = py.test.raises(TypeError, verify, - ffi, "test_function_returns_opaque", "?") - assert str(e.value) == ("function foo: 'struct a' is used as result type," - " but is opaque") - -def test_function_returns_union(): - ffi = FFI() - ffi.cdef("union u1 { int a, b; }; union u1 f1(int);") - lib = verify(ffi, "test_function_returns_union", """ - union u1 { int a, b; }; - static union u1 f1(int x) { union u1 u; u.b = x; return u; } - """) - assert lib.f1(51).a == 51 - -def test_function_returns_partial_struct(): - ffi = FFI() - ffi.cdef("struct aaa { int a; ...; }; struct aaa f1(int);") - lib = verify(ffi, "test_function_returns_partial_struct", """ - struct aaa { int b, a, c; }; - static struct aaa f1(int x) { struct aaa s = {0}; s.a = x; return s; } - """) - assert lib.f1(52).a == 52 - -def test_function_returns_float_complex(): - if sys.platform == 'win32': - py.test.skip("MSVC may not support _Complex") - ffi = FFI() - ffi.cdef("float _Complex f1(float a, float b);"); - lib = verify(ffi, "test_function_returns_float_complex", """ - #include <complex.h> - static float _Complex f1(float a, float b) { return a + I*2.0f*b; } - """, no_cpp=True) # <complex.h> fails on some systems with C++ - result = lib.f1(1.25, 5.1) - assert type(result) == complex - assert result.real == 1.25 # exact - assert (result.imag != 2*5.1) and (abs(result.imag - 2*5.1) < 1e-5) # inexact - -def test_function_returns_double_complex(): - if sys.platform == 'win32': - py.test.skip("MSVC may not support _Complex") - ffi = FFI() - ffi.cdef("double _Complex f1(double a, double b);"); - lib = verify(ffi, "test_function_returns_double_complex", """ - #include <complex.h> - static double _Complex f1(double a, double b) { return a + I*2.0*b; } - """, no_cpp=True) # <complex.h> fails on some systems with C++ - result = lib.f1(1.25, 5.1) - assert type(result) == complex - assert result.real == 1.25 # exact - assert result.imag == 2*5.1 # exact - -def test_function_argument_float_complex(): - if sys.platform == 'win32': - py.test.skip("MSVC may not support _Complex") - ffi = FFI() - ffi.cdef("float f1(float _Complex x);"); - lib = verify(ffi, "test_function_argument_float_complex", """ - #include <complex.h> - static float f1(float _Complex x) { return cabsf(x); } - """, no_cpp=True) # <complex.h> fails on some systems with C++ - x = complex(12.34, 56.78) - result = lib.f1(x) - assert abs(result - abs(x)) < 1e-5 - -def test_function_argument_double_complex(): - if sys.platform == 'win32': - py.test.skip("MSVC may not support _Complex") - ffi = FFI() - ffi.cdef("double f1(double _Complex);"); - lib = verify(ffi, "test_function_argument_double_complex", """ - #include <complex.h> - static double f1(double _Complex x) { return cabs(x); } - """, no_cpp=True) # <complex.h> fails on some systems with C++ - x = complex(12.34, 56.78) - result = lib.f1(x) - assert abs(result - abs(x)) < 1e-11 - -def test_typedef_array_dotdotdot(): - ffi = FFI() - ffi.cdef(""" - typedef int foo_t[...], bar_t[...]; - extern int gv[...]; - typedef int mat_t[...][...]; - typedef int vmat_t[][...]; - """) - lib = verify(ffi, "test_typedef_array_dotdotdot", """ - typedef int foo_t[50], bar_t[50]; - int gv[23]; - typedef int mat_t[6][7]; - typedef int vmat_t[][8]; - """) - assert ffi.sizeof("foo_t") == 50 * ffi.sizeof("int") - assert ffi.sizeof("bar_t") == 50 * ffi.sizeof("int") - assert len(ffi.new("foo_t")) == 50 - assert len(ffi.new("bar_t")) == 50 - assert ffi.sizeof(lib.gv) == 23 * ffi.sizeof("int") - assert ffi.sizeof("mat_t") == 6 * 7 * ffi.sizeof("int") - assert len(ffi.new("mat_t")) == 6 - assert len(ffi.new("mat_t")[3]) == 7 - py.test.raises(ffi.error, ffi.sizeof, "vmat_t") - p = ffi.new("vmat_t", 4) - assert ffi.sizeof(p[3]) == 8 * ffi.sizeof("int") - -def test_typedef_array_dotdotdot_usage(): - ffi = FFI() - ffi.cdef(""" - typedef int foo_t[...]; - typedef int mat_t[...][...]; - struct s { foo_t a; foo_t *b; foo_t **c; }; - int myfunc(foo_t a, foo_t *b, foo_t **c); - struct sm { mat_t a; mat_t *b; mat_t **c; }; - int myfuncm(mat_t a, mat_t *b, mat_t **c); - """) - lib = verify(ffi, "test_typedef_array_dotdotdot_usage", """ - typedef int foo_t[50]; - typedef int mat_t[6][7]; - struct s { foo_t a; foo_t *b; foo_t **c; }; - static int myfunc(foo_t a, foo_t *b, foo_t **c) { return (**c)[49]; } - struct sm { mat_t a; mat_t *b; mat_t **c; }; - static int myfuncm(mat_t a, mat_t *b, mat_t **c) { return (**c)[5][6]; } - """) - assert ffi.sizeof("foo_t") == 50 * ffi.sizeof("int") - p = ffi.new("struct s *") - assert ffi.sizeof(p[0]) == 50 * ffi.sizeof("int") + 2 * ffi.sizeof("void *") - p.a[49] = 321 - p.b = ffi.addressof(p, 'a') - p.c = ffi.addressof(p, 'b') - assert lib.myfunc(ffi.NULL, ffi.NULL, p.c) == 321 - # - assert ffi.sizeof("mat_t") == 42 * ffi.sizeof("int") - p = ffi.new("struct sm *") - assert ffi.sizeof(p[0]) == 42 * ffi.sizeof("int") + 2 * ffi.sizeof("void *") - p.a[5][6] = -321 - p.b = ffi.addressof(p, 'a') - p.c = ffi.addressof(p, 'b') - assert lib.myfuncm(ffi.NULL, ffi.NULL, p.c) == -321 - -def test_call_with_custom_field_pos(): - ffi = FFI() - ffi.cdef(""" - struct foo { int x; ...; }; - struct foo f(void); - struct foo g(int, ...); - """) - lib = verify(ffi, "test_call_with_custom_field_pos", """ - struct foo { int y, x; }; - struct foo f(void) { - struct foo s = { 40, 200 }; - return s; - } - struct foo g(int a, ...) { return f(); } - """) - assert lib.f().x == 200 - e = py.test.raises(NotImplementedError, lib.g, 0) - assert str(e.value) == ( - 'ctype \'struct foo\' not supported as return value. It is a ' - 'struct declared with "...;", but the C calling convention may ' - 'depend on the missing fields; or, it contains anonymous ' - 'struct/unions. Such structs are only supported ' - 'as return value if the function is \'API mode\' and non-variadic ' - '(i.e. declared inside ffibuilder.cdef()+ffibuilder.set_source() ' - 'and not taking a final \'...\' argument)') - -def test_call_with_nested_anonymous_struct(): - if sys.platform == 'win32': - py.test.skip("needs a GCC extension") - ffi = FFI() - ffi.cdef(""" - struct foo { int a; union { int b, c; }; }; - struct foo f(void); - struct foo g(int, ...); - """) - lib = verify(ffi, "test_call_with_nested_anonymous_struct", """ - struct foo { int a; union { int b, c; }; }; - struct foo f(void) { - struct foo s; - s.a = 40; - s.b = 200; - return s; - } - struct foo g(int a, ...) { return f(); } - """) - assert lib.f().b == 200 - e = py.test.raises(NotImplementedError, lib.g, 0) - assert str(e.value) == ( - 'ctype \'struct foo\' not supported as return value. It is a ' - 'struct declared with "...;", but the C calling convention may ' - 'depend on the missing fields; or, it contains anonymous ' - 'struct/unions. Such structs are only supported ' - 'as return value if the function is \'API mode\' and non-variadic ' - '(i.e. declared inside ffibuilder.cdef()+ffibuilder.set_source() ' - 'and not taking a final \'...\' argument)') - -def test_call_with_bitfield(): - ffi = FFI() - ffi.cdef(""" - struct foo { int x:5; }; - struct foo f(void); - struct foo g(int, ...); - """) - lib = verify(ffi, "test_call_with_bitfield", """ - struct foo { int x:5; }; - struct foo f(void) { - struct foo s = { 11 }; - return s; - } - struct foo g(int a, ...) { return f(); } - """) - assert lib.f().x == 11 - e = py.test.raises(NotImplementedError, lib.g, 0) - assert str(e.value) == ( - "ctype 'struct foo' not supported as return value. It is a struct " - "with bit fields, which libffi does not support. Such structs are " - "only supported as return value if the function is 'API mode' and " - "non-variadic (i.e. declared inside ffibuilder.cdef()+ffibuilder." - "set_source() and not taking a final '...' argument)") - -def test_call_with_zero_length_field(): - if sys.platform == 'win32': - py.test.skip("zero-length field not supported by MSVC") - ffi = FFI() - ffi.cdef(""" - struct foo { int a; int x[0]; }; - struct foo f(void); - struct foo g(int, ...); - """) - lib = verify(ffi, "test_call_with_zero_length_field", """ - struct foo { int a; int x[0]; }; - struct foo f(void) { - struct foo s = { 42 }; - return s; - } - struct foo g(int a, ...) { return f(); } - """) - assert lib.f().a == 42 - e = py.test.raises(NotImplementedError, lib.g, 0) - assert str(e.value) == ( - "ctype 'struct foo' not supported as return value. It is a " - "struct with a zero-length array, which libffi does not support." - " Such structs are only supported as return value if the function is " - "'API mode' and non-variadic (i.e. declared inside ffibuilder.cdef()" - "+ffibuilder.set_source() and not taking a final '...' argument)") - -def test_call_with_union(): - ffi = FFI() - ffi.cdef(""" - union foo { int a; char b; }; - union foo f(void); - union foo g(int, ...); - """) - lib = verify(ffi, "test_call_with_union", """ - union foo { int a; char b; }; - union foo f(void) { - union foo s = { 42 }; - return s; - } - union foo g(int a, ...) { return f(); } - """) - assert lib.f().a == 42 - e = py.test.raises(NotImplementedError, lib.g, 0) - assert str(e.value) == ( - "ctype 'union foo' not supported as return value by libffi. " - "Unions are only supported as return value if the function is " - "'API mode' and non-variadic (i.e. declared inside ffibuilder.cdef()" - "+ffibuilder.set_source() and not taking a final '...' argument)") - -def test_call_with_packed_struct(): - if sys.platform == 'win32': - py.test.skip("needs a GCC extension") - ffi = FFI() - ffi.cdef(""" - struct foo { char y; int x; }; - struct foo f(void); - struct foo g(int, ...); - """, packed=True) - lib = verify(ffi, "test_call_with_packed_struct", """ - struct foo { char y; int x; } __attribute__((packed)); - struct foo f(void) { - struct foo s = { 40, 200 }; - return s; - } - struct foo g(int a, ...) { - struct foo s = { 41, 201 }; - return s; - } - """) - assert ord(lib.f().y) == 40 - assert lib.f().x == 200 - e = py.test.raises(NotImplementedError, lib.g, 0) - assert str(e.value) == ( - "ctype 'struct foo' not supported as return value. It is a " - "'packed' structure, with a different layout than expected by libffi." - " Such structs are only supported as return value if the function is " - "'API mode' and non-variadic (i.e. declared inside ffibuilder.cdef()" - "+ffibuilder.set_source() and not taking a final '...' argument)") - -def test_pack_not_supported(): - ffi = FFI() - ffi.cdef("""struct foo { char y; int x; };""", pack=2) - py.test.raises(NotImplementedError, verify, - ffi, "test_pack_not_supported", "") - -def test_gcc_visibility_hidden(): - if sys.platform == 'win32': - py.test.skip("test for gcc/clang") - ffi = FFI() - ffi.cdef(""" - int f(int); - """) - lib = verify(ffi, "test_gcc_visibility_hidden", """ - int f(int a) { return a + 40; } - """, extra_compile_args=['-fvisibility=hidden']) - assert lib.f(2) == 42 - -def test_override_default_definition(): - ffi = FFI() - ffi.cdef("typedef long int16_t, char16_t;") - lib = verify(ffi, "test_override_default_definition", "") - assert ffi.typeof("int16_t") is ffi.typeof("char16_t") is ffi.typeof("long") - -def test_char16_char32_type(no_cpp=False): - if no_cpp is False and sys.platform == "win32": - py.test.skip("aaaaaaa why do modern MSVC compilers still define " - "a very old __cplusplus value") - ffi = FFI() - ffi.cdef(""" - char16_t foo_2bytes(char16_t); - char32_t foo_4bytes(char32_t); - """) - lib = verify(ffi, "test_char16_char32_type" + no_cpp * "_nocpp", """ - #if !defined(__cplusplus) || (!defined(_LIBCPP_VERSION) && __cplusplus < 201103L) - typedef uint_least16_t char16_t; - typedef uint_least32_t char32_t; - #endif - - char16_t foo_2bytes(char16_t a) { return (char16_t)(a + 42); } - char32_t foo_4bytes(char32_t a) { return (char32_t)(a + 42); } - """, no_cpp=no_cpp) - assert lib.foo_2bytes(u+'\u1234') == u+'\u125e' - assert lib.foo_4bytes(u+'\u1234') == u+'\u125e' - assert lib.foo_4bytes(u+'\U00012345') == u+'\U0001236f' - py.test.raises(TypeError, lib.foo_2bytes, u+'\U00012345') - py.test.raises(TypeError, lib.foo_2bytes, 1234) - py.test.raises(TypeError, lib.foo_4bytes, 1234) - -def test_char16_char32_plain_c(): - test_char16_char32_type(no_cpp=True) - -def test_loader_spec(): - ffi = FFI() - lib = verify(ffi, "test_loader_spec", "") - if sys.version_info < (3,): - assert not hasattr(lib, '__loader__') - assert not hasattr(lib, '__spec__') - else: - assert lib.__loader__ is None - assert lib.__spec__ is None - -def test_realize_struct_error(): - ffi = FFI() - ffi.cdef("""typedef ... foo_t; struct foo_s { void (*x)(foo_t); };""") - lib = verify(ffi, "test_realize_struct_error", """ - typedef int foo_t; struct foo_s { void (*x)(foo_t); }; - """) - py.test.raises(TypeError, ffi.new, "struct foo_s *") - -def test_from_buffer_struct(): - ffi = FFI() - ffi.cdef("""struct foo_s { int a, b; };""") - lib = verify(ffi, "test_from_buffer_struct_p", """ - struct foo_s { int a, b; }; - """) - p = ffi.new("struct foo_s *", [-219239, 58974983]) - q = ffi.from_buffer("struct foo_s[]", ffi.buffer(p)) - assert ffi.typeof(q) == ffi.typeof("struct foo_s[]") - assert len(q) == 1 - assert q[0].a == p.a - assert q[0].b == p.b - assert q == p - q = ffi.from_buffer("struct foo_s *", ffi.buffer(p)) - assert ffi.typeof(q) == ffi.typeof("struct foo_s *") - assert q.a == p.a - assert q.b == p.b - assert q[0].a == p.a - assert q[0].b == p.b - assert q == p - -def test_unnamed_bitfield_1(): - ffi = FFI() - ffi.cdef("""struct A { char : 1; };""") - lib = verify(ffi, "test_unnamed_bitfield_1", """ - struct A { char : 1; }; - """) - p = ffi.new("struct A *") - assert ffi.sizeof(p[0]) == 1 - # Note: on gcc, the type name is ignored for anonymous bitfields - # and that's why the result is 1. On MSVC, the result is - # sizeof("char") which is also 1. - -def test_unnamed_bitfield_2(): - ffi = FFI() - ffi.cdef("""struct A { - short c : 1; short : 1; short d : 1; short : 1; };""") - lib = verify(ffi, "test_unnamed_bitfield_2", """ - struct A { - short c : 1; short : 1; short d : 1; short : 1; - }; - """) - p = ffi.new("struct A *") - assert ffi.sizeof(p[0]) == ffi.sizeof("short") - -def test_unnamed_bitfield_3(): - ffi = FFI() - ffi.cdef("""struct A { struct { char : 1; char : 1; } b; };""") - lib = verify(ffi, "test_unnamed_bitfield_3", """ - struct A { struct { char : 1; char : 1; } b; }; - """) - p = ffi.new("struct A *") - assert ffi.sizeof(p[0]) == 1 - # Note: on gcc, the type name is ignored for anonymous bitfields - # and that's why the result is 1. On MSVC, the result is - # sizeof("char") which is also 1. - -def test_unnamed_bitfield_4(): - ffi = FFI() - ffi.cdef("""struct A { struct { - unsigned c : 1; unsigned : 1; unsigned d : 1; unsigned : 1; } a; - }; - struct B { struct A a; };""") - lib = verify(ffi, "test_unnamed_bitfield_4", """ - struct A { struct { - unsigned c : 1; unsigned : 1; unsigned d : 1; unsigned : 1; } a; - }; - struct B { struct A a; }; - """) - b = ffi.new("struct B *") - a = ffi.new("struct A *") - assert ffi.sizeof(a[0]) == ffi.sizeof("unsigned") - assert ffi.sizeof(b[0]) == ffi.sizeof(a[0]) - -def test_struct_with_func_with_struct_pointer_arg(): - ffi = FFI() - ffi.cdef("""struct BinaryTree { - int (* CompareKey)(struct BinaryTree *tree); - };""") - lib = verify(ffi, "test_struct_with_func_with_struct_pointer_arg", """ - struct BinaryTree { - int (* CompareKey)(struct BinaryTree *tree); - }; - """) - ffi.new("struct BinaryTree *") - -def test_struct_with_func_with_struct_arg(): - ffi = FFI() - ffi.cdef("""struct BinaryTree { - int (* CompareKey)(struct BinaryTree tree); - };""") - lib = verify(ffi, "test_struct_with_func_with_struct_arg", """ - struct BinaryTree { - int (* CompareKey)(struct BinaryTree tree); - }; - """) - py.test.raises(RuntimeError, ffi.new, "struct BinaryTree *") - -def test_passing_large_list(): - ffi = FFI() - ffi.cdef("""void passing_large_list(long[]);""") - lib = verify(ffi, "test_passing_large_list", """ - static void passing_large_list(long a[]) { } - """) - arg = list(range(20000000)) - lib.passing_large_list(arg) - # assert did not segfault diff --git a/testing/cffi1/test_unicode_literals.py b/testing/cffi1/test_unicode_literals.py deleted file mode 100644 index e9825db..0000000 --- a/testing/cffi1/test_unicode_literals.py +++ /dev/null @@ -1,43 +0,0 @@ -# -# ---------------------------------------------- -# WARNING, ALL LITERALS IN THIS FILE ARE UNICODE -# ---------------------------------------------- -# -from __future__ import unicode_literals -# -# -# -from _cffi_backend import FFI - - -def test_cast(): - ffi = FFI() - assert int(ffi.cast("int", 3.14)) == 3 # unicode literal - -def test_new(): - ffi = FFI() - assert ffi.new("int[]", [3, 4, 5])[2] == 5 # unicode literal - -def test_typeof(): - ffi = FFI() - tp = ffi.typeof("int[51]") # unicode literal - assert tp.length == 51 - -def test_sizeof(): - ffi = FFI() - assert ffi.sizeof("int[51]") == 51 * 4 # unicode literal - -def test_alignof(): - ffi = FFI() - assert ffi.alignof("int[51]") == 4 # unicode literal - -def test_getctype(): - ffi = FFI() - assert ffi.getctype("int**") == "int * *" # unicode literal - assert type(ffi.getctype("int**")) is str - -def test_callback(): - ffi = FFI() - cb = ffi.callback("int(int)", # unicode literal - lambda x: x + 42) - assert cb(5) == 47 diff --git a/testing/cffi1/test_verify1.py b/testing/cffi1/test_verify1.py deleted file mode 100644 index 33244cc..0000000 --- a/testing/cffi1/test_verify1.py +++ /dev/null @@ -1,2359 +0,0 @@ -import os, sys, math, py -import pytest -from cffi import FFI, FFIError, VerificationError, VerificationMissing, model -from cffi import CDefError -from cffi import recompiler -from testing.support import * -from testing.support import _verify, extra_compile_args -import _cffi_backend - -lib_m = ['m'] -if sys.platform == 'win32': - #there is a small chance this fails on Mingw via environ $CC - import distutils.ccompiler - if distutils.ccompiler.get_default_compiler() == 'msvc': - lib_m = ['msvcrt'] - -class FFI(FFI): - error = _cffi_backend.FFI.error - _extra_compile_args = extra_compile_args - _verify_counter = 0 - - def verify(self, preamble='', *args, **kwds): - # HACK to reuse the tests from ../cffi0/test_verify.py - FFI._verify_counter += 1 - module_name = 'verify%d' % FFI._verify_counter - try: - del self._assigned_source - except AttributeError: - pass - self.set_source(module_name, preamble) - return _verify(self, module_name, preamble, *args, - extra_compile_args=self._extra_compile_args, **kwds) - -class FFI_warnings_not_error(FFI): - _extra_compile_args = [] - - -def test_missing_function(ffi=None): - # uses the FFI hacked above with '-Werror' - if ffi is None: - ffi = FFI() - ffi.cdef("void some_completely_unknown_function();") - try: - lib = ffi.verify() - except (VerificationError, OSError, ImportError): - pass # expected case: we get a VerificationError - else: - # but depending on compiler and loader details, maybe - # 'lib' could actually be imported but will fail if we - # actually try to call the unknown function... Hard - # to test anything more. - pass - -def test_missing_function_import_error(): - # uses the original FFI that just gives a warning during compilation - test_missing_function(ffi=FFI_warnings_not_error()) - -def test_simple_case(): - ffi = FFI() - ffi.cdef("double sin(double x);") - lib = ffi.verify('#include <math.h>', libraries=lib_m) - assert lib.sin(1.23) == math.sin(1.23) - -def _Wconversion(cdef, source, **kargs): - if sys.platform in ('win32', 'darwin'): - py.test.skip("needs GCC") - ffi = FFI() - ffi.cdef(cdef) - py.test.raises(VerificationError, ffi.verify, source, **kargs) - extra_compile_args_orig = extra_compile_args[:] - extra_compile_args.remove('-Wconversion') - try: - lib = ffi.verify(source, **kargs) - finally: - extra_compile_args[:] = extra_compile_args_orig - return lib - -def test_Wconversion_unsigned(): - _Wconversion("unsigned foo(void);", - "int foo(void) { return -1;}") - -def test_Wconversion_integer(): - _Wconversion("short foo(void);", - "long long foo(void) { return 1<<sizeof(short);}") - -def test_Wconversion_floating(): - lib = _Wconversion("float sin(double);", - "#include <math.h>", libraries=lib_m) - res = lib.sin(1.23) - assert res != math.sin(1.23) # not exact, because of double->float - assert abs(res - math.sin(1.23)) < 1E-5 - -def test_Wconversion_float2int(): - _Wconversion("int sinf(float);", - "#include <math.h>", libraries=lib_m) - -def test_Wconversion_double2int(): - _Wconversion("int sin(double);", - "#include <math.h>", libraries=lib_m) - -def test_rounding_1(): - ffi = FFI() - ffi.cdef("double sinf(float x);") - lib = ffi.verify('#include <math.h>', libraries=lib_m) - res = lib.sinf(1.23) - assert res != math.sin(1.23) # not exact, because of double->float - assert abs(res - math.sin(1.23)) < 1E-5 - -def test_rounding_2(): - ffi = FFI() - ffi.cdef("double sin(float x);") - lib = ffi.verify('#include <math.h>', libraries=lib_m) - res = lib.sin(1.23) - assert res != math.sin(1.23) # not exact, because of double->float - assert abs(res - math.sin(1.23)) < 1E-5 - -def test_strlen_exact(): - ffi = FFI() - ffi.cdef("size_t strlen(const char *s);") - lib = ffi.verify("#include <string.h>") - assert lib.strlen(b"hi there!") == 9 - -def test_strlen_approximate(): - lib = _Wconversion("int strlen(char *s);", - "#include <string.h>") - assert lib.strlen(b"hi there!") == 9 - -def test_return_approximate(): - for typename in ['short', 'int', 'long', 'long long']: - ffi = FFI() - ffi.cdef("%s foo(signed char x);" % typename) - lib = ffi.verify("signed char foo(signed char x) { return x;}") - assert lib.foo(-128) == -128 - assert lib.foo(+127) == +127 - -def test_strlen_array_of_char(): - ffi = FFI() - ffi.cdef("size_t strlen(char[]);") - lib = ffi.verify("#include <string.h>") - assert lib.strlen(b"hello") == 5 - -def test_longdouble(): - ffi = FFI() - ffi.cdef("long double sinl(long double x);") - lib = ffi.verify('#include <math.h>', libraries=lib_m) - for input in [1.23, - ffi.cast("double", 1.23), - ffi.cast("long double", 1.23)]: - x = lib.sinl(input) - assert repr(x).startswith("<cdata 'long double'") - assert (float(x) - math.sin(1.23)) < 1E-10 - -def test_longdouble_precision(): - # Test that we don't loose any precision of 'long double' when - # passing through Python and CFFI. - ffi = FFI() - ffi.cdef("long double step1(long double x);") - SAME_SIZE = ffi.sizeof("long double") == ffi.sizeof("double") - lib = ffi.verify(""" - long double step1(long double x) - { - return 4*x-x*x; - } - """) - def do(cast_to_double): - x = 0.9789 - for i in range(10000): - x = lib.step1(x) - if cast_to_double: - x = float(x) - return float(x) - - more_precise = do(False) - less_precise = do(True) - if SAME_SIZE: - assert more_precise == less_precise - else: - assert abs(more_precise - less_precise) > 0.1 - # Check the particular results on Intel - import platform - if (platform.machine().startswith('i386') or - platform.machine().startswith('i486') or - platform.machine().startswith('i586') or - platform.machine().startswith('i686') or - platform.machine().startswith('x86')): - assert abs(more_precise - 0.656769) < 0.001 - assert abs(less_precise - 3.99091) < 0.001 - else: - py.test.skip("don't know the very exact precision of 'long double'") - - -all_primitive_types = model.PrimitiveType.ALL_PRIMITIVE_TYPES -if sys.platform == 'win32': - all_primitive_types = all_primitive_types.copy() - del all_primitive_types['ssize_t'] -all_integer_types = sorted(tp for tp in all_primitive_types - if all_primitive_types[tp] == 'i') -all_float_types = sorted(tp for tp in all_primitive_types - if all_primitive_types[tp] == 'f') - -def all_signed_integer_types(ffi): - return [x for x in all_integer_types if int(ffi.cast(x, -1)) < 0] - -def all_unsigned_integer_types(ffi): - return [x for x in all_integer_types if int(ffi.cast(x, -1)) > 0] - - -def test_primitive_category(): - for typename in all_primitive_types: - tp = model.PrimitiveType(typename) - C = tp.is_char_type() - F = tp.is_float_type() - X = tp.is_complex_type() - I = tp.is_integer_type() - assert C == (typename in ('char', 'wchar_t', 'char16_t', 'char32_t')) - assert F == (typename in ('float', 'double', 'long double')) - assert X == (typename in ('float _Complex', 'double _Complex')) - assert I + F + C + X == 1 # one and only one of them is true - -def test_all_integer_and_float_types(): - typenames = [] - for typename in all_primitive_types: - if (all_primitive_types[typename] == 'c' or - all_primitive_types[typename] == 'j' or # complex - typename == '_Bool' or typename == 'long double'): - pass - else: - typenames.append(typename) - # - ffi = FFI() - ffi.cdef('\n'.join(["%s foo_%s(%s);" % (tp, tp.replace(' ', '_'), tp) - for tp in typenames])) - lib = ffi.verify('\n'.join(["%s foo_%s(%s x) { return (%s)(x+1); }" % - (tp, tp.replace(' ', '_'), tp, tp) - for tp in typenames])) - for typename in typenames: - foo = getattr(lib, 'foo_%s' % typename.replace(' ', '_')) - assert foo(42) == 43 - if sys.version < '3': - assert foo(long(44)) == 45 - assert foo(ffi.cast(typename, 46)) == 47 - py.test.raises(TypeError, foo, ffi.NULL) - # - # check for overflow cases - if all_primitive_types[typename] == 'f': - continue - for value in [-2**80, -2**40, -2**20, -2**10, -2**5, -1, - 2**5, 2**10, 2**20, 2**40, 2**80]: - overflows = int(ffi.cast(typename, value)) != value - if overflows: - py.test.raises(OverflowError, foo, value) - else: - assert foo(value) == value + 1 - -def test_var_signed_integer_types(): - ffi = FFI() - lst = all_signed_integer_types(ffi) - csource = "\n".join(["static %s somevar_%s;" % (tp, tp.replace(' ', '_')) - for tp in lst]) - ffi.cdef(csource) - lib = ffi.verify(csource) - for tp in lst: - varname = 'somevar_%s' % tp.replace(' ', '_') - sz = ffi.sizeof(tp) - max = (1 << (8*sz-1)) - 1 - min = -(1 << (8*sz-1)) - setattr(lib, varname, max) - assert getattr(lib, varname) == max - setattr(lib, varname, min) - assert getattr(lib, varname) == min - py.test.raises(OverflowError, setattr, lib, varname, max+1) - py.test.raises(OverflowError, setattr, lib, varname, min-1) - -def test_var_unsigned_integer_types(): - ffi = FFI() - lst = all_unsigned_integer_types(ffi) - csource = "\n".join(["static %s somevar_%s;" % (tp, tp.replace(' ', '_')) - for tp in lst]) - ffi.cdef(csource) - lib = ffi.verify(csource) - for tp in lst: - varname = 'somevar_%s' % tp.replace(' ', '_') - sz = ffi.sizeof(tp) - if tp != '_Bool': - max = (1 << (8*sz)) - 1 - else: - max = 1 - setattr(lib, varname, max) - assert getattr(lib, varname) == max - setattr(lib, varname, 0) - assert getattr(lib, varname) == 0 - py.test.raises(OverflowError, setattr, lib, varname, max+1) - py.test.raises(OverflowError, setattr, lib, varname, -1) - -def test_fn_signed_integer_types(): - ffi = FFI() - lst = all_signed_integer_types(ffi) - cdefsrc = "\n".join(["%s somefn_%s(%s);" % (tp, tp.replace(' ', '_'), tp) - for tp in lst]) - ffi.cdef(cdefsrc) - verifysrc = "\n".join(["%s somefn_%s(%s x) { return x; }" % - (tp, tp.replace(' ', '_'), tp) for tp in lst]) - lib = ffi.verify(verifysrc) - for tp in lst: - fnname = 'somefn_%s' % tp.replace(' ', '_') - sz = ffi.sizeof(tp) - max = (1 << (8*sz-1)) - 1 - min = -(1 << (8*sz-1)) - fn = getattr(lib, fnname) - assert fn(max) == max - assert fn(min) == min - py.test.raises(OverflowError, fn, max + 1) - py.test.raises(OverflowError, fn, min - 1) - -def test_fn_unsigned_integer_types(): - ffi = FFI() - lst = all_unsigned_integer_types(ffi) - cdefsrc = "\n".join(["%s somefn_%s(%s);" % (tp, tp.replace(' ', '_'), tp) - for tp in lst]) - ffi.cdef(cdefsrc) - verifysrc = "\n".join(["%s somefn_%s(%s x) { return x; }" % - (tp, tp.replace(' ', '_'), tp) for tp in lst]) - lib = ffi.verify(verifysrc) - for tp in lst: - fnname = 'somefn_%s' % tp.replace(' ', '_') - sz = ffi.sizeof(tp) - if tp != '_Bool': - max = (1 << (8*sz)) - 1 - else: - max = 1 - fn = getattr(lib, fnname) - assert fn(max) == max - assert fn(0) == 0 - py.test.raises(OverflowError, fn, max + 1) - py.test.raises(OverflowError, fn, -1) - -def test_char_type(): - ffi = FFI() - ffi.cdef("char foo(char);") - lib = ffi.verify("char foo(char x) { return ++x; }") - assert lib.foo(b"A") == b"B" - py.test.raises(TypeError, lib.foo, b"bar") - py.test.raises(TypeError, lib.foo, "bar") - -def test_wchar_type(): - ffi = FFI() - if ffi.sizeof('wchar_t') == 2: - uniexample1 = u+'\u1234' - uniexample2 = u+'\u1235' - else: - uniexample1 = u+'\U00012345' - uniexample2 = u+'\U00012346' - # - ffi.cdef("wchar_t foo(wchar_t);") - lib = ffi.verify("wchar_t foo(wchar_t x) { return x+1; }") - assert lib.foo(uniexample1) == uniexample2 - -def test_no_argument(): - ffi = FFI() - ffi.cdef("int foo(void);") - lib = ffi.verify("int foo(void) { return 42; }") - assert lib.foo() == 42 - -def test_two_arguments(): - ffi = FFI() - ffi.cdef("int foo(int, int);") - lib = ffi.verify("int foo(int a, int b) { return a - b; }") - assert lib.foo(40, -2) == 42 - -def test_macro(): - ffi = FFI() - ffi.cdef("int foo(int, int);") - lib = ffi.verify("#define foo(a, b) ((a) * (b))") - assert lib.foo(-6, -7) == 42 - -def test_ptr(): - ffi = FFI() - ffi.cdef("int *foo(int *);") - lib = ffi.verify("int *foo(int *a) { return a; }") - assert lib.foo(ffi.NULL) == ffi.NULL - p = ffi.new("int *", 42) - q = ffi.new("int *", 42) - assert lib.foo(p) == p - assert lib.foo(q) != p - -def test_bogus_ptr(): - ffi = FFI() - ffi.cdef("int *foo(int *);") - lib = ffi.verify("int *foo(int *a) { return a; }") - py.test.raises(TypeError, lib.foo, ffi.new("short *", 42)) - - -def test_verify_typedefs(): - py.test.skip("ignored so far") - types = ['signed char', 'unsigned char', 'int', 'long'] - for cdefed in types: - for real in types: - ffi = FFI() - ffi.cdef("typedef %s foo_t;" % cdefed) - if cdefed == real: - ffi.verify("typedef %s foo_t;" % real) - else: - py.test.raises(VerificationError, ffi.verify, - "typedef %s foo_t;" % real) - -def test_nondecl_struct(): - ffi = FFI() - ffi.cdef("typedef struct foo_s foo_t; int bar(foo_t *);") - lib = ffi.verify("typedef struct foo_s foo_t;\n" - "int bar(foo_t *f) { (void)f; return 42; }\n") - assert lib.bar(ffi.NULL) == 42 - -def test_ffi_full_struct(): - def check(verified_code): - ffi = FFI() - ffi.cdef("struct foo_s { char x; int y; long *z; };") - ffi.verify(verified_code) - ffi.new("struct foo_s *", {}) - - check("struct foo_s { char x; int y; long *z; };") - # - if sys.platform != 'win32': # XXX fixme: only gives warnings - py.test.raises(VerificationError, check, - "struct foo_s { char x; int y; int *z; };") - # - py.test.raises(VerificationError, check, - "struct foo_s { int y; long *z; };") # cdef'ed field x is missing - # - e = py.test.raises(FFI.error, check, - "struct foo_s { int y; char x; long *z; };") - assert str(e.value).startswith( - "struct foo_s: wrong offset for field 'x'" - " (cdef says 0, but C compiler says 4)") - # - e = py.test.raises(FFI.error, check, - "struct foo_s { char x; int y; long *z; char extra; };") - assert str(e.value).startswith( - "struct foo_s: wrong total size" - " (cdef says %d, but C compiler says %d)" % ( - 8 + FFI().sizeof('long *'), - 8 + FFI().sizeof('long *') * 2)) - # - # a corner case that we cannot really detect, but where it has no - # bad consequences: the size is the same, but there is an extra field - # that replaces what is just padding in our declaration above - check("struct foo_s { char x, extra; int y; long *z; };") - # - e = py.test.raises(FFI.error, check, - "struct foo_s { char x; short pad; short y; long *z; };") - assert str(e.value).startswith( - "struct foo_s: wrong size for field 'y'" - " (cdef says 4, but C compiler says 2)") - -def test_ffi_nonfull_struct(): - ffi = FFI() - ffi.cdef(""" - struct foo_s { - int x; - ...; - }; - """) - py.test.raises(VerificationMissing, ffi.sizeof, 'struct foo_s') - py.test.raises(VerificationMissing, ffi.offsetof, 'struct foo_s', 'x') - py.test.raises(VerificationMissing, ffi.new, 'struct foo_s *') - ffi.verify(""" - struct foo_s { - int a, b, x, c, d, e; - }; - """) - assert ffi.sizeof('struct foo_s') == 6 * ffi.sizeof('int') - assert ffi.offsetof('struct foo_s', 'x') == 2 * ffi.sizeof('int') - -def test_ffi_nonfull_alignment(): - ffi = FFI() - ffi.cdef("struct foo_s { char x; ...; };") - ffi.verify("struct foo_s { int a, b; char x; };") - assert ffi.sizeof('struct foo_s') == 3 * ffi.sizeof('int') - assert ffi.alignof('struct foo_s') == ffi.sizeof('int') - -def _check_field_match(typename, real, expect_mismatch): - ffi = FFI() - testing_by_size = (expect_mismatch == 'by_size') - if testing_by_size: - expect_mismatch = ffi.sizeof(typename) != ffi.sizeof(real) - ffi.cdef("struct foo_s { %s x; ...; };" % typename) - try: - ffi.verify("struct foo_s { %s x; };" % real) - ffi.new("struct foo_s *", []) # because some mismatches show up lazily - except (VerificationError, ffi.error): - if not expect_mismatch: - if testing_by_size and typename != real: - print("ignoring mismatch between %s* and %s* even though " - "they have the same size" % (typename, real)) - return - raise AssertionError("unexpected mismatch: %s should be accepted " - "as equal to %s" % (typename, real)) - else: - if expect_mismatch: - raise AssertionError("mismatch not detected: " - "%s != %s" % (typename, real)) - -def test_struct_bad_sized_integer(): - for typename in ['int8_t', 'int16_t', 'int32_t', 'int64_t']: - for real in ['int8_t', 'int16_t', 'int32_t', 'int64_t']: - _check_field_match(typename, real, "by_size") - -def test_struct_bad_sized_float(): - for typename in all_float_types: - for real in all_float_types: - _check_field_match(typename, real, "by_size") - -def test_struct_signedness_ignored(): - _check_field_match("int", "unsigned int", expect_mismatch=False) - _check_field_match("unsigned short", "signed short", expect_mismatch=False) - -def test_struct_float_vs_int(): - if sys.platform == 'win32': - py.test.skip("XXX fixme: only gives warnings") - ffi = FFI() - for typename in all_signed_integer_types(ffi): - for real in all_float_types: - _check_field_match(typename, real, expect_mismatch=True) - for typename in all_float_types: - for real in all_signed_integer_types(ffi): - _check_field_match(typename, real, expect_mismatch=True) - -def test_struct_array_field(): - ffi = FFI() - ffi.cdef("struct foo_s { int a[17]; ...; };") - ffi.verify("struct foo_s { int x; int a[17]; int y; };") - assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int') - s = ffi.new("struct foo_s *") - assert ffi.sizeof(s.a) == 17 * ffi.sizeof('int') - -def test_struct_array_no_length(): - ffi = FFI() - ffi.cdef("struct foo_s { int a[]; int y; ...; };\n" - "int bar(struct foo_s *);\n") - lib = ffi.verify("struct foo_s { int x; int a[17]; int y; };\n" - "int bar(struct foo_s *f) { return f->a[14]; }\n") - assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int') - s = ffi.new("struct foo_s *") - assert ffi.typeof(s.a) is ffi.typeof('int[]') # implicit max length - assert len(s.a) == 18 # max length, computed from the size and start offset - s.a[14] = 4242 - assert lib.bar(s) == 4242 - # with no declared length, out-of-bound accesses are not detected - s.a[17] = -521 - assert s.y == s.a[17] == -521 - # - s = ffi.new("struct foo_s *", {'a': list(range(17))}) - assert s.a[16] == 16 - # overflows at construction time not detected either - s = ffi.new("struct foo_s *", {'a': list(range(18))}) - assert s.y == s.a[17] == 17 - -def test_struct_array_guess_length(): - ffi = FFI() - ffi.cdef("struct foo_s { int a[...]; };") - ffi.verify("struct foo_s { int x; int a[17]; int y; };") - assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int') - s = ffi.new("struct foo_s *") - assert ffi.sizeof(s.a) == 17 * ffi.sizeof('int') - with pytest.raises(IndexError): - s.a[17] - -def test_struct_array_c99_1(): - if sys.platform == 'win32': - py.test.skip("requires C99") - ffi = FFI() - ffi.cdef("struct foo_s { int x; int a[]; };") - ffi.verify("struct foo_s { int x; int a[]; };") - assert ffi.sizeof('struct foo_s') == 1 * ffi.sizeof('int') - s = ffi.new("struct foo_s *", [424242, 4]) - assert ffi.sizeof(ffi.typeof(s[0])) == 1 * ffi.sizeof('int') - assert ffi.sizeof(s[0]) == 5 * ffi.sizeof('int') - # ^^^ explanation: if you write in C: "char x[5];", then - # "sizeof(x)" will evaluate to 5. The behavior above is - # a generalization of that to "struct foo_s[len(a)=5] x;" - # if you could do that in C. - assert s.a[3] == 0 - s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]]) - assert ffi.sizeof(s[0]) == 5 * ffi.sizeof('int') - assert s.a[3] == -10 - s = ffi.new("struct foo_s *") - assert ffi.sizeof(s[0]) == 1 * ffi.sizeof('int') - s = ffi.new("struct foo_s *", [424242]) - assert ffi.sizeof(s[0]) == 1 * ffi.sizeof('int') - -def test_struct_array_c99_2(): - if sys.platform == 'win32': - py.test.skip("requires C99") - ffi = FFI() - ffi.cdef("struct foo_s { int x; int a[]; ...; };") - ffi.verify("struct foo_s { int x, y; int a[]; };") - assert ffi.sizeof('struct foo_s') == 2 * ffi.sizeof('int') - s = ffi.new("struct foo_s *", [424242, 4]) - assert ffi.sizeof(s[0]) == 6 * ffi.sizeof('int') - assert s.a[3] == 0 - s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]]) - assert ffi.sizeof(s[0]) == 6 * ffi.sizeof('int') - assert s.a[3] == -10 - s = ffi.new("struct foo_s *") - assert ffi.sizeof(s[0]) == 2 * ffi.sizeof('int') - s = ffi.new("struct foo_s *", [424242]) - assert ffi.sizeof(s[0]) == 2 * ffi.sizeof('int') - -def test_struct_ptr_to_array_field(): - ffi = FFI() - ffi.cdef("struct foo_s { int (*a)[17]; ...; }; struct bar_s { ...; };") - ffi.verify("struct foo_s { int x; int (*a)[17]; int y; };\n" - "struct bar_s { int x; int *a; int y; };") - assert ffi.sizeof('struct foo_s') == ffi.sizeof("struct bar_s") - s = ffi.new("struct foo_s *") - assert ffi.sizeof(s.a) == ffi.sizeof('int(*)[17]') == ffi.sizeof("int *") - -def test_struct_with_bitfield_exact(): - ffi = FFI() - ffi.cdef("struct foo_s { int a:2, b:3; };") - ffi.verify("struct foo_s { int a:2, b:3; };") - s = ffi.new("struct foo_s *") - s.b = 3 - with pytest.raises(OverflowError): - s.b = 4 - assert s.b == 3 - -def test_struct_with_bitfield_enum(): - ffi = FFI() - code = """ - typedef enum { AA, BB, CC } foo_e; - typedef struct { foo_e f:2; } foo_s; - """ - ffi.cdef(code) - ffi.verify(code) - s = ffi.new("foo_s *") - s.f = 1 - assert s.f == 1 - if int(ffi.cast("foo_e", -1)) < 0: - two = -2 - else: - two = 2 - s.f = two - assert s.f == two - -def test_unsupported_struct_with_bitfield_ellipsis(): - ffi = FFI() - py.test.raises(NotImplementedError, ffi.cdef, - "struct foo_s { int a:2, b:3; ...; };") - -def test_global_constants(): - ffi = FFI() - # use 'static const int', as generally documented, although in this - # case the 'static' is completely ignored. - ffi.cdef("static const int AA, BB, CC, DD;") - lib = ffi.verify("#define AA 42\n" - "#define BB (-43) // blah\n" - "#define CC (22*2) /* foobar */\n" - "#define DD ((unsigned int)142) /* foo\nbar */\n") - assert lib.AA == 42 - assert lib.BB == -43 - assert lib.CC == 44 - assert lib.DD == 142 - -def test_global_const_int_size(): - # integer constants: ignore the declared type, always just use the value - for value in [-2**63, -2**31, -2**15, - 2**15-1, 2**15, 2**31-1, 2**31, 2**32-1, 2**32, - 2**63-1, 2**63, 2**64-1]: - ffi = FFI() - if value == int(ffi.cast("long long", value)): - if value < 0: - vstr = '(-%dLL-1)' % (~value,) - else: - vstr = '%dLL' % value - elif value == int(ffi.cast("unsigned long long", value)): - vstr = '%dULL' % value - else: - raise AssertionError(value) - ffi.cdef("static const unsigned short AA;") - lib = ffi.verify("#define AA %s\n" % vstr) - assert lib.AA == value - assert type(lib.AA) is type(int(lib.AA)) - -def test_global_constants_non_int(): - ffi = FFI() - ffi.cdef("static char *const PP;") - lib = ffi.verify('static char *const PP = "testing!";\n') - assert ffi.typeof(lib.PP) == ffi.typeof("char *") - assert ffi.string(lib.PP) == b"testing!" - -def test_nonfull_enum(): - ffi = FFI() - ffi.cdef("enum ee { EE1, EE2, EE3, ... \n \t };") - py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE2') - ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };") - assert ffi.string(ffi.cast('enum ee', 11)) == "EE2" - assert ffi.string(ffi.cast('enum ee', -10)) == "EE3" - # - assert ffi.typeof("enum ee").relements == {'EE1': 10, 'EE2': 11, 'EE3': -10} - assert ffi.typeof("enum ee").elements == {10: 'EE1', 11: 'EE2', -10: 'EE3'} - -def test_full_enum(): - ffi = FFI() - ffi.cdef("enum ee { EE1, EE2, EE3 };") - lib = ffi.verify("enum ee { EE1, EE2, EE3 };") - assert [lib.EE1, lib.EE2, lib.EE3] == [0, 1, 2] - -def test_enum_usage(): - ffi = FFI() - ffi.cdef("enum ee { EE1,EE2 }; typedef struct { enum ee x; } *sp;") - lib = ffi.verify("enum ee { EE1,EE2 }; typedef struct { enum ee x; } *sp;") - assert lib.EE2 == 1 - s = ffi.new("sp", [lib.EE2]) - assert s.x == 1 - s.x = 17 - assert s.x == 17 - -def test_anonymous_enum(): - ffi = FFI() - ffi.cdef("enum { EE1 }; enum { EE2, EE3 };") - lib = ffi.verify("enum { EE1 }; enum { EE2, EE3 };") - assert lib.EE1 == 0 - assert lib.EE2 == 0 - assert lib.EE3 == 1 - -def test_nonfull_anonymous_enum(): - ffi = FFI() - ffi.cdef("enum { EE1, ... }; enum { EE3, ... };") - lib = ffi.verify("enum { EE2, EE1 }; enum { EE3 };") - assert lib.EE1 == 1 - assert lib.EE3 == 0 - -def test_nonfull_enum_syntax2(): - ffi = FFI() - ffi.cdef("enum ee { EE1, EE2=\t..., EE3 };") - py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE1') - ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };") - assert ffi.string(ffi.cast('enum ee', 11)) == 'EE2' - assert ffi.string(ffi.cast('enum ee', -10)) == 'EE3' - # - ffi = FFI() - ffi.cdef("enum ee { EE1, EE2=\t... };") - py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE1') - ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };") - assert ffi.string(ffi.cast('enum ee', 11)) == 'EE2' - # - ffi = FFI() - ffi.cdef("enum ee2 { EE4=..., EE5=..., ... };") - ffi.verify("enum ee2 { EE4=-1234-5, EE5 }; ") - assert ffi.string(ffi.cast('enum ee2', -1239)) == 'EE4' - assert ffi.string(ffi.cast('enum ee2', -1238)) == 'EE5' - -def test_get_set_errno(): - ffi = FFI() - ffi.cdef("int foo(int);") - lib = ffi.verify(""" - static int foo(int x) - { - errno += 1; - return x * 7; - } - """) - ffi.errno = 15 - assert lib.foo(6) == 42 - assert ffi.errno == 16 - -def test_define_int(): - ffi = FFI() - ffi.cdef("#define FOO ...\n" - "\t#\tdefine\tBAR\t...\t\n" - "#define BAZ ...\n") - lib = ffi.verify("#define FOO 42\n" - "#define BAR (-44)\n" - "#define BAZ 0xffffffffffffffffULL\n") - assert lib.FOO == 42 - assert lib.BAR == -44 - assert lib.BAZ == 0xffffffffffffffff - -def test_access_variable(): - ffi = FFI() - ffi.cdef("static int foo(void);\n" - "static int somenumber;") - lib = ffi.verify(""" - static int somenumber = 2; - static int foo(void) { - return somenumber * 7; - } - """) - assert lib.somenumber == 2 - assert lib.foo() == 14 - lib.somenumber = -6 - assert lib.foo() == -42 - assert lib.somenumber == -6 - lib.somenumber = 2 # reset for the next run, if any - -def test_access_address_of_variable(): - # access the address of 'somenumber': need a trick - ffi = FFI() - ffi.cdef("static int somenumber; static int *const somenumberptr;") - lib = ffi.verify(""" - static int somenumber = 2; - #define somenumberptr (&somenumber) - """) - assert lib.somenumber == 2 - lib.somenumberptr[0] = 42 - assert lib.somenumber == 42 - lib.somenumber = 2 # reset for the next run, if any - -def test_access_array_variable(length=5): - ffi = FFI() - ffi.cdef("static int foo(int);\n" - "static int somenumber[%s];" % (length,)) - lib = ffi.verify(""" - static int somenumber[] = {2, 2, 3, 4, 5}; - static int foo(int i) { - return somenumber[i] * 7; - } - """) - if length == '': - # a global variable of an unknown array length is implicitly - # transformed into a global pointer variable, because we can only - # work with array instances whose length we know. using a pointer - # instead of an array gives the correct effects. - assert repr(lib.somenumber).startswith("<cdata 'int *' 0x") - py.test.raises(TypeError, len, lib.somenumber) - else: - assert repr(lib.somenumber).startswith("<cdata 'int[%s]' 0x" % length) - assert len(lib.somenumber) == 5 - assert lib.somenumber[3] == 4 - assert lib.foo(3) == 28 - lib.somenumber[3] = -6 - assert lib.foo(3) == -42 - assert lib.somenumber[3] == -6 - assert lib.somenumber[4] == 5 - lib.somenumber[3] = 4 # reset for the next run, if any - -def test_access_array_variable_length_hidden(): - test_access_array_variable(length='') - -def test_access_struct_variable(): - ffi = FFI() - ffi.cdef("struct foo { int x; ...; };\n" - "static int foo(int);\n" - "static struct foo stuff;") - lib = ffi.verify(""" - struct foo { int x, y, z; }; - static struct foo stuff = {2, 5, 8}; - static int foo(int i) { - switch (i) { - case 0: return stuff.x * 7; - case 1: return stuff.y * 7; - case 2: return stuff.z * 7; - } - return -1; - } - """) - assert lib.stuff.x == 2 - assert lib.foo(0) == 14 - assert lib.foo(1) == 35 - assert lib.foo(2) == 56 - lib.stuff.x = -6 - assert lib.foo(0) == -42 - assert lib.foo(1) == 35 - lib.stuff.x = 2 # reset for the next run, if any - -def test_access_callback(): - ffi = FFI() - ffi.cdef("static int (*cb)(int);\n" - "static int foo(int);\n" - "static void reset_cb(void);") - lib = ffi.verify(""" - static int g(int x) { return x * 7; } - static int (*cb)(int); - static int foo(int i) { return cb(i) - 1; } - static void reset_cb(void) { cb = g; } - """) - lib.reset_cb() - assert lib.foo(6) == 41 - my_callback = ffi.callback("int(*)(int)", lambda n: n * 222) - lib.cb = my_callback - assert lib.foo(4) == 887 - -def test_access_callback_function_typedef(): - ffi = FFI() - ffi.cdef("typedef int mycallback_t(int);\n" - "static mycallback_t *cb;\n" - "static int foo(int);\n" - "static void reset_cb(void);") - lib = ffi.verify(""" - static int g(int x) { return x * 7; } - static int (*cb)(int); - static int foo(int i) { return cb(i) - 1; } - static void reset_cb(void) { cb = g; } - """) - lib.reset_cb() - assert lib.foo(6) == 41 - my_callback = ffi.callback("int(*)(int)", lambda n: n * 222) - lib.cb = my_callback - assert lib.foo(4) == 887 - -def test_call_with_struct_ptr(): - ffi = FFI() - ffi.cdef("typedef struct { int x; ...; } foo_t; int foo(foo_t *);") - lib = ffi.verify(""" - typedef struct { int y, x; } foo_t; - static int foo(foo_t *f) { return f->x * 7; } - """) - f = ffi.new("foo_t *") - f.x = 6 - assert lib.foo(f) == 42 - -def test_unknown_type(): - ffi = FFI() - ffi.cdef(""" - typedef ... token_t; - int foo(token_t *); - #define TOKEN_SIZE ... - """) - lib = ffi.verify(""" - typedef float token_t; - static int foo(token_t *tk) { - if (!tk) - return -42; - *tk += 1.601f; - return (int)*tk; - } - #define TOKEN_SIZE sizeof(token_t) - """) - # we cannot let ffi.new("token_t *") work, because we don't know ahead of - # time if it's ok to ask 'sizeof(token_t)' in the C code or not. - # See test_unknown_type_2. Workaround. - tkmem = ffi.new("char[]", lib.TOKEN_SIZE) # zero-initialized - tk = ffi.cast("token_t *", tkmem) - results = [lib.foo(tk) for i in range(6)] - assert results == [1, 3, 4, 6, 8, 9] - assert lib.foo(ffi.NULL) == -42 - -def test_unknown_type_2(): - ffi = FFI() - ffi.cdef("typedef ... token_t;") - lib = ffi.verify("typedef struct token_s token_t;") - # assert did not crash, even though 'sizeof(token_t)' is not valid in C. - -def test_unknown_type_3(): - ffi = FFI() - ffi.cdef(""" - typedef ... *token_p; - token_p foo(token_p); - """) - lib = ffi.verify(""" - typedef struct _token_s *token_p; - token_p foo(token_p arg) { - if (arg) - return (token_p)0x12347; - else - return (token_p)0x12345; - } - """) - p = lib.foo(ffi.NULL) - assert int(ffi.cast("intptr_t", p)) == 0x12345 - q = lib.foo(p) - assert int(ffi.cast("intptr_t", q)) == 0x12347 - -def test_varargs(): - ffi = FFI() - ffi.cdef("int foo(int x, ...);") - lib = ffi.verify(""" - int foo(int x, ...) { - va_list vargs; - va_start(vargs, x); - x -= va_arg(vargs, int); - x -= va_arg(vargs, int); - va_end(vargs); - return x; - } - """) - assert lib.foo(50, ffi.cast("int", 5), ffi.cast("int", 3)) == 42 - -def test_varargs_exact(): - if sys.platform == 'win32': - py.test.skip("XXX fixme: only gives warnings") - ffi = FFI() - ffi.cdef("int foo(int x, ...);") - py.test.raises(VerificationError, ffi.verify, """ - int foo(long long x, ...) { - return x; - } - """) - -def test_varargs_struct(): - ffi = FFI() - ffi.cdef("struct foo_s { char a; int b; }; int foo(int x, ...);") - lib = ffi.verify(""" - struct foo_s { - char a; int b; - }; - int foo(int x, ...) { - va_list vargs; - struct foo_s s; - va_start(vargs, x); - s = va_arg(vargs, struct foo_s); - va_end(vargs); - return s.a - s.b; - } - """) - s = ffi.new("struct foo_s *", [b'B', 1]) - assert lib.foo(50, s[0]) == ord('A') - -def test_autofilled_struct_as_argument(): - ffi = FFI() - ffi.cdef("struct foo_s { long a; double b; ...; };\n" - "int foo(struct foo_s);") - lib = ffi.verify(""" - struct foo_s { - double b; - long a; - }; - int foo(struct foo_s s) { - return (int)s.a - (int)s.b; - } - """) - s = ffi.new("struct foo_s *", [100, 1]) - assert lib.foo(s[0]) == 99 - assert lib.foo([100, 1]) == 99 - -def test_autofilled_struct_as_argument_dynamic(): - ffi = FFI() - ffi.cdef("struct foo_s { long a; ...; };\n" - "static int (*foo)(struct foo_s);") - lib = ffi.verify(""" - struct foo_s { - double b; - long a; - }; - int foo1(struct foo_s s) { - return (int)s.a - (int)s.b; - } - static int (*foo)(struct foo_s s) = &foo1; - """) - e = py.test.raises(NotImplementedError, lib.foo, "?") - msg = ("ctype 'struct foo_s' not supported as argument. It is a struct " - 'declared with "...;", but the C calling convention may depend on ' - "the missing fields; or, it contains anonymous struct/unions. " - "Such structs are only supported as argument " - "if the function is 'API mode' and non-variadic (i.e. declared " - "inside ffibuilder.cdef()+ffibuilder.set_source() and not taking " - "a final '...' argument)") - assert str(e.value) == msg - -def test_func_returns_struct(): - ffi = FFI() - ffi.cdef(""" - struct foo_s { int aa, bb; }; - struct foo_s foo(int a, int b); - """) - lib = ffi.verify(""" - struct foo_s { int aa, bb; }; - struct foo_s foo(int a, int b) { - struct foo_s r; - r.aa = a*a; - r.bb = b*b; - return r; - } - """) - s = lib.foo(6, 7) - assert repr(s) == "<cdata 'struct foo_s' owning 8 bytes>" - assert s.aa == 36 - assert s.bb == 49 - -def test_func_as_funcptr(): - ffi = FFI() - ffi.cdef("int *(*const fooptr)(void);") - lib = ffi.verify(""" - int *foo(void) { - return (int*)"foobar"; - } - int *(*fooptr)(void) = foo; - """) - foochar = ffi.cast("char *(*)(void)", lib.fooptr) - s = foochar() - assert ffi.string(s) == b"foobar" - -def test_funcptr_as_argument(): - ffi = FFI() - ffi.cdef(""" - void qsort(void *base, size_t nel, size_t width, - int (*compar)(const void *, const void *)); - """) - ffi.verify("#include <stdlib.h>") - -def test_func_as_argument(): - ffi = FFI() - ffi.cdef(""" - void qsort(void *base, size_t nel, size_t width, - int compar(const void *, const void *)); - """) - ffi.verify("#include <stdlib.h>") - -def test_array_as_argument(): - ffi = FFI() - ffi.cdef(""" - size_t strlen(char string[]); - """) - ffi.verify("#include <string.h>") - -def test_enum_as_argument(): - ffi = FFI() - ffi.cdef(""" - enum foo_e { AA, BB, ... }; - int foo_func(enum foo_e); - """) - lib = ffi.verify(""" - enum foo_e { AA, CC, BB }; - int foo_func(enum foo_e e) { return (int)e; } - """) - assert lib.foo_func(lib.BB) == 2 - py.test.raises(TypeError, lib.foo_func, "BB") - -def test_enum_as_function_result(): - ffi = FFI() - ffi.cdef(""" - enum foo_e { AA, BB, ... }; - enum foo_e foo_func(int x); - """) - lib = ffi.verify(""" - enum foo_e { AA, CC, BB }; - enum foo_e foo_func(int x) { return (enum foo_e)x; } - """) - assert lib.foo_func(lib.BB) == lib.BB == 2 - -def test_enum_values(): - ffi = FFI() - ffi.cdef("enum enum1_e { AA, BB };") - lib = ffi.verify("enum enum1_e { AA, BB };") - assert lib.AA == 0 - assert lib.BB == 1 - assert ffi.string(ffi.cast("enum enum1_e", 1)) == 'BB' - -def test_typedef_complete_enum(): - ffi = FFI() - ffi.cdef("typedef enum { AA, BB } enum1_t;") - lib = ffi.verify("typedef enum { AA, BB } enum1_t;") - assert ffi.string(ffi.cast("enum1_t", 1)) == 'BB' - assert lib.AA == 0 - assert lib.BB == 1 - -def test_typedef_broken_complete_enum(): - # xxx this is broken in old cffis, but works with recompiler.py - ffi = FFI() - ffi.cdef("typedef enum { AA, BB } enum1_t;") - lib = ffi.verify("typedef enum { AA, CC, BB } enum1_t;") - assert lib.AA == 0 - assert lib.BB == 2 - -def test_typedef_incomplete_enum(): - ffi = FFI() - ffi.cdef("typedef enum { AA, BB, ... } enum1_t;") - lib = ffi.verify("typedef enum { AA, CC, BB } enum1_t;") - assert ffi.string(ffi.cast("enum1_t", 1)) == '1' - assert ffi.string(ffi.cast("enum1_t", 2)) == 'BB' - assert lib.AA == 0 - assert lib.BB == 2 - -def test_typedef_enum_as_argument(): - ffi = FFI() - ffi.cdef(""" - typedef enum { AA, BB, ... } foo_t; - int foo_func(foo_t); - """) - lib = ffi.verify(""" - typedef enum { AA, CC, BB } foo_t; - int foo_func(foo_t e) { return (int)e; } - """) - assert lib.foo_func(lib.BB) == lib.BB == 2 - py.test.raises(TypeError, lib.foo_func, "BB") - -def test_typedef_enum_as_function_result(): - ffi = FFI() - ffi.cdef(""" - typedef enum { AA, BB, ... } foo_t; - foo_t foo_func(int x); - """) - lib = ffi.verify(""" - typedef enum { AA, CC, BB } foo_t; - foo_t foo_func(int x) { return (foo_t)x; } - """) - assert lib.foo_func(lib.BB) == lib.BB == 2 - -def test_function_typedef(): - ffi = FFI() - ffi.cdef(""" - typedef double func_t(double); - func_t sin; - """) - lib = ffi.verify('#include <math.h>', libraries=lib_m) - assert lib.sin(1.23) == math.sin(1.23) - -def test_opaque_integer_as_function_result(): - #import platform - #if platform.machine().startswith('sparc'): - # py.test.skip('Breaks horribly on sparc (SIGILL + corrupted stack)') - #elif platform.machine() == 'mips64' and sys.maxsize > 2**32: - # py.test.skip('Segfaults on mips64el') - # XXX bad abuse of "struct { ...; }". It only works a bit by chance - # anyway. XXX think about something better :-( - ffi = FFI() - ffi.cdef(""" - typedef struct { ...; } myhandle_t; - myhandle_t foo(void); - """) - lib = ffi.verify(""" - typedef short myhandle_t; - myhandle_t foo(void) { return 42; } - """) - h = lib.foo() - assert ffi.sizeof(h) == ffi.sizeof("short") - -def test_return_partial_struct(): - ffi = FFI() - ffi.cdef(""" - typedef struct { int x; ...; } foo_t; - foo_t foo(void); - """) - lib = ffi.verify(""" - typedef struct { int y, x; } foo_t; - foo_t foo(void) { foo_t r = { 45, 81 }; return r; } - """) - h = lib.foo() - assert ffi.sizeof(h) == 2 * ffi.sizeof("int") - assert h.x == 81 - -def test_take_and_return_partial_structs(): - ffi = FFI() - ffi.cdef(""" - typedef struct { int x; ...; } foo_t; - foo_t foo(foo_t, foo_t); - """) - lib = ffi.verify(""" - typedef struct { int y, x; } foo_t; - foo_t foo(foo_t a, foo_t b) { - foo_t r = { 100, a.x * 5 + b.x * 7 }; - return r; - } - """) - args = ffi.new("foo_t[3]") - args[0].x = 1000 - args[2].x = -498 - h = lib.foo(args[0], args[2]) - assert ffi.sizeof(h) == 2 * ffi.sizeof("int") - assert h.x == 1000 * 5 - 498 * 7 - -def test_cannot_name_struct_type(): - ffi = FFI() - ffi.cdef("typedef struct { int x; } **sp; void foo(sp);") - e = py.test.raises(VerificationError, ffi.verify, - "typedef struct { int x; } **sp; void foo(sp x) { }") - assert 'in argument of foo: unknown type name' in str(e.value) - -def test_dont_check_unnamable_fields(): - ffi = FFI() - ffi.cdef("struct foo_s { struct { int x; } someone; };") - ffi.verify("struct foo_s { struct { int x; } someone; };") - # assert did not crash - -def test_nested_anonymous_struct_exact(): - if sys.platform == 'win32': - py.test.skip("nested anonymous struct/union") - ffi = FFI() - ffi.cdef(""" - struct foo_s { struct { int a; char b; }; union { char c, d; }; }; - """) - assert ffi.offsetof("struct foo_s", "c") == 2 * ffi.sizeof("int") - assert ffi.sizeof("struct foo_s") == 3 * ffi.sizeof("int") - ffi.verify(""" - struct foo_s { struct { int a; char b; }; union { char c, d; }; }; - """) - p = ffi.new("struct foo_s *") - assert ffi.sizeof(p[0]) == 3 * ffi.sizeof("int") # with alignment - p.a = 1234567 - p.b = b'X' - p.c = b'Y' - assert p.a == 1234567 - assert p.b == b'X' - assert p.c == b'Y' - assert p.d == b'Y' - -def test_nested_anonymous_struct_exact_error(): - if sys.platform == 'win32': - py.test.skip("nested anonymous struct/union") - ffi = FFI() - ffi.cdef(""" - struct foo_s { struct { int a; char b; }; union { char c, d; }; }; - """) - py.test.raises(VerificationError, ffi.verify, """ - struct foo_s { struct { int a; short b; }; union { char c, d; }; }; - """) - # works fine now - #py.test.raises(VerificationError, ffi.verify, """ - # struct foo_s { struct { int a; char e, b; }; union { char c, d; }; }; - #""") - -def test_nested_anonymous_struct_inexact_1(): - ffi = FFI() - ffi.cdef(""" - struct foo_s { struct { char b; ...; }; union { char c, d; }; }; - """) - ffi.verify(""" - struct foo_s { int a, padding; char c, d, b; }; - """) - assert ffi.sizeof("struct foo_s") == 3 * ffi.sizeof("int") - -def test_nested_anonymous_struct_inexact_2(): - ffi = FFI() - ffi.cdef(""" - struct foo_s { union { char c, d; }; struct { int a; char b; }; ...; }; - """) - ffi.verify(""" - struct foo_s { int a, padding; char c, d, b; }; - """) - assert ffi.sizeof("struct foo_s") == 3 * ffi.sizeof("int") - -def test_ffi_union(): - ffi = FFI() - ffi.cdef("union foo_u { char x; long *z; };") - ffi.verify("union foo_u { char x; int y; long *z; };") - -def test_ffi_union_partial(): - ffi = FFI() - ffi.cdef("union foo_u { char x; ...; };") - ffi.verify("union foo_u { char x; int y; };") - assert ffi.sizeof("union foo_u") == 4 - -def test_ffi_union_with_partial_struct(): - ffi = FFI() - ffi.cdef("struct foo_s { int x; ...; }; union foo_u { struct foo_s s; };") - ffi.verify("struct foo_s { int a; int x; }; " - "union foo_u { char b[32]; struct foo_s s; };") - assert ffi.sizeof("struct foo_s") == 8 - assert ffi.sizeof("union foo_u") == 32 - -def test_ffi_union_partial_2(): - ffi = FFI() - ffi.cdef("typedef union { char x; ...; } u1;") - ffi.verify("typedef union { char x; int y; } u1;") - assert ffi.sizeof("u1") == 4 - -def test_ffi_union_with_partial_struct_2(): - ffi = FFI() - ffi.cdef("typedef struct { int x; ...; } s1;" - "typedef union { s1 s; } u1;") - ffi.verify("typedef struct { int a; int x; } s1; " - "typedef union { char b[32]; s1 s; } u1;") - assert ffi.sizeof("s1") == 8 - assert ffi.sizeof("u1") == 32 - assert ffi.offsetof("u1", "s") == 0 - -def test_ffi_struct_packed(): - if sys.platform == 'win32': - py.test.skip("needs a GCC extension") - ffi = FFI() - ffi.cdef("struct foo_s { int b; ...; };") - ffi.verify(""" - struct foo_s { - char a; - int b; - } __attribute__((packed)); - """) - -def test_tmpdir(): - import tempfile, os - from testing.udir import udir - tmpdir = tempfile.mkdtemp(dir=str(udir)) - ffi = FFI() - ffi.cdef("int foo(int);") - lib = ffi.verify("int foo(int a) { return a + 42; }", tmpdir=tmpdir) - assert os.listdir(tmpdir) - assert lib.foo(100) == 142 - -def test_relative_to(): - py.test.skip("not available") - import tempfile, os - from testing.udir import udir - tmpdir = tempfile.mkdtemp(dir=str(udir)) - ffi = FFI() - ffi.cdef("int foo(int);") - f = open(os.path.join(tmpdir, 'foo.h'), 'w') - f.write("int foo(int a) { return a + 42; }\n") - f.close() - lib = ffi.verify('#include "foo.h"', - include_dirs=['.'], - relative_to=os.path.join(tmpdir, 'x')) - assert lib.foo(100) == 142 - -def test_bug1(): - ffi = FFI() - ffi.cdef(""" - typedef struct tdlhandle_s { ...; } *tdl_handle_t; - typedef struct my_error_code_ { - tdl_handle_t *rh; - } my_error_code_t; - """) - ffi.verify(""" - typedef struct tdlhandle_s { int foo; } *tdl_handle_t; - typedef struct my_error_code_ { - tdl_handle_t *rh; - } my_error_code_t; - """) - -def test_bool(): - if sys.platform == 'win32': - py.test.skip("_Bool not in MSVC") - ffi = FFI() - ffi.cdef("struct foo_s { _Bool x; };" - "_Bool foo(_Bool); static _Bool (*foop)(_Bool);") - lib = ffi.verify(""" - struct foo_s { _Bool x; }; - int foo(int arg) { - return !arg; - } - _Bool _foofunc(_Bool x) { - return !x; - } - static _Bool (*foop)(_Bool) = _foofunc; - """) - p = ffi.new("struct foo_s *") - p.x = 1 - assert p.x is True - with pytest.raises(OverflowError): - p.x = -1 - with pytest.raises(TypeError): - p.x = 0.0 - assert lib.foop(1) is False - assert lib.foop(True) is False - assert lib.foop(0) is True - py.test.raises(OverflowError, lib.foop, 42) - py.test.raises(TypeError, lib.foop, 0.0) - assert lib.foo(1) is False - assert lib.foo(True) is False - assert lib.foo(0) is True - py.test.raises(OverflowError, lib.foo, 42) - py.test.raises(TypeError, lib.foo, 0.0) - assert int(ffi.cast("_Bool", long(1))) == 1 - assert int(ffi.cast("_Bool", long(0))) == 0 - assert int(ffi.cast("_Bool", long(-1))) == 1 - assert int(ffi.cast("_Bool", 10**200)) == 1 - assert int(ffi.cast("_Bool", 10**40000)) == 1 - # - class Foo(object): - def __int__(self): - self.seen = 1 - return result - f = Foo() - f.seen = 0 - result = 42 - assert int(ffi.cast("_Bool", f)) == 1 - assert f.seen - f.seen = 0 - result = 0 - assert int(ffi.cast("_Bool", f)) == 0 - assert f.seen - # - py.test.raises(TypeError, ffi.cast, "_Bool", []) - -def test_bool_on_long_double(): - if sys.platform == 'win32': - py.test.skip("_Bool not in MSVC") - f = 1E-250 - if f == 0.0 or f*f != 0.0: - py.test.skip("unexpected precision") - ffi = FFI() - ffi.cdef("long double square(long double f); _Bool opposite(_Bool);") - lib = ffi.verify("long double square(long double f) { return f*f; }\n" - "_Bool opposite(_Bool x) { return !x; }") - f0 = lib.square(0.0) - f2 = lib.square(f) - f3 = lib.square(f * 2.0) - if repr(f2) == repr(f3): - py.test.skip("long double doesn't have enough precision") - assert float(f0) == float(f2) == float(f3) == 0.0 # too tiny for 'double' - assert int(ffi.cast("_Bool", f2)) == 1 - assert int(ffi.cast("_Bool", f3)) == 1 - assert int(ffi.cast("_Bool", f0)) == 0 - py.test.raises(TypeError, lib.opposite, f2) - -def test_cannot_pass_float(): - for basetype in ['char', 'short', 'int', 'long', 'long long']: - for sign in ['signed', 'unsigned']: - type = '%s %s' % (sign, basetype) - ffi = FFI() - ffi.cdef("struct foo_s { %s x; };\n" - "int foo(%s);" % (type, type)) - lib = ffi.verify(""" - struct foo_s { %s x; }; - int foo(%s arg) { - return !arg; - } - """ % (type, type)) - p = ffi.new("struct foo_s *") - with pytest.raises(TypeError): - p.x = 0.0 - assert lib.foo(42) == 0 - assert lib.foo(0) == 1 - py.test.raises(TypeError, lib.foo, 0.0) - -def test_addressof(): - ffi = FFI() - ffi.cdef(""" - struct point_s { int x, y; }; - struct foo_s { int z; struct point_s point; }; - struct point_s sum_coord(struct point_s *); - """) - lib = ffi.verify(""" - struct point_s { int x, y; }; - struct foo_s { int z; struct point_s point; }; - struct point_s sum_coord(struct point_s *point) { - struct point_s r; - r.x = point->x + point->y; - r.y = point->x - point->y; - return r; - } - """) - p = ffi.new("struct foo_s *") - p.point.x = 16 - p.point.y = 9 - py.test.raises(TypeError, lib.sum_coord, p.point) - res = lib.sum_coord(ffi.addressof(p.point)) - assert res.x == 25 - assert res.y == 7 - res2 = lib.sum_coord(ffi.addressof(res)) - assert res2.x == 32 - assert res2.y == 18 - py.test.raises(TypeError, lib.sum_coord, res2) - -def test_callback_in_thread(): - py.test.xfail("adapt or remove") - if sys.platform == 'win32': - py.test.skip("pthread only") - import os, subprocess, imp - arg = os.path.join(os.path.dirname(__file__), 'callback_in_thread.py') - g = subprocess.Popen([sys.executable, arg, - os.path.dirname(imp.find_module('cffi')[1])]) - result = g.wait() - assert result == 0 - -def test_keepalive_lib(): - py.test.xfail("adapt or remove") - ffi = FFI() - ffi.cdef("int foobar(void);") - lib = ffi.verify("int foobar(void) { return 42; }") - func = lib.foobar - ffi_r = weakref.ref(ffi) - lib_r = weakref.ref(lib) - del ffi - import gc; gc.collect() # lib stays alive - assert lib_r() is not None - assert ffi_r() is not None - assert func() == 42 - -def test_keepalive_ffi(): - py.test.xfail("adapt or remove") - ffi = FFI() - ffi.cdef("int foobar(void);") - lib = ffi.verify("int foobar(void) { return 42; }") - func = lib.foobar - ffi_r = weakref.ref(ffi) - lib_r = weakref.ref(lib) - del lib - import gc; gc.collect() # ffi stays alive - assert ffi_r() is not None - assert lib_r() is not None - assert func() == 42 - -def test_FILE_stored_in_stdout(): - if not sys.platform.startswith('linux'): - py.test.skip("likely, we cannot assign to stdout") - ffi = FFI() - ffi.cdef("int printf(const char *, ...); FILE *setstdout(FILE *);") - lib = ffi.verify(""" - #include <stdio.h> - FILE *setstdout(FILE *f) { - FILE *result = stdout; - stdout = f; - return result; - } - """) - import os - fdr, fdw = os.pipe() - fw1 = os.fdopen(fdw, 'wb', 256) - old_stdout = lib.setstdout(fw1) - try: - # - fw1.write(b"X") - r = lib.printf(b"hello, %d!\n", ffi.cast("int", 42)) - fw1.close() - assert r == len("hello, 42!\n") - # - finally: - lib.setstdout(old_stdout) - # - result = os.read(fdr, 256) - os.close(fdr) - # the 'X' might remain in the user-level buffer of 'fw1' and - # end up showing up after the 'hello, 42!\n' - assert result == b"Xhello, 42!\n" or result == b"hello, 42!\nX" - -def test_FILE_stored_explicitly(): - ffi = FFI() - ffi.cdef("int myprintf11(const char *, int); extern FILE *myfile;") - lib = ffi.verify(""" - #include <stdio.h> - FILE *myfile; - int myprintf11(const char *out, int value) { - return fprintf(myfile, out, value); - } - """) - import os - fdr, fdw = os.pipe() - fw1 = os.fdopen(fdw, 'wb', 256) - lib.myfile = ffi.cast("FILE *", fw1) - # - fw1.write(b"X") - r = lib.myprintf11(b"hello, %d!\n", ffi.cast("int", 42)) - fw1.close() - assert r == len("hello, 42!\n") - # - result = os.read(fdr, 256) - os.close(fdr) - # the 'X' might remain in the user-level buffer of 'fw1' and - # end up showing up after the 'hello, 42!\n' - assert result == b"Xhello, 42!\n" or result == b"hello, 42!\nX" - -def test_global_array_with_missing_length(): - ffi = FFI() - ffi.cdef("extern int fooarray[];") - lib = ffi.verify("int fooarray[50];") - assert repr(lib.fooarray).startswith("<cdata 'int *'") - -def test_global_array_with_dotdotdot_length(): - ffi = FFI() - ffi.cdef("extern int fooarray[...];") - lib = ffi.verify("int fooarray[50];") - assert repr(lib.fooarray).startswith("<cdata 'int[50]'") - -def test_bad_global_array_with_dotdotdot_length(): - py.test.xfail("was detected only because 23 bytes cannot be divided by 4; " - "redo more generally") - ffi = FFI() - ffi.cdef("extern int fooarray[...];") - py.test.raises(VerificationError, ffi.verify, "char fooarray[23];") - -def test_struct_containing_struct(): - ffi = FFI() - ffi.cdef("struct foo_s { ...; }; struct bar_s { struct foo_s f; ...; };") - ffi.verify("struct foo_s { int x; }; struct bar_s { struct foo_s f; };") - # - ffi = FFI() - ffi.cdef("struct foo_s { struct bar_s f; ...; }; struct bar_s { ...; };") - ffi.verify("struct bar_s { int x; }; struct foo_s { struct bar_s f; };") - -def test_struct_returned_by_func(): - ffi = FFI() - ffi.cdef("typedef ... foo_t; foo_t myfunc(void);") - e = py.test.raises(TypeError, ffi.verify, - "typedef struct { int x; } foo_t; " - "foo_t myfunc(void) { foo_t x = { 42 }; return x; }") - assert str(e.value) == ( - "function myfunc: 'foo_t' is used as result type, but is opaque") - -def test_include(): - ffi1 = FFI() - ffi1.cdef("typedef struct { int x; ...; } foo_t;") - ffi1.verify("typedef struct { int y, x; } foo_t;") - ffi2 = FFI() - ffi2.include(ffi1) - ffi2.cdef("int myfunc(foo_t *);") - lib = ffi2.verify("typedef struct { int y, x; } foo_t;" - "int myfunc(foo_t *p) { return 42 * p->x; }") - res = lib.myfunc(ffi2.new("foo_t *", {'x': 10})) - assert res == 420 - res = lib.myfunc(ffi1.new("foo_t *", {'x': -10})) - assert res == -420 - -def test_include_enum(): - ffi1 = FFI() - ffi1.cdef("enum foo_e { AA, ... };") - lib1 = ffi1.verify("enum foo_e { CC, BB, AA };") - ffi2 = FFI() - ffi2.include(ffi1) - ffi2.cdef("int myfunc(enum foo_e);") - lib2 = ffi2.verify("enum foo_e { CC, BB, AA };" - "int myfunc(enum foo_e x) { return (int)x; }") - res = lib2.myfunc(lib2.AA) - assert res == 2 - -def test_named_pointer_as_argument(): - ffi = FFI() - ffi.cdef("typedef struct { int x; } *mystruct_p;\n" - "mystruct_p ff5a(mystruct_p);") - lib = ffi.verify("typedef struct { int x; } *mystruct_p;\n" - "mystruct_p ff5a(mystruct_p p) { p->x += 40; return p; }") - p = ffi.new("mystruct_p", [-2]) - q = lib.ff5a(p) - assert q == p - assert p.x == 38 - -def test_enum_size(): - cases = [('123', 4, 4294967295), - ('4294967295U', 4, 4294967295), - ('-123', 4, -1), - ('-2147483647-1', 4, -1), - ] - if FFI().sizeof("long") == 8: - cases += [('4294967296L', 8, 2**64-1), - ('%dUL' % (2**64-1), 8, 2**64-1), - ('-2147483649L', 8, -1), - ('%dL-1L' % (1-2**63), 8, -1)] - for hidden_value, expected_size, expected_minus1 in cases: - if sys.platform == 'win32' and 'U' in hidden_value: - continue # skipped on Windows - ffi = FFI() - ffi.cdef("enum foo_e { AA, BB, ... };") - lib = ffi.verify("enum foo_e { AA, BB=%s };" % hidden_value) - assert lib.AA == 0 - assert lib.BB == eval(hidden_value.replace('U', '').replace('L', '')) - assert ffi.sizeof("enum foo_e") == expected_size - if sys.platform != 'win32': - assert int(ffi.cast("enum foo_e", -1)) == expected_minus1 - # test with the large value hidden: - # disabled so far, doesn't work -## for hidden_value, expected_size, expected_minus1 in cases: -## ffi = FFI() -## ffi.cdef("enum foo_e { AA, BB, ... };") -## lib = ffi.verify("enum foo_e { AA, BB=%s };" % hidden_value) -## assert lib.AA == 0 -## assert ffi.sizeof("enum foo_e") == expected_size -## assert int(ffi.cast("enum foo_e", -1)) == expected_minus1 - -def test_enum_bug118(): - maxulong = 256 ** FFI().sizeof("unsigned long") - 1 - for c2, c2c in [(-1, ''), - (-1, ''), - (0xffffffff, 'U'), - (maxulong, 'UL'), - (-int(maxulong / 3), 'L')]: - if c2c and sys.platform == 'win32': - continue # enums may always be signed with MSVC - ffi = FFI() - ffi.cdef("enum foo_e { AA };") - lib = ffi.verify("enum foo_e { AA=%s%s };" % (c2, c2c)) - assert lib.AA == c2 - -def test_string_to_voidp_arg(): - ffi = FFI() - ffi.cdef("int myfunc(void *);") - lib = ffi.verify("int myfunc(void *p) { return ((signed char *)p)[0]; }") - res = lib.myfunc(b"hi!") - assert res == ord(b"h") - p = ffi.new("char[]", b"gah") - res = lib.myfunc(p) - assert res == ord(b"g") - res = lib.myfunc(ffi.cast("void *", p)) - assert res == ord(b"g") - res = lib.myfunc(ffi.cast("int *", p)) - assert res == ord(b"g") - -def test_callback_indirection(): - ffi = FFI() - ffi.cdef(""" - static int (*python_callback)(int how_many, int *values); - int (*const c_callback)(int,...); /* pass this ptr to C routines */ - int some_c_function(int(*cb)(int,...)); - """) - lib = ffi.verify(""" - #include <stdarg.h> - #ifdef _WIN32 - #include <malloc.h> - #define alloca _alloca - #else - # ifdef __FreeBSD__ - # include <stdlib.h> - # else - # include <alloca.h> - # endif - #endif - static int (*python_callback)(int how_many, int *values); - static int c_callback(int how_many, ...) { - va_list ap; - /* collect the "..." arguments into the values[] array */ - int i, *values = alloca((size_t)how_many * sizeof(int)); - va_start(ap, how_many); - for (i=0; i<how_many; i++) - values[i] = va_arg(ap, int); - va_end(ap); - return python_callback(how_many, values); - } - int some_c_function(int(*cb)(int,...)) { - int result = cb(2, 10, 20); - result += cb(3, 30, 40, 50); - return result; - } - """) - seen = [] - @ffi.callback("int(int, int*)") - def python_callback(how_many, values): - seen.append([values[i] for i in range(how_many)]) - return 42 - lib.python_callback = python_callback - - res = lib.some_c_function(lib.c_callback) - assert res == 84 - assert seen == [[10, 20], [30, 40, 50]] - -def test_floatstar_argument(): - ffi = FFI() - ffi.cdef("float sum3floats(float *);") - lib = ffi.verify(""" - float sum3floats(float *f) { - return f[0] + f[1] + f[2]; - } - """) - assert lib.sum3floats((1.5, 2.5, 3.5)) == 7.5 - p = ffi.new("float[]", (1.5, 2.5, 3.5)) - assert lib.sum3floats(p) == 7.5 - -def test_charstar_argument(): - ffi = FFI() - ffi.cdef("char sum3chars(char *);") - lib = ffi.verify(""" - char sum3chars(char *f) { - return (char)(f[0] + f[1] + f[2]); - } - """) - assert lib.sum3chars((b'\x10', b'\x20', b'\x30')) == b'\x60' - p = ffi.new("char[]", b'\x10\x20\x30') - assert lib.sum3chars(p) == b'\x60' - -def test_passing_string_or_NULL(): - ffi = FFI() - ffi.cdef("int seeme1(char *); int seeme2(int *);") - lib = ffi.verify(""" - int seeme1(char *x) { - return (x == NULL); - } - int seeme2(int *x) { - return (x == NULL); - } - """) - assert lib.seeme1(b"foo") == 0 - assert lib.seeme1(ffi.NULL) == 1 - assert lib.seeme2([42, 43]) == 0 - assert lib.seeme2(ffi.NULL) == 1 - py.test.raises(TypeError, lib.seeme1, None) - py.test.raises(TypeError, lib.seeme2, None) - py.test.raises(TypeError, lib.seeme1, 0.0) - py.test.raises(TypeError, lib.seeme2, 0.0) - py.test.raises(TypeError, lib.seeme1, 0) - py.test.raises(TypeError, lib.seeme2, 0) - zeroL = 99999999999999999999 - zeroL -= 99999999999999999999 - py.test.raises(TypeError, lib.seeme2, zeroL) - -def test_typeof_function(): - ffi = FFI() - ffi.cdef("int foo(int, char);") - lib = ffi.verify("int foo(int x, char y) { (void)x; (void)y; return 42; }") - ctype = ffi.typeof(lib.foo) - assert len(ctype.args) == 2 - assert ctype.result == ffi.typeof("int") - -def test_call_with_voidstar_arg(): - ffi = FFI() - ffi.cdef("int f(void *);") - lib = ffi.verify("int f(void *x) { return ((char*)x)[0]; }") - assert lib.f(b"foobar") == ord(b"f") - -def test_dir(): - ffi = FFI() - ffi.cdef("""void somefunc(void); - extern int somevar, somearray[2]; - static char *const sv2; - enum my_e { AA, BB, ... }; - #define FOO ...""") - lib = ffi.verify("""void somefunc(void) { } - int somevar, somearray[2]; - #define sv2 "text" - enum my_e { AA, BB }; - #define FOO 42""") - assert dir(lib) == ['AA', 'BB', 'FOO', 'somearray', - 'somefunc', 'somevar', 'sv2'] - -def test_typeof_func_with_struct_argument(): - ffi = FFI() - ffi.cdef("""struct s { int a; }; int foo(struct s);""") - lib = ffi.verify("""struct s { int a; }; - int foo(struct s x) { return x.a; }""") - s = ffi.new("struct s *", [-1234]) - m = lib.foo(s[0]) - assert m == -1234 - assert repr(ffi.typeof(lib.foo)) == "<ctype 'int(*)(struct s)'>" - -def test_bug_const_char_ptr_array_1(): - ffi = FFI() - ffi.cdef("""extern const char *a[...];""") - lib = ffi.verify("""const char *a[5];""") - assert repr(ffi.typeof(lib.a)) == "<ctype 'char *[5]'>" - -def test_bug_const_char_ptr_array_2(): - ffi = FFI() - ffi.cdef("""extern const int a[];""") - lib = ffi.verify("""const int a[5];""") - assert repr(ffi.typeof(lib.a)) == "<ctype 'int *'>" - -def _test_various_calls(force_libffi): - cdef_source = """ - extern int xvalue; - extern long long ivalue, rvalue; - extern float fvalue; - extern double dvalue; - extern long double Dvalue; - signed char tf_bb(signed char x, signed char c); - unsigned char tf_bB(signed char x, unsigned char c); - short tf_bh(signed char x, short c); - unsigned short tf_bH(signed char x, unsigned short c); - int tf_bi(signed char x, int c); - unsigned int tf_bI(signed char x, unsigned int c); - long tf_bl(signed char x, long c); - unsigned long tf_bL(signed char x, unsigned long c); - long long tf_bq(signed char x, long long c); - unsigned long long tf_bQ(signed char x, unsigned long long c); - float tf_bf(signed char x, float c); - double tf_bd(signed char x, double c); - long double tf_bD(signed char x, long double c); - """ - if force_libffi: - cdef_source = (cdef_source - .replace('tf_', '(*const tf_') - .replace('(signed char x', ')(signed char x')) - ffi = FFI() - ffi.cdef(cdef_source) - lib = ffi.verify(""" - int xvalue; - long long ivalue, rvalue; - float fvalue; - double dvalue; - long double Dvalue; - - typedef signed char b_t; - typedef unsigned char B_t; - typedef short h_t; - typedef unsigned short H_t; - typedef int i_t; - typedef unsigned int I_t; - typedef long l_t; - typedef unsigned long L_t; - typedef long long q_t; - typedef unsigned long long Q_t; - typedef float f_t; - typedef double d_t; - typedef long double D_t; - #define S(letter) xvalue = (int)x; letter##value = (letter##_t)c; - #define R(letter) return (letter##_t)rvalue; - - signed char tf_bb(signed char x, signed char c) { S(i) R(b) } - unsigned char tf_bB(signed char x, unsigned char c) { S(i) R(B) } - short tf_bh(signed char x, short c) { S(i) R(h) } - unsigned short tf_bH(signed char x, unsigned short c) { S(i) R(H) } - int tf_bi(signed char x, int c) { S(i) R(i) } - unsigned int tf_bI(signed char x, unsigned int c) { S(i) R(I) } - long tf_bl(signed char x, long c) { S(i) R(l) } - unsigned long tf_bL(signed char x, unsigned long c) { S(i) R(L) } - long long tf_bq(signed char x, long long c) { S(i) R(q) } - unsigned long long tf_bQ(signed char x, unsigned long long c) { S(i) R(Q) } - float tf_bf(signed char x, float c) { S(f) R(f) } - double tf_bd(signed char x, double c) { S(d) R(d) } - long double tf_bD(signed char x, long double c) { S(D) R(D) } - """) - lib.rvalue = 0x7182838485868788 - for kind, cname in [('b', 'signed char'), - ('B', 'unsigned char'), - ('h', 'short'), - ('H', 'unsigned short'), - ('i', 'int'), - ('I', 'unsigned int'), - ('l', 'long'), - ('L', 'unsigned long'), - ('q', 'long long'), - ('Q', 'unsigned long long'), - ('f', 'float'), - ('d', 'double'), - ('D', 'long double')]: - sign = +1 if 'unsigned' in cname else -1 - lib.xvalue = 0 - lib.ivalue = 0 - lib.fvalue = 0 - lib.dvalue = 0 - lib.Dvalue = 0 - fun = getattr(lib, 'tf_b' + kind) - res = fun(-42, sign * 99) - if kind == 'D': - res = float(res) - assert res == int(ffi.cast(cname, 0x7182838485868788)) - assert lib.xvalue == -42 - if kind in 'fdD': - assert float(getattr(lib, kind + 'value')) == -99.0 - else: - assert lib.ivalue == sign * 99 - -def test_various_calls_direct(): - _test_various_calls(force_libffi=False) - -def test_various_calls_libffi(): - _test_various_calls(force_libffi=True) - -def test_ptr_to_opaque(): - ffi = FFI() - ffi.cdef("typedef ... foo_t; int f1(foo_t*); foo_t *f2(int);") - lib = ffi.verify(""" - #include <stdlib.h> - typedef struct { int x; } foo_t; - int f1(foo_t* p) { - int x = p->x; - free(p); - return x; - } - foo_t *f2(int x) { - foo_t *p = malloc(sizeof(foo_t)); - p->x = x; - return p; - } - """) - p = lib.f2(42) - x = lib.f1(p) - assert x == 42 - -def _run_in_multiple_threads(test1): - test1() - import sys - try: - import thread - except ImportError: - import _thread as thread - errors = [] - def wrapper(lock): - try: - test1() - except: - errors.append(sys.exc_info()) - lock.release() - locks = [] - for i in range(10): - _lock = thread.allocate_lock() - _lock.acquire() - thread.start_new_thread(wrapper, (_lock,)) - locks.append(_lock) - for _lock in locks: - _lock.acquire() - if errors: - raise errors[0][1] - -def test_errno_working_even_with_pypys_jit(): - ffi = FFI() - ffi.cdef("int f(int);") - lib = ffi.verify(""" - #include <errno.h> - int f(int x) { return (errno = errno + x); } - """) - @_run_in_multiple_threads - def test1(): - ffi.errno = 0 - for i in range(10000): - e = lib.f(1) - assert e == i + 1 - assert ffi.errno == e - for i in range(10000): - ffi.errno = i - e = lib.f(42) - assert e == i + 42 - -def test_getlasterror_working_even_with_pypys_jit(): - if sys.platform != 'win32': - py.test.skip("win32-only test") - ffi = FFI() - ffi.cdef("void SetLastError(DWORD);") - lib = ffi.dlopen("Kernel32.dll") - @_run_in_multiple_threads - def test1(): - for i in range(10000): - n = (1 << 29) + i - lib.SetLastError(n) - assert ffi.getwinerror()[0] == n - -def test_verify_dlopen_flags(): - if not hasattr(sys, 'setdlopenflags'): - py.test.skip("requires sys.setdlopenflags()") - # Careful with RTLD_GLOBAL. If by chance the FFI is not deleted - # promptly, like on PyPy, then other tests may see the same - # exported symbols as well. So we must not export a simple name - # like 'foo'! - old = sys.getdlopenflags() - try: - ffi1 = FFI() - ffi1.cdef("extern int foo_verify_dlopen_flags_1;") - sys.setdlopenflags(ffi1.RTLD_GLOBAL | ffi1.RTLD_NOW) - lib1 = ffi1.verify("int foo_verify_dlopen_flags_1;") - finally: - sys.setdlopenflags(old) - - ffi2 = FFI() - ffi2.cdef("int *getptr(void);") - lib2 = ffi2.verify(""" - extern int foo_verify_dlopen_flags_1; - static int *getptr(void) { return &foo_verify_dlopen_flags_1; } - """) - p = lib2.getptr() - assert ffi1.addressof(lib1, 'foo_verify_dlopen_flags_1') == p - -def test_consider_not_implemented_function_type(): - ffi = FFI() - ffi.cdef("typedef union { int a; float b; } Data;" - "typedef struct { int a:2; } MyStr;" - "typedef void (*foofunc_t)(Data);" - "typedef Data (*bazfunc_t)(void);" - "typedef MyStr (*barfunc_t)(void);") - fooptr = ffi.cast("foofunc_t", 123) - bazptr = ffi.cast("bazfunc_t", 123) - barptr = ffi.cast("barfunc_t", 123) - # assert did not crash so far - e = py.test.raises(NotImplementedError, fooptr, ffi.new("Data *")) - assert str(e.value) == ( - "ctype 'Data' not supported as argument by libffi. Unions are only " - "supported as argument if the function is 'API mode' and " - "non-variadic (i.e. declared inside ffibuilder.cdef()+" - "ffibuilder.set_source() and not taking a final '...' argument)") - e = py.test.raises(NotImplementedError, bazptr) - assert str(e.value) == ( - "ctype 'Data' not supported as return value by libffi. Unions are " - "only supported as return value if the function is 'API mode' and " - "non-variadic (i.e. declared inside ffibuilder.cdef()+" - "ffibuilder.set_source() and not taking a final '...' argument)") - e = py.test.raises(NotImplementedError, barptr) - assert str(e.value) == ( - "ctype 'MyStr' not supported as return value. It is a struct with " - "bit fields, which libffi does not support. Such structs are only " - "supported as return value if the function is 'API mode' and non-" - "variadic (i.e. declared inside ffibuilder.cdef()+ffibuilder." - "set_source() and not taking a final '...' argument)") - -def test_verify_extra_arguments(): - ffi = FFI() - ffi.cdef("#define ABA ...") - lib = ffi.verify("", define_macros=[('ABA', '42')]) - assert lib.ABA == 42 - -def test_implicit_unicode_on_windows(): - from cffi import FFIError - if sys.platform != 'win32': - py.test.skip("win32-only test") - ffi = FFI() - e = py.test.raises(FFIError, ffi.cdef, "int foo(LPTSTR);") - assert str(e.value) == ("The Windows type 'LPTSTR' is only available after" - " you call ffi.set_unicode()") - for with_unicode in [True, False]: - ffi = FFI() - ffi.set_unicode(with_unicode) - ffi.cdef(""" - DWORD GetModuleFileName(HMODULE hModule, LPTSTR lpFilename, - DWORD nSize); - """) - lib = ffi.verify(""" - #include <windows.h> - """, libraries=['Kernel32']) - outbuf = ffi.new("TCHAR[]", 200) - n = lib.GetModuleFileName(ffi.NULL, outbuf, 500) - assert 0 < n < 500 - for i in range(n): - #print repr(outbuf[i]) - assert ord(outbuf[i]) != 0 - assert ord(outbuf[n]) == 0 - assert ord(outbuf[0]) < 128 # should be a letter, or '\' - -def test_define_known_value(): - ffi = FFI() - ffi.cdef("#define FOO 0x123") - lib = ffi.verify("#define FOO 0x123") - assert lib.FOO == 0x123 - -def test_define_wrong_value(): - ffi = FFI() - ffi.cdef("#define FOO 123") - lib = ffi.verify("#define FOO 124") # used to complain - with pytest.raises(ffi.error) as e: - lib.FOO - assert str(e.value) == ("the C compiler says 'FOO' is equal to 124 (0x7c)," - " but the cdef disagrees") - -def test_some_integer_type_for_issue73(): - ffi = FFI() - ffi.cdef(""" - typedef int... AnIntegerWith32Bits; - typedef AnIntegerWith32Bits (*AFunctionReturningInteger) (void); - AnIntegerWith32Bits InvokeFunction(AFunctionReturningInteger); - """) - lib = ffi.verify(""" - #ifdef __LP64__ - typedef int AnIntegerWith32Bits; - #else - typedef long AnIntegerWith32Bits; - #endif - typedef AnIntegerWith32Bits (*AFunctionReturningInteger) (void); - AnIntegerWith32Bits InvokeFunction(AFunctionReturningInteger f) { - return f(); - } - """) - @ffi.callback("AFunctionReturningInteger") - def add(): - return 3 + 4 - x = lib.InvokeFunction(add) - assert x == 7 - -def test_unsupported_some_primitive_types(): - ffi = FFI() - py.test.raises((FFIError, # with pycparser <= 2.17 - CDefError), # with pycparser >= 2.18 - ffi.cdef, """typedef void... foo_t;""") - # - ffi.cdef("typedef int... foo_t;") - py.test.raises(VerificationError, ffi.verify, "typedef float foo_t;") - -def test_windows_dllimport_data(): - if sys.platform != 'win32': - py.test.skip("Windows only") - from testing.udir import udir - tmpfile = udir.join('dllimport_data.c') - tmpfile.write('int my_value = 42;\n') - ffi = FFI() - ffi.cdef("int my_value;") - lib = ffi.verify("extern __declspec(dllimport) int my_value;", - sources = [str(tmpfile)]) - assert lib.my_value == 42 - -def test_macro_var(): - ffi = FFI() - ffi.cdef("extern int myarray[50], my_value;") - lib = ffi.verify(""" - int myarray[50]; - int *get_my_value(void) { - static int index = 0; - return &myarray[index++]; - } - #define my_value (*get_my_value()) - """) - assert lib.my_value == 0 # [0] - lib.my_value = 42 # [1] - assert lib.myarray[1] == 42 - assert lib.my_value == 0 # [2] - lib.myarray[3] = 63 - assert lib.my_value == 63 # [3] - p = ffi.addressof(lib, 'my_value') # [4] - assert p[-1] == 63 - assert p[0] == 0 - assert p == lib.myarray + 4 - p[1] = 82 - assert lib.my_value == 82 # [5] - -def test_const_pointer_to_pointer(): - ffi = FFI() - ffi.cdef("struct s { char *const *a; };") - ffi.verify("struct s { char *const *a; };") - -def test_share_FILE(): - ffi1 = FFI() - ffi1.cdef("void do_stuff(FILE *);") - lib1 = ffi1.verify("void do_stuff(FILE *f) { (void)f; }") - ffi2 = FFI() - ffi2.cdef("FILE *barize(void);") - lib2 = ffi2.verify("FILE *barize(void) { return NULL; }") - lib1.do_stuff(lib2.barize()) - -def test_win_common_types(): - if sys.platform != 'win32': - py.test.skip("Windows only") - ffi = FFI() - ffi.set_unicode(True) - ffi.verify("") - assert ffi.typeof("PBYTE") is ffi.typeof("unsigned char *") - if sys.maxsize > 2**32: - expected = "unsigned long long" - else: - expected = "unsigned int" - assert ffi.typeof("UINT_PTR") is ffi.typeof(expected) - assert ffi.typeof("PTSTR") is ffi.typeof("wchar_t *") - -def _only_test_on_linux_intel(): - if not sys.platform.startswith('linux'): - py.test.skip('only running the memory-intensive test on Linux') - import platform - machine = platform.machine() - if 'x86' not in machine and 'x64' not in machine: - py.test.skip('only running the memory-intensive test on x86/x64') - -def test_ffi_gc_size_arg(): - _only_test_on_linux_intel() - ffi = FFI() - ffi.cdef("void *malloc(size_t); void free(void *);") - lib = ffi.verify(r""" - #include <stdlib.h> - """) - for i in range(2000): - p = lib.malloc(20*1024*1024) # 20 MB - p1 = ffi.cast("char *", p) - for j in range(0, 20*1024*1024, 4096): - p1[j] = b'!' - p = ffi.gc(p, lib.free, 20*1024*1024) - del p - # with PyPy's GC, the above would rapidly consume 40 GB of RAM - # without the third argument to ffi.gc() - -def test_ffi_gc_size_arg_2(): - # a variant of the above: this "attack" works on cpython's cyclic gc too - # and I found no obvious way to prevent that. So for now, this test - # is skipped on CPython, where it eats all the memory. - if '__pypy__' not in sys.builtin_module_names: - py.test.skip("find a way to tweak the cyclic GC of CPython") - _only_test_on_linux_intel() - ffi = FFI() - ffi.cdef("void *malloc(size_t); void free(void *);") - lib = ffi.verify(r""" - #include <stdlib.h> - """) - class X(object): - pass - for i in range(2000): - p = lib.malloc(50*1024*1024) # 50 MB - p1 = ffi.cast("char *", p) - for j in range(0, 50*1024*1024, 4096): - p1[j] = b'!' - p = ffi.gc(p, lib.free, 50*1024*1024) - x = X() - x.p = p - x.cyclic = x - del p, x - -def test_ffi_new_with_cycles(): - # still another variant, with ffi.new() - if '__pypy__' not in sys.builtin_module_names: - py.test.skip("find a way to tweak the cyclic GC of CPython") - ffi = FFI() - ffi.cdef("") - lib = ffi.verify("") - class X(object): - pass - for i in range(2000): - p = ffi.new("char[]", 50*1024*1024) # 50 MB - for j in range(0, 50*1024*1024, 4096): - p[j] = b'!' - x = X() - x.p = p - x.cyclic = x - del p, x diff --git a/testing/cffi1/test_zdist.py b/testing/cffi1/test_zdist.py deleted file mode 100644 index efc1d86..0000000 --- a/testing/cffi1/test_zdist.py +++ /dev/null @@ -1,426 +0,0 @@ -import sys, os, py -import subprocess -import cffi -from testing.udir import udir -from shutil import rmtree -from tempfile import mkdtemp - - -def chdir_to_tmp(f): - f.chdir_to_tmp = True - return f - -def from_outside(f): - f.chdir_to_tmp = False - return f - - -class TestDist(object): - - def setup_method(self, meth): - self.executable = os.path.abspath(sys.executable) - self.rootdir = os.path.abspath(os.path.dirname(os.path.dirname( - cffi.__file__))) - self.udir = udir.join(meth.__name__) - os.mkdir(str(self.udir)) - if meth.chdir_to_tmp: - self.saved_cwd = os.getcwd() - os.chdir(str(self.udir)) - - def teardown_method(self, meth): - if hasattr(self, 'saved_cwd'): - os.chdir(self.saved_cwd) - - def run(self, args, cwd=None): - env = os.environ.copy() - # a horrible hack to prevent distutils from finding ~/.pydistutils.cfg - # (there is the --no-user-cfg option, but not in Python 2.6...) - # NOTE: pointing $HOME to a nonexistent directory can break certain things - # that look there for configuration (like ccache). - tmp_home = mkdtemp() - assert tmp_home != None, "cannot create temporary homedir" - env['HOME'] = tmp_home - if cwd is None: - newpath = self.rootdir - if 'PYTHONPATH' in env: - newpath += os.pathsep + env['PYTHONPATH'] - env['PYTHONPATH'] = newpath - try: - subprocess.check_call([self.executable] + args, cwd=cwd, env=env) - finally: - rmtree(tmp_home) - - def _prepare_setuptools(self): - if hasattr(TestDist, '_setuptools_ready'): - return - try: - import setuptools - except ImportError: - py.test.skip("setuptools not found") - if os.path.exists(os.path.join(self.rootdir, 'setup.py')): - self.run(['setup.py', 'egg_info'], cwd=self.rootdir) - TestDist._setuptools_ready = True - - def check_produced_files(self, content, curdir=None): - if curdir is None: - curdir = str(self.udir) - found_so = None - for name in os.listdir(curdir): - if (name.endswith('.so') or name.endswith('.pyd') or - name.endswith('.dylib') or name.endswith('.dll')): - found_so = os.path.join(curdir, name) - # foo.so => foo - parts = name.split('.') - del parts[-1] - if len(parts) > 1 and parts[-1] != 'bar': - # foo.cpython-34m.so => foo, but foo.bar.so => foo.bar - del parts[-1] - name = '.'.join(parts) - # foo_d => foo (Python 2 debug builds) - if name.endswith('_d') and hasattr(sys, 'gettotalrefcount'): - name = name[:-2] - name += '.SO' - if name.startswith('pycparser') and name.endswith('.egg'): - continue # no clue why this shows up sometimes and not others - if name == '.eggs': - continue # seems new in 3.5, ignore it - assert name in content, "found unexpected file %r" % ( - os.path.join(curdir, name),) - value = content.pop(name) - if value is None: - assert name.endswith('.SO') or ( - os.path.isfile(os.path.join(curdir, name))) - else: - subdir = os.path.join(curdir, name) - assert os.path.isdir(subdir) - if value == '?': - continue - found_so = self.check_produced_files(value, subdir) or found_so - assert content == {}, "files or dirs not produced in %r: %r" % ( - curdir, content.keys()) - return found_so - - @chdir_to_tmp - def test_empty(self): - self.check_produced_files({}) - - @chdir_to_tmp - def test_abi_emit_python_code_1(self): - ffi = cffi.FFI() - ffi.set_source("package_name_1.mymod", None) - ffi.emit_python_code('xyz.py') - self.check_produced_files({'xyz.py': None}) - - @chdir_to_tmp - def test_abi_emit_python_code_2(self): - ffi = cffi.FFI() - ffi.set_source("package_name_1.mymod", None) - py.test.raises(IOError, ffi.emit_python_code, 'unexisting/xyz.py') - - @from_outside - def test_abi_emit_python_code_3(self): - ffi = cffi.FFI() - ffi.set_source("package_name_1.mymod", None) - ffi.emit_python_code(str(self.udir.join('xyt.py'))) - self.check_produced_files({'xyt.py': None}) - - @chdir_to_tmp - def test_abi_compile_1(self): - ffi = cffi.FFI() - ffi.set_source("mod_name_in_package.mymod", None) - x = ffi.compile() - self.check_produced_files({'mod_name_in_package': {'mymod.py': None}}) - assert x == os.path.join('.', 'mod_name_in_package', 'mymod.py') - - @chdir_to_tmp - def test_abi_compile_2(self): - ffi = cffi.FFI() - ffi.set_source("mod_name_in_package.mymod", None) - x = ffi.compile('build2') - self.check_produced_files({'build2': { - 'mod_name_in_package': {'mymod.py': None}}}) - assert x == os.path.join('build2', 'mod_name_in_package', 'mymod.py') - - @from_outside - def test_abi_compile_3(self): - ffi = cffi.FFI() - ffi.set_source("mod_name_in_package.mymod", None) - tmpdir = str(self.udir.join('build3')) - x = ffi.compile(tmpdir) - self.check_produced_files({'build3': { - 'mod_name_in_package': {'mymod.py': None}}}) - assert x == os.path.join(tmpdir, 'mod_name_in_package', 'mymod.py') - - @chdir_to_tmp - def test_api_emit_c_code_1(self): - ffi = cffi.FFI() - ffi.set_source("package_name_1.mymod", "/*code would be here*/") - ffi.emit_c_code('xyz.c') - self.check_produced_files({'xyz.c': None}) - - @chdir_to_tmp - def test_api_emit_c_code_2(self): - ffi = cffi.FFI() - ffi.set_source("package_name_1.mymod", "/*code would be here*/") - py.test.raises(IOError, ffi.emit_c_code, 'unexisting/xyz.c') - - @from_outside - def test_api_emit_c_code_3(self): - ffi = cffi.FFI() - ffi.set_source("package_name_1.mymod", "/*code would be here*/") - ffi.emit_c_code(str(self.udir.join('xyu.c'))) - self.check_produced_files({'xyu.c': None}) - - @chdir_to_tmp - def test_api_compile_1(self): - ffi = cffi.FFI() - ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") - x = ffi.compile() - if sys.platform != 'win32': - sofile = self.check_produced_files({ - 'mod_name_in_package': {'mymod.SO': None, - 'mymod.c': None, - 'mymod.o': None}}) - assert os.path.isabs(x) and os.path.samefile(x, sofile) - else: - self.check_produced_files({ - 'mod_name_in_package': {'mymod.SO': None, - 'mymod.c': None}, - 'Release': '?'}) - - @chdir_to_tmp - def test_api_compile_2(self): - ffi = cffi.FFI() - ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") - x = ffi.compile('output') - if sys.platform != 'win32': - sofile = self.check_produced_files({ - 'output': {'mod_name_in_package': {'mymod.SO': None, - 'mymod.c': None, - 'mymod.o': None}}}) - assert os.path.isabs(x) and os.path.samefile(x, sofile) - else: - self.check_produced_files({ - 'output': {'mod_name_in_package': {'mymod.SO': None, - 'mymod.c': None}, - 'Release': '?'}}) - - @from_outside - def test_api_compile_3(self): - ffi = cffi.FFI() - ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") - x = ffi.compile(str(self.udir.join('foo'))) - if sys.platform != 'win32': - sofile = self.check_produced_files({ - 'foo': {'mod_name_in_package': {'mymod.SO': None, - 'mymod.c': None, - 'mymod.o': None}}}) - assert os.path.isabs(x) and os.path.samefile(x, sofile) - else: - self.check_produced_files({ - 'foo': {'mod_name_in_package': {'mymod.SO': None, - 'mymod.c': None}, - 'Release': '?'}}) - - @chdir_to_tmp - def test_api_compile_explicit_target_1(self): - ffi = cffi.FFI() - ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") - x = ffi.compile(target="foo.bar.*") - if sys.platform != 'win32': - sofile = self.check_produced_files({ - 'mod_name_in_package': {'foo.bar.SO': None, - 'mymod.c': None, - 'mymod.o': None}}) - assert os.path.isabs(x) and os.path.samefile(x, sofile) - else: - self.check_produced_files({ - 'mod_name_in_package': {'foo.bar.SO': None, - 'mymod.c': None}, - 'Release': '?'}) - - @chdir_to_tmp - def test_api_compile_explicit_target_3(self): - ffi = cffi.FFI() - ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") - x = ffi.compile(target="foo.bar.baz") - if sys.platform != 'win32': - self.check_produced_files({ - 'mod_name_in_package': {'foo.bar.baz': None, - 'mymod.c': None, - 'mymod.o': None}}) - sofile = os.path.join(str(self.udir), - 'mod_name_in_package', 'foo.bar.baz') - assert os.path.isabs(x) and os.path.samefile(x, sofile) - else: - self.check_produced_files({ - 'mod_name_in_package': {'foo.bar.baz': None, - 'mymod.c': None}, - 'Release': '?'}) - - @chdir_to_tmp - def test_api_distutils_extension_1(self): - ffi = cffi.FFI() - ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") - ext = ffi.distutils_extension() - self.check_produced_files({'build': { - 'mod_name_in_package': {'mymod.c': None}}}) - if hasattr(os.path, 'samefile'): - assert os.path.samefile(ext.sources[0], - 'build/mod_name_in_package/mymod.c') - - @from_outside - def test_api_distutils_extension_2(self): - ffi = cffi.FFI() - ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") - ext = ffi.distutils_extension(str(self.udir.join('foo'))) - self.check_produced_files({'foo': { - 'mod_name_in_package': {'mymod.c': None}}}) - if hasattr(os.path, 'samefile'): - assert os.path.samefile(ext.sources[0], - str(self.udir.join('foo/mod_name_in_package/mymod.c'))) - - - def _make_distutils_api(self): - os.mkdir("src") - os.mkdir(os.path.join("src", "pack1")) - with open(os.path.join("src", "pack1", "__init__.py"), "w") as f: - pass - with open("setup.py", "w") as f: - f.write("""if 1: - # https://bugs.python.org/issue23246 - import sys - if sys.platform == 'win32': - try: - import setuptools - except ImportError: - pass - - import cffi - ffi = cffi.FFI() - ffi.set_source("pack1.mymod", "/*code would be here*/") - - from distutils.core import setup - setup(name='example1', - version='0.1', - packages=['pack1'], - package_dir={'': 'src'}, - ext_modules=[ffi.distutils_extension()]) - """) - - @chdir_to_tmp - def test_distutils_api_1(self): - self._make_distutils_api() - self.run(["setup.py", "build"]) - self.check_produced_files({'setup.py': None, - 'build': '?', - 'src': {'pack1': {'__init__.py': None}}}) - - @chdir_to_tmp - def test_distutils_api_2(self): - self._make_distutils_api() - self.run(["setup.py", "build_ext", "-i"]) - self.check_produced_files({'setup.py': None, - 'build': '?', - 'src': {'pack1': {'__init__.py': None, - 'mymod.SO': None}}}) - - def _make_setuptools_abi(self): - self._prepare_setuptools() - os.mkdir("src0") - os.mkdir(os.path.join("src0", "pack2")) - with open(os.path.join("src0", "pack2", "__init__.py"), "w") as f: - pass - with open(os.path.join("src0", "pack2", "_build.py"), "w") as f: - f.write("""if 1: - import cffi - ffi = cffi.FFI() - ffi.set_source("pack2.mymod", None) - """) - with open("setup.py", "w") as f: - f.write("""if 1: - from setuptools import setup - setup(name='example1', - version='0.1', - packages=['pack2'], - package_dir={'': 'src0'}, - cffi_modules=["src0/pack2/_build.py:ffi"]) - """) - - @chdir_to_tmp - def test_setuptools_abi_1(self): - self._make_setuptools_abi() - self.run(["setup.py", "build"]) - self.check_produced_files({'setup.py': None, - 'build': '?', - 'src0': {'pack2': {'__init__.py': None, - '_build.py': None}}}) - - @chdir_to_tmp - def test_setuptools_abi_2(self): - self._make_setuptools_abi() - self.run(["setup.py", "build_ext", "-i"]) - self.check_produced_files({'setup.py': None, - 'src0': {'pack2': {'__init__.py': None, - '_build.py': None, - 'mymod.py': None}}}) - - def _make_setuptools_api(self): - self._prepare_setuptools() - os.mkdir("src1") - os.mkdir(os.path.join("src1", "pack3")) - with open(os.path.join("src1", "pack3", "__init__.py"), "w") as f: - pass - with open(os.path.join("src1", "pack3", "_build.py"), "w") as f: - f.write("""if 1: - import cffi - ffi = cffi.FFI() - ffi.set_source("pack3.mymod", "/*code would be here*/") - ffi._hi_there = 42 - """) - with open("setup.py", "w") as f: - f.write("from __future__ import print_function\n" - """if 1: - from setuptools import setup - from distutils.command.build_ext import build_ext - import os - - class TestBuildExt(build_ext): - def pre_run(self, ext, ffi): - print('_make_setuptools_api: in pre_run:', end=" ") - assert ffi._hi_there == 42 - assert ext.name == "pack3.mymod" - fn = os.path.join(os.path.dirname(self.build_lib), - '..', 'see_me') - print('creating %r' % (fn,)) - open(fn, 'w').close() - - setup(name='example1', - version='0.1', - packages=['pack3'], - package_dir={'': 'src1'}, - cffi_modules=["src1/pack3/_build.py:ffi"], - cmdclass={'build_ext': TestBuildExt}, - ) - """) - - @chdir_to_tmp - def test_setuptools_api_1(self): - self._make_setuptools_api() - self.run(["setup.py", "build"]) - self.check_produced_files({'setup.py': None, - 'build': '?', - 'see_me': None, - 'src1': {'pack3': {'__init__.py': None, - '_build.py': None}}}) - - @chdir_to_tmp - def test_setuptools_api_2(self): - self._make_setuptools_api() - self.run(["setup.py", "build_ext", "-i"]) - self.check_produced_files({'setup.py': None, - 'build': '?', - 'see_me': None, - 'src1': {'pack3': {'__init__.py': None, - '_build.py': None, - 'mymod.SO': None}}}) diff --git a/testing/embedding/__init__.py b/testing/embedding/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/testing/embedding/__init__.py +++ /dev/null diff --git a/testing/embedding/add1-test.c b/testing/embedding/add1-test.c deleted file mode 100644 index b9ede18..0000000 --- a/testing/embedding/add1-test.c +++ /dev/null @@ -1,21 +0,0 @@ -#include <stdio.h> - -#ifdef _MSC_VER -#include <windows.h> -#endif - -extern int add1(int, int); - - -int main(void) -{ - int x, y; - x = add1(40, 2); - y = add1(100, -5); - printf("got: %d %d\n", x, y); -#ifdef _MSC_VER - if (x == 0 && y == 0) - Sleep(2000); -#endif - return 0; -} diff --git a/testing/embedding/add1.py b/testing/embedding/add1.py deleted file mode 100644 index 6f89ae9..0000000 --- a/testing/embedding/add1.py +++ /dev/null @@ -1,37 +0,0 @@ -import cffi - -ffi = cffi.FFI() - -ffi.embedding_api(""" - int add1(int, int); -""") - -ffi.embedding_init_code(r""" - import sys, time - sys.stdout.write("preparing") - for i in range(3): - sys.stdout.flush() - # Windows: sometimes time.sleep() doesn't sleep at all. - # This appears to occur on recent versions of python only. - t_end = time.time() + 0.19 - while time.time() < t_end: - time.sleep(0.2) - sys.stdout.write(".") - sys.stdout.write("\n") - - from _add1_cffi import ffi - - int(ord("A")) # check that built-ins are there - - @ffi.def_extern() - def add1(x, y): - sys.stdout.write("adding %d and %d\n" % (x, y)) - sys.stdout.flush() - return x + y -""") - -ffi.set_source("_add1_cffi", """ -""") - -fn = ffi.compile(verbose=True) -print('FILENAME: %s' % (fn,)) diff --git a/testing/embedding/add2-test.c b/testing/embedding/add2-test.c deleted file mode 100644 index 9620843..0000000 --- a/testing/embedding/add2-test.c +++ /dev/null @@ -1,14 +0,0 @@ -#include <stdio.h> - -extern int add1(int, int); -extern int add2(int, int, int); - - -int main(void) -{ - int x, y; - x = add1(40, 2); - y = add2(100, -5, -20); - printf("got: %d %d\n", x, y); - return 0; -} diff --git a/testing/embedding/add2.py b/testing/embedding/add2.py deleted file mode 100644 index 311a464..0000000 --- a/testing/embedding/add2.py +++ /dev/null @@ -1,29 +0,0 @@ -import cffi - -ffi = cffi.FFI() - -ffi.embedding_api(""" - int add2(int, int, int); -""") - -ffi.embedding_init_code(r""" - import sys - sys.stdout.write("prepADD2\n") - - assert '_add2_cffi' in sys.modules - m = sys.modules['_add2_cffi'] - import _add2_cffi - ffi = _add2_cffi.ffi - - @ffi.def_extern() - def add2(x, y, z): - sys.stdout.write("adding %d and %d and %d\n" % (x, y, z)) - sys.stdout.flush() - return x + y + z -""") - -ffi.set_source("_add2_cffi", """ -""") - -fn = ffi.compile(verbose=True) -print('FILENAME: %s' % (fn,)) diff --git a/testing/embedding/add3.py b/testing/embedding/add3.py deleted file mode 100644 index 1361912..0000000 --- a/testing/embedding/add3.py +++ /dev/null @@ -1,24 +0,0 @@ -import cffi - -ffi = cffi.FFI() - -ffi.embedding_api(""" - int add3(int, int, int, int); -""") - -ffi.embedding_init_code(r""" - from _add3_cffi import ffi - import sys - - @ffi.def_extern() - def add3(x, y, z, t): - sys.stdout.write("adding %d, %d, %d, %d\n" % (x, y, z, t)) - sys.stdout.flush() - return x + y + z + t -""") - -ffi.set_source("_add3_cffi", """ -""") - -fn = ffi.compile(verbose=True) -print('FILENAME: %s' % (fn,)) diff --git a/testing/embedding/add_recursive-test.c b/testing/embedding/add_recursive-test.c deleted file mode 100644 index cd29b79..0000000 --- a/testing/embedding/add_recursive-test.c +++ /dev/null @@ -1,27 +0,0 @@ -#include <stdio.h> - -#ifdef _MSC_VER -# define DLLIMPORT __declspec(dllimport) -#else -# define DLLIMPORT extern -#endif - -DLLIMPORT int add_rec(int, int); -DLLIMPORT int (*my_callback)(int); - -static int some_callback(int x) -{ - printf("some_callback(%d)\n", x); - fflush(stdout); - return add_rec(x, 9); -} - -int main(void) -{ - int x, y; - my_callback = some_callback; - x = add_rec(40, 2); - y = add_rec(100, -5); - printf("got: %d %d\n", x, y); - return 0; -} diff --git a/testing/embedding/add_recursive.py b/testing/embedding/add_recursive.py deleted file mode 100644 index a88aa8f..0000000 --- a/testing/embedding/add_recursive.py +++ /dev/null @@ -1,33 +0,0 @@ -import cffi - -ffi = cffi.FFI() - -ffi.embedding_api(""" - extern int (*my_callback)(int); - int add_rec(int, int); -""") - -ffi.embedding_init_code(r""" - from _add_recursive_cffi import ffi, lib - import sys - print("preparing REC") - sys.stdout.flush() - - @ffi.def_extern() - def add_rec(x, y): - print("adding %d and %d" % (x, y)) - sys.stdout.flush() - return x + y - - x = lib.my_callback(400) - print('<<< %d >>>' % (x,)) -""") - -ffi.set_source("_add_recursive_cffi", """ -/* use CFFI_DLLEXPORT: on windows, it expands to __declspec(dllexport), - which is needed to export a variable from a dll */ -CFFI_DLLEXPORT int (*my_callback)(int); -""") - -fn = ffi.compile(verbose=True) -print('FILENAME: %s' % (fn,)) diff --git a/testing/embedding/empty.py b/testing/embedding/empty.py deleted file mode 100644 index aa8d830..0000000 --- a/testing/embedding/empty.py +++ /dev/null @@ -1,10 +0,0 @@ -import cffi - -ffi = cffi.FFI() - -ffi.embedding_api("") - -ffi.set_source("_empty_cffi", "") - -fn = ffi.compile(verbose=True) -print('FILENAME: %s' % (fn,)) diff --git a/testing/embedding/initerror.py b/testing/embedding/initerror.py deleted file mode 100644 index 775cf56..0000000 --- a/testing/embedding/initerror.py +++ /dev/null @@ -1,18 +0,0 @@ -import cffi - -ffi = cffi.FFI() - -ffi.embedding_api(""" - int add1(int, int); -""") - -ffi.embedding_init_code(r""" - raise KeyError -""") - -ffi.set_source("_initerror_cffi", """ -""") - -fn = ffi.compile(verbose=True) -print('FILENAME: %s' % (fn,)) - diff --git a/testing/embedding/perf-test.c b/testing/embedding/perf-test.c deleted file mode 100644 index 2195bf5..0000000 --- a/testing/embedding/perf-test.c +++ /dev/null @@ -1,90 +0,0 @@ -#include <stdio.h> -#include <assert.h> -#include <sys/time.h> -#ifdef PTEST_USE_THREAD -# include <pthread.h> -static pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; -static pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER; -static int remaining; -#endif - - -extern int add1(int, int); - - -static double time_delta(struct timeval *stop, struct timeval *start) -{ - return (stop->tv_sec - start->tv_sec) + - 1e-6 * (stop->tv_usec - start->tv_usec); -} - -static double measure(void) -{ - long long i, iterations; - int result; - struct timeval start, stop; - double elapsed; - - add1(0, 0); /* prepare off-line */ - - i = 0; - iterations = 1000; - result = gettimeofday(&start, NULL); - assert(result == 0); - - while (1) { - for (; i < iterations; i++) { - add1(((int)i) & 0xaaaaaa, ((int)i) & 0x555555); - } - result = gettimeofday(&stop, NULL); - assert(result == 0); - - elapsed = time_delta(&stop, &start); - assert(elapsed >= 0.0); - if (elapsed > 2.5) - break; - iterations = iterations * 3 / 2; - } - - return elapsed / (double)iterations; -} - -static void *start_routine(void *arg) -{ - double t = measure(); - printf("time per call: %.3g\n", t); - -#ifdef PTEST_USE_THREAD - pthread_mutex_lock(&mutex1); - remaining -= 1; - if (!remaining) - pthread_cond_signal(&cond1); - pthread_mutex_unlock(&mutex1); -#endif - - return arg; -} - - -int main(void) -{ -#ifndef PTEST_USE_THREAD - start_routine(0); -#else - pthread_t th; - int i, status; - - add1(0, 0); /* this is the main thread */ - - remaining = PTEST_USE_THREAD; - for (i = 0; i < PTEST_USE_THREAD; i++) { - status = pthread_create(&th, NULL, start_routine, NULL); - assert(status == 0); - } - pthread_mutex_lock(&mutex1); - while (remaining) - pthread_cond_wait(&cond1, &mutex1); - pthread_mutex_unlock(&mutex1); -#endif - return 0; -} diff --git a/testing/embedding/perf.py b/testing/embedding/perf.py deleted file mode 100644 index a8d20f4..0000000 --- a/testing/embedding/perf.py +++ /dev/null @@ -1,21 +0,0 @@ -import cffi - -ffi = cffi.FFI() - -ffi.embedding_api(""" - int add1(int, int); -""") - -ffi.embedding_init_code(r""" - from _perf_cffi import ffi - - @ffi.def_extern() - def add1(x, y): - return x + y -""") - -ffi.set_source("_perf_cffi", """ -""") - -fn = ffi.compile(verbose=True) -print('FILENAME: %s' % (fn,)) diff --git a/testing/embedding/test_basic.py b/testing/embedding/test_basic.py deleted file mode 100644 index 8d2e776..0000000 --- a/testing/embedding/test_basic.py +++ /dev/null @@ -1,214 +0,0 @@ -import py -import sys, os, re -import shutil, subprocess, time -from testing.udir import udir -import cffi - - -local_dir = os.path.dirname(os.path.abspath(__file__)) -_link_error = '?' - -def check_lib_python_found(tmpdir): - global _link_error - if _link_error == '?': - ffi = cffi.FFI() - kwds = {} - ffi._apply_embedding_fix(kwds) - ffi.set_source("_test_lib_python_found", "", **kwds) - try: - ffi.compile(tmpdir=tmpdir, verbose=True) - except cffi.VerificationError as e: - _link_error = e - else: - _link_error = None - if _link_error: - py.test.skip(str(_link_error)) - - -def prefix_pythonpath(): - cffi_base = os.path.dirname(os.path.dirname(local_dir)) - pythonpath = org_env.get('PYTHONPATH', '').split(os.pathsep) - if cffi_base not in pythonpath: - pythonpath.insert(0, cffi_base) - return os.pathsep.join(pythonpath) - -def copy_away_env(): - global org_env - try: - org_env - except NameError: - org_env = os.environ.copy() - - -class EmbeddingTests: - _compiled_modules = {} - - def setup_method(self, meth): - check_lib_python_found(str(udir.ensure('embedding', dir=1))) - self._path = udir.join('embedding', meth.__name__) - if sys.platform == "win32" or sys.platform == "darwin": - self._compiled_modules.clear() # workaround - - def get_path(self): - return str(self._path.ensure(dir=1)) - - def _run_base(self, args, **kwds): - print('RUNNING:', args, kwds) - return subprocess.Popen(args, **kwds) - - def _run(self, args): - popen = self._run_base(args, cwd=self.get_path(), - stdout=subprocess.PIPE, - universal_newlines=True) - output = popen.stdout.read() - err = popen.wait() - if err: - raise OSError(("popen failed with exit code %r: %r\n\n%s" % ( - err, args, output)).rstrip()) - print(output.rstrip()) - return output - - def prepare_module(self, name): - self.patch_environment() - if name not in self._compiled_modules: - path = self.get_path() - filename = '%s.py' % name - # NOTE: if you have an .egg globally installed with an older - # version of cffi, this will not work, because sys.path ends - # up with the .egg before the PYTHONPATH entries. I didn't - # find a solution to that: we could hack sys.path inside the - # script run here, but we can't hack it in the same way in - # execute(). - pathname = os.path.join(path, filename) - with open(pathname, 'w') as g: - g.write(''' -# https://bugs.python.org/issue23246 -import sys -if sys.platform == 'win32': - try: - import setuptools - except ImportError: - pass -''') - with open(os.path.join(local_dir, filename), 'r') as f: - g.write(f.read()) - - output = self._run([sys.executable, pathname]) - match = re.compile(r"\bFILENAME: (.+)").search(output) - assert match - dynamic_lib_name = match.group(1) - if sys.platform == 'win32': - assert dynamic_lib_name.endswith('_cffi.dll') - elif sys.platform == 'darwin': - assert dynamic_lib_name.endswith('_cffi.dylib') - else: - assert dynamic_lib_name.endswith('_cffi.so') - self._compiled_modules[name] = dynamic_lib_name - return self._compiled_modules[name] - - def compile(self, name, modules, opt=False, threads=False, defines={}): - path = self.get_path() - filename = '%s.c' % name - shutil.copy(os.path.join(local_dir, filename), path) - shutil.copy(os.path.join(local_dir, 'thread-test.h'), path) - import distutils.ccompiler - curdir = os.getcwd() - try: - os.chdir(self.get_path()) - c = distutils.ccompiler.new_compiler() - print('compiling %s with %r' % (name, modules)) - extra_preargs = [] - debug = True - if sys.platform == 'win32': - libfiles = [] - for m in modules: - m = os.path.basename(m) - assert m.endswith('.dll') - libfiles.append('Release\\%s.lib' % m[:-4]) - modules = libfiles - extra_preargs.append('/MANIFEST') - debug = False # you need to install extra stuff - # for this to work - elif threads: - extra_preargs.append('-pthread') - objects = c.compile([filename], macros=sorted(defines.items()), - debug=debug) - c.link_executable(objects + modules, name, extra_preargs=extra_preargs) - finally: - os.chdir(curdir) - - def patch_environment(self): - copy_away_env() - path = self.get_path() - # for libpypy-c.dll or Python27.dll - path = os.path.split(sys.executable)[0] + os.path.pathsep + path - env_extra = {'PYTHONPATH': prefix_pythonpath()} - if sys.platform == 'win32': - envname = 'PATH' - else: - envname = 'LD_LIBRARY_PATH' - libpath = org_env.get(envname) - if libpath: - libpath = path + os.path.pathsep + libpath - else: - libpath = path - env_extra[envname] = libpath - for key, value in sorted(env_extra.items()): - if os.environ.get(key) != value: - print('* setting env var %r to %r' % (key, value)) - os.environ[key] = value - - def execute(self, name): - path = self.get_path() - print('running %r in %r' % (name, path)) - executable_name = name - if sys.platform == 'win32': - executable_name = os.path.join(path, executable_name + '.exe') - else: - executable_name = os.path.join('.', executable_name) - popen = self._run_base([executable_name], cwd=path, - stdout=subprocess.PIPE, - universal_newlines=True) - result = popen.stdout.read() - err = popen.wait() - if err: - raise OSError("%r failed with exit code %r" % ( - os.path.join(path, executable_name), err)) - return result - - -class TestBasic(EmbeddingTests): - def test_empty(self): - empty_cffi = self.prepare_module('empty') - - def test_basic(self): - add1_cffi = self.prepare_module('add1') - self.compile('add1-test', [add1_cffi]) - output = self.execute('add1-test') - assert output == ("preparing...\n" - "adding 40 and 2\n" - "adding 100 and -5\n" - "got: 42 95\n") - - def test_two_modules(self): - add1_cffi = self.prepare_module('add1') - add2_cffi = self.prepare_module('add2') - self.compile('add2-test', [add1_cffi, add2_cffi]) - output = self.execute('add2-test') - assert output == ("preparing...\n" - "adding 40 and 2\n" - "prepADD2\n" - "adding 100 and -5 and -20\n" - "got: 42 75\n") - - def test_init_time_error(self): - initerror_cffi = self.prepare_module('initerror') - self.compile('add1-test', [initerror_cffi]) - output = self.execute('add1-test') - assert output == "got: 0 0\n" # plus lots of info to stderr - - def test_embedding_with_unicode(self): - withunicode_cffi = self.prepare_module('withunicode') - self.compile('add1-test', [withunicode_cffi]) - output = self.execute('add1-test') - assert output == "255\n4660\n65244\ngot: 0 0\n" diff --git a/testing/embedding/test_performance.py b/testing/embedding/test_performance.py deleted file mode 100644 index a0e8458..0000000 --- a/testing/embedding/test_performance.py +++ /dev/null @@ -1,52 +0,0 @@ -import sys -from testing.embedding.test_basic import EmbeddingTests - -if sys.platform == 'win32': - import pytest - pytestmark = pytest.mark.skip("written with POSIX functions") - - -class TestPerformance(EmbeddingTests): - def test_perf_single_threaded(self): - perf_cffi = self.prepare_module('perf') - self.compile('perf-test', [perf_cffi], opt=True) - output = self.execute('perf-test') - print('='*79) - print(output.rstrip()) - print('='*79) - - def test_perf_in_1_thread(self): - perf_cffi = self.prepare_module('perf') - self.compile('perf-test', [perf_cffi], opt=True, threads=True, - defines={'PTEST_USE_THREAD': '1'}) - output = self.execute('perf-test') - print('='*79) - print(output.rstrip()) - print('='*79) - - def test_perf_in_2_threads(self): - perf_cffi = self.prepare_module('perf') - self.compile('perf-test', [perf_cffi], opt=True, threads=True, - defines={'PTEST_USE_THREAD': '2'}) - output = self.execute('perf-test') - print('='*79) - print(output.rstrip()) - print('='*79) - - def test_perf_in_4_threads(self): - perf_cffi = self.prepare_module('perf') - self.compile('perf-test', [perf_cffi], opt=True, threads=True, - defines={'PTEST_USE_THREAD': '4'}) - output = self.execute('perf-test') - print('='*79) - print(output.rstrip()) - print('='*79) - - def test_perf_in_8_threads(self): - perf_cffi = self.prepare_module('perf') - self.compile('perf-test', [perf_cffi], opt=True, threads=True, - defines={'PTEST_USE_THREAD': '8'}) - output = self.execute('perf-test') - print('='*79) - print(output.rstrip()) - print('='*79) diff --git a/testing/embedding/test_recursive.py b/testing/embedding/test_recursive.py deleted file mode 100644 index b85e7ed..0000000 --- a/testing/embedding/test_recursive.py +++ /dev/null @@ -1,15 +0,0 @@ -from testing.embedding.test_basic import EmbeddingTests - - -class TestRecursive(EmbeddingTests): - def test_recursive(self): - add_recursive_cffi = self.prepare_module('add_recursive') - self.compile('add_recursive-test', [add_recursive_cffi]) - output = self.execute('add_recursive-test') - assert output == ("preparing REC\n" - "some_callback(400)\n" - "adding 400 and 9\n" - "<<< 409 >>>\n" - "adding 40 and 2\n" - "adding 100 and -5\n" - "got: 42 95\n") diff --git a/testing/embedding/test_thread.py b/testing/embedding/test_thread.py deleted file mode 100644 index 9a5936d..0000000 --- a/testing/embedding/test_thread.py +++ /dev/null @@ -1,65 +0,0 @@ -from testing.embedding.test_basic import EmbeddingTests - - -class TestThread(EmbeddingTests): - def test_first_calls_in_parallel(self): - add1_cffi = self.prepare_module('add1') - self.compile('thread1-test', [add1_cffi], threads=True) - for i in range(20): - output = self.execute('thread1-test') - assert output == ("starting\n" - "preparing...\n" + - "adding 40 and 2\n" * 10 + - "done\n") - - def _take_out(self, text, content): - assert content in text - i = text.index(content) - return text[:i] + text[i+len(content):] - - def test_init_different_modules_in_different_threads(self): - add1_cffi = self.prepare_module('add1') - add2_cffi = self.prepare_module('add2') - self.compile('thread2-test', [add1_cffi, add2_cffi], threads=True) - for i in range(3): - output = self.execute('thread2-test') - print('='*79) - print(output) - print('='*79) - output = self._take_out(output, "preparing") - output = self._take_out(output, ".") - output = self._take_out(output, ".") - # at least the 3rd dot should be after everything from ADD2 - assert output == ("starting\n" - "prepADD2\n" - "adding 1000 and 200 and 30\n" - ".\n" - "adding 40 and 2\n" - "done\n") - - def test_alt_issue(self): - add1_cffi = self.prepare_module('add1') - add2_cffi = self.prepare_module('add2') - self.compile('thread2-test', [add1_cffi, add2_cffi], - threads=True, defines={'T2TEST_AGAIN_ADD1': '1'}) - output = self.execute('thread2-test') - output = self._take_out(output, "adding 40 and 2\n") - assert output == ("starting\n" - "preparing...\n" - "adding -1 and -1\n" - "prepADD2\n" - "adding 1000 and 200 and 30\n" - "done\n") - - def test_load_in_parallel_more(self): - add2_cffi = self.prepare_module('add2') - add3_cffi = self.prepare_module('add3') - self.compile('thread3-test', [add2_cffi, add3_cffi], threads=True) - for i in range(150): - output = self.execute('thread3-test') - for j in range(10): - output = self._take_out(output, "adding 40 and 2 and 100\n") - output = self._take_out(output, "adding 1000, 200, 30, 4\n") - assert output == ("starting\n" - "prepADD2\n" - "done\n") diff --git a/testing/embedding/test_tlocal.py b/testing/embedding/test_tlocal.py deleted file mode 100644 index 6e7c5af..0000000 --- a/testing/embedding/test_tlocal.py +++ /dev/null @@ -1,10 +0,0 @@ -from testing.embedding.test_basic import EmbeddingTests - - -class TestThreadLocal(EmbeddingTests): - def test_thread_local(self): - tlocal_cffi = self.prepare_module('tlocal') - self.compile('tlocal-test', [tlocal_cffi], threads=True) - for i in range(10): - output = self.execute('tlocal-test') - assert output == "done\n" diff --git a/testing/embedding/thread-test.h b/testing/embedding/thread-test.h deleted file mode 100644 index f66cf70..0000000 --- a/testing/embedding/thread-test.h +++ /dev/null @@ -1,96 +0,0 @@ -/************************************************************/ -#ifndef _MSC_VER -/************************************************************/ - - -#include <pthread.h> - -/* don't include <semaphore.h>, it is not available on OS/X */ - -typedef struct { - pthread_mutex_t mutex1; - pthread_cond_t cond1; - unsigned int value; -} sem_t; - -static int sem_init(sem_t *sem, int pshared, unsigned int value) -{ - assert(pshared == 0); - sem->value = value; - return (pthread_mutex_init(&sem->mutex1, NULL) || - pthread_cond_init(&sem->cond1, NULL)); -} - -static int sem_post(sem_t *sem) -{ - pthread_mutex_lock(&sem->mutex1); - sem->value += 1; - pthread_cond_signal(&sem->cond1); - pthread_mutex_unlock(&sem->mutex1); - return 0; -} - -static int sem_wait(sem_t *sem) -{ - pthread_mutex_lock(&sem->mutex1); - while (sem->value == 0) - pthread_cond_wait(&sem->cond1, &sem->mutex1); - sem->value -= 1; - pthread_mutex_unlock(&sem->mutex1); - return 0; -} - - -/************************************************************/ -#else -/************************************************************/ - - -/* Very quick and dirty, just what I need for these tests. - Don't use directly in any real code! -*/ - -#include <Windows.h> -#include <assert.h> - -typedef HANDLE sem_t; -typedef HANDLE pthread_t; - -static int sem_init(sem_t *sem, int pshared, unsigned int value) -{ - assert(pshared == 0); - assert(value == 0); - *sem = CreateSemaphore(NULL, 0, 999, NULL); - return *sem ? 0 : -1; -} - -static int sem_post(sem_t *sem) -{ - return ReleaseSemaphore(*sem, 1, NULL) ? 0 : -1; -} - -static int sem_wait(sem_t *sem) -{ - WaitForSingleObject(*sem, INFINITE); - return 0; -} - -static DWORD WINAPI myThreadProc(LPVOID lpParameter) -{ - void *(* start_routine)(void *) = (void *(*)(void *))lpParameter; - start_routine(NULL); - return 0; -} - -static int pthread_create(pthread_t *thread, void *attr, - void *start_routine(void *), void *arg) -{ - assert(arg == NULL); - *thread = CreateThread(NULL, 0, myThreadProc, start_routine, 0, NULL); - return *thread ? 0 : -1; -} - - -/************************************************************/ -#endif -/************************************************************/ diff --git a/testing/embedding/thread1-test.c b/testing/embedding/thread1-test.c deleted file mode 100644 index 70bb861..0000000 --- a/testing/embedding/thread1-test.c +++ /dev/null @@ -1,43 +0,0 @@ -#include <stdio.h> -#include <assert.h> -#include "thread-test.h" - -#define NTHREADS 10 - - -extern int add1(int, int); - -static sem_t done; - - -static void *start_routine(void *arg) -{ - int x, status; - x = add1(40, 2); - assert(x == 42); - - status = sem_post(&done); - assert(status == 0); - - return arg; -} - -int main(void) -{ - pthread_t th; - int i, status = sem_init(&done, 0, 0); - assert(status == 0); - - printf("starting\n"); - fflush(stdout); - for (i = 0; i < NTHREADS; i++) { - status = pthread_create(&th, NULL, start_routine, NULL); - assert(status == 0); - } - for (i = 0; i < NTHREADS; i++) { - status = sem_wait(&done); - assert(status == 0); - } - printf("done\n"); - return 0; -} diff --git a/testing/embedding/thread2-test.c b/testing/embedding/thread2-test.c deleted file mode 100644 index 62f5ec8..0000000 --- a/testing/embedding/thread2-test.c +++ /dev/null @@ -1,57 +0,0 @@ -#include <stdio.h> -#include <assert.h> -#include "thread-test.h" - -extern int add1(int, int); -extern int add2(int, int, int); - -static sem_t done; - - -static void *start_routine_1(void *arg) -{ - int x, status; - x = add1(40, 2); - assert(x == 42); - - status = sem_post(&done); - assert(status == 0); - - return arg; -} - -static void *start_routine_2(void *arg) -{ - int x, status; -#ifdef T2TEST_AGAIN_ADD1 - add1(-1, -1); -#endif - x = add2(1000, 200, 30); - assert(x == 1230); - - status = sem_post(&done); - assert(status == 0); - - return arg; -} - -int main(void) -{ - pthread_t th; - int i, status = sem_init(&done, 0, 0); - assert(status == 0); - - printf("starting\n"); - fflush(stdout); - status = pthread_create(&th, NULL, start_routine_1, NULL); - assert(status == 0); - status = pthread_create(&th, NULL, start_routine_2, NULL); - assert(status == 0); - - for (i = 0; i < 2; i++) { - status = sem_wait(&done); - assert(status == 0); - } - printf("done\n"); - return 0; -} diff --git a/testing/embedding/thread3-test.c b/testing/embedding/thread3-test.c deleted file mode 100644 index 69ada27..0000000 --- a/testing/embedding/thread3-test.c +++ /dev/null @@ -1,56 +0,0 @@ -#include <stdio.h> -#include <assert.h> -#include "thread-test.h" - -extern int add2(int, int, int); -extern int add3(int, int, int, int); - -static sem_t done; - - -static void *start_routine_2(void *arg) -{ - int x, status; - x = add2(40, 2, 100); - assert(x == 142); - - status = sem_post(&done); - assert(status == 0); - - return arg; -} - -static void *start_routine_3(void *arg) -{ - int x, status; - x = add3(1000, 200, 30, 4); - assert(x == 1234); - - status = sem_post(&done); - assert(status == 0); - - return arg; -} - -int main(void) -{ - pthread_t th; - int i, status = sem_init(&done, 0, 0); - assert(status == 0); - - printf("starting\n"); - fflush(stdout); - for (i = 0; i < 10; i++) { - status = pthread_create(&th, NULL, start_routine_2, NULL); - assert(status == 0); - status = pthread_create(&th, NULL, start_routine_3, NULL); - assert(status == 0); - } - for (i = 0; i < 20; i++) { - status = sem_wait(&done); - assert(status == 0); - } - printf("done\n"); - fflush(stdout); /* this is occasionally needed on Windows */ - return 0; -} diff --git a/testing/embedding/tlocal-test.c b/testing/embedding/tlocal-test.c deleted file mode 100644 index b78a03d..0000000 --- a/testing/embedding/tlocal-test.c +++ /dev/null @@ -1,47 +0,0 @@ -#include <stdio.h> -#include <assert.h> -#include "thread-test.h" - -#define NTHREADS 10 - - -extern int add1(int, int); - -static sem_t done; - - -static void *start_routine(void *arg) -{ - int i, x, expected, status; - - expected = add1(40, 2); - assert((expected % 1000) == 42); - - for (i=0; i<10; i++) { - x = add1(50, i); - assert(x == expected + 8 + i); - } - - status = sem_post(&done); - assert(status == 0); - - return arg; -} - -int main(void) -{ - pthread_t th; - int i, status = sem_init(&done, 0, 0); - assert(status == 0); - - for (i = 0; i < NTHREADS; i++) { - status = pthread_create(&th, NULL, start_routine, NULL); - assert(status == 0); - } - for (i = 0; i < NTHREADS; i++) { - status = sem_wait(&done); - assert(status == 0); - } - printf("done\n"); - return 0; -} diff --git a/testing/embedding/tlocal.py b/testing/embedding/tlocal.py deleted file mode 100644 index 7800dff..0000000 --- a/testing/embedding/tlocal.py +++ /dev/null @@ -1,33 +0,0 @@ -import cffi - -ffi = cffi.FFI() - -ffi.embedding_api(""" - int add1(int, int); -""") - -ffi.embedding_init_code(r""" - from _tlocal_cffi import ffi - import itertools - try: - import thread - g_seen = itertools.count().next - except ImportError: - import _thread as thread # py3 - g_seen = itertools.count().__next__ - tloc = thread._local() - - @ffi.def_extern() - def add1(x, y): - try: - num = tloc.num - except AttributeError: - num = tloc.num = g_seen() * 1000 - return x + y + num -""") - -ffi.set_source("_tlocal_cffi", """ -""") - -fn = ffi.compile(verbose=True) -print('FILENAME: %s' % (fn,)) diff --git a/testing/embedding/withunicode.py b/testing/embedding/withunicode.py deleted file mode 100644 index 839c6cd..0000000 --- a/testing/embedding/withunicode.py +++ /dev/null @@ -1,26 +0,0 @@ -import sys, cffi -if sys.version_info < (3,): - u_prefix = "u" -else: - u_prefix = "" - unichr = chr - - -ffi = cffi.FFI() - -ffi.embedding_api(u""" - int add1(int, int); -""") - -ffi.embedding_init_code((""" - import sys, time - for c in %s'""" + unichr(0x00ff) + unichr(0x1234) + unichr(0xfedc) + """': - sys.stdout.write(str(ord(c)) + '\\n') - sys.stdout.flush() -""") % u_prefix) - -ffi.set_source("_withunicode_cffi", """ -""") - -fn = ffi.compile(verbose=True) -print('FILENAME: %s' % (fn,)) diff --git a/testing/support.py b/testing/support.py deleted file mode 100644 index 6339a94..0000000 --- a/testing/support.py +++ /dev/null @@ -1,119 +0,0 @@ -import sys, os - -if sys.version_info < (3,): - __all__ = ['u', 'arraytostring'] - - class U(object): - def __add__(self, other): - return eval('u'+repr(other).replace(r'\\u', r'\u') - .replace(r'\\U', r'\U')) - u = U() - long = long # for further "from testing.support import long" - assert u+'a\x00b' == eval(r"u'a\x00b'") - assert u+'a\u1234b' == eval(r"u'a\u1234b'") - assert u+'a\U00012345b' == eval(r"u'a\U00012345b'") - def arraytostring(a): - return a.tostring() - -else: - __all__ = ['u', 'unicode', 'long', 'arraytostring'] - u = "" - unicode = str - long = int - def arraytostring(a): - return a.tobytes() - - -class StdErrCapture(object): - """Capture writes to sys.stderr (not to the underlying file descriptor).""" - def __enter__(self): - try: - from StringIO import StringIO - except ImportError: - from io import StringIO - self.old_stderr = sys.stderr - sys.stderr = f = StringIO() - if hasattr(sys, '__unraisablehook__'): # work around pytest - self.old_unraisablebook = sys.unraisablehook # on recent CPythons - sys.unraisablehook = sys.__unraisablehook__ - return f - def __exit__(self, *args): - sys.stderr = self.old_stderr - if hasattr(self, 'old_unraisablebook'): - sys.unraisablehook = self.old_unraisablebook - - -class FdWriteCapture(object): - """xxx limited to capture at most 512 bytes of output, according - to the Posix manual.""" - - def __init__(self, capture_fd=2): # stderr by default - if sys.platform == 'win32': - import py - py.test.skip("seems not to work, too bad") - self.capture_fd = capture_fd - - def __enter__(self): - import os - self.read_fd, self.write_fd = os.pipe() - self.copy_fd = os.dup(self.capture_fd) - os.dup2(self.write_fd, self.capture_fd) - return self - - def __exit__(self, *args): - import os - os.dup2(self.copy_fd, self.capture_fd) - os.close(self.copy_fd) - os.close(self.write_fd) - self._value = os.read(self.read_fd, 512) - os.close(self.read_fd) - - def getvalue(self): - return self._value - -def _verify(ffi, module_name, preamble, *args, **kwds): - import imp - from cffi.recompiler import recompile - from .udir import udir - assert module_name not in sys.modules, "module name conflict: %r" % ( - module_name,) - kwds.setdefault('tmpdir', str(udir)) - outputfilename = recompile(ffi, module_name, preamble, *args, **kwds) - module = imp.load_dynamic(module_name, outputfilename) - # - # hack hack hack: copy all *bound methods* from module.ffi back to the - # ffi instance. Then calls like ffi.new() will invoke module.ffi.new(). - for name in dir(module.ffi): - if not name.startswith('_'): - attr = getattr(module.ffi, name) - if attr is not getattr(ffi, name, object()): - setattr(ffi, name, attr) - def typeof_disabled(*args, **kwds): - raise NotImplementedError - ffi._typeof = typeof_disabled - for name in dir(ffi): - if not name.startswith('_') and not hasattr(module.ffi, name): - setattr(ffi, name, NotImplemented) - return module.lib - - -# For testing, we call gcc with "-Werror". This is fragile because newer -# versions of gcc are always better at producing warnings, particularly for -# auto-generated code. We need here to adapt and silence them as needed. - -if sys.platform == 'win32': - extra_compile_args = [] # no obvious -Werror equivalent on MSVC -else: - if (sys.platform == 'darwin' and - [int(x) for x in os.uname()[2].split('.')] >= [11, 0, 0]): - # assume a standard clang or gcc - extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion', - '-Wno-unused-parameter', - '-Wno-unreachable-code'] - # special things for clang - extra_compile_args.append('-Qunused-arguments') - else: - # assume a standard gcc - extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion', - '-Wno-unused-parameter', - '-Wno-unreachable-code'] diff --git a/testing/udir.py b/testing/udir.py deleted file mode 100644 index 59db1c4..0000000 --- a/testing/udir.py +++ /dev/null @@ -1,140 +0,0 @@ -import py -import sys, os, atexit - - -# This is copied from PyPy's vendored py lib. The latest py lib release -# (1.8.1) contains a bug and crashes if it sees another temporary directory -# in which we don't have write permission (e.g. because it's owned by someone -# else). -def make_numbered_dir(prefix='session-', rootdir=None, keep=3, - lock_timeout = 172800, # two days - min_timeout = 300): # five minutes - """ return unique directory with a number greater than the current - maximum one. The number is assumed to start directly after prefix. - if keep is true directories with a number less than (maxnum-keep) - will be removed. - """ - if rootdir is None: - rootdir = py.path.local.get_temproot() - - def parse_num(path): - """ parse the number out of a path (if it matches the prefix) """ - bn = path.basename - if bn.startswith(prefix): - try: - return int(bn[len(prefix):]) - except ValueError: - pass - - # compute the maximum number currently in use with the - # prefix - lastmax = None - while True: - maxnum = -1 - for path in rootdir.listdir(): - num = parse_num(path) - if num is not None: - maxnum = max(maxnum, num) - - # make the new directory - try: - udir = rootdir.mkdir(prefix + str(maxnum+1)) - except py.error.EEXIST: - # race condition: another thread/process created the dir - # in the meantime. Try counting again - if lastmax == maxnum: - raise - lastmax = maxnum - continue - break - - # put a .lock file in the new directory that will be removed at - # process exit - if lock_timeout: - lockfile = udir.join('.lock') - mypid = os.getpid() - if hasattr(lockfile, 'mksymlinkto'): - lockfile.mksymlinkto(str(mypid)) - else: - lockfile.write(str(mypid)) - def try_remove_lockfile(): - # in a fork() situation, only the last process should - # remove the .lock, otherwise the other processes run the - # risk of seeing their temporary dir disappear. For now - # we remove the .lock in the parent only (i.e. we assume - # that the children finish before the parent). - if os.getpid() != mypid: - return - try: - lockfile.remove() - except py.error.Error: - pass - atexit.register(try_remove_lockfile) - - # prune old directories - if keep: - for path in rootdir.listdir(): - num = parse_num(path) - if num is not None and num <= (maxnum - keep): - if min_timeout: - # NB: doing this is needed to prevent (or reduce - # a lot the chance of) the following situation: - # 'keep+1' processes call make_numbered_dir() at - # the same time, they create dirs, but then the - # last process notices the first dir doesn't have - # (yet) a .lock in it and kills it. - try: - t1 = path.lstat().mtime - t2 = lockfile.lstat().mtime - if abs(t2-t1) < min_timeout: - continue # skip directories too recent - except py.error.Error: - continue # failure to get a time, better skip - lf = path.join('.lock') - try: - t1 = lf.lstat().mtime - t2 = lockfile.lstat().mtime - if not lock_timeout or abs(t2-t1) < lock_timeout: - continue # skip directories still locked - except py.error.Error: - pass # assume that it means that there is no 'lf' - try: - path.remove(rec=1) - except KeyboardInterrupt: - raise - except: # this might be py.error.Error, WindowsError ... - pass - - # make link... - try: - username = os.environ['USER'] #linux, et al - except KeyError: - try: - username = os.environ['USERNAME'] #windows - except KeyError: - username = 'current' - - src = str(udir) - dest = src[:src.rfind('-')] + '-' + username - try: - os.unlink(dest) - except OSError: - pass - try: - os.symlink(src, dest) - except (OSError, AttributeError, NotImplementedError): - pass - - return udir - - -udir = make_numbered_dir(prefix = 'ffi-') - - -# Windows-only workaround for some configurations: see -# https://bugs.python.org/issue23246 (Python 2.7.9) -if sys.platform == 'win32': - try: - import setuptools - except ImportError: - pass |