From 0231243d64093d5f13ee5d14ab2ec02143a4a16e Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Fri, 7 Feb 2020 17:02:55 +0100 Subject: Move some whatsnew entries into the "Older version" heading --- doc/source/whatsnew.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst index 660608a..674ad22 100644 --- a/doc/source/whatsnew.rst +++ b/doc/source/whatsnew.rst @@ -82,8 +82,11 @@ v1.13 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). @@ -94,13 +97,13 @@ v1.12.3 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. @@ -116,7 +119,7 @@ v1.12.1 v1.12 -===== +----- * `Direct support for pkg-config`__. @@ -154,9 +157,6 @@ v1.12 .. _`issue #362`: https://bitbucket.org/cffi/cffi/issues/362/ -Older Versions -============== - v1.11.5 ------- -- cgit v1.2.1 From 8cbc8d5c2aebd965a27752af1fae727da9e125df Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Thu, 13 Feb 2020 09:45:24 +0100 Subject: typo in overview code (thanks __imp__) --- doc/source/overview.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/overview.rst b/doc/source/overview.rst index 32970fa..60e3a01 100644 --- a/doc/source/overview.rst +++ b/doc/source/overview.rst @@ -358,7 +358,7 @@ Linux for example). It can be called from Python: from _pi.lib import pi_approx approx = pi_approx(10) - assert str(pi_approximation).startswith("3.") + assert str(approx).startswith("3.") approx = pi_approx(10000) assert str(approx).startswith("3.1") -- cgit v1.2.1 From f99387a411e26dffeeb9edc0a08e0af3c47465ff Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Thu, 13 Feb 2020 10:02:21 +0100 Subject: Last change on bitbucket --- README.md | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 3e7862d..9711660 100644 --- a/README.md +++ b/README.md @@ -5,26 +5,7 @@ Foreign Function Interface for Python calling C code. Please see the [Documentation](http://cffi.readthedocs.org/) or uncompiled in the doc/ subdirectory. -Download --------- +This project moved +------------------ -[Download page](https://bitbucket.org/cffi/cffi/downloads) - -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. +The new home is https://foss.heptapod.net/pypy/cffi -- cgit v1.2.1 From 7ea8164f22c36b64fe1d88c8b74e961aecfb399d Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Thu, 13 Feb 2020 10:08:24 +0100 Subject: Fix the very first URL in the docs. Many many more might have to be fixed. --- doc/source/installation.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/source/installation.rst b/doc/source/installation.rst index 557b580..82fcb0e 100644 --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -60,8 +60,8 @@ Download and Installation: - SHA256: 2d384f4a127a15ba701207f7639d94106693b6cd64173d6c8988e2c25f3ac2b6 -* Or grab the most current version from the `Bitbucket page`_: - ``hg clone https://bitbucket.org/cffi/cffi`` +* 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 @@ -71,7 +71,7 @@ Download and Installation: install cffi yet, you need first ``python setup_base.py build_ext -f -i``) -.. _`Bitbucket page`: https://bitbucket.org/cffi/cffi +.. _`Heptapod page`: https://foss.heptapod.net/pypy/cffi Demos: -- cgit v1.2.1 From 3d7cdd07ac7b93e983f865325acfb7ea23851c38 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Thu, 13 Feb 2020 10:09:29 +0100 Subject: Backed out changeset af4aa63d5fc8 --- README.md | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9711660..3e7862d 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,26 @@ Foreign Function Interface for Python calling C code. Please see the [Documentation](http://cffi.readthedocs.org/) or uncompiled in the doc/ subdirectory. -This project moved ------------------- +Download +-------- -The new home is https://foss.heptapod.net/pypy/cffi +[Download page](https://bitbucket.org/cffi/cffi/downloads) + +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. -- cgit v1.2.1 From 39738b5be335e12afedbf39c5ef3c14ed1d50d35 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Fri, 6 Mar 2020 23:24:22 +0100 Subject: Vendor in pypy's version of py.path.local.make_numbered_dir(), which is more resilient against random OS Errors like EACCES --- testing/udir.py | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 129 insertions(+), 2 deletions(-) diff --git a/testing/udir.py b/testing/udir.py index 4dd0a11..59db1c4 100644 --- a/testing/udir.py +++ b/testing/udir.py @@ -1,7 +1,134 @@ import py -import sys +import sys, os, atexit -udir = py.path.local.make_numbered_dir(prefix = 'ffi-') + +# 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 -- cgit v1.2.1 From e9ab2af8126f2eaa27990ec8d2e80fe97d118c92 Mon Sep 17 00:00:00 2001 From: Matti Picus Date: Wed, 1 Apr 2020 07:24:20 +0300 Subject: support python2 builds with msvc>13 --- testing/cffi0/backend_tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testing/cffi0/backend_tests.py b/testing/cffi0/backend_tests.py index 063e9f2..e0567e0 100644 --- a/testing/cffi0/backend_tests.py +++ b/testing/cffi0/backend_tests.py @@ -12,8 +12,8 @@ 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 sys.version_info >= (3,): - py.test.skip("dlopen(None) cannot work on Windows for Python 3") + 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: -- cgit v1.2.1 From a7341764dcc849afc44b8efd814e727b078b22ca Mon Sep 17 00:00:00 2001 From: Matti Picus Date: Wed, 1 Apr 2020 07:30:18 +0300 Subject: add missing import --- testing/cffi0/backend_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/cffi0/backend_tests.py b/testing/cffi0/backend_tests.py index e0567e0..ab013a1 100644 --- a/testing/cffi0/backend_tests.py +++ b/testing/cffi0/backend_tests.py @@ -1,7 +1,7 @@ import py import pytest import platform -import sys, ctypes +import sys, ctypes, ctypes.util from cffi import FFI, CDefError, FFIError, VerificationMissing from testing.support import * -- cgit v1.2.1 From 73bddb1cdea031159593e5c62b9d9df242cbd657 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Sun, 12 Apr 2020 10:08:27 +0200 Subject: Fix a warning (or an error, with -Werror) on pypy3 --- cffi/_embedding.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cffi/_embedding.h b/cffi/_embedding.h index 34a4a66..e49c290 100644 --- a/cffi/_embedding.h +++ b/cffi/_embedding.h @@ -361,11 +361,11 @@ PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(const void *[]); /* forward */ static struct _cffi_pypy_init_s { const char *name; - void (*func)(const void *[]); + void *func; /* function pointer */ const char *code; } _cffi_pypy_init = { _CFFI_MODULE_NAME, - (void(*)(const void *[]))_CFFI_PYTHON_STARTUP_FUNC, + _CFFI_PYTHON_STARTUP_FUNC, _CFFI_PYTHON_STARTUP_CODE, }; -- cgit v1.2.1 From c2b8c1694a1ffcca2e673de7ca700cdc7b251e9f Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Sun, 12 Apr 2020 10:12:44 +0200 Subject: Fix the issue tracker link. More fixes will follow (#443) --- doc/source/goals.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/goals.rst b/doc/source/goals.rst index 0fda659..f66df21 100644 --- a/doc/source/goals.rst +++ b/doc/source/goals.rst @@ -65,5 +65,5 @@ everything you need to access C code and nothing more. --- the authors, Armin Rigo and Maciej Fijalkowski -.. _`issue tracker`: https://bitbucket.org/cffi/cffi/issues +.. _`issue tracker`: https://foss.heptapod.net/pypy/cffi/issues .. _`mailing list`: https://groups.google.com/forum/#!forum/python-cffi -- cgit v1.2.1 From 51f577576c16486b46c8c4abb2c8425fa9533063 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Thu, 16 Apr 2020 22:53:03 +0200 Subject: copy this from pypy --- testing/cffi1/test_recompiler.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py index 78abaa0..bb64ba2 100644 --- a/testing/cffi1/test_recompiler.py +++ b/testing/cffi1/test_recompiler.py @@ -38,6 +38,9 @@ def verify(ffi, module_name, source, *args, **kwds): 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(): -- cgit v1.2.1 From 680aa960da02dfa304c6b889489c05c38de89318 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Wed, 20 May 2020 09:50:30 +0200 Subject: Issue #447 Fix the name of some types to be exactly the name under which they are exposed. Put underscores in front of the CDataXxx type names and a docstring that explains why. --- c/_cffi_backend.c | 40 +++++++++++++++++++++++++++------------- c/test_c.py | 9 ++++++++- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index 7c9cd26..c37a6d4 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -653,7 +653,7 @@ static PyMethodDef ctypedescr_methods[] = { static PyTypeObject CTypeDescr_Type = { PyVarObject_HEAD_INIT(NULL, 0) - "_cffi_backend.CTypeDescr", + "_cffi_backend.CType", offsetof(CTypeDescrObject, ct_name), sizeof(char), (destructor)ctypedescr_dealloc, /* tp_dealloc */ @@ -3354,7 +3354,7 @@ static PyMethodDef cdata_methods[] = { static PyTypeObject CData_Type = { PyVarObject_HEAD_INIT(NULL, 0) - "_cffi_backend.CData", + "_cffi_backend.__CDataBase", sizeof(CDataObject), 0, (destructor)cdata_dealloc, /* tp_dealloc */ @@ -3373,7 +3373,9 @@ static PyTypeObject CData_Type = { (setattrofunc)cdata_setattro, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ - 0, /* tp_doc */ + "The internal base type for CData objects. Use FFI.CData to access " + "it. Always check with isinstance(): subtypes are sometimes returned " + "on CPython, for performance reasons.", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ cdata_richcompare, /* tp_richcompare */ @@ -3396,7 +3398,7 @@ static PyTypeObject CData_Type = { static PyTypeObject CDataOwning_Type = { PyVarObject_HEAD_INIT(NULL, 0) - "_cffi_backend.CDataOwn", + "_cffi_backend.__CDataOwn", sizeof(CDataObject), 0, (destructor)cdataowning_dealloc, /* tp_dealloc */ @@ -3415,7 +3417,8 @@ static PyTypeObject CDataOwning_Type = { 0, /* inherited */ /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ - 0, /* tp_doc */ + "This is an internal subtype of __CDataBase for performance only on " + "CPython. Check with isinstance(x, ffi.CData).", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* inherited */ /* tp_richcompare */ @@ -3438,7 +3441,7 @@ static PyTypeObject CDataOwning_Type = { static PyTypeObject CDataOwningGC_Type = { PyVarObject_HEAD_INIT(NULL, 0) - "_cffi_backend.CDataOwnGC", + "_cffi_backend.__CDataOwnGC", sizeof(CDataObject_own_structptr), 0, (destructor)cdataowninggc_dealloc, /* tp_dealloc */ @@ -3458,7 +3461,8 @@ static PyTypeObject CDataOwningGC_Type = { 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES /* tp_flags */ | Py_TPFLAGS_HAVE_GC, - 0, /* tp_doc */ + "This is an internal subtype of __CDataBase for performance only on " + "CPython. Check with isinstance(x, ffi.CData).", /* tp_doc */ (traverseproc)cdataowninggc_traverse, /* tp_traverse */ (inquiry)cdataowninggc_clear, /* tp_clear */ 0, /* inherited */ /* tp_richcompare */ @@ -3481,7 +3485,7 @@ static PyTypeObject CDataOwningGC_Type = { static PyTypeObject CDataFromBuf_Type = { PyVarObject_HEAD_INIT(NULL, 0) - "_cffi_backend.CDataFromBuf", + "_cffi_backend.__CDataFromBuf", sizeof(CDataObject_frombuf), 0, (destructor)cdatafrombuf_dealloc, /* tp_dealloc */ @@ -3501,7 +3505,8 @@ static PyTypeObject CDataFromBuf_Type = { 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES /* tp_flags */ | Py_TPFLAGS_HAVE_GC, - 0, /* tp_doc */ + "This is an internal subtype of __CDataBase for performance only on " + "CPython. Check with isinstance(x, ffi.CData).", /* tp_doc */ (traverseproc)cdatafrombuf_traverse, /* tp_traverse */ (inquiry)cdatafrombuf_clear, /* tp_clear */ 0, /* inherited */ /* tp_richcompare */ @@ -3524,7 +3529,7 @@ static PyTypeObject CDataFromBuf_Type = { static PyTypeObject CDataGCP_Type = { PyVarObject_HEAD_INIT(NULL, 0) - "_cffi_backend.CDataGCP", + "_cffi_backend.__CDataGCP", sizeof(CDataObject_gcp), 0, (destructor)cdatagcp_dealloc, /* tp_dealloc */ @@ -3547,7 +3552,8 @@ static PyTypeObject CDataGCP_Type = { | Py_TPFLAGS_HAVE_FINALIZE #endif | Py_TPFLAGS_HAVE_GC, - 0, /* tp_doc */ + "This is an internal subtype of __CDataBase for performance only on " + "CPython. Check with isinstance(x, ffi.CData).", /* tp_doc */ (traverseproc)cdatagcp_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* inherited */ /* tp_richcompare */ @@ -3608,7 +3614,7 @@ cdataiter_dealloc(CDataIterObject *it) static PyTypeObject CDataIter_Type = { PyVarObject_HEAD_INIT(NULL, 0) - "_cffi_backend.CDataIter", /* tp_name */ + "_cffi_backend.__CData_iterator", /* tp_name */ sizeof(CDataIterObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ @@ -4363,7 +4369,7 @@ static PyMethodDef dl_methods[] = { static PyTypeObject dl_type = { PyVarObject_HEAD_INIT(NULL, 0) - "_cffi_backend.Library", /* tp_name */ + "_cffi_backend.__Library", /* tp_name */ sizeof(DynLibObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ @@ -7882,6 +7888,14 @@ init_cffi_backend(void) if (PyModule_AddObject(m, "buffer", (PyObject *)&MiniBuffer_Type) < 0) INITERROR; + Py_INCREF(&CTypeDescr_Type); + if (PyModule_AddObject(m, "CType", (PyObject *)&CTypeDescr_Type) < 0) + INITERROR; + + Py_INCREF(&CField_Type); + if (PyModule_AddObject(m, "CField", (PyObject *)&CField_Type) < 0) + INITERROR; + init_cffi_tls(); if (PyErr_Occurred()) INITERROR; diff --git a/c/test_c.py b/c/test_c.py index 3b34999..85e75b7 100644 --- a/c/test_c.py +++ b/c/test_c.py @@ -109,7 +109,7 @@ def test_cast_to_signed_char(): p = new_primitive_type("signed char") x = cast(p, -65 + 17*256) assert repr(x) == "" - assert repr(type(x)) == "<%s '_cffi_backend.CData'>" % type_or_class + assert repr(type(x)) == "<%s '_cffi_backend.__CDataBase'>" % type_or_class assert int(x) == -65 x = cast(p, -66 + (1<<199)*256) assert repr(x) == "" @@ -4453,3 +4453,10 @@ def test_huge_structure(): BStruct = new_struct_type("struct foo") complete_struct_or_union(BStruct, [('a1', BArray, -1)]) assert sizeof(BStruct) == sys.maxsize + +def test_type_names(): + assert (CType.__module__, CType.__name__) == ('_cffi_backend', 'CType') + assert (CField.__module__, CField.__name__) == ('_cffi_backend', 'CField') + CData, CType1 = _get_types() + assert CType1 is CType + assert (CData.__module__, CData.__name__) == ('_cffi_backend','__CDataBase') -- cgit v1.2.1 From bb48fdcc86c5bd2c211fba2ac4cffe8b6efb49e2 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Wed, 20 May 2020 23:29:46 +0200 Subject: #453 Special-case typedefs of arrays with '...' length, so that they can be used through recompiler.py as if they had a specified length in most cases. --- cffi/cparser.py | 18 +++++++++++++++--- cffi/model.py | 7 +++++-- cffi/vengine_cpy.py | 4 ++-- cffi/vengine_gen.py | 4 ++-- testing/cffi1/test_recompiler.py | 34 ++++++++++++++++++++++++++++++++++ 5 files changed, 58 insertions(+), 9 deletions(-) diff --git a/cffi/cparser.py b/cffi/cparser.py index ea27c48..d7069a7 100644 --- a/cffi/cparser.py +++ b/cffi/cparser.py @@ -395,7 +395,8 @@ class Parser(object): realtype = self._get_unknown_ptr_type(decl) else: realtype, quals = self._get_type_and_quals( - decl.type, name=decl.name, partial_length_ok=True) + 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 @@ -562,7 +563,8 @@ class Parser(object): return model.NamedPointerType(type, declname, quals) return model.PointerType(type, quals) - def _get_type_and_quals(self, typenode, name=None, partial_length_ok=False): + 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 @@ -579,8 +581,18 @@ class Parser(object): 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) + partial_length_ok=partial_length_ok, + typedef_example=typedef_example) return model.ArrayType(tp, length), quals # if isinstance(typenode, pycparser.c_ast.PtrDecl): diff --git a/cffi/model.py b/cffi/model.py index 5f1b0d2..ad1c176 100644 --- a/cffi/model.py +++ b/cffi/model.py @@ -307,11 +307,14 @@ class ArrayType(BaseType): 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 == '...': + 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 @@ -430,7 +433,7 @@ class StructOrUnion(StructOrUnionOrEnum): fsize = fieldsize[i] ftype = self.fldtypes[i] # - if isinstance(ftype, ArrayType) and ftype.length == '...': + 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)) diff --git a/cffi/vengine_cpy.py b/cffi/vengine_cpy.py index cb344ce..6de0df0 100644 --- a/cffi/vengine_cpy.py +++ b/cffi/vengine_cpy.py @@ -762,7 +762,7 @@ class VCPythonEngine(object): 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 == '...')) + size_too = tp.length_is_unknown()) else: tp_ptr = model.PointerType(tp) self._generate_cpy_const(False, name, tp_ptr, category='var') @@ -774,7 +774,7 @@ class VCPythonEngine(object): value = getattr(library, name) if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the # sense that "a=..." is forbidden - if tp.length == '...': + if tp.length_is_unknown(): assert isinstance(value, tuple) (value, size) = value BItemType = self.ffi._get_cached_btype(tp.item) diff --git a/cffi/vengine_gen.py b/cffi/vengine_gen.py index a64ff64..2642152 100644 --- a/cffi/vengine_gen.py +++ b/cffi/vengine_gen.py @@ -565,7 +565,7 @@ class VGenericEngine(object): def _generate_gen_variable_decl(self, tp, name): if isinstance(tp, model.ArrayType): - if tp.length == '...': + if tp.length_is_unknown(): prnt = self._prnt funcname = '_cffi_sizeof_%s' % (name,) self.export_symbols.append(funcname) @@ -584,7 +584,7 @@ class VGenericEngine(object): 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 == '...': + 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) diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py index bb64ba2..397270b 100644 --- a/testing/cffi1/test_recompiler.py +++ b/testing/cffi1/test_recompiler.py @@ -2122,6 +2122,40 @@ def test_typedef_array_dotdotdot(): 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(""" -- cgit v1.2.1 From 6c6b4102077dec4eb249d3ca42a1bd0b343aece2 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Tue, 26 May 2020 15:51:56 +0200 Subject: Issue #454 Try harder to avoid #line directives confuse the rest of pre-parsing --- cffi/cparser.py | 37 ++++++++++++++++++++++++++++++--- testing/cffi0/test_parsing.py | 48 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 81 insertions(+), 4 deletions(-) diff --git a/cffi/cparser.py b/cffi/cparser.py index d7069a7..d978465 100644 --- a/cffi/cparser.py +++ b/cffi/cparser.py @@ -29,6 +29,7 @@ _r_comment = re.compile(r"/\*.*?\*/|//([^\n\\]|\\.)*?$", _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\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*\]") @@ -163,10 +164,37 @@ def _warn_for_non_extern_non_static_global_variable(decl): "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. 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 literal! - csource = _r_comment.sub(' ', csource) + # 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): @@ -219,7 +247,10 @@ def _preprocess(csource): 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. - return csource.replace('...', ' __dotdotdot__ '), macros + 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 diff --git a/testing/cffi0/test_parsing.py b/testing/cffi0/test_parsing.py index 3fc3783..5f2d7ec 100644 --- a/testing/cffi0/test_parsing.py +++ b/testing/cffi0/test_parsing.py @@ -174,7 +174,7 @@ def test_remove_line_continuation_comments(): double // blah \\ more comments x(void); - double // blah\\\\ + double // blah // blah\\\\ y(void); double // blah\\ \ etc @@ -185,6 +185,52 @@ def test_remove_line_continuation_comments(): 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" + +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" + +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" + def test_line_continuation_in_defines(): ffi = FFI(backend=FakeBackend()) ffi.cdef(""" -- cgit v1.2.1 From ab1d6096f86f3364b78a19a67e6fd908acb57c1a Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Thu, 28 May 2020 15:50:56 +0200 Subject: Systematically expose all types on the _cffi_backend module under their correct name. This name starts with '__' if it is supposed to be non-portable and not guaranteed. --- c/_cffi_backend.c | 76 +++++++++++++++++++++++++++---------------------------- c/cffi1_module.c | 15 ----------- c/cglob.c | 2 +- c/ffi_obj.c | 2 +- c/lib_obj.c | 2 +- c/test_c.py | 45 +++++++++++++++++++++++++++----- 6 files changed, 79 insertions(+), 63 deletions(-) diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index c37a6d4..5174648 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -3354,7 +3354,7 @@ static PyMethodDef cdata_methods[] = { static PyTypeObject CData_Type = { PyVarObject_HEAD_INIT(NULL, 0) - "_cffi_backend.__CDataBase", + "_cffi_backend._CDataBase", sizeof(CDataObject), 0, (destructor)cdata_dealloc, /* tp_dealloc */ @@ -3417,7 +3417,7 @@ static PyTypeObject CDataOwning_Type = { 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 " + "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 */ @@ -3461,7 +3461,7 @@ static PyTypeObject CDataOwningGC_Type = { 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 " + "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 */ @@ -3505,7 +3505,7 @@ static PyTypeObject CDataFromBuf_Type = { 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 " + "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 */ @@ -3552,7 +3552,7 @@ static PyTypeObject CDataGCP_Type = { | Py_TPFLAGS_HAVE_FINALIZE #endif | Py_TPFLAGS_HAVE_GC, - "This is an internal subtype of __CDataBase for performance only on " + "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 */ @@ -4369,7 +4369,7 @@ static PyMethodDef dl_methods[] = { static PyTypeObject dl_type = { PyVarObject_HEAD_INIT(NULL, 0) - "_cffi_backend.__Library", /* tp_name */ + "_cffi_backend.CLibrary", /* tp_name */ sizeof(DynLibObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ @@ -7794,6 +7794,22 @@ init_cffi_backend(void) PyObject *m, *v; int i; static char init_done = 0; + static PyTypeObject *all_types[] = { + &dl_type, + &CTypeDescr_Type, + &CField_Type, + &CData_Type, + &CDataOwning_Type, + &CDataOwningGC_Type, + &CDataFromBuf_Type, + &CDataGCP_Type, + &CDataIter_Type, + &MiniBuffer_Type, + &FFI_Type, + &Lib_Type, + &GlobSupport_Type, + NULL + }; v = PySys_GetObject("version"); if (v == NULL || !PyText_Check(v) || @@ -7819,26 +7835,22 @@ init_cffi_backend(void) INITERROR; } - if (PyType_Ready(&dl_type) < 0) - INITERROR; - if (PyType_Ready(&CTypeDescr_Type) < 0) - INITERROR; - if (PyType_Ready(&CField_Type) < 0) - INITERROR; - if (PyType_Ready(&CData_Type) < 0) - INITERROR; - if (PyType_Ready(&CDataOwning_Type) < 0) - INITERROR; - if (PyType_Ready(&CDataOwningGC_Type) < 0) - INITERROR; - if (PyType_Ready(&CDataFromBuf_Type) < 0) - INITERROR; - if (PyType_Ready(&CDataGCP_Type) < 0) - INITERROR; - if (PyType_Ready(&CDataIter_Type) < 0) - INITERROR; - if (PyType_Ready(&MiniBuffer_Type) < 0) - INITERROR; + /* readify all types and add them to the module */ + for (i = 0; all_types[i] != NULL; i++) { + PyTypeObject *tp = all_types[i]; + PyObject *tpo = (PyObject *)tp; + if (strncmp(tp->tp_name, "_cffi_backend.", 14) != 0) { + PyErr_Format(PyExc_ImportError, + "'%s' is an ill-formed type name", tp->tp_name); + INITERROR; + } + if (PyType_Ready(tp) < 0) + INITERROR; + + Py_INCREF(tpo); + if (PyModule_AddObject(m, tp->tp_name + 14, tpo) < 0) + INITERROR; + } if (!init_done) { v = PyText_FromString("_cffi_backend"); @@ -7884,18 +7896,6 @@ init_cffi_backend(void) INITERROR; } - Py_INCREF(&MiniBuffer_Type); - if (PyModule_AddObject(m, "buffer", (PyObject *)&MiniBuffer_Type) < 0) - INITERROR; - - Py_INCREF(&CTypeDescr_Type); - if (PyModule_AddObject(m, "CType", (PyObject *)&CTypeDescr_Type) < 0) - INITERROR; - - Py_INCREF(&CField_Type); - if (PyModule_AddObject(m, "CField", (PyObject *)&CField_Type) < 0) - INITERROR; - init_cffi_tls(); if (PyErr_Occurred()) INITERROR; diff --git a/c/cffi1_module.c b/c/cffi1_module.c index d688b80..06a84fe 100644 --- a/c/cffi1_module.c +++ b/c/cffi1_module.c @@ -26,11 +26,6 @@ static int init_ffi_lib(PyObject *m) int i, res; static char init_done = 0; - if (PyType_Ready(&FFI_Type) < 0) - return -1; - if (PyType_Ready(&Lib_Type) < 0) - return -1; - if (!init_done) { if (init_global_types_dict(FFI_Type.tp_dict) < 0) return -1; @@ -62,16 +57,6 @@ static int init_ffi_lib(PyObject *m) } init_done = 1; } - - x = (PyObject *)&FFI_Type; - Py_INCREF(x); - if (PyModule_AddObject(m, "FFI", x) < 0) - return -1; - x = (PyObject *)&Lib_Type; - Py_INCREF(x); - if (PyModule_AddObject(m, "Lib", x) < 0) - return -1; - return 0; } diff --git a/c/cglob.c b/c/cglob.c index 9ee4025..e97767c 100644 --- a/c/cglob.c +++ b/c/cglob.c @@ -20,7 +20,7 @@ static void glob_support_dealloc(GlobSupportObject *gs) static PyTypeObject GlobSupport_Type = { PyVarObject_HEAD_INIT(NULL, 0) - "FFIGlobSupport", + "_cffi_backend.__FFIGlobSupport", sizeof(GlobSupportObject), 0, (destructor)glob_support_dealloc, /* tp_dealloc */ diff --git a/c/ffi_obj.c b/c/ffi_obj.c index 1e8cc6f..05c3a6d 100644 --- a/c/ffi_obj.c +++ b/c/ffi_obj.c @@ -1137,7 +1137,7 @@ static PyGetSetDef ffi_getsets[] = { static PyTypeObject FFI_Type = { PyVarObject_HEAD_INIT(NULL, 0) - "CompiledFFI", + "_cffi_backend.FFI", sizeof(FFIObject), 0, (destructor)ffi_dealloc, /* tp_dealloc */ diff --git a/c/lib_obj.c b/c/lib_obj.c index 0c3d7d1..38bf3d5 100644 --- a/c/lib_obj.c +++ b/c/lib_obj.c @@ -589,7 +589,7 @@ static PyMethodDef lib_methods[] = { static PyTypeObject Lib_Type = { PyVarObject_HEAD_INIT(NULL, 0) - "CompiledLib", + "_cffi_backend.Lib", sizeof(LibObject), 0, (destructor)lib_dealloc, /* tp_dealloc */ diff --git a/c/test_c.py b/c/test_c.py index 85e75b7..fa20517 100644 --- a/c/test_c.py +++ b/c/test_c.py @@ -109,7 +109,7 @@ def test_cast_to_signed_char(): p = new_primitive_type("signed char") x = cast(p, -65 + 17*256) assert repr(x) == "" - assert repr(type(x)) == "<%s '_cffi_backend.__CDataBase'>" % type_or_class + assert repr(type(x)) == "<%s '_cffi_backend._CDataBase'>" % type_or_class assert int(x) == -65 x = cast(p, -66 + (1<<199)*256) assert repr(x) == "" @@ -4454,9 +4454,40 @@ def test_huge_structure(): complete_struct_or_union(BStruct, [('a1', BArray, -1)]) assert sizeof(BStruct) == sys.maxsize -def test_type_names(): - assert (CType.__module__, CType.__name__) == ('_cffi_backend', 'CType') - assert (CField.__module__, CField.__name__) == ('_cffi_backend', 'CField') - CData, CType1 = _get_types() - assert CType1 is CType - assert (CData.__module__, CData.__name__) == ('_cffi_backend','__CDataBase') +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) -- cgit v1.2.1 From 82389a693860fa1add7ced57334f9b18c7f04d0e Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Fri, 29 May 2020 10:27:40 +0200 Subject: #454 Second try with '# NUMBER' instead of '#line NUMBER', as gcc seems to output --- cffi/cparser.py | 8 ++++---- testing/cffi0/test_parsing.py | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/cffi/cparser.py b/cffi/cparser.py index d978465..74830e9 100644 --- a/cffi/cparser.py +++ b/cffi/cparser.py @@ -29,7 +29,7 @@ _r_comment = re.compile(r"/\*.*?\*/|//([^\n\\]|\\.)*?$", _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\b.*$", 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*\]") @@ -166,9 +166,9 @@ def _warn_for_non_extern_non_static_global_variable(decl): def _remove_line_directives(csource): # _r_line_directive matches whole lines, without the final \n, if they - # start with '#line' with some spacing allowed. This function stores - # them away and replaces them with exactly the string '#line@N', where - # N is the index in the list 'line_directives'. + # 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) diff --git a/testing/cffi0/test_parsing.py b/testing/cffi0/test_parsing.py index 5f2d7ec..a5e4587 100644 --- a/testing/cffi0/test_parsing.py +++ b/testing/cffi0/test_parsing.py @@ -199,6 +199,21 @@ def test_dont_remove_comment_in_line_directives(): 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(): @@ -214,6 +229,18 @@ def test_multiple_line_directives(): 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()) @@ -229,6 +256,20 @@ def test_commented_line_directive(): */ 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(): -- cgit v1.2.1 From fc1d60bfb4a1e8ac58126ff4df672d0f8483c721 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Tue, 2 Jun 2020 12:55:49 +0200 Subject: Issue #458 py3.10 support --- c/_cffi_backend.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index 5174648..b3c1c86 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -111,6 +111,9 @@ # define PyText_InternInPlace PyUnicode_InternInPlace # define PyText_InternFromString PyUnicode_InternFromString # define PyIntOrLong_Check PyLong_Check +# if PY_VERSION_HEX < 0x030900a4 +# define Py_SET_REFCNT(obj, val) (Py_REFCNT(obj) = (val)) +# endif #else # define STR_OR_BYTES "str" # define PyText_Type PyString_Type @@ -404,10 +407,10 @@ ctypedescr_dealloc(CTypeDescrObject *ct) if (ct->ct_unique_key != NULL) { /* revive dead object temporarily for DelItem */ - Py_REFCNT(ct) = 43; + Py_SET_REFCNT(ct, 43); PyDict_DelItem(unique_cache, ct->ct_unique_key); assert(Py_REFCNT(ct) == 42); - Py_REFCNT(ct) = 0; + Py_SET_REFCNT(ct, 0); Py_DECREF(ct->ct_unique_key); } Py_XDECREF(ct->ct_itemdescr); -- cgit v1.2.1 From 03700246095f6494a20b8ef0ffd217c45062ecfb Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Wed, 10 Jun 2020 22:53:01 +0200 Subject: Oops, 2.7 fix --- c/_cffi_backend.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index b3c1c86..f647d39 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -111,9 +111,6 @@ # define PyText_InternInPlace PyUnicode_InternInPlace # define PyText_InternFromString PyUnicode_InternFromString # define PyIntOrLong_Check PyLong_Check -# if PY_VERSION_HEX < 0x030900a4 -# define Py_SET_REFCNT(obj, val) (Py_REFCNT(obj) = (val)) -# endif #else # define STR_OR_BYTES "str" # define PyText_Type PyString_Type @@ -151,6 +148,10 @@ (PyCObject_FromVoidPtr(pointer, destructor)) #endif +#if PY_VERSION_HEX < 0x030900a4 +# define Py_SET_REFCNT(obj, val) (Py_REFCNT(obj) = (val)) +#endif + /************************************************************/ /* base type flag: exactly one of the following: */ -- cgit v1.2.1 From 7fdc30c2cc119fafe7a571739d75527533fad727 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Tue, 16 Jun 2020 14:13:02 +0200 Subject: (untested) don't use deprecated "u" in PyArg_ParseTuple() --- c/_cffi_backend.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index f647d39..6f2f59e 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -4451,8 +4451,8 @@ static void *b_do_dlopen(PyObject *args, const char **p_printable_filename, { PyObject *s = PyTuple_GET_ITEM(args, 0); #ifdef MS_WIN32 - Py_UNICODE *filenameW; - if (PyArg_ParseTuple(args, "u|i:load_library", &filenameW, &flags)) + PyObject *filename_unicode; + if (PyArg_ParseTuple(args, "U|i:load_library", &filename_unicode, &flags)) { #if PY_MAJOR_VERSION < 3 s = PyUnicode_AsUTF8String(s); @@ -4464,7 +4464,14 @@ static void *b_do_dlopen(PyObject *args, const char **p_printable_filename, if (*p_printable_filename == NULL) return NULL; - handle = dlopenW(filenameW); + Py_ssize_t sz1 = PyUnicode_GetSize(filename_unicode) + 1; + sz1 *= 2; /* should not be needed, but you never know */ + wchar_t *w1 = alloca(sizeof(wchar_t) * sz1); + sz1 = PyUnicode_AsWideChar(filename_unicode, w1, sz1 - 1); + if (sz1 < 0) + return NULL; + w1[sz1] = 0; + handle = dlopenW(w1); goto got_handle; } PyErr_Clear(); -- cgit v1.2.1 From edb39f03e3841dc682f2517c7c1020397dcfc6c2 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Wed, 1 Jul 2020 10:55:19 +0200 Subject: Always call ffi_prep_closure_loc(), possibly with the extra argument being equal to the first one (this is exactly what ffi_prep_closure() does, and that function is now marked as deprecated). --- c/_cffi_backend.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index 6f2f59e..e2ad390 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -6255,13 +6255,17 @@ static PyObject *b_callback(PyObject *self, PyObject *args) "return type or with '...'", ct->ct_name); goto error; } -#ifdef CFFI_TRUST_LIBFFI + /* NOTE: if we don't have CFFI_TRUST_LIBFFI, then this will invoke + * ffi_prep_closure_loc() with the last argument being equal to the + * first one. With the current versions of libffi (June 2020), then + * this is just what occurs inside ffi anyway by calling + * ffi_prep_closure()---but this latter function is now deprecated. + * Please be aware that although this calls ffi_prep_closure_loc(), + * it is NOT always safe against read-write-execute memory attacks, + * as described in the lengthy comment before CFFI_TRUST_LIBFFI. + */ if (ffi_prep_closure_loc(closure, &cif_descr->cif, invoke_callback, infotuple, closure_exec) != FFI_OK) { -#else - if (ffi_prep_closure(closure, &cif_descr->cif, - invoke_callback, infotuple) != FFI_OK) { -#endif PyErr_SetString(PyExc_SystemError, "libffi failed to build this callback"); goto error; -- cgit v1.2.1 From 91dfef348250ffaf7acda85834f5eb09b43be2f7 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Mon, 6 Jul 2020 21:13:08 +0200 Subject: windows fix --- c/_cffi_backend.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index e2ad390..71e22f1 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -4454,6 +4454,8 @@ static void *b_do_dlopen(PyObject *args, const char **p_printable_filename, 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) @@ -4464,10 +4466,11 @@ static void *b_do_dlopen(PyObject *args, const char **p_printable_filename, if (*p_printable_filename == NULL) return NULL; - Py_ssize_t sz1 = PyUnicode_GetSize(filename_unicode) + 1; + sz1 = PyUnicode_GetSize(filename_unicode) + 1; sz1 *= 2; /* should not be needed, but you never know */ - wchar_t *w1 = alloca(sizeof(wchar_t) * sz1); - sz1 = PyUnicode_AsWideChar(filename_unicode, w1, sz1 - 1); + w1 = alloca(sizeof(wchar_t) * sz1); + sz1 = PyUnicode_AsWideChar((PyUnicodeObject *)filename_unicode, + w1, sz1 - 1); if (sz1 < 0) return NULL; w1[sz1] = 0; -- cgit v1.2.1 From c5d0cc50b6cded01068108f6f2a87ab363cdee74 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Mon, 6 Jul 2020 21:25:27 +0200 Subject: Windows: complain explicitly in _cffi_backend.FFI().dlopen(None) --- c/_cffi_backend.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index 71e22f1..19801f0 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -4498,6 +4498,13 @@ static void *b_do_dlopen(PyObject *args, const char **p_printable_filename, 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); #ifdef MS_WIN32 -- cgit v1.2.1 From fc9349e8aa5a533ff51f5ec6c592e8024f2d727e Mon Sep 17 00:00:00 2001 From: Lawrence D'Anna Date: Thu, 9 Jul 2020 18:39:23 -0700 Subject: MacOS 11, arm64, use system libffi Use the system libffi if deployment target is at least 10.15 --- setup.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e1dd39d..c28147e 100644 --- a/setup.py +++ b/setup.py @@ -71,6 +71,10 @@ def get_config(): config = Distribution().get_command_obj('config') return config +def macosx_deployment_target(): + from distutils.sysconfig import get_config_var + return tuple(map(int, get_config_var("MACOSX_DEPLOYMENT_TARGET").split('.'))) + def ask_supports_thread(): config = get_config() ok = (sys.platform != 'win32' and @@ -145,7 +149,13 @@ if COMPILE_LIBFFI: sources.extend(os.path.join(COMPILE_LIBFFI, filename) for filename in _filenames) else: - use_pkg_config() + if 'darwin' in sys.platform and macosx_deployment_target() >= (10, 15): + # use libffi from Mac OS SDK + extra_compile_args += ['-iwithsysroot/usr/include/ffi'] + define_macros += [('CFFI_TRUST_LIBFFI', '1')] + libraries += ['ffi'] + else: + use_pkg_config() ask_supports_thread() ask_supports_sync_synchronize() -- cgit v1.2.1 From 6684336eafc9aafebbccb8f48c3b82cc913b3aa8 Mon Sep 17 00:00:00 2001 From: Lawrence D'Anna Date: Thu, 9 Jul 2020 18:39:23 -0700 Subject: MacOS 11, arm64, ffi_prep_cif_var On Apple arm64, theABI for a variardic function is different than the ABI for a fixed-arg function, even of the same arg types. In order to account for this, CFFI must call ffi_prep_cif_var for variardic calls. see: https://developer.apple.com/library/archive/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html --- c/_cffi_backend.c | 27 ++++++++++++++++++++++----- setup.py | 3 ++- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index 19801f0..6a8bccb 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -2892,7 +2892,7 @@ static PyObject * convert_struct_to_owning_object(char *data, CTypeDescrObject *ct); /*forward*/ static cif_description_t * -fb_prepare_cif(PyObject *fargs, CTypeDescrObject *, ffi_abi); /*forward*/ +fb_prepare_cif(PyObject *fargs, CTypeDescrObject *, Py_ssize_t, int, ffi_abi); /*forward*/ static PyObject *new_primitive_type(const char *name); /*forward*/ @@ -3085,7 +3085,7 @@ cdata_call(CDataObject *cd, PyObject *args, PyObject *kwds) #else fabi = PyLong_AS_LONG(PyTuple_GET_ITEM(signature, 0)); #endif - cif_descr = fb_prepare_cif(fvarargs, fresult, fabi); + cif_descr = fb_prepare_cif(fvarargs, fresult, nargs_declared, true, fabi); if (cif_descr == NULL) goto error; } @@ -5810,7 +5810,10 @@ static CTypeDescrObject *fb_prepare_ctype(struct funcbuilder_s *fb, static cif_description_t *fb_prepare_cif(PyObject *fargs, CTypeDescrObject *fresult, + Py_ssize_t nargs_declared, + int ellipsis, ffi_abi fabi) + { char *buffer; cif_description_t *cif_descr; @@ -5837,8 +5840,22 @@ static cif_description_t *fb_prepare_cif(PyObject *fargs, assert(funcbuffer.bufferp == buffer + funcbuffer.nb_bytes); cif_descr = (cif_description_t *)buffer; - if (ffi_prep_cif(&cif_descr->cif, fabi, funcbuffer.nargs, - funcbuffer.rtype, funcbuffer.atypes) != FFI_OK) { + ffi_status status; +#if HAVE_FFI_PREP_CIF_VAR + if (ellipsis) { + status = ffi_prep_cif_var(&cif_descr->cif, fabi, + nargs_declared, funcbuffer.nargs, + funcbuffer.rtype, funcbuffer.atypes); + } else +#endif +#if !HAVE_FFI_PREP_CIF_VAR && defined(__arm64__) && defined(__APPLE__) +#error Apple Arm64 ABI requires ffi_prep_cif_var +#endif + { + 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; @@ -5882,7 +5899,7 @@ static PyObject *new_function_type(PyObject *fargs, /* tuple */ is computed here. */ cif_description_t *cif_descr; - cif_descr = fb_prepare_cif(fargs, fresult, fabi); + cif_descr = fb_prepare_cif(fargs, fresult, 0, ellipsis, fabi); if (cif_descr == NULL) { if (PyErr_ExceptionMatches(PyExc_NotImplementedError)) { PyErr_Clear(); /* will get the exception if we see an diff --git a/setup.py b/setup.py index c28147e..fc8d705 100644 --- a/setup.py +++ b/setup.py @@ -152,7 +152,8 @@ else: if 'darwin' in sys.platform and macosx_deployment_target() >= (10, 15): # use libffi from Mac OS SDK extra_compile_args += ['-iwithsysroot/usr/include/ffi'] - define_macros += [('CFFI_TRUST_LIBFFI', '1')] + define_macros += [('CFFI_TRUST_LIBFFI', '1'), + ('HAVE_FFI_PREP_CIF_VAR', '1')] libraries += ['ffi'] else: use_pkg_config() -- cgit v1.2.1 From 6e2ce7bdd4741ed2df55ad5c7480a8ef0224f815 Mon Sep 17 00:00:00 2001 From: Lawrence D'Anna Date: Thu, 9 Jul 2020 18:39:23 -0700 Subject: on darwin, arm64 bitfields act like x86_64 --- c/_cffi_backend.c | 4 +++- testing/cffi0/test_ffi_backend.py | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index 6a8bccb..5c5fe45 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -5022,7 +5022,9 @@ static int complete_sflags(int sflags) #ifdef MS_WIN32 sflags |= SF_MSVC_BITFIELDS; #else -# if defined(__arm__) || defined(__aarch64__) +# if defined(__APPLE__) && defined(__arm64__) + sflags |= SF_GCC_X86_BITFIELDS; +# elif defined(__arm__) || defined(__aarch64__) sflags |= SF_GCC_ARM_BITFIELDS; # else sflags |= SF_GCC_X86_BITFIELDS; diff --git a/testing/cffi0/test_ffi_backend.py b/testing/cffi0/test_ffi_backend.py index 6937a79..3552196 100644 --- a/testing/cffi0/test_ffi_backend.py +++ b/testing/cffi0/test_ffi_backend.py @@ -246,7 +246,10 @@ class TestBitfield: 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("platform.machine().startswith(('arm', 'aarch64'))") + @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) @@ -260,6 +263,8 @@ class TestBitfield: 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") @@ -273,7 +278,10 @@ class TestBitfield: 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("platform.machine().startswith(('arm', 'aarch64'))") + @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) @@ -285,6 +293,8 @@ class TestBitfield: 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") -- cgit v1.2.1 From ec801d43b7df6ccdeb5fda62c6cb1ae00261492a Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Fri, 10 Jul 2020 04:00:04 +0200 Subject: non-mac: cannot use 'true' in C sources without some more includes; we'd need to write '1'. But in this case we can simplify a bit the function signature, removing its need --- c/_cffi_backend.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index 5c5fe45..474ee06 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -2892,7 +2892,8 @@ 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, int, ffi_abi); /*forward*/ +fb_prepare_cif(PyObject *fargs, CTypeDescrObject *, Py_ssize_t, ffi_abi); + /*forward*/ static PyObject *new_primitive_type(const char *name); /*forward*/ @@ -3085,7 +3086,7 @@ cdata_call(CDataObject *cd, PyObject *args, PyObject *kwds) #else fabi = PyLong_AS_LONG(PyTuple_GET_ITEM(signature, 0)); #endif - cif_descr = fb_prepare_cif(fvarargs, fresult, nargs_declared, true, fabi); + cif_descr = fb_prepare_cif(fvarargs, fresult, nargs_declared, fabi); if (cif_descr == NULL) goto error; } @@ -5812,8 +5813,7 @@ static CTypeDescrObject *fb_prepare_ctype(struct funcbuilder_s *fb, static cif_description_t *fb_prepare_cif(PyObject *fargs, CTypeDescrObject *fresult, - Py_ssize_t nargs_declared, - int ellipsis, + Py_ssize_t variadic_nargs_declared, ffi_abi fabi) { @@ -5844,9 +5844,9 @@ static cif_description_t *fb_prepare_cif(PyObject *fargs, cif_descr = (cif_description_t *)buffer; ffi_status status; #if HAVE_FFI_PREP_CIF_VAR - if (ellipsis) { + if (variadic_nargs_declared >= 0) { status = ffi_prep_cif_var(&cif_descr->cif, fabi, - nargs_declared, funcbuffer.nargs, + variadic_nargs_declared, funcbuffer.nargs, funcbuffer.rtype, funcbuffer.atypes); } else #endif @@ -5901,7 +5901,7 @@ static PyObject *new_function_type(PyObject *fargs, /* tuple */ is computed here. */ cif_description_t *cif_descr; - cif_descr = fb_prepare_cif(fargs, fresult, 0, ellipsis, fabi); + cif_descr = fb_prepare_cif(fargs, fresult, -1, fabi); if (cif_descr == NULL) { if (PyErr_ExceptionMatches(PyExc_NotImplementedError)) { PyErr_Clear(); /* will get the exception if we see an -- cgit v1.2.1 From 549b877275d9104cc313a0b714548b58cb3fdd69 Mon Sep 17 00:00:00 2001 From: Lawrence D'Anna Date: Thu, 9 Jul 2020 20:12:58 -0700 Subject: only trust ffi_closure_alloc on arm --- c/_cffi_backend.c | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index 5c5fe45..ed89753 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -84,7 +84,7 @@ * known that on the NetBSD kernel, a different strategy is used which * should not be open to the fork() bug. */ -#ifdef __NetBSD__ +#if defined(__NetBSD__) || (CFFI_LIBFFI_FROM_MACOS && (defined(__arm__) || defined(__arm64__))) # define CFFI_TRUST_LIBFFI #endif diff --git a/setup.py b/setup.py index fc8d705..217504a 100644 --- a/setup.py +++ b/setup.py @@ -152,7 +152,7 @@ else: if 'darwin' in sys.platform and macosx_deployment_target() >= (10, 15): # use libffi from Mac OS SDK extra_compile_args += ['-iwithsysroot/usr/include/ffi'] - define_macros += [('CFFI_TRUST_LIBFFI', '1'), + define_macros += [('CFFI_LIBFFI_FROM_MACOS', '1'), ('HAVE_FFI_PREP_CIF_VAR', '1')] libraries += ['ffi'] else: -- cgit v1.2.1 From f505faad51bf7ea87b4dcf879bdd848a59c43ddb Mon Sep 17 00:00:00 2001 From: Lawrence D'Anna Date: Fri, 10 Jul 2020 10:41:35 -0700 Subject: Backed out changeset 9dd88cced01c --- c/_cffi_backend.c | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index ed89753..5c5fe45 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -84,7 +84,7 @@ * known that on the NetBSD kernel, a different strategy is used which * should not be open to the fork() bug. */ -#if defined(__NetBSD__) || (CFFI_LIBFFI_FROM_MACOS && (defined(__arm__) || defined(__arm64__))) +#ifdef __NetBSD__ # define CFFI_TRUST_LIBFFI #endif diff --git a/setup.py b/setup.py index 217504a..fc8d705 100644 --- a/setup.py +++ b/setup.py @@ -152,7 +152,7 @@ else: if 'darwin' in sys.platform and macosx_deployment_target() >= (10, 15): # use libffi from Mac OS SDK extra_compile_args += ['-iwithsysroot/usr/include/ffi'] - define_macros += [('CFFI_LIBFFI_FROM_MACOS', '1'), + define_macros += [('CFFI_TRUST_LIBFFI', '1'), ('HAVE_FFI_PREP_CIF_VAR', '1')] libraries += ['ffi'] else: -- cgit v1.2.1 From 56207d5c8b9dd155bb08522e397a42224caa1fea Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Sat, 11 Jul 2020 14:01:18 +0200 Subject: add comment --- setup.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index fc8d705..8216851 100644 --- a/setup.py +++ b/setup.py @@ -150,7 +150,10 @@ if COMPILE_LIBFFI: for filename in _filenames) else: if 'darwin' in sys.platform and macosx_deployment_target() >= (10, 15): - # use libffi from Mac OS SDK + # use libffi from Mac OS SDK if we're targetting 10.15 (including + # on arm64). This libffi is safe against the crash-after-fork + # issue described in _cffi_backend.c. Also, arm64 uses a different + # ABI for calls to vararg functions as opposed to regular functions. extra_compile_args += ['-iwithsysroot/usr/include/ffi'] define_macros += [('CFFI_TRUST_LIBFFI', '1'), ('HAVE_FFI_PREP_CIF_VAR', '1')] -- cgit v1.2.1 From c1424db82d1fd5438706601fd46676814377d1fb Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Fri, 17 Jul 2020 09:47:54 +0200 Subject: Revert 094d9124341e: not all libffi versions in use have a ffi_prep_closure_loc() symbol. --- c/_cffi_backend.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index 474ee06..32c8708 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -6284,17 +6284,21 @@ static PyObject *b_callback(PyObject *self, PyObject *args) "return type or with '...'", ct->ct_name); goto error; } - /* NOTE: if we don't have CFFI_TRUST_LIBFFI, then this will invoke - * ffi_prep_closure_loc() with the last argument being equal to the - * first one. With the current versions of libffi (June 2020), then - * this is just what occurs inside ffi anyway by calling - * ffi_prep_closure()---but this latter function is now deprecated. - * Please be aware that although this calls ffi_prep_closure_loc(), - * it is NOT always safe against read-write-execute memory attacks, - * as described in the lengthy comment before CFFI_TRUST_LIBFFI. + /* NOTE: ffi_prep_closure() is marked as deprecated. We could just + * call ffi_prep_closure_loc() instead, which is what ffi_prep_closure() + * does. However, cffi also runs on older systems with a libffi that + * doesn't have ffi_prep_closure_loc() at all---notably, the OS X + * machines on Azure are like that (June 2020). I didn't find a way to + * version-check the included ffi.h. So you will have to live with the + * deprecation warning for now. */ +#ifdef CFFI_TRUST_LIBFFI if (ffi_prep_closure_loc(closure, &cif_descr->cif, invoke_callback, infotuple, closure_exec) != FFI_OK) { +#else + if (ffi_prep_closure(closure, &cif_descr->cif, + invoke_callback, infotuple) != FFI_OK) { +#endif PyErr_SetString(PyExc_SystemError, "libffi failed to build this callback"); goto error; -- cgit v1.2.1 From 536ff3c1f67090c0cedd19b7a3e9a731634b1af3 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Fri, 17 Jul 2020 09:52:28 +0200 Subject: Windows fix --- c/_cffi_backend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index 32c8708..8e76880 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -5820,6 +5820,7 @@ static cif_description_t *fb_prepare_cif(PyObject *fargs, char *buffer; cif_description_t *cif_descr; struct funcbuilder_s funcbuffer; + ffi_status status; funcbuffer.nb_bytes = 0; funcbuffer.bufferp = NULL; @@ -5842,7 +5843,6 @@ static cif_description_t *fb_prepare_cif(PyObject *fargs, assert(funcbuffer.bufferp == buffer + funcbuffer.nb_bytes); cif_descr = (cif_description_t *)buffer; - ffi_status status; #if HAVE_FFI_PREP_CIF_VAR if (variadic_nargs_declared >= 0) { status = ffi_prep_cif_var(&cif_descr->cif, fabi, -- cgit v1.2.1 From d4305759c2355db4c450d58ca2c236d879fa4790 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Fri, 17 Jul 2020 10:24:27 +0200 Subject: hack around until I make recent pytest versions happy --- c/test_c.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/c/test_c.py b/c/test_c.py index fa20517..383bca2 100644 --- a/c/test_c.py +++ b/c/test_c.py @@ -4,12 +4,17 @@ import pytest def _setup_path(): import os, sys if '__pypy__' in sys.builtin_module_names: - py.test.skip("_cffi_backend.c: not tested on top of pypy, " - "use pypy/module/_cffi_backend/test/ instead.") + global pytestmark + pytestmark = pytest.mark.skip( + "_cffi_backend.c: not tested on top of pypy, " + "use pypy/module/_cffi_backend/test/ instead.") + return False sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) -_setup_path() + return True +if _setup_path(): + from _cffi_backend import _testfunc, _get_types, _get_common_types from _cffi_backend import * -from _cffi_backend import _testfunc, _get_types, _get_common_types, __version__ +from _cffi_backend import __version__ # ____________________________________________________________ -- cgit v1.2.1 From c421c605a3fd6dd2d96f5969d4ca368b4fd12b20 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Fri, 17 Jul 2020 10:38:07 +0200 Subject: Comment more --- c/_cffi_backend.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index 8e76880..e6adf3c 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -6290,7 +6290,10 @@ static PyObject *b_callback(PyObject *self, PyObject *args) * doesn't have ffi_prep_closure_loc() at all---notably, the OS X * machines on Azure are like that (June 2020). I didn't find a way to * version-check the included ffi.h. So you will have to live with the - * deprecation warning for now. + * deprecation warning for now. (We could try to check for an unrelated + * macro like FFI_API which happens to be defined in libffi 3.3 and not + * before, but that sounds very obscure. And I prefer a compile-time + * warning rather than a cross-version binary compatibility problem.) */ #ifdef CFFI_TRUST_LIBFFI if (ffi_prep_closure_loc(closure, &cif_descr->cif, -- cgit v1.2.1 From ac54caf4440e0a09ce1aa7935bee71512c7ca329 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Sat, 18 Jul 2020 23:56:43 +0200 Subject: Extra tests for using a 3-bytes struct as a return type --- testing/cffi0/test_function.py | 17 +++++++++++++++++ testing/cffi0/test_ownlib.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/testing/cffi0/test_function.py b/testing/cffi0/test_function.py index 6312707..4f2c864 100644 --- a/testing/cffi0/test_function.py +++ b/testing/cffi0/test_function.py @@ -240,6 +240,23 @@ class TestFunction(object): 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(""" diff --git a/testing/cffi0/test_ownlib.py b/testing/cffi0/test_ownlib.py index 990f259..6a69cf6 100644 --- a/testing/cffi0/test_ownlib.py +++ b/testing/cffi0/test_ownlib.py @@ -35,6 +35,10 @@ typedef struct { long bottom; } RECT; +typedef struct { + unsigned char a, b, c; +} THREEBYTES; + EXPORT int PointInRect(RECT *prc, POINT pt) { @@ -107,6 +111,15 @@ 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): @@ -397,3 +410,20 @@ class TestOwnLib(object): 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") + 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 -- cgit v1.2.1 From 16e5c49fcccfeb92a132e9bf9144a23cdf9e5594 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Sun, 19 Jul 2020 00:18:09 +0200 Subject: tentative windows fix for 3ea8b17d97f0 --- c/libffi_msvc/ffi.c | 10 ++++++---- c/libffi_msvc/prep_cif.c | 5 ++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/c/libffi_msvc/ffi.c b/c/libffi_msvc/ffi.c index 836f171..edae9e5 100644 --- a/c/libffi_msvc/ffi.c +++ b/c/libffi_msvc/ffi.c @@ -103,7 +103,7 @@ void ffi_prep_args(char *stack, extended_cif *ecif) } } #ifdef _WIN64 - else if (z > 8) + else if (z != 1 && z != 2 && z != 4 && z != 8) { /* On Win64, if a single argument takes more than 8 bytes, then it is always passed by reference. */ @@ -144,9 +144,11 @@ ffi_status ffi_prep_cif_machdep(ffi_cif *cif) /* MSVC returns small structures in registers. Put in cif->flags the value FFI_TYPE_STRUCT only if the structure is big enough; otherwise, put the 4- or 8-bytes integer type. */ - if (cif->rtype->size <= 4) + if (cif->rtype->size == 1 || + cif->rtype->size == 2 || + cif->rtype->size == 4) cif->flags = FFI_TYPE_INT; - else if (cif->rtype->size <= 8) + else if (cif->rtype->size == 8) cif->flags = FFI_TYPE_SINT64; else cif->flags = FFI_TYPE_STRUCT; @@ -378,7 +380,7 @@ ffi_prep_incoming_args_SYSV(char *stack, void **rvalue, /* because we're little endian, this is what it turns into. */ #ifdef _WIN64 - if (z > 8) + if (z != 1 && z != 2 && z != 4 && z != 8) { /* On Win64, if a single argument takes more than 8 bytes, then it is always passed by reference. */ diff --git a/c/libffi_msvc/prep_cif.c b/c/libffi_msvc/prep_cif.c index 5dacfff..df94a98 100644 --- a/c/libffi_msvc/prep_cif.c +++ b/c/libffi_msvc/prep_cif.c @@ -117,7 +117,10 @@ ffi_status ffi_prep_cif(/*@out@*/ /*@partial@*/ ffi_cif *cif, /* Make space for the return structure pointer */ if (cif->rtype->type == FFI_TYPE_STRUCT #ifdef _WIN32 - && (cif->rtype->size > 8) /* MSVC returns small structs in registers */ + && (cif->rtype->size != 1) /* MSVC returns small structs in registers */ + && (cif->rtype->size != 2) + && (cif->rtype->size != 4) + && (cif->rtype->size != 8) #endif #ifdef SPARC && (cif->abi != FFI_V9 || cif->rtype->size > 32) -- cgit v1.2.1 From 63b741f4e24663796b730cbab6a7b25dc6ece004 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Sun, 19 Jul 2020 10:18:24 +0200 Subject: ctypes on windows doesn't correctly return 3-bytes structs --- testing/cffi0/test_ownlib.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testing/cffi0/test_ownlib.py b/testing/cffi0/test_ownlib.py index 6a69cf6..ffad879 100644 --- a/testing/cffi0/test_ownlib.py +++ b/testing/cffi0/test_ownlib.py @@ -414,6 +414,8 @@ class TestOwnLib(object): 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 { -- cgit v1.2.1 From 663ed8ddd7744cd68077141f9b7b0b910c82c09b Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Sun, 19 Jul 2020 11:35:02 +0200 Subject: Add a hypothesis test. Mostly checks libffi, or our own libffi_msvc on windows --- testing/cffi0/test_function_args.py | 89 +++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 testing/cffi0/test_function_args.py diff --git a/testing/cffi0/test_function_args.py b/testing/cffi0/test_function_args.py new file mode 100644 index 0000000..ed94c97 --- /dev/null +++ b/testing/cffi0/test_function_args.py @@ -0,0 +1,89 @@ +import pytest +try: + from hypothesis import given + from hypothesis import strategies as st +except ImportError as e: + def test_types(): + pytest.skip(str(e)) +else: + + from cffi import FFI + import random + + ALL_PRIMITIVES = [ + 'unsigned char', + 'short', + 'int', + 'long', + 'long long', + 'float', + 'double', + 'long double', + ] + def _make_struct(s): + return st.lists(s, min_size=1) + types = st.recursive(st.sampled_from(ALL_PRIMITIVES), _make_struct) + + def draw_primitive(ffi, typename): + value = random.random() * 2**40 + if typename != 'long double': + return ffi.cast(typename, value) + else: + return value + + + @given(st.lists(types), types) + def test_types(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) + + ffi = FFI() + ffi.cdef("\n".join(cdefs)) + + 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) + received_arguments = [] + + _tp_long_double = ffi.typeof("long double") + 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 + + def callback(*args): + received_arguments.append([expand(arg) for arg in args]) + return returned_value + + fptr = ffi.callback("%s(*)(%s)" % (result, ','.join(args)), callback) + received_return = fptr(*passed_args) + + assert len(received_arguments) == 1 + assert passed_args == received_arguments[0] + assert expand(received_return) == returned_value -- cgit v1.2.1 From e4bba72b9a9aa979f2111bd5b171d6afc4135daa Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Sun, 19 Jul 2020 12:19:12 +0200 Subject: Check with the real C compiler --- testing/cffi0/test_function_args.py | 89 -------------------------- testing/cffi1/test_function_args.py | 124 ++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 89 deletions(-) delete mode 100644 testing/cffi0/test_function_args.py create mode 100644 testing/cffi1/test_function_args.py diff --git a/testing/cffi0/test_function_args.py b/testing/cffi0/test_function_args.py deleted file mode 100644 index ed94c97..0000000 --- a/testing/cffi0/test_function_args.py +++ /dev/null @@ -1,89 +0,0 @@ -import pytest -try: - from hypothesis import given - from hypothesis import strategies as st -except ImportError as e: - def test_types(): - pytest.skip(str(e)) -else: - - from cffi import FFI - import random - - ALL_PRIMITIVES = [ - 'unsigned char', - 'short', - 'int', - 'long', - 'long long', - 'float', - 'double', - 'long double', - ] - def _make_struct(s): - return st.lists(s, min_size=1) - types = st.recursive(st.sampled_from(ALL_PRIMITIVES), _make_struct) - - def draw_primitive(ffi, typename): - value = random.random() * 2**40 - if typename != 'long double': - return ffi.cast(typename, value) - else: - return value - - - @given(st.lists(types), types) - def test_types(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) - - ffi = FFI() - ffi.cdef("\n".join(cdefs)) - - 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) - received_arguments = [] - - _tp_long_double = ffi.typeof("long double") - 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 - - def callback(*args): - received_arguments.append([expand(arg) for arg in args]) - return returned_value - - fptr = ffi.callback("%s(*)(%s)" % (result, ','.join(args)), callback) - received_return = fptr(*passed_args) - - assert len(received_arguments) == 1 - assert passed_args == received_arguments[0] - assert expand(received_return) == returned_value diff --git a/testing/cffi1/test_function_args.py b/testing/cffi1/test_function_args.py new file mode 100644 index 0000000..d0eb154 --- /dev/null +++ b/testing/cffi1/test_function_args.py @@ -0,0 +1,124 @@ +import pytest +try: + from hypothesis import given, settings + from hypothesis import strategies as st +except ImportError as e: + def test_types(): + pytest.skip(str(e)) +else: + + from cffi import FFI + import random + from .test_recompiler import verify + + ALL_PRIMITIVES = [ + 'unsigned char', + 'short', + 'int', + 'long', + 'long long', + 'float', + 'double', + 'long double', + ] + def _make_struct(s): + return st.lists(s, min_size=1) + types = st.recursive(st.sampled_from(ALL_PRIMITIVES), _make_struct) + + 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=10) + def test_types(tp_args, tp_result): + global TEST_RUN_COUNTER + 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("}") + + ffi = FFI() + ffi.cdef("\n".join(cdefs)) + lib = verify(ffi, 'test_function_args_%d' % TEST_RUN_COUNTER, + "\n".join(source), no_cpp=True) + + 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 + received_return = 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) -- cgit v1.2.1 From cb9ed25dea2911d7eabd435a2f8499c8932a8e58 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Sun, 19 Jul 2020 12:35:40 +0200 Subject: force calls via libffi. shows the problem recently solved on windows --- testing/cffi1/test_function_args.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/testing/cffi1/test_function_args.py b/testing/cffi1/test_function_args.py index d0eb154..56234f2 100644 --- a/testing/cffi1/test_function_args.py +++ b/testing/cffi1/test_function_args.py @@ -23,7 +23,10 @@ else: ] def _make_struct(s): return st.lists(s, min_size=1) - types = st.recursive(st.sampled_from(ALL_PRIMITIVES), _make_struct) + 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 @@ -36,7 +39,7 @@ else: @given(st.lists(types), types) - @settings(max_examples=10) + @settings(max_examples=20) def test_types(tp_args, tp_result): global TEST_RUN_COUNTER cdefs = [] @@ -102,8 +105,8 @@ else: write(ffi.addressof(lib, 'testfargs_result'), returned_value) - ## CALL - received_return = lib.testfargs(*passed_args) + ## CALL forcing libffi + received_return = ffi.addressof(lib, 'testfargs')(*passed_args) ## _tp_long_double = ffi.typeof("long double") -- cgit v1.2.1 From 72606f799f833cc668cf66c1c2f8c22877292187 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Sun, 19 Jul 2020 13:26:23 +0200 Subject: Test the callbacks, too --- testing/cffi1/test_function_args.py | 43 +++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/testing/cffi1/test_function_args.py b/testing/cffi1/test_function_args.py index 56234f2..6fe88b7 100644 --- a/testing/cffi1/test_function_args.py +++ b/testing/cffi1/test_function_args.py @@ -8,7 +8,7 @@ except ImportError as e: else: from cffi import FFI - import random + import sys, random from .test_recompiler import verify ALL_PRIMITIVES = [ @@ -19,7 +19,7 @@ else: 'long long', 'float', 'double', - 'long double', + #'long double', --- on x86 it can give libffi crashes ] def _make_struct(s): return st.lists(s, min_size=1) @@ -87,6 +87,22 @@ else: 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('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() + 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]] @@ -122,6 +138,29 @@ else: 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 tested assuming the CALL worked correctly + 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 + + 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) + received_return = fptr(*passed_args) + assert len(seen_args) == 1 + assert passed_args == seen_args[0] ret = ffi.new(result + "*", received_return) check(ret, returned_value) -- cgit v1.2.1 From 55b802b60822fad44a98733359815f98a3e79ecd Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Sun, 19 Jul 2020 13:39:13 +0200 Subject: Keep sys.path, otherwise it's testing another version of _cffi_backend.so --- testing/cffi1/test_function_args.py | 1 + 1 file changed, 1 insertion(+) diff --git a/testing/cffi1/test_function_args.py b/testing/cffi1/test_function_args.py index 6fe88b7..28259f6 100644 --- a/testing/cffi1/test_function_args.py +++ b/testing/cffi1/test_function_args.py @@ -92,6 +92,7 @@ else: 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)): -- cgit v1.2.1 From 4a84c1ce423b43d77df51fb465983c8b0daee359 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Tue, 21 Jul 2020 18:03:35 +0200 Subject: A new set of segfaults on Windows using callbacks --- testing/cffi1/test_function_args.py | 39 +++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/testing/cffi1/test_function_args.py b/testing/cffi1/test_function_args.py index 28259f6..6a3aaec 100644 --- a/testing/cffi1/test_function_args.py +++ b/testing/cffi1/test_function_args.py @@ -42,6 +42,7 @@ else: @settings(max_examples=20) def test_types(tp_args, tp_result): global TEST_RUN_COUNTER + print(tp_args, tp_result) cdefs = [] structs = {} @@ -82,6 +83,21 @@ else: source.append(" return testfargs_result;") source.append("}") + typedef_line = "typedef %s;" % (signature.replace('testfargs', + '(*mycallback_t)'),) + assert signature.endswith(')') + sig_callback = "%s testfcallback(%s)" % (result, + ', '.join(['mycallback_t callback'] + + ['%s a%d' % (arg, i) for (i, arg) in enumerate(args)])) + 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(["a%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, @@ -142,7 +158,7 @@ else: ret = ffi.new(result + "*", received_return) check(ret, returned_value) - ## CALLBACK tested assuming the CALL worked correctly + ## CALLBACK def expand(value): if isinstance(value, ffi.CData): t = ffi.typeof(value) @@ -153,13 +169,32 @@ else: 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) + 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('def callback(*args): return ffi.new("%s *")[0]\n' % result) + f.write('fptr = ffi.callback("%s(%s)", callback)\n' % (result, + ','.join(args))) + f.write('lib.testfcallback(fptr, %s)\n' % aliststr) + f.close() + 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) - received_return = fptr(*passed_args) + received_return = lib.testfcallback(fptr, *passed_args) assert len(seen_args) == 1 assert passed_args == seen_args[0] -- cgit v1.2.1 From cbf6917094a3f1f5aa84a580e5756857059c6d2e Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Tue, 21 Jul 2020 18:13:48 +0200 Subject: pff, it seems that "running for more than 0.2 seconds" is also a failure condition, which hypothesis then tries to reduce, giving not reasonable answer because here one test run of any size takes around 0.2 seconds --- testing/cffi1/test_function_args.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testing/cffi1/test_function_args.py b/testing/cffi1/test_function_args.py index 6a3aaec..52e58a7 100644 --- a/testing/cffi1/test_function_args.py +++ b/testing/cffi1/test_function_args.py @@ -1,6 +1,6 @@ import pytest try: - from hypothesis import given, settings + from hypothesis import given, settings, example from hypothesis import strategies as st except ImportError as e: def test_types(): @@ -39,7 +39,7 @@ else: @given(st.lists(types), types) - @settings(max_examples=20) + @settings(max_examples=20, deadline=5000) # 5000ms def test_types(tp_args, tp_result): global TEST_RUN_COUNTER print(tp_args, tp_result) -- cgit v1.2.1 From 30a683e4aa0ae6428755872293c03552088ac74d Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Tue, 21 Jul 2020 19:00:02 +0200 Subject: Skip this test by default. It keeps finding corner cases that are bugs of libffi but not cffi --- testing/cffi1/test_function_args.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/testing/cffi1/test_function_args.py b/testing/cffi1/test_function_args.py index 52e58a7..5703e5f 100644 --- a/testing/cffi1/test_function_args.py +++ b/testing/cffi1/test_function_args.py @@ -1,5 +1,10 @@ import pytest try: + # comment out the following line to run this test. + # the latest on x86-64 linux: https://github.com/libffi/libffi/issues/574 + 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: @@ -39,7 +44,7 @@ else: @given(st.lists(types), types) - @settings(max_examples=20, deadline=5000) # 5000ms + @settings(max_examples=100, deadline=5000) # 5000ms def test_types(tp_args, tp_result): global TEST_RUN_COUNTER print(tp_args, tp_result) @@ -86,16 +91,14 @@ else: typedef_line = "typedef %s;" % (signature.replace('testfargs', '(*mycallback_t)'),) assert signature.endswith(')') - sig_callback = "%s testfcallback(%s)" % (result, - ', '.join(['mycallback_t callback'] + - ['%s a%d' % (arg, i) for (i, arg) in enumerate(args)])) + 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(["a%d" % i for i in range(len(args))])) + ', '.join(["testfargs_arg%d" % i for i in range(len(args))])) source.append("}") ffi = FFI() @@ -194,7 +197,7 @@ else: return returned_value fptr = ffi.callback("%s(%s)" % (result, ','.join(args)), callback) - received_return = lib.testfcallback(fptr, *passed_args) + received_return = lib.testfcallback(fptr) assert len(seen_args) == 1 assert passed_args == seen_args[0] -- cgit v1.2.1 From 5e2c0f61537ee5cce787c0baceabc050917401da Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Tue, 21 Jul 2020 19:07:11 +0200 Subject: fix this logic --- testing/cffi1/test_function_args.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/testing/cffi1/test_function_args.py b/testing/cffi1/test_function_args.py index 5703e5f..84c47bb 100644 --- a/testing/cffi1/test_function_args.py +++ b/testing/cffi1/test_function_args.py @@ -180,13 +180,10 @@ else: 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('def callback(*args): return ffi.new("%s *")[0]\n' % result) f.write('fptr = ffi.callback("%s(%s)", callback)\n' % (result, ','.join(args))) - f.write('lib.testfcallback(fptr, %s)\n' % aliststr) + f.write('lib.testfcallback(fptr)\n') f.close() rc = subprocess.call([sys.executable, 'run1.py'], cwd=str(udir)) assert rc == 0, rc -- cgit v1.2.1 From 1a1e1e6ee5c4a7411a7ce8c319739ccab0eb4035 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Tue, 21 Jul 2020 19:54:52 +0200 Subject: #465 Callback returning non-small struct failed on Windows --- c/libffi_msvc/ffi.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/c/libffi_msvc/ffi.c b/c/libffi_msvc/ffi.c index edae9e5..dab2b50 100644 --- a/c/libffi_msvc/ffi.c +++ b/c/libffi_msvc/ffi.c @@ -289,16 +289,12 @@ ffi_closure_SYSV (ffi_closure *closure, char *argp) _asm fld DWORD PTR [eax] ; // asm ("flds (%0)" : : "r" (resp) : "st" ); } - else if (rtype == FFI_TYPE_DOUBLE) + else if (rtype == FFI_TYPE_DOUBLE || rtype == FFI_TYPE_LONGDOUBLE) { _asm mov eax, resp ; _asm fld QWORD PTR [eax] ; // asm ("fldl (%0)" : : "r" (resp) : "st", "st(1)" ); } - else if (rtype == FFI_TYPE_LONGDOUBLE) - { -// asm ("fldt (%0)" : : "r" (resp) : "st", "st(1)" ); - } else if (rtype == FFI_TYPE_SINT64) { _asm mov edx, resp ; @@ -309,6 +305,10 @@ ffi_closure_SYSV (ffi_closure *closure, char *argp) // : : "r"(resp) // : "eax", "edx"); } + else if (rtype == FFI_TYPE_STRUCT) + { + _asm mov eax, resp ; + } #else /* now, do a generic return based on the value of rtype */ if (rtype == FFI_TYPE_INT) @@ -319,14 +319,10 @@ ffi_closure_SYSV (ffi_closure *closure, char *argp) { asm ("flds (%0)" : : "r" (resp) : "st" ); } - else if (rtype == FFI_TYPE_DOUBLE) + else if (rtype == FFI_TYPE_DOUBLE || rtype == FFI_TYPE_LONGDOUBLE) { asm ("fldl (%0)" : : "r" (resp) : "st", "st(1)" ); } - else if (rtype == FFI_TYPE_LONGDOUBLE) - { - asm ("fldt (%0)" : : "r" (resp) : "st", "st(1)" ); - } else if (rtype == FFI_TYPE_SINT64) { asm ("movl 0(%0),%%eax;" @@ -334,6 +330,10 @@ ffi_closure_SYSV (ffi_closure *closure, char *argp) : : "r"(resp) : "eax", "edx"); } + else if (rtype == FFI_TYPE_STRUCT) + { + asm ("movl %0,%%eax" : : "r" (resp) : "eax"); + } #endif #endif -- cgit v1.2.1 From 8cc7ea55cdac358b4520d12e86c5882ba4c00824 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Tue, 21 Jul 2020 20:14:11 +0200 Subject: next fix, for win64 --- c/libffi_msvc/ffi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/c/libffi_msvc/ffi.c b/c/libffi_msvc/ffi.c index dab2b50..8a56c87 100644 --- a/c/libffi_msvc/ffi.c +++ b/c/libffi_msvc/ffi.c @@ -342,6 +342,8 @@ ffi_closure_SYSV (ffi_closure *closure, char *argp) result types except for floats; we have to 'mov xmm0, rax' in the caller to correct this. */ + if (rtype == FFI_TYPE_STRUCT) + return resp; return *(void **)resp; #endif } -- cgit v1.2.1 From 764eadc587f8ab2d67389bb1c1f850921b9f1bae Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Tue, 21 Jul 2020 20:25:56 +0200 Subject: more of the same, for win64 --- c/libffi_msvc/ffi.c | 5 +++++ testing/cffi1/test_function_args.py | 13 +++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/c/libffi_msvc/ffi.c b/c/libffi_msvc/ffi.c index 8a56c87..b9e324f 100644 --- a/c/libffi_msvc/ffi.c +++ b/c/libffi_msvc/ffi.c @@ -451,6 +451,11 @@ ffi_prep_closure_loc (ffi_closure* closure, || cif->arg_types[3]->type == FFI_TYPE_DOUBLE)) mask |= 8; + /* if we return a non-small struct, then the first argument is a pointer + * to the return area, and all real arguments are shifted by one */ + if (cif->flags == FFI_TYPE_STRUCT) + mask = (mask & ~8) << 1; + /* 41 BB ---- mov r11d,mask */ BYTES("\x41\xBB"); INT(mask); diff --git a/testing/cffi1/test_function_args.py b/testing/cffi1/test_function_args.py index 84c47bb..202015b 100644 --- a/testing/cffi1/test_function_args.py +++ b/testing/cffi1/test_function_args.py @@ -1,9 +1,10 @@ -import pytest +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 - raise ImportError("this test is skipped because it keeps finding " - "failures in libffi, instead of cffi") + 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 @@ -120,6 +121,7 @@ else: 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 @@ -142,6 +144,7 @@ else: write(ffi.addressof(lib, 'testfargs_result'), returned_value) ## CALL forcing libffi + print("CALL forcing libffi") received_return = ffi.addressof(lib, 'testfargs')(*passed_args) ## @@ -183,8 +186,9 @@ else: 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('lib.testfcallback(fptr)\n') + 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 @@ -194,6 +198,7 @@ else: 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 -- cgit v1.2.1 From 102d52ff9669b55dfa12fff2563483948065fa3b Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Fri, 24 Jul 2020 12:56:14 +0200 Subject: Allow ffi.embedding_init_code("unicode-with-non-ascii-chars") --- cffi/recompiler.py | 18 ++++++++++++++++-- testing/embedding/test_basic.py | 6 ++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/cffi/recompiler.py b/cffi/recompiler.py index d66ff7f..1309572 100644 --- a/cffi/recompiler.py +++ b/cffi/recompiler.py @@ -1296,14 +1296,28 @@ class Recompiler: 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): - prnt(('// ' + line).rstrip()) + 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,' % (ord(c),) + printed_line += '%d,' % (c,) prnt(printed_line) # ---------- diff --git a/testing/embedding/test_basic.py b/testing/embedding/test_basic.py index 894ace5..8d2e776 100644 --- a/testing/embedding/test_basic.py +++ b/testing/embedding/test_basic.py @@ -206,3 +206,9 @@ class TestBasic(EmbeddingTests): 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" -- cgit v1.2.1 From 920a379d9ad7be9106947f58f248c8fcd50ae9a7 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Fri, 24 Jul 2020 13:54:05 +0200 Subject: pff I love Python 3 --- testing/cffi1/test_function_args.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testing/cffi1/test_function_args.py b/testing/cffi1/test_function_args.py index 202015b..30c6fed 100644 --- a/testing/cffi1/test_function_args.py +++ b/testing/cffi1/test_function_args.py @@ -9,8 +9,9 @@ try: from hypothesis import given, settings, example from hypothesis import strategies as st except ImportError as e: + e1 = e def test_types(): - pytest.skip(str(e)) + pytest.skip(str(e1)) else: from cffi import FFI -- cgit v1.2.1 From beb36c88405b9d44c4cf5a0f869e1ac03052e111 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Fri, 24 Jul 2020 14:00:56 +0200 Subject: forgot this file --- testing/embedding/withunicode.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 testing/embedding/withunicode.py diff --git a/testing/embedding/withunicode.py b/testing/embedding/withunicode.py new file mode 100644 index 0000000..839c6cd --- /dev/null +++ b/testing/embedding/withunicode.py @@ -0,0 +1,26 @@ +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,)) -- cgit v1.2.1 From 85d1d8120ce6e38daaff6b58dc68476c5709f0ca Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Fri, 24 Jul 2020 14:54:55 +0200 Subject: whatsnew: v1.14.1 --- doc/source/whatsnew.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst index 674ad22..4b5199b 100644 --- a/doc/source/whatsnew.rst +++ b/doc/source/whatsnew.rst @@ -3,6 +3,25 @@ What's New ====================== +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 ===== -- cgit v1.2.1